/** @module util/dragdrop */
import React from 'react';
import ReactDOM from 'react-dom';
import { Icon, message } from 'react-aiot';
import { ajax, i18n, rmlOpts } from '.';
import $ from 'jquery';
/**
* On CTRL holding add class 'aiot-helper-method-append' to document body.
*/
export function anyKeyHolding() {
$(document).on('keydown',e => $('body').addClass('aiot-helper-method-append'));
$(document).on('keyup', e => $('body').removeClass('aiot-helper-method-append'));
}
/**
* jQuery's draggable helper container.
*
* @param {object} props Properties
* @param {int} props.count The count
* @type React.Element
*/
const DragHelper = ({ count }) => (<div>
<div className="aiot-helper-method-move">
<Icon type="swap" /> { i18n(count > 1 ? 'move' : 'moveOne', { count }) }
<p>{ i18n('moveTip') }</p>
</div>
<div className="aiot-helper-method-append">
<Icon type="copy" /> { i18n(count > 1 ? 'append' : 'appendOne', { count }) }
<p>{ i18n('appendTip') }</p>
</div>
</div>);
/**
* Enables / Reinitializes the droppable nodes. If a draggable item is dropped
* here the given posts are moved to the category. You have to provide a ReactJS
* element to reload the tree.
*
* @param {React.Element} element The element
*/
export function droppable(element) {
const dom = $(element.ref.container).find('.aiot-node.aiot-droppable[data-id!=\'all\']'),
{ attachmentsBrowser } = element;
dom.droppable({
activeClass: 'aiot-state-default',
hoverClass: 'aiot-state-hover',
tolerance: 'pointer',
drop: async function(event, ui) {
const ids = [],
toTmp = $(event.target).attr('data-id'),
to = toTmp === 'all' ? toTmp : +toTmp,
activeId = element.getSelectedId(),
elements = [],
fnFade = percent => elements.forEach(obj => obj.fadeTo(250, percent)),
isCopy = $('body').hasClass('aiot-helper-method-append'),
{ store } = element.props;
// Get dragged items
iterateDraggedItem(ui.draggable, element, tr => {
ids.push(+tr.find('input[name="media[]"]').attr("value"));
elements.push(tr);
}, (attributes, attachmentsBrowser) => {
ids.push(attributes.id);
elements.push(attachmentsBrowser.$el.find('li[data-id="' + attributes.id + '"]'));
});
element.setState({ isTreeLinkDisabled: true }); // Disable tree
fnFade(0.3);
// Make folders updateable in grid mode
if (attachmentsBrowser) {
// If the target is "Uncategorized" the current folder has to be refreshed, too
store.addFoldersNeedsRefresh(to);
to === +rmlOpts.rootId && store.addFoldersNeedsRefresh(activeId);
}
// Get i18n key
const isOne = ids.length === 1, i18nProps = {
count: ids.length,
category: $(event.target).find('.aiot-node-name').html()
}, i18nGet = key => i18n((isCopy ? 'append' : 'move') + key + (isOne ? 'One' : ''), i18nProps);
const hide = message.loading(i18nGet('LoadingText'));
try {
const { counts } = await ajax('attachments/bulk/move', {
method: 'PUT',
data: { ids, to, isCopy }
});
message.success(i18nGet('Success'));
element.fetchCounts(counts);
// Update items view
const fadeBack = isCopy || (!isCopy && activeId === to) || activeId === 'all';
fadeBack ? fnFade(1) : elements.forEach(obj => obj.remove());
// Refresh view if necessery
if ((activeId === 'all' && isCopy) || (isCopy && activeId === to)) {
element.handleReload();
}
// Deselect for the next bulk selection action
elements.forEach(obj => {
let attachmentPreview = obj.children('.attachment-preview');
obj.hasClass('selected') && attachmentPreview.length && attachmentPreview.click();
});
// Add no media
if (!element.attachmentsBrowser && !$(".wp-list-table tbody tr").length) {
$(".wp-list-table tbody").html('<tr class="no-items"><td class="colspanchange" colspan="6">' + rmlOpts.lang.noEntries + '</td></tr></tbody>');
}
} catch (e) {
message.error(e.responseJSON.message);
fnFade(1);
} finally {
hide();
element.setState(prevState => ({
isTreeLinkDisabled: false
})); // Enable tree
}
}
});
}
/*
* Iterates through the UI and gets the collection of dragged items.
*
* @param {jQuery} ui The draggable ui object
* @param {React.Element} container The AIOT container
* @param {function} [listMode] Function to iterate over list mode items (<tr> object)
* @param {function} [gridMode] Function to iterate over grid mode items (attributes, attachmentsBrowser)
* @returns {int} The count of selected items
*/
function iterateDraggedItem(ui, { attachmentsBrowser }, listMode, gridMode) {
if (attachmentsBrowser) {
// Grid mode
const selection = attachmentsBrowser.options.selection.models;
if (selection.length) {
selection.forEach(model => {
gridMode && gridMode(model.attributes, attachmentsBrowser);
});
return selection.length;
}else{
const id = ui.data('id'), models = attachmentsBrowser.collection.models;
gridMode && gridMode(models.filter(model => model.id === id)[0], attachmentsBrowser);
return 1;
}
}else{
// List mode
const trs = $('input[name="media[]"]:checked');
if (trs.length) {
trs.each(function() {
listMode && listMode($(this).parents('tr'));
});
}else{
listMode && listMode(ui);
}
return trs.length || 1;
}
}
/**
* Make the list table draggable if sort mode is not active.
*
* @param {React.Element} element The element
* @param {boolean} [destroy=false] If true the draggable gets destroyed
*/
export function draggable(element, destroy) {
// Get selector
const attachmentsBrowser = element.attachmentsBrowser,
{ isMoveable, isWPAttachmentsSortMode } = element.state,
selector = attachmentsBrowser ? attachmentsBrowser.$el.find('ul.attachments > li')
: $('#wpbody-content .wp-list-table tbody tr:not(.no-items)');
// Make draggable
if (destroy || !isMoveable || isWPAttachmentsSortMode) {
try {
selector.draggable('destroy');
}catch(e) {
// Silence is golden.
}
}else{
selector.draggable({
revert: 'invalid',
revertDuration: 0,
appendTo: 'body',
cursorAt: { top: 0, left: 0 },
distance: 10,
refreshPositions: true,
helper: (event) => {
const helper = $('<div class="aiot-helper"></div>').appendTo($('body')),
count = iterateDraggedItem($(event.currentTarget), element);
ReactDOM.render(<DragHelper count={ count } />, helper.get(0));
return helper;
},
start: event => {
$('body').addClass('aiot-currently-dragging');
// FIX https://bugs.jqueryui.com/ticket/4261
$(document.activeElement).blur();
},
stop: () => {
$('body').removeClass("aiot-currently-dragging");
}
});
}
}