Skip to content

Proof of concept: Floating buttons and position storage #177

@flipzoom

Description

@flipzoom

Short description of the enhancement

There is a UX problem that has been bothering me at ProcessWire for years. In the editing process of pages, especially those which, for example, become very long due to a repeater, the workflow of saving is very cumbersome.

You edit the content and have to scroll up or down each time to save it. In addition, after each saving, the scroll position is at the top again.

I changed this behavior in a proof of concept. In ProcessPageEdit I clone the current buttons and fade them in as floating buttons on the left side when the original buttons are no longer visible. This saves the user a lot of time.

In addition, I use a cookie to save the current scroll position of the page, which is restored after saving the page. When you leave the site, this cookie is removed.

This behavior makes the editing of pages from my experience and feedback from my customers much more effective. This allows users to change content faster, save and view their changes.

Here is a screencast:

proof-of-concept-float-buttons-and-position-saving

What do you think of this modification?

Here is the changed code, which of course can be improved significantly. But for a test hack, it works well for me.

Javascript extension

/**
 * ------------------------------------------------------------------------
 * + Clone save and draft buttons for better UX
 * + Save scroll position
 * ------------------------------------------------------------------------
 */
 $(function(){

    // ------------------------------------------------------------------------
    // Only on page edit
    // ------------------------------------------------------------------------
    if($("body[class*='ProcessPageEdit']").length > 0) {

        // ------------------------------------------------------------------------
        // Check if draft buttons present
        // ------------------------------------------------------------------------
        if(($("button#submit_save") && $("button#submit_save_draft")) || ($("button#Inputfield_submit_publish") && $("button#submit_save_draft"))) {

            // ------------------------------------------------------------------------
            // Create clone holder
            // ------------------------------------------------------------------------
            var cloned          = false, 
                topThreshold    = 100, 
                bottomThreshold = 1500;
            $cloneHolder        = $('<div id="submit_float"></div>');
            $cloneHolder.appendTo('form#ProcessPageEdit');

            // ------------------------------------------------------------------------
            // On scroll events
            // ------------------------------------------------------------------------
            $(document).on('scroll', function(){

                // ------------------------------------------------------------------------
                // If not cloned, create clones
                // ------------------------------------------------------------------------
                if($(document).scrollTop() > topThreshold && !cloned) {

                    $cloneSave      = $("button#submit_save").eq(0).clone(true);
                    $cloneDraft     = $("button#submit_save_draft").eq(0).clone(true);
                    $clonePublish   = $("button#Inputfield_submit_publish").eq(0).clone(true);
                    $clonePreview   = $("a#_ProcessPageEditView").eq(0).clone(true);
                    cloned          = true;

                    $clonePreview.addClass('ui-button ui-widget ui-corner-all pw-head-button ui-state-default preview_float');
                    $cloneSave.attr('id', 'submit_save_float').removeClass('pw-button-dropdown-main');
                    $cloneDraft.attr('id', 'submit_draft_float');
                    $clonePublish.attr('id', 'submit_publish_float').removeClass('pw-button-dropdown-main');

                    $clonePreview.appendTo('div#submit_float');
                    $cloneDraft.appendTo('div#submit_float');
                    $cloneSave.appendTo('div#submit_float');
                    $clonePublish.appendTo('div#submit_float');

                    $cloneDraft.bind('click', function(){
                        $(this).addClass('loader').children().find('i').addClass('fa-spin');
                    });
                    $cloneSave.bind('click', function(){
                        $(this).addClass('loader').children().find('i').addClass('fa-spin');
                    });
                    $clonePublish.bind('click', function(){
                        $(this).addClass('loader').children().find('i').addClass('fa-spin');
                    });
                }

                // ------------------------------------------------------------------------
                // Show clones on scroll
                // ------------------------------------------------------------------------
                if($(document).scrollTop() > topThreshold && !$cloneHolder.hasClass('showit') && $(document).scrollTop() < ($('html').prop('scrollHeight') - bottomThreshold)) {
                    $cloneHolder.addClass('showit');

                // ------------------------------------------------------------------------
                // Hide clones on top and bittom
                // -------------------------------------------------------------------------
                } else if($(document).scrollTop() > ($('html').prop('scrollHeight') - bottomThreshold) && $cloneHolder.hasClass('showit') || $(document).scrollTop() < topThreshold && $cloneHolder.hasClass('showit')) {
                    $cloneHolder.removeClass('showit');
                }
            });
        }

        // ------------------------------------------------------------------------
        // Get page edit id on process-page-edit
        // ------------------------------------------------------------------------
        var pageEditID  = $("body").attr("class").match(/ProcessPageEdit-id-[\w-]*\b/), 
            pageEditID  = pageEditID[0];

        // ------------------------------------------------------------------------
        // Check if position is present on page load
        // ------------------------------------------------------------------------
        if($.cookie("process-edit-scroll-position")) {

            var scrollData = $.cookie("process-edit-scroll-position");

            // ------------------------------------------------------------------------
            // If same page stored, scroll to position
            // ------------------------------------------------------------------------
            if(scrollData["id"] == pageEditID) {
                $(document).scrollTop(scrollData["position"]);

            // ------------------------------------------------------------------------
            // Else delete cookie
            // ------------------------------------------------------------------------
            } else {
                $.cookie("process-edit-scroll-position", null);
            }
        }

        // ------------------------------------------------------------------------
        // Bind scroll event and save current scroll position after scroll 
        // event ended
        // ------------------------------------------------------------------------
        $(window).scroll(function() {
            clearTimeout($.data(this, 'scrollTimer'));
            $.data(this, 'scrollTimer', setTimeout(function() {
                $.cookie("process-edit-scroll-position", {"id": pageEditID, "position": $(document).scrollTop()}, {expires: 21600});
            }, 250));
        });

    // ------------------------------------------------------------------------
    // No page edit, delete cookie
    // ------------------------------------------------------------------------
    } else {
        $.cookie("process-edit-scroll-position", null);
    }
 });

CSS (as LESS) extension

// ------------------------------------------------------------------------
// Backend floating save buttons
// ------------------------------------------------------------------------
div#submit_float {
    position: fixed;
    z-index: 100;
    left: 30px;
    bottom: 30px;
    opacity: 0;
    transition: opacity .3s ease;

    &.showit {
        opacity: 1;
    }

    button, 
    a {
        float: none;
        display: block;
        border-radius: 50%;
        width: 85px;
        height: 85px;
        font-size: .75rem !important;
        padding: .1rem !important;
        box-shadow: 0 0 20px rgba(0,0,0,.3) !important;

        &.preview_float {
            padding-top: 22px !important;
            background: #6d6d6d !important;
            border-color: #6d6d6d !important;

            &:hover {
                background: #828282 !important;
                border-color: #828282 !important;
            }

            &:before {
                display: block;
                font-size: 1.6rem!important;
                margin-bottom: .2rem;
                font: normal normal normal 14px/1 FontAwesome;
                content: "\f06e";
            }
        }

        span#_ProcessPageEditViewDropdownToggle {
            display: none;
        }

        span.ui-button-text {
            padding: 0 !important;

            i {
                display: block;
                font-size: 1.6rem !important;
                margin-bottom: .2rem;
            }
        }

        &.loader {
            span.ui-button-text {
                i:before {
                    content: "\f110";
                }
            }
        }

        &#submit_draft_float, 
        &.preview_float {
            margin-bottom: .5rem;
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions