Browse Source

Major footer/navbar changes on mobile

pull/317/head
Aigars Silkalns 6 months ago
parent
commit
c5dad4a92e
5 changed files with 1844 additions and 117 deletions
  1. +378
    -114
      src/assets/scripts/app.js
  2. +113
    -0
      src/assets/styles/index.scss
  3. +158
    -3
      src/assets/styles/spec/components/pageContainer.scss
  4. +241
    -0
      src/assets/styles/spec/components/topbar.scss
  5. +954
    -0
      src/assets/styles/utils/mobile.scss

+ 378
- 114
src/assets/scripts/app.js View File

@ -1,6 +1,6 @@
/**
* Modern Adminator Application
* Main application entry point - replaces jQuery-based initialization
* Main application entry point with enhanced mobile support
*/
import bootstrap from 'bootstrap';
@ -24,7 +24,6 @@ import './vectorMaps';
import './chat';
import './email';
import './googleMaps';
import './utils';
class AdminatorApp {
constructor() {
@ -43,7 +42,7 @@ class AdminatorApp {
init() {
if (this.isInitialized) return;
console.log('🚀 Initializing Adminator App (Modern Version)');
console.log('🚀 Initializing Adminator App (Mobile Optimized)');
try {
// Initialize core components
@ -52,6 +51,7 @@ class AdminatorApp {
this.initDataTables();
this.initDatePickers();
this.initTheme();
this.initMobileEnhancements();
// Setup global event listeners
this.setupGlobalEvents();
@ -201,97 +201,107 @@ class AdminatorApp {
try {
picker.showPicker();
} catch (e) {
// Fallback for browsers that don't support showPicker
console.log('📅 Date picker opened via icon click');
}
}
});
// Also make the entire icon container clickable
const iconContainer = inputGroup.querySelector('.input-group-text');
if (iconContainer) {
iconContainer.style.cursor = 'pointer';
DOM.on(iconContainer, 'click', (event) => {
event.preventDefault();
event.stopPropagation();
picker.focus();
if (picker.showPicker && typeof picker.showPicker === 'function') {
try {
picker.showPicker();
} catch (e) {
console.log('📅 Date picker opened via icon container click');
}
}
});
}
}
}
});
// Add date validation
[...startDatePickers, ...endDatePickers].forEach(picker => {
DOM.on(picker, 'change', (event) => {
const isValid = DateUtils.form.validateDateInput(event.target.value);
if (!isValid) {
event.target.classList.add('is-invalid');
console.warn('Invalid date format:', event.target.value);
} else {
event.target.classList.remove('is-invalid');
console.log('Valid date selected:', DateUtils.formatters.shortDate(event.target.value));
}
});
// Add focus/blur handlers for better UX
DOM.on(picker, 'focus', () => {
picker.classList.add('focus');
});
DOM.on(picker, 'blur', () => {
picker.classList.remove('focus');
});
});
}
/**
* Initialize theme
* Initialize theme system with toggle
*/
initTheme() {
console.log('🌙 Initializing theme system');
// Initialize theme system first
Theme.init();
// inject toggle switch if missing
const navRight = document.querySelector('.nav-right');
if (navRight && !document.getElementById('theme-toggle')) {
// Inject theme toggle if missing - with retry mechanism
setTimeout(() => {
const navRight = DOM.select('.nav-right');
console.log('🔍 nav-right found:', !!navRight);
console.log('🔍 navRight element:', navRight);
console.log('🔍 theme-toggle exists:', DOM.exists('#theme-toggle'));
console.log('🔍 All nav-right li elements:', navRight ? navRight.querySelectorAll('li').length : 0);
// Debug the DOM structure
if (navRight) {
console.log('🔍 navRight children:', Array.from(navRight.children).map(child => ({
tagName: child.tagName,
className: child.className,
id: child.id
})));
}
if (navRight && !DOM.exists('#theme-toggle')) {
const li = document.createElement('li');
li.className = 'theme-toggle d-flex ai-c';
li.innerHTML = `
<div class="form-check form-switch d-flex ai-c" style="margin: 0; padding: 0;">
<label class="form-check-label me-2 text-nowrap c-grey-700" for="theme-toggle" style="font-size: 12px; margin-right: 8px;">
<i class="ti-sun" style="margin-right: 4px;"></i>Light
<i class="ti-sun" style="margin-right: 4px;"></i><span class="theme-label">Light</span>
</label>
<input class="form-check-input" type="checkbox" id="theme-toggle" style="margin: 0;">
<label class="form-check-label ms-2 text-nowrap c-grey-700" for="theme-toggle" style="font-size: 12px; margin-left: 8px;">
Dark<i class="ti-moon" style="margin-left: 4px;"></i>
<span class="theme-label">Dark</span><i class="ti-moon" style="margin-left: 4px;"></i>
</label>
</div>
`;
navRight.insertBefore(li, navRight.firstElementChild);
const toggleInput = li.querySelector('#theme-toggle');
const updateSwitch = () => {
toggleInput.checked = Theme.current() === 'dark';
};
updateSwitch();
toggleInput.addEventListener('change', (e) => {
Theme.apply(e.target.checked ? 'dark' : 'light');
});
// Insert before user dropdown (last item) - safer approach
const lastItem = navRight.querySelector('li:last-child');
console.log('🔍 lastItem found:', !!lastItem);
console.log('🔍 lastItem parent:', lastItem ? lastItem.parentNode : null);
console.log('🔍 navRight:', navRight);
if (lastItem && lastItem.parentNode === navRight) {
navRight.insertBefore(li, lastItem);
console.log('✅ Theme toggle inserted before last item');
} else {
navRight.appendChild(li);
console.log('✅ Theme toggle appended to nav-right (safer approach)');
}
// Add toggle functionality
const toggle = DOM.select('#theme-toggle');
if (toggle) {
// Set initial state
const currentTheme = Theme.current();
toggle.checked = currentTheme === 'dark';
DOM.on(toggle, 'change', () => {
Theme.apply(toggle.checked ? 'dark' : 'light');
});
// Listen for theme changes from other sources
window.addEventListener('adminator:themeChanged', (event) => {
toggle.checked = event.detail.theme === 'dark';
// Update charts when theme changes
const charts = this.components.get('charts');
if (charts) charts.redrawCharts();
});
}
} else {
console.log('❌ No nav-right found or theme-toggle already exists');
}
}, 100); // Wait 100ms for DOM to be fully ready
}
window.addEventListener('adminator:themeChanged', () => {
updateSwitch();
const charts = this.components.get('charts');
if (charts) charts.redrawCharts();
});
/**
* Initialize mobile-specific enhancements
*/
initMobileEnhancements() {
console.log('📱 Initializing mobile enhancements');
this.enhanceMobileDropdowns();
this.enhanceMobileSearch();
// Prevent horizontal scroll on mobile
if (this.isMobile()) {
document.body.style.overflowX = 'hidden';
}
}
@ -299,55 +309,312 @@ class AdminatorApp {
* Setup global event listeners
*/
setupGlobalEvents() {
// Global resize handler
let resizeTimer;
window.addEventListener('resize', () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
this.handleResize();
}, 150);
});
// Global click handler for dynamic content
document.addEventListener('click', (e) => {
this.handleGlobalClick(e);
// Global click handler
DOM.on(document, 'click', (event) => this.handleGlobalClick(event));
// Window resize handler with debouncing
let resizeTimeout;
DOM.on(window, 'resize', () => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => this.handleResize(), 250);
});
// Custom event for masonry recalculation
window.EVENT = new Event('resize');
console.log('🌐 Global event listeners set up');
}
/**
* Handle window resize
* Handle window resize events
*/
handleResize() {
// Notify charts to resize
const charts = this.components.get('charts');
if (charts) {
charts.redrawCharts();
console.log('📐 Window resized, updating mobile features');
// Close all mobile-specific overlays when switching to desktop
if (!this.isMobile()) {
document.body.style.overflow = '';
document.body.style.overflowX = '';
// Close dropdowns
const dropdowns = DOM.selectAll('.nav-right .dropdown');
dropdowns.forEach(dropdown => {
dropdown.classList.remove('show');
const menu = dropdown.querySelector('.dropdown-menu');
if (menu) menu.classList.remove('show');
});
// Close search
const searchBox = DOM.select('.search-box');
const searchInput = DOM.select('.search-input');
if (searchBox && searchInput) {
searchBox.classList.remove('active');
searchInput.classList.remove('active');
}
} else {
// Re-enable mobile overflow protection
document.body.style.overflowX = 'hidden';
}
// Dispatch resize event for other components
window.dispatchEvent(new CustomEvent('adminator:resize'));
// Re-apply mobile enhancements
this.enhanceMobileDropdowns();
this.enhanceMobileSearch();
}
/**
* Handle global clicks
* Handle global click events
*/
handleGlobalClick(event) {
// Handle any global click events here
// This can be used for analytics, debugging, etc.
// Close mobile dropdowns when clicking outside
if (!event.target.closest('.dropdown')) {
const dropdowns = DOM.selectAll('.nav-right .dropdown');
dropdowns.forEach(dropdown => {
dropdown.classList.remove('show');
const menu = dropdown.querySelector('.dropdown-menu');
if (menu) menu.classList.remove('show');
});
document.body.style.overflow = '';
}
// Close search when clicking outside
if (!event.target.closest('.search-box') && !event.target.closest('.search-input')) {
const searchBox = DOM.select('.search-box');
const searchInput = DOM.select('.search-input');
if (searchBox && searchInput) {
searchBox.classList.remove('active');
searchInput.classList.remove('active');
document.body.style.overflow = '';
document.body.classList.remove('mobile-menu-open');
}
}
}
/**
* Check if we're on a mobile device
*/
isMobile() {
return window.innerWidth <= 768;
}
/**
* Get component instance
* Enhanced mobile dropdown handling with improved email layout
*/
enhanceMobileDropdowns() {
if (!this.isMobile()) return;
const dropdowns = DOM.selectAll('.nav-right .dropdown');
dropdowns.forEach(dropdown => {
const toggle = dropdown.querySelector('.dropdown-toggle');
const menu = dropdown.querySelector('.dropdown-menu');
if (toggle && menu) {
// Remove existing listeners to prevent duplicates
const newToggle = toggle.cloneNode(true);
toggle.replaceWith(newToggle);
// Add click functionality for mobile dropdowns
DOM.on(newToggle, 'click', (e) => {
e.preventDefault();
e.stopPropagation();
// Close search if open
const searchBox = DOM.select('.search-box');
const searchInput = DOM.select('.search-input');
if (searchBox && searchInput) {
searchBox.classList.remove('active');
searchInput.classList.remove('active');
}
// Close other dropdowns first
dropdowns.forEach(otherDropdown => {
if (otherDropdown !== dropdown) {
otherDropdown.classList.remove('show');
const otherMenu = otherDropdown.querySelector('.dropdown-menu');
if (otherMenu) otherMenu.classList.remove('show');
}
});
// Toggle current dropdown
const isOpen = dropdown.classList.contains('show');
if (isOpen) {
dropdown.classList.remove('show');
menu.classList.remove('show');
document.body.style.overflow = '';
document.body.classList.remove('mobile-menu-open');
} else {
dropdown.classList.add('show');
menu.classList.add('show');
document.body.style.overflow = 'hidden';
document.body.classList.add('mobile-menu-open');
}
});
// Enhanced mobile close button functionality
DOM.on(menu, 'click', (e) => {
// Check if clicked on the close area (::before pseudo-element area)
const rect = menu.getBoundingClientRect();
const clickY = e.clientY - rect.top;
// If clicked in top 50px (close button area)
if (clickY <= 50) {
dropdown.classList.remove('show');
menu.classList.remove('show');
document.body.style.overflow = '';
document.body.classList.remove('mobile-menu-open');
e.preventDefault();
e.stopPropagation();
}
});
}
});
// Close dropdowns on escape key
DOM.on(document, 'keydown', (e) => {
if (e.key === 'Escape') {
dropdowns.forEach(dropdown => {
dropdown.classList.remove('show');
const menu = dropdown.querySelector('.dropdown-menu');
if (menu) menu.classList.remove('show');
});
document.body.style.overflow = '';
document.body.classList.remove('mobile-menu-open');
}
});
}
/**
* Enhanced mobile search handling - Full-width search bar
*/
enhanceMobileSearch() {
const searchBox = DOM.select('.search-box');
const searchInput = DOM.select('.search-input');
if (searchBox && searchInput) {
const searchToggle = searchBox.querySelector('a');
const searchField = searchInput.querySelector('input');
if (searchToggle && searchField) {
console.log('🔍 Setting up full-width search functionality');
// Remove existing listeners to prevent duplication
const newSearchToggle = searchToggle.cloneNode(true);
searchToggle.replaceWith(newSearchToggle);
DOM.on(newSearchToggle, 'click', (e) => {
e.preventDefault();
e.stopPropagation();
console.log('🔍 Full-width search toggle clicked');
// Close any open dropdowns first
const dropdowns = DOM.selectAll('.nav-right .dropdown');
dropdowns.forEach(dropdown => {
dropdown.classList.remove('show');
const menu = dropdown.querySelector('.dropdown-menu');
if (menu) menu.classList.remove('show');
});
// Toggle search state
const isActive = searchInput.classList.contains('active');
const searchIcon = newSearchToggle.querySelector('i');
if (isActive) {
// Close search
searchInput.classList.remove('active');
document.body.classList.remove('search-open');
// Change icon back to search
if (searchIcon) {
searchIcon.className = 'ti-search';
}
// Clear input
if (searchField) {
searchField.value = '';
searchField.blur();
}
console.log('🔍 Full-width search closed');
} else {
// Open search
searchInput.classList.add('active');
document.body.classList.add('search-open');
// Change icon to close
if (searchIcon) {
searchIcon.className = 'ti-close';
}
// Focus the input after a short delay
setTimeout(() => {
if (searchField) {
searchField.focus();
console.log('🔍 Search field focused');
}
}, 100);
console.log('🔍 Full-width search opened');
}
});
// Close search on escape
DOM.on(document, 'keydown', (e) => {
if (e.key === 'Escape' && searchInput.classList.contains('active')) {
searchInput.classList.remove('active');
document.body.classList.remove('search-open');
// Reset icon
const searchIcon = newSearchToggle.querySelector('i');
if (searchIcon) {
searchIcon.className = 'ti-search';
}
// Clear input
if (searchField) {
searchField.value = '';
searchField.blur();
}
console.log('🔍 Full-width search closed via escape');
}
});
// Handle search input
DOM.on(searchField, 'keypress', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
const query = searchField.value.trim();
if (query) {
console.log('🔍 Search query:', query);
// Implement your search logic here
// For demo, close search after "searching"
searchInput.classList.remove('active');
document.body.classList.remove('search-open');
const searchIcon = newSearchToggle.querySelector('i');
if (searchIcon) {
searchIcon.className = 'ti-search';
}
searchField.value = '';
searchField.blur();
}
}
});
console.log('🔍 Full-width search functionality initialized');
}
}
}
/**
* Get a component by name
*/
getComponent(name) {
return this.components.get(name);
}
/**
* Check if app is initialized
* Check if app is ready
*/
isReady() {
return this.isInitialized;
@ -357,45 +624,42 @@ class AdminatorApp {
* Destroy the application
*/
destroy() {
console.log('🗑️ Destroying Adminator App');
// Destroy all components
this.components.forEach((component, name) => {
if (typeof component.destroy === 'function') {
component.destroy();
}
console.log(`🗑️ ${name} component destroyed`);
});
this.components.clear();
this.isInitialized = false;
console.log('🧹 Adminator App destroyed');
}
/**
* Refresh all components (useful for dynamic content)
* Refresh/reinitialize the application
*/
refresh() {
console.log('🔄 Refreshing Adminator App');
// Refresh sidebar active links
const sidebar = this.components.get('sidebar');
if (sidebar) {
sidebar.refreshActiveLink();
}
// Reinitialize charts if needed
const charts = this.components.get('charts');
if (charts) {
charts.redrawCharts();
if (this.isInitialized) {
this.destroy();
}
setTimeout(() => {
this.init();
}, 100);
}
}
// Create global app instance
// Initialize the application
console.log('📱 Starting Adminator (Mobile Optimized)');
const app = new AdminatorApp();
// Export for external access
// Make app globally available for debugging
window.AdminatorApp = app;
// Export for module usage
export default app;

+ 113
- 0
src/assets/styles/index.scss View File

@ -4,6 +4,7 @@
@use 'spec/index' as *;
@use 'vendor/index' as *;
@import "utils/theme.css";
@import "utils/mobile.scss";
body {
background: var(--c-bkg-body);
@ -83,6 +84,118 @@ body {
}
}
}
// Mobile theme toggle adjustments
@media (max-width: 991px) {
padding: 0 6px;
height: 65px;
.form-check {
.form-check-label {
font-size: 10px;
&:first-child {
margin-right: 4px;
}
&:last-child {
margin-left: 4px;
}
}
.form-check-input {
width: 2rem;
height: 1rem;
}
}
}
// Very small mobile adjustments
@media (max-width: 480px) {
padding: 0 4px;
.form-check {
flex-direction: column;
align-items: center;
text-align: center;
.form-check-label {
font-size: 8px;
margin: 1px 0;
white-space: nowrap;
i {
margin: 0 2px;
}
}
.form-check-input {
width: 1.5rem;
height: 0.8rem;
margin: 2px 0;
}
}
}
}
}
// Mobile dropdown menu improvements
@media (max-width: 767px) {
.header {
.nav-right {
.dropdown-menu {
position: fixed !important;
top: 65px !important;
left: 5px !important;
right: 5px !important;
width: auto !important;
max-width: none !important;
min-width: auto !important;
transform: none !important;
z-index: 1050;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
max-height: calc(100vh - 85px);
overflow-y: auto;
}
.notifications .dropdown-menu {
max-height: calc(100vh - 85px);
overflow-y: auto;
}
}
}
}
// Mobile search input overlay
@media (max-width: 480px) {
.header {
.search-input {
&.active {
position: absolute;
top: 65px;
left: 0;
right: 0;
background: var(--c-bkg-card);
border-top: 1px solid var(--c-border);
padding: 10px;
z-index: 999;
input {
margin-top: 0;
width: 100%;
padding: 10px;
border: 1px solid var(--c-border);
border-radius: 4px;
background: var(--c-bkg-card);
color: var(--c-text-base);
&::placeholder {
color: var(--c-text-muted);
}
}
}
}
}
}


