mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-20 17:22:51 -05:00
Initial SQL Agent merge for March release (#961)
* WIP * wip * SQL Agent wip * wip * Initial control host (wip) * Initial hookup of SQL Agent service to job component * Update agent package.json * Hook up getJobs call * A couple job view updates * Add some more agent views * Rename some 'agent' classes to 'jobManagement' * job history page (#852) * added back button, run actions and overview accordion * refactoring * overview table complete * fixed the dropdown arrow for the overview section * added table for prev job list * fixed agent job result type * code cleaning and code review comments * fixed yarn.lock conflicts * added function for job history * changed vscode-languageclient version * changed yarn lock file * fixed yarn lock file * fixed yarn file * fixed css paths * added images to packaging step * fix resource path for packaging * Switch back getJobs return type * Make enum const * Remove sqlops const * WIP * WIP * Feature/agent1 adbist (#899) * added back button, run actions and overview accordion * refactoring * overview table complete * fixed the dropdown arrow for the overview section * added table for prev job list * fixed agent job result type * code cleaning and code review comments * fixed yarn.lock conflicts * added function for job history * changed vscode-languageclient version * changed yarn lock file * fixed yarn lock file * fixed yarn file * fixed css paths * added images to packaging step * fix resource path for packaging * added steps lists * fixed style and dimensions * fixed conflicts * implemented job list * added the Date and Status columns * update yarn files * merged feature/agent1 * added theme styling for light theme * changed yarn lock files * made job history page css more specific * Add visiblity check to job view * Clean up jobs styling and call getJobHistory * Add more Job Table styling * Enable detail view in job table * Use updated slickgrid repo * vbumped slickgrid * Convert rowdetail slickgrid plug to TypeScript * Feature/agent1 adbist (#945) * added back button, run actions and overview accordion * refactoring * overview table complete * fixed the dropdown arrow for the overview section * added table for prev job list * fixed agent job result type * code cleaning and code review comments * fixed yarn.lock conflicts * added function for job history * changed vscode-languageclient version * changed yarn lock file * fixed yarn lock file * fixed yarn file * fixed css paths * added images to packaging step * fix resource path for packaging * added steps lists * fixed style and dimensions * fixed conflicts * implemented job list * added the Date and Status columns * update yarn files * merged feature/agent1 * added theme styling for light theme * changed yarn lock files * added method signatures for job history with DMP * added methods for job running * added job actions to sqlops * Refer to dataprotocol from feature/agentDmp1 branch * Update SQL Tools version to 1.4.0-alpha.13 * Change Feb to March in release note prompt * SQL Agent extension metadata * add feature explicitly in client creation * Update Agent job registration * Update package.json * Feature/agent1 adbist (#955) * added back button, run actions and overview accordion * refactoring * overview table complete * fixed the dropdown arrow for the overview section * added table for prev job list * fixed agent job result type * code cleaning and code review comments * fixed yarn.lock conflicts * added function for job history * changed vscode-languageclient version * changed yarn lock file * fixed yarn lock file * fixed yarn file * fixed css paths * added images to packaging step * fix resource path for packaging * added steps lists * fixed style and dimensions * fixed conflicts * implemented job list * added the Date and Status columns * update yarn files * merged feature/agent1 * added theme styling for light theme * changed yarn lock files * added method signatures for job history with DMP * added methods for job running * added job actions to sqlops * navigation works but is really slow to load data * Add jobs view icon * Misc. cleanups
This commit is contained in:
393
src/sql/base/browser/ui/table/plugins/rowDetailView.ts
Normal file
393
src/sql/base/browser/ui/table/plugins/rowDetailView.ts
Normal file
@@ -0,0 +1,393 @@
|
||||
// Adopted and converted to typescript from https://github.com/6pac/SlickGrid/blob/master/plugins/slick.rowdetailview.js
|
||||
// heavily modified
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
|
||||
export class RowDetailView {
|
||||
|
||||
public onAsyncResponse = new Slick.Event<any>();
|
||||
public onAsyncEndUpdate = new Slick.Event<any>();
|
||||
public onAfterRowDetailToggle = new Slick.Event<any>();
|
||||
public onBeforeRowDetailToggle = new Slick.Event<any>();
|
||||
|
||||
private _grid: any;
|
||||
private _expandedRows: any = [];
|
||||
private _handler = new Slick.EventHandler();
|
||||
private _defaults: any = {
|
||||
columnId: '_detail_selector',
|
||||
cssClass: null,
|
||||
toolTip: '',
|
||||
width: 30
|
||||
};
|
||||
|
||||
private _dataView: any;
|
||||
private _options: any;
|
||||
|
||||
constructor(options) {
|
||||
this._options = mixin(options, this._defaults, false);
|
||||
}
|
||||
|
||||
public init(grid: any) {
|
||||
this._grid = grid;
|
||||
this._dataView = this._grid.getData();
|
||||
|
||||
// Update the minRowBuffer so that the view doesn't disappear when it's at top of screen + the original default 3
|
||||
this._grid.getOptions().minRowBuffer = this._options.panelRows + 3;
|
||||
|
||||
this._handler
|
||||
.subscribe(this._grid.onClick, (e, args) => this.handleClick(e, args))
|
||||
.subscribe(this._grid.onSort, (e, args) => this.handleSort(e, args))
|
||||
.subscribe(this._grid.onScroll, (e, args) => this.handleScroll(e, args));
|
||||
|
||||
this._grid.getData().onRowCountChanged.subscribe(() => { this._grid.updateRowCount(); this._grid.render(); });
|
||||
this._grid.getData().onRowsChanged.subscribe((e, a) => { this._grid.invalidateRows(a.rows); this._grid.render(); });
|
||||
|
||||
// subscribe to the onAsyncResponse so that the plugin knows when the user server side calls finished
|
||||
this.subscribeToOnAsyncResponse();
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
this._handler.unsubscribeAll();
|
||||
this.onAsyncResponse.unsubscribe(undefined);
|
||||
this.onAsyncEndUpdate.unsubscribe(undefined);
|
||||
this.onAfterRowDetailToggle.unsubscribe(undefined);
|
||||
this.onBeforeRowDetailToggle.unsubscribe(undefined);
|
||||
}
|
||||
|
||||
public getOptions(options: any) {
|
||||
return this._options;
|
||||
}
|
||||
|
||||
public setOptions(options: any) {
|
||||
this._options = $.extend(true, {}, this._options, options);
|
||||
}
|
||||
|
||||
public handleClick(e: any, args: any) {
|
||||
// clicking on a row select checkbox
|
||||
if (this._options.useRowClick || this._grid.getColumns()[args.cell].id === this._options.columnId && $(e.target).hasClass("detailView-toggle")) {
|
||||
// if editing, try to commit
|
||||
if (this._grid.getEditorLock().isActive() && !this._grid.getEditorLock().commitCurrentEdit()) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
return;
|
||||
}
|
||||
|
||||
var item = this._dataView.getItem(args.row);
|
||||
|
||||
// trigger an event before toggling
|
||||
this.onBeforeRowDetailToggle.notify({
|
||||
grid: this._grid,
|
||||
item: item
|
||||
}, e, this);
|
||||
|
||||
this.toggleRowSelection(item);
|
||||
|
||||
// trigger an event after toggling
|
||||
this.onAfterRowDetailToggle.notify({
|
||||
grid: this._grid,
|
||||
item: item
|
||||
}, e, this);
|
||||
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
}
|
||||
|
||||
// Sort will just collapse all of the open items
|
||||
public handleSort(e, args) {
|
||||
this.collapseAll();
|
||||
}
|
||||
|
||||
// If we scroll save detail views that go out of cache range
|
||||
public handleScroll(e, args) {
|
||||
|
||||
var range = this._grid.getRenderedRange();
|
||||
|
||||
var start = (range.top > 0 ? range.top : 0);
|
||||
var end = (range.bottom > this._dataView.getLength() ? range.bottom : this._dataView.getLength());
|
||||
|
||||
// Get the item at the top of the view
|
||||
var topMostItem = this._dataView.getItemByIdx(start);
|
||||
|
||||
// Check it is a parent item
|
||||
if (topMostItem._parent === undefined)
|
||||
{
|
||||
// This is a standard row as we have no parent.
|
||||
var nextItem = this._dataView.getItemByIdx(start + 1);
|
||||
if(nextItem !== undefined && nextItem._parent !== undefined)
|
||||
{
|
||||
// This is likely the expanded Detail Row View
|
||||
// Check for safety
|
||||
if(nextItem._parent === topMostItem)
|
||||
{
|
||||
this.saveDetailView(topMostItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the bottom most item that is likely to go off screen
|
||||
var bottomMostItem = this._dataView.getItemByIdx(end - 1);
|
||||
|
||||
// If we are a detailView and we are about to go out of cache view
|
||||
if(bottomMostItem._parent !== undefined)
|
||||
{
|
||||
this.saveDetailView(bottomMostItem._parent);
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle between showing and hiding a row
|
||||
public toggleRowSelection(row) {
|
||||
this._grid.getData().beginUpdate();
|
||||
this.handleAccordionShowHide(row);
|
||||
this._grid.getData().endUpdate();
|
||||
}
|
||||
|
||||
// Collapse all of the open items
|
||||
public collapseAll() {
|
||||
for (var i = this._expandedRows.length - 1; i >= 0; i--) {
|
||||
this.collapseItem(this._expandedRows[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Saves the current state of the detail view
|
||||
public saveDetailView(item) {
|
||||
var view = $('#innerDetailView_' + item.id);
|
||||
if (view) {
|
||||
var html = $('#innerDetailView_' + item.id).html();
|
||||
if(html !== undefined) {
|
||||
item._detailContent = html;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Colapse an Item so it is notlonger seen
|
||||
public collapseItem(item) {
|
||||
|
||||
// Save the details on the collapse assuming onetime loading
|
||||
if (this._options.loadOnce) {
|
||||
this.saveDetailView(item);
|
||||
}
|
||||
|
||||
item._collapsed = true;
|
||||
for (let idx = 1; idx <= item._sizePadding; idx++) {
|
||||
this._dataView.deleteItem(item.id + '.' + idx);
|
||||
}
|
||||
item._sizePadding = 0;
|
||||
this._dataView.updateItem(item.id, item);
|
||||
|
||||
// Remove the item from the expandedRows
|
||||
this._expandedRows = this._expandedRows.filter((r) => {
|
||||
return r.id !== item.id;
|
||||
});
|
||||
}
|
||||
|
||||
// Expand a row given the dataview item that is to be expanded
|
||||
public expandItem(item) {
|
||||
item._collapsed = false;
|
||||
this._expandedRows.push(item);
|
||||
|
||||
// In the case something went wrong loading it the first time such a scroll of screen before loaded
|
||||
if (!item._detailContent) {
|
||||
item._detailViewLoaded = false;
|
||||
}
|
||||
|
||||
// display pre-loading template
|
||||
if (!item._detailViewLoaded || this._options.loadOnce !== true) {
|
||||
item._detailContent = this._options.preTemplate(item);
|
||||
} else {
|
||||
this.onAsyncResponse.notify({
|
||||
itemDetail: item,
|
||||
detailView: item._detailContent
|
||||
}, undefined, this);
|
||||
this.applyTemplateNewLineHeight(item);
|
||||
this._dataView.updateItem(item.id, item);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.applyTemplateNewLineHeight(item);
|
||||
this._dataView.updateItem(item.id, item);
|
||||
|
||||
// async server call
|
||||
this._options.process(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* subscribe to the onAsyncResponse so that the plugin knows when the user server side calls finished
|
||||
* the response has to be as "args.itemDetail" with it's data back
|
||||
*/
|
||||
public subscribeToOnAsyncResponse() {
|
||||
this.onAsyncResponse.subscribe((e, args) => {
|
||||
if (!args || !args.itemDetail) {
|
||||
throw 'Slick.RowDetailView plugin requires the onAsyncResponse() to supply "args.itemDetail" property.';
|
||||
}
|
||||
|
||||
// If we just want to load in a view directly we can use detailView property to do so
|
||||
if (args.detailView) {
|
||||
args.itemDetail._detailContent = args.detailView;
|
||||
} else {
|
||||
args.itemDetail._detailContent = this._options.postTemplate(args.itemDetail);
|
||||
}
|
||||
|
||||
args.itemDetail._detailViewLoaded = true;
|
||||
|
||||
var idxParent = this._dataView.getIdxById(args.itemDetail.id);
|
||||
this._dataView.updateItem(args.itemDetail.id, args.itemDetail);
|
||||
|
||||
// trigger an event once the post template is finished loading
|
||||
this.onAsyncEndUpdate.notify({
|
||||
grid: this._grid,
|
||||
itemDetail: args.itemDetail
|
||||
}, e, this);
|
||||
});
|
||||
}
|
||||
|
||||
public handleAccordionShowHide(item) {
|
||||
if (item) {
|
||||
if (!item._collapsed) {
|
||||
this.collapseItem(item);
|
||||
} else {
|
||||
this.expandItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////
|
||||
public getPaddingItem(parent, offset) {
|
||||
var item: any = {};
|
||||
|
||||
for (let prop in this._grid.getData()) {
|
||||
item[prop] = null;
|
||||
}
|
||||
item.id = parent.id + '.' + offset;
|
||||
|
||||
//additional hidden padding metadata fields
|
||||
item._collapsed = true;
|
||||
item._isPadding = true;
|
||||
item._parent = parent;
|
||||
item._offset = offset;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
//create the detail ctr node. this belongs to the dev & can be custom-styled as per
|
||||
//////////////////////////////////////////////////////////////
|
||||
public applyTemplateNewLineHeight(item) {
|
||||
// the height seems to be calculated by the template row count (how many line of items does the template have)
|
||||
let rowCount = this._options.panelRows;
|
||||
|
||||
//calculate padding requirements based on detail-content..
|
||||
//ie. worst-case: create an invisible dom node now &find it's height.
|
||||
let lineHeight = 13; //we know cuz we wrote the custom css innit ;)
|
||||
item._sizePadding = Math.ceil(((rowCount * 2) * lineHeight) / this._grid.getOptions().rowHeight);
|
||||
item._height = (item._sizePadding * this._grid.getOptions().rowHeight);
|
||||
|
||||
let idxParent = this._dataView.getIdxById(item.id);
|
||||
for (var idx = 1; idx <= item._sizePadding; idx++) {
|
||||
this._dataView.insertItem(idxParent + idx, this.getPaddingItem(item, idx));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public getColumnDefinition() {
|
||||
return {
|
||||
id: this._options.columnId,
|
||||
name: '',
|
||||
toolTip: this._options.toolTip,
|
||||
field: 'sel',
|
||||
width: this._options.width,
|
||||
resizable: false,
|
||||
sortable: false,
|
||||
cssClass: this._options.cssClass,
|
||||
formatter: (row, cell, value, columnDef, dataContext) => this.detailSelectionFormatter(row, cell, value, columnDef, dataContext)
|
||||
};
|
||||
}
|
||||
|
||||
public detailSelectionFormatter(row, cell, value, columnDef, dataContext) {
|
||||
|
||||
if (dataContext._collapsed === undefined) {
|
||||
dataContext._collapsed = true,
|
||||
dataContext._sizePadding = 0, //the required number of pading rows
|
||||
dataContext._height = 0, //the actual height in pixels of the detail field
|
||||
dataContext._isPadding = false,
|
||||
dataContext._parent = undefined,
|
||||
dataContext._offset = 0
|
||||
}
|
||||
|
||||
if (dataContext._isPadding === true) {
|
||||
//render nothing
|
||||
} else if (dataContext._collapsed) {
|
||||
return '<div class=\'detailView-toggle expand\'></div>';
|
||||
} else {
|
||||
var html = [];
|
||||
var rowHeight = this._grid.getOptions().rowHeight;
|
||||
var bottomMargin = 5;
|
||||
|
||||
//V313HAX:
|
||||
//putting in an extra closing div after the closing toggle div and ommiting a
|
||||
//final closing div for the detail ctr div causes the slickgrid renderer to
|
||||
//insert our detail div as a new column ;) ~since it wraps whatever we provide
|
||||
//in a generic div column container. so our detail becomes a child directly of
|
||||
//the row not the cell. nice =) ~no need to apply a css change to the parent
|
||||
//slick-cell to escape the cell overflow clipping.
|
||||
|
||||
//sneaky extra </div> inserted here-----------------v
|
||||
html.push("<div class='detailView-toggle collapse'></div></div>");
|
||||
|
||||
html.push("<div id='cellDetailView_", dataContext.id, "' class='dynamic-cell-detail' "); //apply custom css to detail
|
||||
html.push("style='height:", dataContext._height, "px;"); //set total height of padding
|
||||
html.push("top:", rowHeight, "px'>"); //shift detail below 1st row
|
||||
html.push("<div id='detailViewContainer_", dataContext.id, "' class='detail-container' style='max-height:" + (dataContext._height - rowHeight + bottomMargin) + "px'>"); //sub ctr for custom styling
|
||||
html.push("<div id='innerDetailView_" , dataContext.id , "'>" , dataContext._detailContent, "</div></div>");
|
||||
//&omit a final closing detail container </div> that would come next
|
||||
|
||||
return html.join('');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public resizeDetailView(item) {
|
||||
if (!item) return;
|
||||
|
||||
// Grad each of the dom items
|
||||
var mainContainer = document.getElementById('detailViewContainer_' + item.id);
|
||||
var cellItem = document.getElementById('cellDetailView_' + item.id);
|
||||
var inner = document.getElementById('innerDetailView_' + item.id);
|
||||
|
||||
if (!mainContainer || !cellItem || !inner) return;
|
||||
|
||||
for (var idx = 1; idx <= item._sizePadding; idx++) {
|
||||
this._dataView.deleteItem(item.id + "." + idx);
|
||||
}
|
||||
|
||||
var rowHeight = this._grid.getOptions().rowHeight; // height of a row
|
||||
var lineHeight = 13; //we know cuz we wrote the custom css innit ;)
|
||||
|
||||
// Get the inner Item height as this will be the actual size
|
||||
var itemHeight = inner.clientHeight;
|
||||
|
||||
// Now work out how many rows
|
||||
var rowCount = Math.ceil(itemHeight / rowHeight) + 1;
|
||||
|
||||
item._sizePadding = Math.ceil(((rowCount * 2) * lineHeight) / rowHeight);
|
||||
item._height = (item._sizePadding * rowHeight);
|
||||
|
||||
// If the padding is now more than the original minRowBuff we need to increase it
|
||||
if (this._grid.getOptions().minRowBuffer < item._sizePadding)
|
||||
{
|
||||
// Update the minRowBuffer so that the view doesn't disappear when it's at top of screen + the original default 3
|
||||
this._grid.getOptions().minRowBuffer =item._sizePadding + 3;
|
||||
}
|
||||
|
||||
mainContainer.setAttribute("style", "max-height: " + item._height + "px");
|
||||
if (cellItem) {
|
||||
cellItem.setAttribute("style", "height: " + item._height + "px;top:" + rowHeight + "px");
|
||||
}
|
||||
|
||||
let idxParent = this._dataView.getIdxById(item.id);
|
||||
for (var idx = 1; idx <= item._sizePadding; idx++) {
|
||||
this._dataView.insertItem(idxParent + idx, this.getPaddingItem(item, idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user