mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-24 17:04:14 +01:00
5e3c67a5cf
classes are now uppercase and in their own files. lowercase classes are deprecated. Interfaces are now actual interfaces that should be implemented instead of creating and returning an ai Object every time
292 lines
12 KiB
TypeScript
292 lines
12 KiB
TypeScript
/**
|
|
* EGroupware egw_action framework - egw action framework
|
|
*
|
|
* @link https://www.egroupware.org
|
|
* @author Andreas Stöckel <as@stylite.de>
|
|
* @copyright 2011 by Andreas Stöckel
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
* @package egw_action
|
|
*/
|
|
|
|
import {EgwActionImplementation} from "./EgwActionImplementation";
|
|
import {egw} from "../jsapi/egw_global";
|
|
import {EgwActionObjectInterface} from "./EgwActionObjectInterface";
|
|
|
|
export class EgwDragActionImplementation implements EgwActionImplementation {
|
|
type = "drag";
|
|
helper: HTMLDivElement = null;
|
|
ddTypes: any[] = [];
|
|
selected: any[] = [];
|
|
defaultDDHelper: (_selected) => HTMLDivElement = (_selected) => {
|
|
// Table containing clone of rows
|
|
const table: HTMLTableElement = (document.createElement("table"));
|
|
table.classList.add('egwGridView_grid', 'et2_egw_action_ddHelper_row');
|
|
// tr element to use as last row to show 'more ...' label
|
|
const moreRow: HTMLTableRowElement = (document.createElement('tr'))
|
|
moreRow.classList.add('et2_egw_action_ddHelper_moreRow');
|
|
// Main div helper container
|
|
const div: HTMLDivElement = (document.createElement("div"));
|
|
div.append(table);
|
|
|
|
let rows = [];
|
|
// Maximum number of rows to show
|
|
let maxRows = 3;
|
|
// item label
|
|
const itemLabel = egw.lang(
|
|
(
|
|
egw.link_get_registry(egw.app_name(), _selected.length > 1 ? 'entries' : 'entry') || egw.app_name()
|
|
) as string
|
|
);
|
|
let index = 0;
|
|
|
|
// Take select all into account when counting number of rows, because they may not be
|
|
// in _selected object
|
|
const pseudoNumRows = (_selected[0]?._context?._selectionMgr?._selectAll) ?
|
|
_selected[0]._context?._selectionMgr?._total : _selected.length;
|
|
|
|
for (const egwActionObject of _selected) {
|
|
const row: Node = (egwActionObject.iface.getDOMNode()).cloneNode(true);
|
|
if (row) {
|
|
rows.push(row);
|
|
table.append(row);
|
|
}
|
|
index++;
|
|
if (index == maxRows) {
|
|
// Label to show number of items
|
|
const spanCnt = (document.createElement('span'))
|
|
spanCnt.classList.add('et2_egw_action_ddHelper_itemsCnt')
|
|
div.append(spanCnt);
|
|
|
|
spanCnt.textContent = (pseudoNumRows + ' ' + itemLabel);
|
|
// Number of not shown rows
|
|
const restRows = pseudoNumRows - maxRows;
|
|
if (restRows > 0) {
|
|
moreRow.textContent = egw.lang(`${pseudoNumRows - maxRows} more ${itemLabel} selected ...`);
|
|
}
|
|
table.append(moreRow);
|
|
break;
|
|
}
|
|
}
|
|
|
|
const text = (document.createElement('div'))
|
|
text.classList.add('et2_egw_action_ddHelper_tip');
|
|
div.append(text);
|
|
|
|
// Add notice of Ctrl key, if supported
|
|
if ('draggable' in document.createElement('span') &&
|
|
navigator && navigator.userAgent.indexOf('Chrome') >= 0 && egw.app_name() == 'filemanager') // currently only filemanager supports drag out
|
|
{
|
|
if (rows.length == 1)
|
|
{
|
|
text.textContent=(egw.lang('You may drag file out to your desktop', itemLabel));
|
|
}
|
|
else
|
|
{
|
|
text.textContent=(egw.lang('Note: If you drag out these selected rows to desktop only the first selected row will be downloaded.', itemLabel));
|
|
}
|
|
}
|
|
// Final html DOM return as helper structure
|
|
return div;
|
|
};
|
|
|
|
registerAction: (_actionObjectInterface: EgwActionObjectInterface, _triggerCallback: Function, _context: any) => boolean = (_aoi, _callback, _context) => {
|
|
const node = _aoi.getDOMNode() && _aoi.getDOMNode()[0] ? _aoi.getDOMNode()[0] : _aoi.getDOMNode();
|
|
|
|
if (node) {
|
|
// Prevent selection
|
|
node.onselectstart = function () {
|
|
return false;
|
|
};
|
|
if (!(window.FileReader && 'draggable' in document.createElement('span'))) {
|
|
// No DnD support
|
|
return;
|
|
}
|
|
|
|
// It shouldn't be so hard to get the action...
|
|
let action = null;
|
|
const groups = _context.getActionImplementationGroups();
|
|
if (!groups.drag) {
|
|
return;
|
|
}
|
|
|
|
// Bind mouse handlers
|
|
//TODO can i just remove jquery.off??
|
|
//jQuery(node).off("mousedown")
|
|
node.addEventListener("mousedown", (event) => {
|
|
if (_context.isSelection(event)) {
|
|
node.setAttribute("draggable", false);
|
|
} else if (event.which != 3) {
|
|
document.getSelection().removeAllRanges();
|
|
}
|
|
})
|
|
node.addEventListener("mouseup", (event) => {
|
|
if (_context.isSelection(event) && document.getSelection().type === 'Range') {
|
|
//let the draggable be reactivated by another click up as the range selection is
|
|
// not working as expected in shadow-dom as expected in all browsers
|
|
} else {
|
|
node.setAttribute("draggable", true);
|
|
}
|
|
|
|
// Set cursor back to auto. Seems FF can't handle cursor reversion
|
|
document.body.style.cursor = 'auto'
|
|
})
|
|
|
|
|
|
node.setAttribute('draggable', true);
|
|
const ai = this
|
|
const dragstart = function (event) {
|
|
|
|
// The helper function is called before the start function
|
|
// is evoked. Call the given callback function. The callback
|
|
// function will gather the selected elements and action links
|
|
// and call the doExecuteImplementation function. This
|
|
// will call the onExecute function of the first action
|
|
// in order to obtain the helper object (stored in ai.helper)
|
|
// and the multiple dragDropTypes (ai.ddTypes)
|
|
_callback.call(_context, false, ai);
|
|
|
|
if (action && egw.app_name() == 'filemanager') {
|
|
if (_context.isSelection(event)) return;
|
|
|
|
// Get all selected
|
|
const selected = ai.selected;
|
|
|
|
// Set file data
|
|
for (let i = 0; i < 1; i++) {
|
|
let d = selected[i].data || egw.dataGetUIDdata(selected[i].id).data || {};
|
|
if (d && d.mime && d.download_url) {
|
|
let url = d.download_url;
|
|
|
|
// NEED an absolute URL
|
|
if (url[0] == '/') url = egw.link(url);
|
|
// egw.link adds the webserver, but that might not be an absolute URL - try again
|
|
if (url[0] == '/') url = window.location.origin + url;
|
|
event.dataTransfer.setData("DownloadURL", d.mime + ':' + d.name + ':' + url);
|
|
}
|
|
}
|
|
event.dataTransfer.effectAllowed = 'copy';
|
|
|
|
if (event.dataTransfer.types.length == 0) {
|
|
// No file data? Abort: drag does nothing
|
|
event.preventDefault();
|
|
return;
|
|
}
|
|
} else {
|
|
event.dataTransfer.effectAllowed = 'linkMove';
|
|
}
|
|
|
|
|
|
const data = {
|
|
ddTypes: ai.ddTypes,
|
|
selected: ai.selected.map((item) => {
|
|
return {id: item.id}
|
|
})
|
|
};
|
|
|
|
if (!ai.helper) {
|
|
ai.helper = ai.defaultDDHelper(ai.selected);
|
|
}
|
|
// Add a basic class to the helper in order to standardize the background layout
|
|
ai.helper.classList.add('et2_egw_action_ddHelper', 'ui-draggable-dragging');
|
|
document.body.append(ai.helper);
|
|
this.classList.add('drag--moving');
|
|
|
|
event.dataTransfer.setData('application/json', JSON.stringify(data))
|
|
|
|
event.dataTransfer.setDragImage(ai.helper, 12, 12);
|
|
|
|
this.setAttribute('data-egwActionObjID', JSON.stringify(data.selected));
|
|
};
|
|
|
|
const dragend = (_) => {
|
|
const helper = document.querySelector('.et2_egw_action_ddHelper');
|
|
if (helper) helper.remove();
|
|
const draggable = document.querySelector('.drag--moving');
|
|
if (draggable) draggable.classList.remove('drag--moving');
|
|
// cleanup drop hover class from all other DOMs if there's still anything left
|
|
Array.from(document.getElementsByClassName('et2dropzone drop-hover')).forEach(_i=>{_i.classList.remove('drop-hover')})
|
|
};
|
|
|
|
// Drag Event listeners
|
|
node.addEventListener('dragstart', dragstart, false);
|
|
node.addEventListener('dragend', dragend, false);
|
|
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
unregisterAction: (_actionObjectInterface: EgwActionObjectInterface) => boolean =(_aoi) => {
|
|
const node = _aoi.getDOMNode();
|
|
|
|
if (node) {
|
|
node.setAttribute('draggable', "false");
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Builds the context menu and shows it at the given position/DOM-Node.
|
|
*
|
|
* @param {string} _context
|
|
* @param {array} _selected
|
|
* @param {object} _links
|
|
*/
|
|
executeImplementation: (_context: any, _selected: any, _links: any) => any = (_context, _selected, _links) => {
|
|
// Reset the helper object of the action implementation
|
|
this.helper = null;
|
|
let hasLink = false;
|
|
|
|
// Store the drag-drop types
|
|
this.ddTypes = [];
|
|
this.selected = _selected;
|
|
|
|
// Call the onExecute event of the first actionObject
|
|
for (const k in _links) {
|
|
if (_links[k].visible) {
|
|
hasLink = true;
|
|
|
|
// Only execute the following code if a JS function is registered
|
|
// for the action and this is the first action link
|
|
if (!this.helper && _links[k].actionObj.onExecute.hasHandler()) {
|
|
this.helper = _links[k].actionObj.execute(_selected);
|
|
}
|
|
|
|
// Push the dragType of the associated action object onto the
|
|
// drag type list - this allows an element to support multiple
|
|
// drag/drop types.
|
|
const type: string[] = Array.isArray(_links[k].actionObj.dragType)
|
|
? _links[k].actionObj.dragType
|
|
: [_links[k].actionObj.dragType];
|
|
for (const i of type) {
|
|
if (this.ddTypes.indexOf(i) === -1) {
|
|
this.ddTypes.push(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// If no helper has been defined, create a default one
|
|
if (!this.helper && hasLink) {
|
|
this.helper = this.defaultDDHelper(_selected);
|
|
}
|
|
|
|
return true;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @deprecated use upper case class
|
|
*/
|
|
export class egwDragActionImplementation extends EgwDragActionImplementation {
|
|
}
|
|
|
|
let _dragActionImpl = null
|
|
|
|
export function getDragImplementation():EgwDragActionImplementation {
|
|
if (!_dragActionImpl) {
|
|
_dragActionImpl = new EgwDragActionImplementation();
|
|
}
|
|
return _dragActionImpl
|
|
}
|