+ 158
- 3
src/assets/styles/spec/components/pageContainer.scss View File

@ -10,15 +10,20 @@
// + @Main Content
// + @Full Container
// + @Collapsed State
// + @Mobile Layout Fixes
// ---------------------------------------------------------
// @Page Container
// @Page Container - MODERN LAYOUT APPROACH
// ---------------------------------------------------------
.page-container {
min-height: 100vh;
padding-left: $offscreen-size;
transition: all 0.2s ease;
// Modern flexbox layout to prevent footer overlap
display: flex;
flex-direction: column;
@include between($breakpoint-md, $breakpoint-xl) {
padding-left: $collapsed-size;
@ -30,16 +35,30 @@
}
// ---------------------------------------------------------
// @Main Content
// @Main Content - FLEXIBLE LAYOUT
// ---------------------------------------------------------
.main-content {
padding: 85px 20px 20px;
min-height: calc(100vh - 61px);
// Flex-grow to push footer to bottom
flex: 1 0 auto;
min-height: 0; // Allow flex shrinking
// Ensure content doesn't overflow
overflow-x: hidden;
@include to($breakpoint-md) {
padding: 85px 5px 5px;
}
@include to($breakpoint-sm) {
padding: 85px 10px 30px; // Extra bottom padding on mobile
}
@include to(400px) {
padding: 85px 5px 40px; // Even more bottom padding on tiny screens
}
}
.remain-height {
@ -68,6 +87,142 @@
}
}
// ---------------------------------------------------------
// @Mobile Layout Fixes - AGGRESSIVE FOOTER SOLUTION
// ---------------------------------------------------------
// Footer - completely redesigned for mobile
footer {
// Flex-shrink: 0 to prevent compression
flex: 0 0 auto;
// Positioning
position: relative;
z-index: 1;
// Styling
margin-top: auto;
padding: 20px;
text-align: center;
border-top: 1px solid var(--c-border);
background: var(--c-bkg-card);
color: var(--c-text-muted);
font-size: 12px;
line-height: 1.4;
// Prevent any potential overflow
word-wrap: break-word;
overflow-wrap: break-word;
// Tablet footer adjustments
@include to($breakpoint-md) {
padding: 18px 15px;
font-size: 11px;
}
// Mobile footer adjustments
@include to($breakpoint-sm) {
padding: 15px 10px;
font-size: 10px;
line-height: 1.3;
span {
display: inline-block;
max-width: 100%;
a {
color: var(--c-primary);
text-decoration: none;
word-break: break-word;
&:hover {
text-decoration: underline;
}
}
}
}
// Tiny screen footer adjustments
@include to(400px) {
padding: 12px 8px;
font-size: 9px;
line-height: 1.2;
span {
display: block;
// Break long text on new lines for readability
&::after {
content: "";
display: block;
height: 2px;
}
}
}
}
// Ensure body and html take full height for flex layout
html, body {
height: 100%;
margin: 0;
padding: 0;
}
// Global mobile overflow prevention
@include to($breakpoint-sm) {
body {
overflow-x: hidden;
}
// Prevent any element from causing horizontal scroll
* {
max-width: 100%;
box-sizing: border-box;
}
}
// Additional mobile content spacing
@include to($breakpoint-sm) {
.page-container {
.main-content {
// Extra margin-bottom to ensure footer never overlaps
margin-bottom: 20px;
// Responsive content adjustments
.row {
margin-left: 0;
margin-right: 0;
}
.col-md-6, .col-md-3, .col-md-12 {
padding-left: 5px;
padding-right: 5px;
}
}
}
}
// Emergency footer overlap prevention
@include to(480px) {
.page-container {
// Force minimum height that accounts for content
min-height: calc(100vh - 80px);
.main-content {
// Ensure there's always space for footer
padding-bottom: 60px !important;
margin-bottom: 20px !important;
}
}
footer {
// Stick to bottom on very small screens
position: relative;
margin-top: auto;
clear: both;
}
}
// ---------------------------------------------------------
// @Collapsed State
// ---------------------------------------------------------


