Stop the Scroll! v2.0 – Float the Admin Notice Above the Editor in Code Snippets Pro

Hey Look Note Floats

This snippet enhances Code Snippets Pro ❤️ by making admin notices float above the editor and automatically dismiss after a configurable time period. It keeps important messages visible while you’re editing, then cleanly removes them to prevent interface clutter.

Once upon a time, when you hit ‘Save’ on a snippet inside the Code Snippets Pro ❤️ code editor, the entire page would refresh.

It got the job done, but it was bothersome because you couldn’t undo or see what went wrong, etc.

So the clever developers of Code Snippets Pro ❤️ made a wonderful change where it would save your code without! refreshing the page, and then an admin notice would display at the very top of the page that would indicate if your snippet was saved correctly or if there was an error, etc.

It got the job done, but it was bothersome because if you were working on a large block of code and were scrolled down far enough, the notice would be hidden and you might never… “notice” …if something had gone wrong. And you would keep working and changing stuff and writing awesome snips and and and annnnnd… then you would scroll up and realize there was an error. But you had no clue when it happened or how many times you needed to press Undo! Undo! Undo!

So the clever developers of Code Snippets Pro ❤️ did a great job quickly solving this by ensuring the page would scroll the notice into view anytime a notice appeared.

It got the job done, but it was bothersome, to me, at least. I kept losing my place of where I was working.

So this snippet keeps notices visible without interrupting your work by floating them at the top of your window.

After a few seconds, they’ll smoothly fade away – keeping you informed without cluttering your workspace.

It gets the job done, and isn’t so bothersome.

What it does…

This code snippet adds a little bit of CSS to the admin notice in the Code Snippets Pro ❤️ plugin for WordPress. If you’re on the Add New Snippet or Edit Snippet page, it makes the admin notice float at the top of your window on top of the code snippet editor so that when you save the page doesn’t scroll away. This ensures that the notice is always visible and you don’t lose your place, even when working on a large block of code and scrolled far down the page.

Here’s What You Can Customize

Basic Customization

Look for these constants at the top of the class definition:

  • DEFAULT_DISMISS_DELAY – How long notices stay visible (default: 5000 milliseconds)
  • DEFAULT_NOTICE_OFFSET – Distance from top of viewport (default: 40 pixels)

Advanced Customization

Advanced users can modify these values using the WordPress options API with the option name floating_notice_settings. Add this code to another snippet or your theme’s functions.php:

// Example: Change settings via WordPress options API
$options = get_option('floating_notice_settings', []);
$options['dismiss_delay'] = 3000;  // 3 seconds
$options['notice_offset'] = 60;    // 60px from top
update_option('floating_notice_settings', $options);

Using the options API allows settings to persist across snippet updates.

Why it does it…

By default, the admin notice in Code Snippets Pro ❤️ appears at the top of the page when a snippet is saved or when there is an error. And to make sure you see it, the page scrolls to the notice anytime a notice appears. If you are working on a large block of code and scrolled far down the page, it can get a little distracting every time the page scrolls up to the notice when you save. This modification addresses this issue by making the notice float above the editor, ensuring it is always visible and preventing the page from scrolling.

How it does it…

How It Does It

This code snippet creates floating, auto-dismissing admin notices in the Code Snippets Pro ❤️ editor. Here’s how it works:

  • The code implements a class WP_Floating_Admin_Notice_With_AutoDismiss with configurable settings for dismiss delay and notice offset.
  • Class constants define default values (5 second dismiss delay, 40px top offset).
  • A WordPress options API integration allows persistent storage of custom settings.
  • The styling uses CSS position: sticky and z-index: 999 to float notices above the editor.
  • JavaScript implements the auto-dismiss functionality:
    • A main event listener waits for page load
    • An IIFE creates a private scope to avoid conflicts
    • The autoDismissNotice() function adds a fade-out class after the configured delay
    • A MutationObserver watches for new notices and applies the auto-dismiss behavior
  • The code checks for specific admin pages to only load on snippet edit screens
  • Security measures include input sanitization, output escaping, and proper WordPress coding standards
  • Error handling ensures graceful degradation if settings are missing
  • CSS transitions provide smooth fade-out animations when notices are dismissed

Download JSON for importing into Code Snippets Pro
View on…

See the code…

<?php

/**
 * Title: Floating Admin Notice with Auto-Dismiss
 * Description: Makes admin notices float above the Code Snippets Pro editor and automatically dismiss after a configurable time period. Perfect for keeping notices visible while scrolling but preventing them from cluttering the interface.
 * Version: 2.0.0
 * Author: SnipSnip.pro & brandonjp.com
 * Last Updated: 2024-11-30
 * Blog URL: https://snipsnip.pro [Placeholder for future post]
 * Requirements: WordPress 5.0+, Code Snippets Pro plugin
 * License: GPL v2 or later
 * 
 * Changelog:
 * 2.0.0 - Added auto-dismiss functionality
 *       - Added configuration options
 *       - Added security measures
 *       - Improved error handling
 */

