@@ -101,32 +101,16 @@ function processNotificationQueue() {
101101 // Calculate position based on existing notifications
102102 const topPosition = 80 + ( activeNotifications . length * 70 ) ;
103103
104- notification . style . cssText = `
105- position: fixed;
106- top: ${ topPosition } px;
107- right: 20px;
108- padding: 12px 20px;
109- border-radius: 8px;
110- background: var(--bg-secondary);
111- border: 1px solid var(--border);
112- box-shadow: var(--shadow-lg);
113- z-index: 10002;
114- font-size: 14px;
115- max-width: 350px;
116- animation: slideInFromRight 0.3s ease-out;
117- transition: top 0.3s ease-out;
118- ` ;
104+ // Static appearance is handled by the .app-notification CSS class so that
105+ // the mobile media query can override layout properties (right/left/max-width).
106+ // Inline styles here are limited to the two truly dynamic values: vertical
107+ // position and type-specific border colour.
108+ notification . classList . add ( 'app-notification' ) ;
109+ notification . style . top = `${ topPosition } px` ;
119110
120- // Add type-specific styling
121- if ( type === 'warning' ) {
122- notification . style . borderLeft = '4px solid #ff9800' ;
123- } else if ( type === 'error' ) {
124- notification . style . borderLeft = '4px solid #f44336' ;
125- } else if ( type === 'success' ) {
126- notification . style . borderLeft = '4px solid #4caf50' ;
127- } else {
128- notification . style . borderLeft = '4px solid #2196f3' ;
129- }
111+ // Type-specific left border colour
112+ const borderColors = { warning : '#ff9800' , error : '#f44336' , success : '#4caf50' , info : '#2196f3' } ;
113+ notification . style . borderLeft = `4px solid ${ borderColors [ type ] || borderColors . info } ` ;
130114
131115 document . body . appendChild ( notification ) ;
132116 activeNotifications . push ( notification ) ;
@@ -457,13 +441,37 @@ export function initTheme() {
457441// ============================================================================
458442
459443/**
460- * Toggle control panel visibility (for mobile)
444+ * Toggle active filters bar expanded state.
445+ * On desktop this pins the bar open alongside hover; on mobile (where
446+ * :hover doesn't persist) this is the sole expand/collapse mechanism.
447+ */
448+ export function toggleActiveFiltersBar ( ) {
449+ const bar = document . getElementById ( 'activeFiltersBar' ) ;
450+ if ( ! bar ) return ;
451+ const expanded = bar . classList . toggle ( 'expanded' ) ;
452+ const header = bar . querySelector ( '.active-filters-bar-header' ) ;
453+ if ( header ) header . setAttribute ( 'aria-expanded' , String ( expanded ) ) ;
454+ }
455+
456+ /**
457+ * Toggle control panel visibility (for mobile).
458+ * On mobile this shows/hides the bottom-sheet panel. It also:
459+ * - Toggles the backdrop overlay so the map is dimmed while open.
460+ * - Sets data-panel-open on the FAB so CSS can hide it while the sheet is up.
461461 */
462462export function togglePanel ( ) {
463463 const panel = document . getElementById ( 'controlsPanel' ) ;
464- if ( panel ) {
465- panel . classList . toggle ( 'visible' ) ;
466- }
464+ if ( ! panel ) return ;
465+
466+ const isOpen = panel . classList . toggle ( 'visible' ) ;
467+
468+ // Show/hide the semi-transparent backdrop behind the bottom sheet
469+ const backdrop = document . querySelector ( '.mobile-panel-backdrop' ) ;
470+ if ( backdrop ) backdrop . classList . toggle ( 'active' , isOpen ) ;
471+
472+ // Let CSS know the panel state so the FAB can be hidden while open
473+ const fab = document . querySelector ( '.toggle-panel-btn' ) ;
474+ if ( fab ) fab . dataset . panelOpen = isOpen ;
467475}
468476
469477/**
@@ -510,6 +518,8 @@ export function toggleDataTable() {
510518
511519 if ( panel . style . display === 'none' || panel . style . display === '' ) {
512520 panel . style . display = 'flex' ;
521+ // On mobile the FAB must be raised above the table panel
522+ document . body . classList . add ( 'data-table-open' ) ;
513523 showTableLoading ( ) ;
514524
515525 // Use setTimeout to allow loading indicator to display
@@ -529,6 +539,7 @@ export function toggleDataTable() {
529539 } , 50 ) ;
530540 } else {
531541 panel . style . display = 'none' ;
542+ document . body . classList . remove ( 'data-table-open' ) ;
532543 }
533544}
534545
@@ -1744,6 +1755,28 @@ export function initUI() {
17441755 columnPicker . style . display = 'none' ;
17451756 }
17461757 } ) ;
1758+
1759+ // Swipe-down to dismiss the mobile bottom sheet.
1760+ // Only triggers when the panel is open, the swipe starts while the panel's
1761+ // own scroll position is at the top (so normal upward scrolling is not
1762+ // blocked), and the downward distance exceeds 80px.
1763+ const controlsPanel = document . getElementById ( 'controlsPanel' ) ;
1764+ if ( controlsPanel ) {
1765+ let swipeStartY = 0 ;
1766+ let swipeStartScrollTop = 0 ;
1767+
1768+ controlsPanel . addEventListener ( 'touchstart' , function ( e ) {
1769+ swipeStartY = e . touches [ 0 ] . clientY ;
1770+ swipeStartScrollTop = controlsPanel . scrollTop ;
1771+ } , { passive : true } ) ;
1772+
1773+ controlsPanel . addEventListener ( 'touchend' , function ( e ) {
1774+ const deltaY = e . changedTouches [ 0 ] . clientY - swipeStartY ;
1775+ if ( deltaY > 80 && swipeStartScrollTop === 0 && controlsPanel . classList . contains ( 'visible' ) ) {
1776+ togglePanel ( ) ;
1777+ }
1778+ } , { passive : true } ) ;
1779+ }
17471780}
17481781
17491782// Export constants for use in HTML onclick handlers
0 commit comments