mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Remove the large and unnecessary dependency on React and 87 other related libraries. Fixes #4018
This commit is contained in:
committed by
Dave Page
parent
d7bf6ec69f
commit
4b895941b3
@@ -10,8 +10,8 @@
|
||||
export default class HistoryCollection {
|
||||
|
||||
constructor(history_model) {
|
||||
this.historyList = history_model;
|
||||
this.onChange(() => {});
|
||||
this.historyList = _.sortBy(history_model, o=>o.start_time);
|
||||
this.onAdd(() => {});
|
||||
}
|
||||
|
||||
length() {
|
||||
@@ -19,8 +19,10 @@ export default class HistoryCollection {
|
||||
}
|
||||
|
||||
add(object) {
|
||||
this.historyList.push(object);
|
||||
this.onChangeHandler(this.historyList);
|
||||
/* add object in sorted order */
|
||||
let pushAt = _.sortedIndex(this.historyList, object, o=>o.start_time);
|
||||
this.historyList.splice(pushAt, 0, object);
|
||||
this.onAddHandler(object);
|
||||
}
|
||||
|
||||
reset() {
|
||||
@@ -28,8 +30,8 @@ export default class HistoryCollection {
|
||||
this.onResetHandler(this.historyList);
|
||||
}
|
||||
|
||||
onChange(onChangeHandler) {
|
||||
this.onChangeHandler = onChangeHandler;
|
||||
onAdd(onAddHandler) {
|
||||
this.onAddHandler = onAddHandler;
|
||||
}
|
||||
|
||||
onReset(onResetHandler) {
|
||||
81
web/pgadmin/static/js/sqleditor/history/query_history.js
Normal file
81
web/pgadmin/static/js/sqleditor/history/query_history.js
Normal file
@@ -0,0 +1,81 @@
|
||||
import QueryHistoryDetails from './query_history_details';
|
||||
import { QueryHistoryEntries } from './query_history_entries';
|
||||
import Split from 'split.js';
|
||||
import gettext from 'sources/gettext';
|
||||
import $ from 'jquery';
|
||||
|
||||
export default class QueryHistory {
|
||||
constructor(parentNode, histModel) {
|
||||
this.parentNode = parentNode;
|
||||
this.histCollection = histModel;
|
||||
this.editorPref = {};
|
||||
|
||||
this.histCollection.onAdd(this.onAddEntry.bind(this));
|
||||
this.histCollection.onReset(this.onResetEntries.bind(this));
|
||||
}
|
||||
|
||||
focus() {
|
||||
if (this.queryHistEntries) {
|
||||
this.queryHistEntries.focus();
|
||||
}
|
||||
}
|
||||
|
||||
onAddEntry(entry) {
|
||||
if (this.histCollection.length() == 1) {
|
||||
this.render();
|
||||
} else if (this.queryHistEntries) {
|
||||
this.queryHistEntries.addEntry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
onResetEntries() {
|
||||
this.isRendered = false;
|
||||
this.queryHistEntries = null;
|
||||
this.queryHistDetails = null;
|
||||
this.render();
|
||||
}
|
||||
|
||||
setEditorPref(editorPref) {
|
||||
this.editorPref = editorPref;
|
||||
if(this.queryHistDetails) {
|
||||
this.queryHistDetails.setEditorPref(this.editorPref);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.histCollection.length() == 0) {
|
||||
this.parentNode.empty()
|
||||
.removeClass('d-flex')
|
||||
.append(
|
||||
`<div class='alert alert-info pg-panel-message'>${gettext(
|
||||
'No history found'
|
||||
)}</div>`
|
||||
);
|
||||
} else {
|
||||
this.parentNode.empty().addClass('d-flex');
|
||||
let $histEntries = $('<div><div>').appendTo(this.parentNode);
|
||||
let $histDetails = $('<div><div>').appendTo(this.parentNode);
|
||||
|
||||
Split([$histEntries[0], $histDetails[0]], {
|
||||
gutterSize: 1,
|
||||
cursor: 'ew-resize',
|
||||
});
|
||||
|
||||
this.queryHistDetails = new QueryHistoryDetails($histDetails);
|
||||
this.queryHistDetails.setEditorPref(this.editorPref);
|
||||
this.queryHistDetails.render();
|
||||
|
||||
this.queryHistEntries = new QueryHistoryEntries($histEntries);
|
||||
this.queryHistEntries.onSelectedChange(
|
||||
(entry => {
|
||||
this.queryHistDetails.setEntry(entry);
|
||||
}).bind(this)
|
||||
);
|
||||
this.queryHistEntries.render();
|
||||
|
||||
this.histCollection.historyList.map((entry)=>{
|
||||
this.queryHistEntries.addEntry(entry);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
177
web/pgadmin/static/js/sqleditor/history/query_history_details.js
Normal file
177
web/pgadmin/static/js/sqleditor/history/query_history_details.js
Normal file
@@ -0,0 +1,177 @@
|
||||
import CodeMirror from 'bundled_codemirror';
|
||||
import clipboard from 'sources/selection/clipboard';
|
||||
import moment from 'moment';
|
||||
import $ from 'jquery';
|
||||
|
||||
export default class QueryHistoryDetails {
|
||||
constructor(parentNode) {
|
||||
this.parentNode = parentNode;
|
||||
this.isCopied = false;
|
||||
this.timeout = null;
|
||||
this.isRendered = false;
|
||||
this.sqlFontSize = null;
|
||||
|
||||
this.editorPref = {
|
||||
'sql_font_size': '1em',
|
||||
};
|
||||
}
|
||||
|
||||
setEntry(entry) {
|
||||
this.entry = entry;
|
||||
if (this.isRendered) {
|
||||
this.selectiveRender();
|
||||
} else {
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
setEditorPref(editorPref={}) {
|
||||
this.editorPref = {
|
||||
...this.editorPref,
|
||||
...editorPref,
|
||||
};
|
||||
|
||||
if(this.query_codemirror) {
|
||||
$(this.query_codemirror.getWrapperElement()).css(
|
||||
'font-size',this.editorPref.sql_font_size
|
||||
);
|
||||
|
||||
this.query_codemirror.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
parseErrorMessage(message) {
|
||||
return message.match(/ERROR:\s*([^\n\r]*)/i)
|
||||
? message.match(/ERROR:\s*([^\n\r]*)/i)[1]
|
||||
: message;
|
||||
}
|
||||
|
||||
formatDate(date) {
|
||||
return moment(date).format('M-D-YY HH:mm:ss');
|
||||
}
|
||||
|
||||
copyAllHandler() {
|
||||
clipboard.copyTextToClipboard(this.entry.query);
|
||||
|
||||
this.clearPreviousTimeout();
|
||||
|
||||
this.updateCopyButton(true);
|
||||
|
||||
this.timeout = setTimeout(() => {
|
||||
this.updateCopyButton(false);
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
clearPreviousTimeout() {
|
||||
if (this.timeout) {
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
updateCopyButton(copied) {
|
||||
if (copied) {
|
||||
this.$copyBtn.attr('class', 'was-copied');
|
||||
this.$copyBtn.text('Copied!');
|
||||
} else {
|
||||
this.$copyBtn.attr('class', 'copy-all');
|
||||
this.$copyBtn.text('Copy All');
|
||||
}
|
||||
}
|
||||
|
||||
updateQueryMetaData() {
|
||||
let itemTemplate = (data, description) => {
|
||||
return `<div class='item'>
|
||||
<span class='value'>${data}</span>
|
||||
<span class='description'>${description}</span>
|
||||
</div>`;
|
||||
};
|
||||
|
||||
this.$metaData.empty().append(
|
||||
`<div class='metadata'>
|
||||
${itemTemplate(this.formatDate(this.entry.start_time), 'Date')}
|
||||
${itemTemplate(
|
||||
this.entry.row_affected.toLocaleString(),
|
||||
'Rows Affected'
|
||||
)}
|
||||
${itemTemplate(this.entry.total_time, 'Duration')}
|
||||
</div>`
|
||||
);
|
||||
}
|
||||
|
||||
updateMessageContent() {
|
||||
this.$message_content
|
||||
.empty()
|
||||
.append(`<pre class='content-value'>${this.entry.message}</pre>`);
|
||||
}
|
||||
|
||||
updateErrorMessage() {
|
||||
if (!this.entry.status) {
|
||||
this.$errMsgBlock.removeClass('d-none');
|
||||
this.$errMsgBlock.empty().append(
|
||||
`<div class='history-error-text'>
|
||||
<span>Error Message</span> ${this.parseErrorMessage(
|
||||
this.entry.message
|
||||
)}
|
||||
</div>`
|
||||
);
|
||||
} else {
|
||||
this.$errMsgBlock.addClass('d-none');
|
||||
this.$errMsgBlock.empty();
|
||||
}
|
||||
}
|
||||
|
||||
selectiveRender() {
|
||||
this.updateErrorMessage();
|
||||
this.updateCopyButton(false);
|
||||
this.updateQueryMetaData();
|
||||
this.query_codemirror.setValue(this.entry.query);
|
||||
this.updateMessageContent();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.entry) {
|
||||
this.parentNode.empty().append(
|
||||
`<div id='query_detail' class='query-detail'>
|
||||
<div class='error-message-block'></div>
|
||||
<div class='metadata-block'></div>
|
||||
<div class='query-statement-block'>
|
||||
<div id='history-detail-query'>
|
||||
<button class='' tabindex=0 accesskey='y'></button>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<hr class='block-divider'/>
|
||||
</div>
|
||||
<div class='message-block'>
|
||||
<div class='message'>
|
||||
<div class='message-header'>Messages</div>
|
||||
<div class='content'></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
);
|
||||
|
||||
this.$errMsgBlock = this.parentNode.find('.error-message-block');
|
||||
this.$copyBtn = this.parentNode.find('#history-detail-query button');
|
||||
this.$copyBtn.off('click').on('click', this.copyAllHandler.bind(this));
|
||||
this.$metaData = this.parentNode.find('.metadata-block');
|
||||
this.query_codemirror = CodeMirror(
|
||||
this.parentNode.find('#history-detail-query div')[0],
|
||||
{
|
||||
tabindex: -1,
|
||||
mode: 'text/x-pgsql',
|
||||
readOnly: true,
|
||||
}
|
||||
);
|
||||
$(this.query_codemirror.getWrapperElement()).css(
|
||||
'font-size',this.editorPref.sql_font_size
|
||||
);
|
||||
this.$message_content = this.parentNode.find('.message-block .content');
|
||||
|
||||
this.isRendered = true;
|
||||
this.selectiveRender();
|
||||
}
|
||||
}
|
||||
}
|
||||
230
web/pgadmin/static/js/sqleditor/history/query_history_entries.js
Normal file
230
web/pgadmin/static/js/sqleditor/history/query_history_entries.js
Normal file
@@ -0,0 +1,230 @@
|
||||
import moment from 'moment';
|
||||
import $ from 'jquery';
|
||||
|
||||
const ARROWUP = 38;
|
||||
const ARROWDOWN = 40;
|
||||
|
||||
export class QueryHistoryEntryDateGroup {
|
||||
constructor(date, groupKey) {
|
||||
this.date = date;
|
||||
this.formatString = 'MMM DD YYYY';
|
||||
this.groupKey = groupKey;
|
||||
}
|
||||
|
||||
getDatePrefix() {
|
||||
let prefix = '';
|
||||
if (this.isDaysBefore(0)) {
|
||||
prefix = 'Today - ';
|
||||
} else if (this.isDaysBefore(1)) {
|
||||
prefix = 'Yesterday - ';
|
||||
}
|
||||
return prefix;
|
||||
}
|
||||
|
||||
getDateFormatted(momentToFormat) {
|
||||
return momentToFormat.format(this.formatString);
|
||||
}
|
||||
|
||||
getDateMoment() {
|
||||
return moment(this.date);
|
||||
}
|
||||
|
||||
isDaysBefore(before) {
|
||||
return (
|
||||
this.getDateFormatted(this.getDateMoment()) ===
|
||||
this.getDateFormatted(moment().subtract(before, 'days'))
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return $(`<div class='query-group' data-key='${this.groupKey}'>
|
||||
<div class='date-label'>${this.getDatePrefix()}${this.getDateFormatted(
|
||||
this.getDateMoment()
|
||||
)}</div>
|
||||
<ul class='query-entries'></ul>
|
||||
</div>`);
|
||||
}
|
||||
}
|
||||
|
||||
export class QueryHistoryItem {
|
||||
constructor(entry) {
|
||||
this.entry = entry;
|
||||
this.$el = null;
|
||||
this.onClickHandler = null;
|
||||
}
|
||||
|
||||
onClick(onClickHandler) {
|
||||
this.onClickHandler = onClickHandler;
|
||||
if (this.$el) {
|
||||
this.$el.off('click').on('click', e => {
|
||||
this.onClickHandler($(e.currentTarget));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
formatDate(date) {
|
||||
return moment(date).format('HH:mm:ss');
|
||||
}
|
||||
|
||||
render() {
|
||||
this.$el = $(
|
||||
`<li class='list-item' tabindex='0' data-key='${this.formatDate(this.entry.start_time)}'>
|
||||
<div class='entry ${this.entry.status ? '' : 'error'}'>
|
||||
<div class='query'>${this.entry.query}</div>
|
||||
<div class='other-info'>
|
||||
<div class='timestamp'>${this.formatDate(this.entry.start_time)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>`
|
||||
)
|
||||
.data('entrydata', this.entry)
|
||||
.on('click', e => {
|
||||
this.onClickHandler($(e.currentTarget));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class QueryHistoryEntries {
|
||||
constructor(parentNode) {
|
||||
this.parentNode = parentNode;
|
||||
this.$selectedItem = null;
|
||||
this.groupKeyFormat = 'YYYY MM DD';
|
||||
|
||||
this.$el = null;
|
||||
}
|
||||
|
||||
onSelectedChange(onSelectedChangeHandler) {
|
||||
this.onSelectedChangeHandler = onSelectedChangeHandler;
|
||||
}
|
||||
|
||||
focus() {
|
||||
let self = this;
|
||||
|
||||
if (!this.$selectedItem) {
|
||||
this.setSelectedListItem(this.$el.find('.list-item').first());
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
self.$selectedItem.trigger('click');
|
||||
}, 500);
|
||||
}
|
||||
|
||||
isArrowDown(event) {
|
||||
return (event.keyCode || event.which) === ARROWDOWN;
|
||||
}
|
||||
|
||||
isArrowUp(event) {
|
||||
return (event.keyCode || event.which) === ARROWUP;
|
||||
}
|
||||
|
||||
navigateUpAndDown(event) {
|
||||
let arrowKeys = [ARROWUP, ARROWDOWN];
|
||||
let key = event.keyCode || event.which;
|
||||
if (arrowKeys.indexOf(key) > -1) {
|
||||
event.preventDefault();
|
||||
this.onKeyDownHandler(event);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
onKeyDownHandler(event) {
|
||||
if (this.isArrowDown(event)) {
|
||||
if (this.$selectedItem.next().length > 0) {
|
||||
this.setSelectedListItem(this.$selectedItem.next());
|
||||
} else {
|
||||
/* if last, jump to next group */
|
||||
let $group = this.$selectedItem.closest('.query-group');
|
||||
if ($group.next().length > 0) {
|
||||
this.setSelectedListItem(
|
||||
$group.next().find('.list-item').first()
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (this.isArrowUp(event)) {
|
||||
if (this.$selectedItem.prev().length > 0) {
|
||||
this.setSelectedListItem(this.$selectedItem.prev());
|
||||
} else {
|
||||
/* if first, jump to prev group */
|
||||
let $group = this.$selectedItem.closest('.query-group');
|
||||
if ($group.prev().length > 0) {
|
||||
this.setSelectedListItem(
|
||||
$group.prev().find('.list-item').last()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onSelectListItem(event) {
|
||||
this.setSelectedListItem($(event.currentTarget));
|
||||
}
|
||||
|
||||
dateAsGroupKey(date) {
|
||||
return moment(date).format(this.groupKeyFormat);
|
||||
}
|
||||
|
||||
setSelectedListItem($listItem) {
|
||||
if (this.$selectedItem) {
|
||||
this.$selectedItem.removeClass('selected');
|
||||
}
|
||||
$listItem.addClass('selected');
|
||||
this.$selectedItem = $listItem;
|
||||
this.$selectedItem[0].scrollIntoView(false);
|
||||
|
||||
if (this.onSelectedChangeHandler) {
|
||||
this.onSelectedChangeHandler(this.$selectedItem.data('entrydata'));
|
||||
}
|
||||
}
|
||||
|
||||
addEntry(entry) {
|
||||
/* Add the entry in respective date group in descending sorted order. */
|
||||
let groups = this.$el.find('.query-group');
|
||||
let groupsKeys = $.map(groups, group => {
|
||||
return $(group).attr('data-key');
|
||||
});
|
||||
let entryGroupKey = this.dateAsGroupKey(entry.start_time);
|
||||
let groupIdx = _.indexOf(groupsKeys, entryGroupKey);
|
||||
|
||||
let $groupEl = null;
|
||||
/* if no groups present */
|
||||
if (groups.length == 0) {
|
||||
$groupEl = new QueryHistoryEntryDateGroup(
|
||||
entry.start_time,
|
||||
entryGroupKey
|
||||
).render();
|
||||
this.$el.prepend($groupEl);
|
||||
} else if (groupIdx < 0 && groups.length != 0) {
|
||||
/* if groups are present, but this is a new group */
|
||||
$groupEl = new QueryHistoryEntryDateGroup(
|
||||
entry.start_time,
|
||||
entryGroupKey
|
||||
).render();
|
||||
if (groups[groupIdx]) {
|
||||
$groupEl.insertBefore(groups[groupIdx]);
|
||||
} else {
|
||||
this.$el.prepend($groupEl);
|
||||
}
|
||||
} else if (groupIdx >= 0) {
|
||||
/* if groups present, but this is a new one */
|
||||
$groupEl = $(groups[groupIdx]);
|
||||
}
|
||||
|
||||
let newItem = new QueryHistoryItem(entry);
|
||||
newItem.onClick(this.setSelectedListItem.bind(this));
|
||||
newItem.render();
|
||||
|
||||
$groupEl.find('.query-entries').prepend(newItem.$el);
|
||||
this.setSelectedListItem(newItem.$el);
|
||||
}
|
||||
|
||||
render() {
|
||||
let self = this;
|
||||
self.$el = $(`
|
||||
<div id='query_list' class='query-history' tabindex='0'>
|
||||
</div>
|
||||
`).on('keydown', this.navigateUpAndDown.bind(this));
|
||||
|
||||
self.parentNode.empty().append(self.$el);
|
||||
}
|
||||
}
|
||||
@@ -173,7 +173,9 @@ function updateUIPreferences(sqlEditor) {
|
||||
sqlEditor.query_tool_obj.refresh();
|
||||
|
||||
/* Render history to reflect Font size change */
|
||||
sqlEditor.render_history_grid();
|
||||
sqlEditor.historyComponent.setEditorPref({
|
||||
'sql_font_size' : sql_font_size,
|
||||
});
|
||||
}
|
||||
|
||||
export {updateUIPreferences};
|
||||
|
||||
Reference in New Issue
Block a user