+ 241
- 0
src/assets/styles/spec/components/topbar.scss View File

@ -10,6 +10,7 @@
// + @Topbar
// + @Collapsed State
// + @Mobile Responsive Fixes
// ---------------------------------------------------------
// @Topbar
@ -195,6 +196,244 @@
}
}
// ---------------------------------------------------------
// @Mobile Responsive Fixes - AGGRESSIVE APPROACH
// ---------------------------------------------------------
// Tablet mobile fixes (768px to 991px)
@include to($breakpoint-md) {
.header {
.header-container {
padding: 0 10px;
.nav-left {
margin-left: 5px;
> li > a {
padding: 0 8px !important;
}
}
.nav-right {
margin-right: 5px;
> li {
> a {
padding: 0 8px !important;
// Hide text in user dropdown on tablet
.peer:last-child {
display: none;
}
}
}
// Make theme toggle more compact
.theme-toggle {
padding: 0 5px !important;
.form-check-label {
font-size: 9px !important;
margin: 0 3px !important;
}
.form-check-input {
width: 1.8rem !important;
height: 1rem !important;
}
}
}
}
}
}
// Small mobile phones (576px to 767px)
@include to($breakpoint-sm) {
.header {
.header-container {
height: auto;
min-height: $header-height;
padding: 0 5px;
.nav-left {
margin-left: 2px;
> li > a {
padding: 0 5px !important;
}
// Hide search toggle on small mobile - make it icon only when active
.search-box:not(.active) {
display: none;
}
}
.nav-right {
margin-right: 2px;
> li {
> a {
padding: 0 4px !important;
// Hide all text content, keep only icons
span {
display: none;
}
.peer:last-child {
display: none;
}
}
}
// Ultra-compact theme toggle
.theme-toggle {
padding: 0 3px !important;
.form-check {
flex-direction: column;
align-items: center;
.form-check-label {
font-size: 7px !important;
margin: 0 !important;
line-height: 1 !important;
span {
display: none !important;
}
}
.form-check-input {
width: 1.5rem !important;
height: 0.8rem !important;
margin: 1px 0 !important;
}
}
}
}
// Full-screen mobile dropdowns
.nav-right .dropdown-menu {
position: fixed !important;
top: $header-height !important;
left: 0 !important;
right: 0 !important;
bottom: 0 !important;
width: 100vw !important;
height: calc(100vh - #{$header-height}) !important;
max-width: none !important;
min-width: auto !important;
transform: none !important;
border-radius: 0 !important;
z-index: 9999;
overflow-y: auto;
// Close button for mobile dropdowns
&::before {
content: "✕ Close";
position: sticky;
top: 0;
display: block;
background: var(--c-primary);
color: white;
text-align: center;
padding: 10px;
cursor: pointer;
font-weight: bold;
z-index: 10000;
}
}
}
}
}
// Extra small mobile phones (less than 576px)
@include to(480px) {
.header {
.header-container {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: nowrap;
padding: 0 2px;
.nav-left {
flex: 0 0 auto;
margin: 0;
> li {
float: none;
display: inline-block;
> a {
padding: 0 3px !important;
font-size: 14px !important;
}
}
// Search overlay for tiny screens
.search-input.active {
position: fixed;
top: $header-height;
left: 0;
right: 0;
background: var(--c-bkg-card);
border-top: 1px solid var(--c-border);
padding: 15px;
z-index: 9998;
input {
width: 100%;
padding: 12px;
font-size: 16px;
border: 1px solid var(--c-border);
border-radius: 6px;
background: var(--c-bkg-card);
color: var(--c-text-base);
margin-top: 0;
&::placeholder {
color: var(--c-text-muted);
}
}
}
}
.nav-right {
flex: 0 0 auto;
margin: 0;
> li {
float: none;
display: inline-block;
> a {
padding: 0 2px !important;
font-size: 12px !important;
}
}
// Minimal theme toggle
.theme-toggle {
padding: 0 1px !important;
.form-check {
.form-check-label {
display: none !important;
}
.form-check-input {
width: 1.2rem !important;
height: 0.7rem !important;
}
}
}
}
}
}
}
// ---------------------------------------------------------
// @Collapsed State
// ---------------------------------------------------------
@ -212,3 +451,5 @@
}
}
}

+ 954
- 0
src/assets/styles/utils/mobile.scss View File

@ -0,0 +1,954 @@
// Mobile Utility Classes and Fixes
// This file contains mobile-specific utility classes and responsive fixes
// Mobile text utilities - Only hide theme labels on very small screens
@media (max-width: 480px) {
.theme-toggle .form-check-label {
display: none !important;
}
.theme-toggle {
padding: 0 6px !important;
height: 60px !important; // Match header height
justify-content: center !important;
min-height: 60px !important;
.form-check {
height: 100% !important;
.form-check-label {
font-size: 11px !important; // Slightly smaller for very small screens
font-weight: 500 !important;
i {
font-size: 14px !important; // Still reasonable for tiny screens
}
}
.form-check-input {
margin: 0 6px !important;
width: 38px !important; // Bigger than before but not huge for tiny screens
height: 22px !important; // Bigger than before but not huge for tiny screens
border-radius: 11px !important;
border-width: 2px !important;
}
}
}
// Very small screen adjustments
.d-none-xs {
display: none !important;
}
.fs-xs {
font-size: 10px !important;
}
.p-xs {
padding: 5px !important;
}
.m-xs {
margin: 5px !important;
}
}
// Mobile dropdown improvements
@media (max-width: 767px) {
.dropdown-menu {
// Ensure all dropdowns are mobile-friendly
&.show {
position: fixed !important;
top: 65px !important;
left: 5px !important;
right: 5px !important;
width: auto !important;
transform: none !important;
z-index: 1050;
max-height: calc(100vh - 85px);
overflow-y: auto;
}
}
// Mobile notification improvements
.notifications .dropdown-menu {
.scrollable {
max-height: 300px;
overflow-y: auto;
}
}
}
// Mobile header compact mode
@media (max-width: 991px) {
.header .nav-right > li > a {
padding: 0 6px !important;
}
.header .nav-left > li > a {
padding: 0 8px !important;
}
}
// Ultra-compact mode for very small screens
@media (max-width: 480px) {
.header .nav-right > li > a {
padding: 0 4px !important;
font-size: 14px !important;
}
.header .nav-left > li > a {
padding: 0 6px !important;
}
// Hide search on very small screens when not active
.search-box:not(.active) {
display: none !important;
}
}
// Mobile-specific spacing utilities
.mobile-compact {
@media (max-width: 767px) {
padding: 5px !important;
margin: 2px !important;
}
}
.mobile-hidden {
@media (max-width: 767px) {
display: none !important;
}
}
.mobile-only {
display: none !important;
@media (max-width: 767px) {
display: block !important;
}
}
// Prevent horizontal scroll on mobile
.mobile-no-scroll {
@media (max-width: 767px) {
overflow-x: hidden !important;
}
}
// COMPREHENSIVE Mobile Header Fixes
// Better layout, bigger icons, full-width search, and desktop fixes
// =============================================================================
// DESKTOP FIXES - Remove top spacing issue
// =============================================================================
@media screen and (min-width: 768px) {
.header {
margin-top: 0 !important;
top: 0 !important;
}
.page-container {
padding-top: 61px !important; // Standard header height + 1px border
}
.main-content {
margin-top: 0 !important;
padding-top: 20px !important;
}
// DESKTOP THEME TOGGLE - Make sure it's fully visible
.theme-toggle {
display: flex !important;
align-items: center !important;
height: 65px !important;
padding: 0 15px !important;
.form-check {
margin: 0 !important;
display: flex !important;
align-items: center !important;
.form-check-label {
color: var(--c-text-muted) !important;
font-size: 11px !important;
font-weight: 500 !important;
text-transform: uppercase !important;
letter-spacing: 0.5px !important;
display: inline !important;
i {
font-size: 12px !important;
}
}
.form-check-input {
width: 2.5rem !important;
height: 1.25rem !important;
background-color: var(--c-border) !important;
border: 1px solid var(--c-border) !important;
cursor: pointer !important;
margin: 0 8px !important;
&:checked {
background-color: var(--c-primary) !important;
border-color: var(--c-primary) !important;
}
&:focus {
box-shadow: 0 0 0 0.2rem color-mix(in srgb, var(--c-primary) 25%, transparent) !important;
border-color: var(--c-primary) !important;
}
}
}
}
}
// =============================================================================
// HEADER NAVIGATION MOBILE FIXES - ENHANCED LAYOUT
// =============================================================================
// Mobile header fixes with improved spacing and layout
@media screen and (max-width: 767px) {
// Force header to be fixed and proper height
.header {
position: fixed !important;
top: 0 !important;
left: 0 !important;
right: 0 !important;
z-index: 1000 !important;
width: 100% !important;
height: auto !important;
min-height: 60px !important;
padding: 0 !important;
margin: 0 !important;
margin-top: 0 !important; // Ensure no top margin on mobile
}
// Header container - IMPROVED EDGE-TO-EDGE LAYOUT
.header .header-container {
display: flex !important;
align-items: center !important;
justify-content: space-between !important;
flex-wrap: nowrap !important;
padding: 8px 8px !important; // Reduced side padding for more space
height: auto !important;
min-height: 60px !important;
max-height: 60px !important;
overflow: visible !important;
gap: 12px !important; // Larger gap to push items apart
}
// LEFT SECTION: Logo + Hamburger + Search - PUSHED MORE LEFT
.header .nav-left {
display: flex !important;
align-items: center !important;
flex: 1 1 auto !important;
margin: 0 !important;
padding: 0 !important;
float: none !important;
max-width: 65% !important; // Increased width
gap: 4px !important; // Tighter spacing between left items
justify-content: flex-start !important; // Push to left edge
// Logo first - positioned at far left
&::before {
content: "A" !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
width: 32px !important;
height: 32px !important;
background: #007bff !important;
color: white !important;
border-radius: 6px !important;
font-weight: bold !important;
font-size: 18px !important;
flex-shrink: 0 !important;
order: -1 !important;
margin-right: 6px !important; // Small margin to separate from hamburger
}
> li {
display: inline-flex !important;
align-items: center !important;
margin: 0 !important;
float: none !important;
> a {
padding: 8px 6px !important; // Reduced padding for tighter spacing
margin: 0 !important;
min-height: auto !important;
line-height: 1 !important;
display: flex !important;
align-items: center !important;
border-radius: 4px !important;
i {
font-size: 20px !important;
margin: 0 !important;
}
// Hover state for better UX
&:hover {
background: rgba(0, 0, 0, 0.05) !important;
}
}
// Sidebar toggle (hamburger)
&:first-child > a {
padding: 8px 6px !important;
i {
font-size: 22px !important;
}
}
// Search toggle
&.search-box > a {
padding: 8px 6px !important;
i {
font-size: 20px !important;
}
}
}
// FULL-WIDTH SEARCH BAR - CLEANER DESIGN
.search-input {
display: none !important; // Hidden by default
position: fixed !important; // Fixed positioning for full control
top: 60px !important; // Right below header
left: 0 !important;
right: 0 !important;
background: var(--c-bkg-card) !important;
border-bottom: 1px solid var(--c-border) !important;
padding: 15px 20px !important; // More padding for better appearance
z-index: 9999 !important;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !important; // Subtle shadow
&.active {
display: block !important;
}
input {
width: 100% !important;
padding: 12px 16px !important; // Better padding
font-size: 16px !important;
border: 1px solid var(--c-border) !important;
border-radius: 8px !important; // Rounded corners
background: var(--c-bkg-body) !important;
color: var(--c-text-base) !important;
margin: 0 !important;
outline: none !important;
&::placeholder {
color: var(--c-text-muted) !important;
}
&:focus {
outline: none !important;
border-color: var(--c-primary) !important;
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25) !important;
}
}
}
}
// RIGHT SECTION: Notifications + Messages + Theme Toggle + Profile - PERFECT ALIGNMENT
.header .nav-right {
display: flex !important;
align-items: center !important;
flex: 0 0 auto !important;
margin: 0 !important;
padding: 0 !important;
float: none !important;
flex-wrap: nowrap !important;
gap: 4px !important; // Consistent spacing
justify-content: flex-end !important; // Push to right edge
height: 60px !important; // Match header height exactly
> li {
display: flex !important;
align-items: center !important;
justify-content: center !important;
margin: 0 !important;
padding: 0 !important;
float: none !important;
flex: 0 0 auto !important;
position: relative !important;
height: 60px !important; // Force exact height for all items
min-height: 60px !important; // Ensure minimum height
> a {
padding: 0 !important; // NO padding for perfect alignment
margin: 0 !important;
width: 44px !important; // Fixed width for all nav items
height: 44px !important; // Fixed height for all nav items
line-height: 1 !important;
display: flex !important;
align-items: center !important; // Perfect vertical centering
justify-content: center !important; // Perfect horizontal centering
position: relative !important;
border-radius: 50% !important; // Circular touch targets
background: transparent !important;
transition: all 0.2s ease !important; // Smooth transitions
i {
font-size: 20px !important; // Consistent icon size for all nav items
margin: 0 !important;
display: block !important;
line-height: 1 !important;
text-align: center !important;
}
// Hide text content, keep only icons
span:not(.counter) {
display: none !important;
}
// Hide user avatar text
.peer:last-child {
display: none !important;
}
// Hover state - subtle and theme-consistent
&:hover {
background: var(--c-grey-100) !important;
transform: scale(1.05) !important;
transition: all 0.2s ease !important;
}
}
// User dropdown - special styling for avatar
&:last-child > a {
.peer {
&:first-child {
margin-right: 0 !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
}
img {
width: 36px !important;
height: 36px !important;
max-width: 36px !important;
max-height: 36px !important;
border-radius: 50% !important;
object-fit: cover !important;
}
}
}
}
// NOTIFICATION COUNTERS - CORRECTLY ANCHORED
> li {
position: relative !important; // This is the anchor
.counter {
position: absolute !important;
top: 10px !important;
right: 10px !important;
z-index: 10 !important;
// The design of the counter itself is inherited
}
}
// Theme toggle - perfectly aligned with other nav icons
.theme-toggle {
padding: 0 !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
height: 60px !important; // Match header height exactly
min-height: 60px !important; // Force exact height
width: 44px !important; // Same width as other nav items
.form-check {
margin: 0 !important;
padding: 0 !important;
align-items: center !important;
justify-content: center !important;
display: flex !important;
height: 44px !important; // Same height as other nav items
width: 44px !important; // Same width as other nav items
border-radius: 50% !important; // Match other nav items
position: relative !important;
.form-check-label {
display: none !important; // Hide labels on mobile for consistency
}
.form-check-input {
width: 28px !important; // Smaller switch to fit in circular area
height: 16px !important; // Smaller switch to fit in circular area
margin: 0 !important; // No margin for perfect centering
flex-shrink: 0 !important;
border-radius: 8px !important; // Proportional border radius
border-width: 1px !important; // Standard border
position: absolute !important;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%) !important; // Perfect centering
}
// Hover state to match other nav items
&:hover {
background: var(--c-grey-100) !important;
transform: scale(1.05) !important;
transition: all 0.2s ease !important;
}
}
}
}
// Full-screen mobile dropdowns
.header .nav-right .dropdown-menu {
position: fixed !important;
top: 60px !important;
left: 0 !important;
right: 0 !important;
bottom: 0 !important;
width: 100vw !important;
height: calc(100vh - 60px) !important;
max-width: none !important;
min-width: auto !important;
transform: none !important;
border-radius: 0 !important;
z-index: 9999 !important;
overflow-y: auto !important;
border: none !important;
margin: 0 !important;
padding: 0 !important;
// Mobile close button
&::before {
content: "✕ Close" !important;
position: sticky !important;
top: 0 !important;
display: block !important;
background: var(--c-primary) !important;
color: white !important;
text-align: center !important;
padding: 15px !important;
cursor: pointer !important;
font-weight: bold !important;
z-index: 10000 !important;
font-size: 16px !important;
}
// UNIFIED EMAIL/NOTIFICATION MOBILE LAYOUT
.peers {
padding: 15px 20px !important;
flex-wrap: wrap !important;
align-items: flex-start !important;
.peer {
max-width: 100% !important;
img {
width: 40px !important;
height: 40px !important;
margin-right: 12px !important;
flex-shrink: 0 !important;
}
&.peer-greed {
flex: 1 !important;
min-width: 0 !important;
// NOTIFICATIONS STYLE - Direct content (span > fw-500 + c-grey-600, then p > small)
> span {
display: block !important;
margin-bottom: 4px !important;
.fw-500 {
font-size: 14px !important;
font-weight: 600 !important;
margin: 0 !important;
color: var(--c-text-base) !important;
display: inline !important;
}
.c-grey-600 {
font-size: 13px !important;
color: var(--c-text-muted) !important;
line-height: 1.4 !important;
display: inline !important;
}
}
> p {
margin: 0 !important;
small {
font-size: 12px !important;
color: var(--c-text-muted) !important;
}
}
// EMAILS STYLE - Match notifications exactly
> div {
// Completely restructure emails to match notification layout
display: block !important;
.peers {
display: inline !important; // Make name and action on same line
margin: 0 !important;
.peer {
&:first-child {
// Name section
p {
font-size: 14px !important;
font-weight: 600 !important;
margin: 0 !important;
color: var(--c-text-base) !important;
display: inline !important;
}
// Add action text after name
&::after {
content: " sent you a message" !important;
font-size: 13px !important;
color: var(--c-text-muted) !important;
font-weight: normal !important;
}
}
&:last-child {
// Move timestamp to its own line below (like notifications)
display: block !important;
margin-top: 2px !important;
small {
font-size: 12px !important;
color: var(--c-text-muted) !important;
display: block !important;
}
}
}
}
// Hide the email preview text completely to match notifications
> .c-grey-600,
.c-grey-600.fsz-sm {
display: none !important;
}
}
}
}
}
// Header items in dropdown
.pX-20 {
padding-left: 20px !important;
padding-right: 20px !important;
.fw-600 {
font-size: 16px !important;
font-weight: 600 !important;
}
}
// Footer links in dropdown
.ta-c {
padding: 15px 20px !important;
a {
font-size: 14px !important;
font-weight: 500 !important;
}
}
}
}
// Extra small screens - refined adjustments
@media screen and (max-width: 479px) {
.header .header-container {
padding: 8px 5px !important; // Even less padding for tiny screens
gap: 8px !important;
}
.header .nav-left {
max-width: 60% !important;
gap: 2px !important; // Even tighter spacing
&::before {
width: 28px !important;
height: 28px !important;
font-size: 16px !important;
margin-right: 4px !important;
}
> li > a {
padding: 8px 4px !important;
i {
font-size: 18px !important;
}
}
}
.header .nav-right {
gap: 1px !important; // Minimal spacing
> li > a {
padding: 8px 4px !important;
i {
font-size: 18px !important; // Slightly smaller for tiny screens
}
}
.theme-toggle .form-check-input {
width: 28px !important;
height: 16px !important;
}
}
// Full-width search bar on tiny screens
.header .nav-left .search-input {
padding: 12px 15px !important;
input {
padding: 10px 12px !important;
font-size: 16px !important; // Prevent zoom on iOS
}
}
}
// =============================================================================
// FOOTER OVERLAP FIXES - MAINTAINING PREVIOUS FIXES
// =============================================================================
// Global layout fixes
html, body {
height: 100% !important;
margin: 0 !important;
padding: 0 !important;
overflow-x: hidden !important;
}
// Page container - force flexbox layout
.page-container {
display: flex !important;
flex-direction: column !important;
min-height: 100vh !important;
margin: 0 !important;
padding-top: 60px !important;
}
// Main content - flexible
.main-content {
flex: 1 0 auto !important;
padding: 20px 10px 40px !important;
margin: 0 !important;
min-height: 0 !important;
overflow-x: hidden !important;
}
// Footer - fixed to bottom
footer {
flex: 0 0 auto !important;
margin-top: auto !important;
padding: 15px 10px !important;
background: var(--c-bkg-card) !important;
border-top: 1px solid var(--c-border) !important;
text-align: center !important;
font-size: 11px !important;
line-height: 1.3 !important;
z-index: 10 !important;
position: relative !important;
width: 100% !important;
clear: both !important;
// CRITICAL: Override lh-0 class that causes overlap
&.lh-0 {
line-height: 1.3 !important; // Force proper line height
}
}
@media screen and (max-width: 767px) {
.page-container {
padding-left: 0 !important;
min-height: 100vh !important; // Ensure full height
position: relative !important; // Ensure positioning context
}
.main-content {
padding: 15px 8px 60px !important; // Increased bottom padding for footer space
margin-bottom: 0 !important;
width: 100% !important;
box-sizing: border-box !important;
}
footer {
position: relative !important; // Ensure footer stays in flow
width: 100% !important;
padding: 15px 10px !important; // Increased padding for better spacing
font-size: 11px !important; // Slightly larger for readability
line-height: 1.4 !important; // Better line height for mobile
background: var(--c-bkg-card) !important;
border-top: 1px solid var(--c-border) !important;
margin-top: auto !important;
box-sizing: border-box !important;
// CRITICAL MOBILE FIXES: Override all conflicting utility classes
&.lh-0 {
line-height: 1.4 !important; // Override line-height: 0 that causes overlap
}
&.p-30 {
padding: 15px 10px !important; // Override desktop padding
}
&.fsz-sm {
font-size: 11px !important; // Ensure readable font size on mobile
}
span {
display: block !important; // Force block for better wrapping
word-wrap: break-word !important;
max-width: 100% !important;
text-align: center !important;
line-height: 1.4 !important;
// Handle long text better
hyphens: auto !important;
word-break: break-word !important;
a {
color: var(--c-primary) !important;
text-decoration: none !important;
display: inline !important; // Keep link inline within text
&:hover {
text-decoration: underline !important;
}
}
}
}
}
@media screen and (max-width: 479px) {
.main-content {
padding: 10px 8px 50px !important; // Adequate bottom space for footer
}
footer {
padding: 12px 8px !important;
font-size: 10px !important;
line-height: 1.5 !important; // Better readability on small screens
// Override utility classes for small screens
&.lh-0 {
line-height: 1.5 !important; // Override line-height: 0
}
&.p-30 {
padding: 12px 8px !important; // Override desktop padding
}
&.fsz-sm {
font-size: 10px !important; // Ensure readable font size
}
span {
display: block !important;
margin: 0 !important; // Remove extra margins
padding: 0 !important;
text-align: center !important;
// Split long copyright text into multiple lines if needed
word-spacing: normal !important;
letter-spacing: normal !important;
// Ensure links are readable
a {
display: inline !important;
white-space: nowrap !important; // Keep "Colorlib" as one word
}
}
}
}
// Additional mobile footer fixes
@media screen and (max-width: 360px) {
footer {
font-size: 9px !important;
padding: 10px 5px !important;
// Override utility classes for extra small screens
&.lh-0 {
line-height: 1.6 !important; // Even better line height for tiny screens
}
&.p-30 {
padding: 10px 5px !important; // Override desktop padding
}
&.fsz-sm {
font-size: 9px !important; // Readable font size for tiny screens
}
span {
line-height: 1.6 !important; // Ensure good readability
// For very small screens, ensure text doesn't overlap
word-break: break-word !important;
overflow-wrap: break-word !important;
// Ensure links are readable
a {
font-weight: bold !important;
color: var(--c-primary) !important;
}
}
}
}
// =============================================================================
// UTILITY CLASSES FOR MOBILE
// =============================================================================
// Prevent body scroll when mobile menus are open
body.mobile-menu-open {
overflow: hidden !important;
position: fixed !important;
width: 100% !important;
}
// Emergency fixes for any remaining issues
@media screen and (max-width: 767px) {
// Ensure dropdowns don't break layout
.dropdown-menu.show {
position: fixed !important;
}
// Prevent content overflow
.row {
margin-left: 0 !important;
margin-right: 0 !important;
}
[class*="col-"] {
padding-left: 5px !important;
padding-right: 5px !important;
}
// Hide on mobile utility
.d-none-mobile {
display: none !important;
}
// Force no horizontal scroll
* {
max-width: 100% !important;
box-sizing: border-box !important;
}
}

Loading…
Cancel
Save