if (!class_exists('WP_Floating_Admin_Notice_With_AutoDismiss')):

    class WP_Floating_Admin_Notice_With_AutoDismiss {
        /**
         * Default configuration values
         */
        const DEFAULT_DISMISS_DELAY = 5000; // milliseconds
        const DEFAULT_NOTICE_OFFSET = 40; // pixels from top
        const OPTION_NAME = 'floating_notice_settings';

        /**
         * Store instance configuration
         */
        private $settings;

        /**
         * Initialize the functionality
         *
         * @since 2.0.0
         */
        public function __construct() {
            $this->init_settings();
            add_action('admin_enqueue_scripts', [$this, 'add_snippet_page_styles']);
            add_action('admin_enqueue_scripts', [$this, 'add_snippet_page_scripts']);
            add_filter('code_snippets/scroll_to_notices', '__return_false');
            add_action('admin_init', [$this, 'register_settings']);
        }

        /**
         * Initialize settings with defaults
         *
         * @since 2.0.0
         */
        private function init_settings() {
            $defaults = [
                'dismiss_delay' => self::DEFAULT_DISMISS_DELAY,
                'notice_offset' => self::DEFAULT_NOTICE_OFFSET
            ];
            
            $saved_settings = get_option(self::OPTION_NAME, []);
            $this->settings = wp_parse_args($saved_settings, $defaults);
        }

        /**
         * Register settings in WordPress
         *
         * @since 2.0.0
         */
        public function register_settings() {
            register_setting(
                'floating_notice_options',
                self::OPTION_NAME,
                [
                    'type' => 'array',
                    'sanitize_callback' => [$this, 'sanitize_settings']
                ]
            );
        }

        /**
         * Sanitize settings before saving
         *
         * @since 2.0.0
         * @param array $input The raw settings input
         * @return array Sanitized settings
         */
        public function sanitize_settings($input) {
            $sanitized = [];
            
            $sanitized['dismiss_delay'] = isset($input['dismiss_delay']) 
                ? absint($input['dismiss_delay']) 
                : self::DEFAULT_DISMISS_DELAY;

            $sanitized['notice_offset'] = isset($input['notice_offset'])
                ? absint($input['notice_offset'])
                : self::DEFAULT_NOTICE_OFFSET;

            return $sanitized;
        }

        /**
         * Add custom styles for floating notices
         *
         * @since 2.0.0
         * @param string $hook The current admin page
         */
        public function add_snippet_page_styles($hook) {
            if (!$this->is_snippet_page($hook)) {
                return;
            }

            $css = <<<'STYLES'
div#edit-snippet-form-container div#message {
    position: sticky;
    z-index: 999;
    transition: opacity 0.3s ease-in-out;
}

div#edit-snippet-form-container div#message.notice-dismissing {
    opacity: 0;
}
STYLES;

            // Add dynamic top offset
            $offset = esc_attr($this->settings['notice_offset']);
            $css .= "\ndiv#edit-snippet-form-container div#message { top: {$offset}px; }";

            wp_add_inline_style('wp-admin', $css);
        }

        /**
         * Add JavaScript for auto-dismissing notices
         *
         * @since 2.0.0
         * @param string $hook The current admin page
         */
        public function add_snippet_page_scripts($hook) {
            if (!$this->is_snippet_page($hook)) {
                return;
            }

            $delay = esc_js($this->settings['dismiss_delay']);
            
            $js = <<<'JAVASCRIPT'
window.addEventListener("load", function() {
    (function createAutoDismissScope() {
        "use strict";
        
        function autoDismissNotice(notice) {
            if (!notice) return;
            
            setTimeout(() => {
                notice.classList.add('notice-dismissing');
                setTimeout(() => {
                    notice.style.display = 'none';
                }, 300);
            }, DISMISS_DELAY);
        }

        function initAutoDismiss() {
            const notices = document.querySelectorAll('#edit-snippet-form-container #message');
            notices.forEach(autoDismissNotice);

            // Watch for new notices
            const observer = new MutationObserver((mutations) => {
                mutations.forEach((mutation) => {
                    mutation.addedNodes.forEach((node) => {
                        if (node.id === 'message') {
                            autoDismissNotice(node);
                        }
                    });
                });
            });

            const container = document.getElementById('edit-snippet-form-container');
            if (container) {
                observer.observe(container, { childList: true, subtree: true });
            }
        }

        initAutoDismiss();
    })();
});
JAVASCRIPT;

            // Add dynamic delay value
            $js = "const DISMISS_DELAY = {$delay};\n" . $js;
            
            wp_add_inline_script('jquery', $js);
        }

        /**
         * Check if current page is a snippet editor page
         *
         * @since 2.0.0
         * @param string $hook The current admin page hook
         * @return boolean True if on snippet page
         */
        private function is_snippet_page($hook) {
            return in_array($hook, ['snippets_page_add-snippet', 'snippets_page_edit-snippet']);
        }
    }

endif;

// Initialize if class exists
if (class_exists('WP_Floating_Admin_Notice_With_AutoDismiss')):
    new WP_Floating_Admin_Notice_With_AutoDismiss();
endif;