Source: public/src/hooks/sortable.js

/** @module hooks/sortable */

import { i18n, hooks, urlParam, addUrlParam, ajax } from '../util';
import $ from 'jquery';
import store from '../store';
import { message } from 'react-aiot';

const WP_TABLE_LIST_SELECTOR = '.wp-list-table.media tbody';

/**
 * Apply an order to attachments browser without reloading the collection.
 */
export function applyToAttachmentsBrowser(attachmentsBrowser, selected, orderby = 'rml', order = 'ASC') {
    let filter;
    if (attachmentsBrowser && (filter = attachmentsBrowser.toolbar.get('rml_folder').filters[selected.id])) {
        const props = attachmentsBrowser.collection.props, o = { silent: true };
        if (selected.contentCustomOrder === 1) {
            filter.props.orderby = 'rml';
	    	filter.props.order = 'ASC';
	    	props.set({ orderby, order }, o);
        }else{
            delete filter.props.orderby;
	    	delete filter.props.order;
	    	props.set({ orderby: 'date', order: 'DESC' }, o);
        }
	}
}

/**
 * An item gets relocated, so update the table or grid view.
 */
hooks.register('attachment/relocate', (node, attachmentId, nextId, lastIdInView, next, e, ui) => {
    const attachmentsBrowser = $(ui.item).parents(".attachments-browser").data('backboneView');
    ui.item.stop().fadeTo(100, 0.2);
    ajax('attachments/' + attachmentId, {
        method: 'PUT',
        data: {
            folderId: node.id,
            nextId,
            lastId: lastIdInView
        }
    }).done(() => {
        node.setter(node => (node.contentCustomOrder = 1));
        applyToAttachmentsBrowser(attachmentsBrowser, node);
        ui.item.stop().fadeTo(100, 1);
    });
});

/**
 * Prepare sortable in list table mode.
 */
hooks.register('ready', () => {
    let lastIdInView;
    
    // Grid mode
    $(document).on('sortstart', '.attachments-browser ul.attachments', function(e, ui) {
        const { collection } = $(this).parents(".attachments-browser").data('backboneView');
        lastIdInView = collection.models[collection.models.length - 1].id;
    }).on('sortupdate', '.attachments-browser ul.attachments', function(e, ui) {
        const next = ui.item.next(),
            nextId = next.html() ? next.data('id') : false,
            attachmentId = ui.item.data('id'),
            folder = $(this).parents(".attachments-browser").data('backboneView').controller.$RmlAppTree.getTreeItemById();
        
        /**
         * An attachment is relocated and should be saved to the server.
         * 
         * @event module:util/hooks#attachment/relocate
         * @param {module:store/TreeNode~TreeNode} folder The tree node
         * @param {int} attachmentId The attachment id
         * @param {int} nextId The next id
         * @param {int} lastIdInView
         * @param {jQuery} next
         * @this wp.media.view.AttachmentsBrowser
         */
        hooks.call('attachment/relocate', [folder, attachmentId, nextId, lastIdInView, next, e, ui]);
    });
    
    // List mode
    $(WP_TABLE_LIST_SELECTOR).sortable({
        disabled: true,
        appendTo: 'body',
        tolerance: 'pointer',
        scrollSensitivity: 50,
        placeholder: 'ui-sortable-helper-wp-media-list',
        scrollSpeed: 50,
        distance: 10,
        cursor: 'move',
        start: function(e, ui) {
            ui.placeholder.height(ui.helper[0].scrollHeight);
            
            // The last ID (grid mode is done in the backbone collection)
            lastIdInView = +$(WP_TABLE_LIST_SELECTOR + ' tr:last input[name="media[]"]').val();
        },
        update: function(e, ui) {
            const next = ui.item.next(),
                nextId = next.html() ? next.find('input[name="media[]"]').val() : false,
                attachmentId = ui.item.find('input[name="media[]"]').val(),
                folderId = $('.rml-container .aiot-active').data('id');
            
            hooks.call('attachment/relocate', [store.getTreeItemById(folderId), attachmentId, nextId, lastIdInView, next, e, ui]);
        }
    });
});

/**
 * Checks if a filter is active.
 * 
 * @param {object} [attachmentsBrowser] If set the filter is searched in the backbone controller
 */
export function isFilterActive(attachmentsBrowser) {
    if (attachmentsBrowser) {
        const filters = ['monthnum', 'year', 'uploadedTo', 'type'],
            { props } = attachmentsBrowser.collection;
        for (let i = 0; i < filters.length; i++) {
            if (props.get(filters[i])) {
                return true;
            }
        }
        return false;
    }else{
        // List
        return !!urlParam('attachment-filter');
    }
}

/**
 * Checks if a orderby is active.
 * 
 * @param {object} [attachmentsBrowser] If set the filter is searched in the backbone controller
 */
export function isOrderByActive(attachmentsBrowser, orderby = 'rml', order = 'ASC') {
    if (attachmentsBrowser) {
        const { props } = attachmentsBrowser.collection, propOrder = props.get('order') || 'DESC';
        return props.get('orderby') === orderby && propOrder.toUpperCase() === order.toUpperCase();
    }else{
        // List
        const propOrder = urlParam('order') || 'DESC';
        return urlParam('orderby') === orderby && propOrder.toUpperCase() === order.toUpperCase();
    }
}

/**
 * @returns {string}
 */
export function orderUrl(href = window.location.href) {
    return addUrlParam(addUrlParam(href, 'orderby', 'rml'), 'order',  'asc');
}

/**
 * Toggle the sortable mode. Popup a message if custom order is not disabled, yet.
 * If custom order is enabled check the different list and grid mode behavior.
 * 
 * @param {object} selected The selected node
 * @parma {boolean} mode The mode to activate
 * @param {object} [attachmentsBrowser] If set the filter is searched in the backbone controller
 */
export function toggleSortable(selected, mode, attachmentsBrowser) {
    const orderByActive = isOrderByActive(attachmentsBrowser) || isOrderByActive(attachmentsBrowser, 'date', 'DESC'),
        filterActive = isFilterActive(attachmentsBrowser),
        redirect = !orderByActive || filterActive;
    
    // Redirect to the sortable mode
    if (redirect && mode) {
        if (!attachmentsBrowser) {
            const href = orderUrl();
            window.location.href = href + "#order";
        }else{
            // Grid mode, show popup that the filters should be deactivated
            message.error(i18n('orderFilterActive'));
        }
        return false;
    }
    
    // Toggle sortable
    (attachmentsBrowser ? attachmentsBrowser.attachments.$el : $(WP_TABLE_LIST_SELECTOR)).sortable(mode ? 'enable' : 'disable');
    
    return true;
}