Allow some objects to be dragged/dropped into the Query Tool to insert their signature into the query text. Fixes #4139

This commit is contained in:
Aditya Toshniwal
2019-06-27 10:30:05 -04:00
committed by Dave Page
parent bdb8f20aed
commit 173b812b93
10 changed files with 390 additions and 3 deletions

View File

@@ -8,6 +8,7 @@
//////////////////////////////////////////////////////////////////////////
import {isValidData} from 'sources/utils';
import $ from 'jquery';
export class TreeNode {
constructor(id, data, domNode, parent) {
@@ -97,6 +98,87 @@ export class Tree {
constructor() {
this.rootNode = new TreeNode(undefined, {});
this.aciTreeApi = undefined;
this.draggableTypes = {};
}
/*
*
* The dropDetailsFunc should return an object of sample
* {text: 'xyz', cur: {from:0, to:0} where text is the drop text and
* cur is selection range of text after dropping. If returned as
* string, by default cursor will be set to the end of text
*/
registerDraggableType(typeOrTypeDict, dropDetailsFunc=null) {
if(typeof typeOrTypeDict == 'object') {
Object.keys(typeOrTypeDict).forEach((type)=>{
this.registerDraggableType(type, typeOrTypeDict[type]);
});
} else {
if(dropDetailsFunc != null) {
typeOrTypeDict.replace(/ +/, ' ').split(' ').forEach((type)=>{
this.draggableTypes[type] = dropDetailsFunc;
});
}
}
}
getDraggable(type) {
if(this.draggableTypes[type]) {
return this.draggableTypes[type];
} else {
return null;
}
}
prepareDraggable(data, item) {
let dropDetailsFunc = this.getDraggable(data._type);
if(dropDetailsFunc != null) {
item.find('.aciTreeItem')
.attr('draggable', true)
.on('dragstart', (e)=> {
let dropDetails = dropDetailsFunc(data, item);
let origEvent = e.originalEvent;
if(typeof dropDetails == 'string') {
dropDetails = {
text:dropDetails,
cur:{
from:dropDetails.length,
to: dropDetails.length,
},
};
} else {
if(!dropDetails.cur) {
dropDetails = {
...dropDetails,
cur:{
from:dropDetails.text.length,
to: dropDetails.text.length,
},
};
}
}
origEvent.dataTransfer.setData('text', JSON.stringify(dropDetails));
/* setDragImage is not supported in IE. We leave it to
* its default look and feel
*/
if(origEvent.dataTransfer.setDragImage) {
let dragItem = $(`
<div class="drag-tree-node">
<span>${dropDetails.text}</span>
</div>`
);
$('body .drag-tree-node').remove();
$('body').append(dragItem);
origEvent.dataTransfer.setDragImage(dragItem[0], 0, 0);
}
});
}
}
addNewNode(id, data, domNode, parentPath) {
@@ -163,6 +245,9 @@ export class Tree {
if (eventName === 'added') {
const id = api.getId(item);
const data = api.itemData(item);
this.prepareDraggable(data, item);
const parentId = this.translateTreeNodeIdFromACITree(api.parent(item));
this.addNewNode(id, data, item, parentId);
}

View File

@@ -8,6 +8,7 @@
//////////////////////////////////////////////////////////////////////////
import _ from 'underscore';
import { getTreeNodeHierarchyFromIdentifier } from 'sources/tree/pgadmin_tree_node';
export function parseShortcutValue(obj) {
var shortcut = '';
@@ -83,3 +84,118 @@ export function getGCD(inp_arr) {
export function getMod(no, divisor) {
return ((no % divisor) + divisor) % divisor;
}
export function parseFuncParams(label) {
let paramArr = [],
funcName = '',
paramStr = '';
if(label.endsWith('()')) {
funcName = label.substring(0, label.length-2);
} else if(!label.endsWith(')')) {
funcName = label;
} else if(!label.endsWith('()') && label.endsWith(')')) {
let i = 0,
startBracketPos = label.length;
/* Parse through the characters in reverse to find the param start bracket */
i = label.length-2;
while(i >= 0) {
if(label[i] == '(') {
startBracketPos = i;
break;
} else if(label[i] == '"') {
/* If quotes, skip all the chars till next quote */
i--;
while(label[i] != '"') i--;
}
i--;
}
funcName = label.substring(0, startBracketPos);
paramStr = label.substring(startBracketPos+1, label.length-1);
let paramStart = 0,
paramName = '',
paramModes = ['IN', 'OUT', 'INOUT', 'VARIADIC'];
paramStart = i = 0;
while(i < paramStr.length) {
if(paramStr[i] == '"') {
/* If quotes, skip all the chars till next quote */
i++;
while(paramStr[i] != '"') i++;
} else if (paramStr[i] == ' ') {
/* if paramName is already set, ignore till comma
* Or if paramName is parsed as one of the modes, reset.
*/
if(paramName == '' || paramModes.indexOf(paramName) > -1 ) {
paramName = paramStr.substring(paramStart, i);
paramStart = i+1;
}
}
else if (paramStr[i] == ',') {
paramArr.push([paramName, paramStr.substring(paramStart, i)]);
paramName = '';
paramStart = i+1;
}
i++;
}
paramArr.push([paramName, paramStr.substring(paramStart)]);
}
return {
'func_name': funcName,
'param_string': paramStr,
'params': paramArr,
};
}
export function quote_ident(value) {
/* check if the string is number or not */
let quoteIt = false;
if (!isNaN(parseInt(value))){
quoteIt = true;
}
if(value.search(/[^a-z0-9_]/g) > -1) {
/* escape double quotes */
value = value.replace(/"/g, '""');
quoteIt = true;
}
if(quoteIt) {
return `"${value}"`;
} else {
return value;
}
}
export function fully_qualify(pgBrowser, data, item) {
const parentData = getTreeNodeHierarchyFromIdentifier.call(pgBrowser, item);
let namespace = '';
if (parentData.schema !== undefined) {
namespace = quote_ident(parentData.schema._label);
}
else if (parentData.view !== undefined) {
namespace = quote_ident(parentData.view._label);
}
else if (parentData.catalog !== undefined) {
namespace = quote_ident(parentData.catalog._label);
}
if (parentData.package !== undefined && data._type != 'package') {
if(namespace == '') {
namespace = quote_ident(parentData.package._label);
} else {
namespace += '.' + quote_ident(parentData.package._label);
}
}
if(namespace != '') {
return namespace + '.' + quote_ident(data._label);
} else {
return quote_ident(data._label);
}
}