This is an initial PR for a new model-driven UI where extensions can provide definitions of the components & how they're laid out using Containers.
#1140, #1141, #1142, #1143 and #1144 are all tracking additional work needed to improve the initial implementation and fix some issues with the implementation.

Features:
- Supports defining a FlexContainer that maps to a flexbox-based layout.
- Supports creating a card component, which is a key-value pair based control that will lay out simple information to a user. Eventually this will have an optional set of actions associated with it.
- Has a sample project which shows how to use the API and was used for verification
This commit is contained in:
Kevin Cunnane 2018-04-13 15:59:18 -07:00 коммит произвёл GitHub
Родитель e022f4a0d1
Коммит b2c70e9301
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
63 изменённых файлов: 13238 добавлений и 84 удалений

3
.vscode/launch.json поставляемый
Просмотреть файл

@ -89,7 +89,8 @@
"skipFiles": [
"**/winjs*.js"
],
"webRoot": "${workspaceFolder}"
"webRoot": "${workspaceFolder}",
"timeout": 15000
},
{
"type": "node",

3
samples/sqlservices/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,3 @@
node_modules
*.vsix
typings/sqlops.proposed.d.ts

57
samples/sqlservices/.vscode/launch.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,57 @@
// A launch configuration that launches the extension inside a new window
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
// To debug the extension:
// 1. please install the "SQL Operations Studio Debug" extension into VSCode
// 2. Ensure sqlops is added to your path:
// - open SQL Operations Studio
// - run the command "Install 'sqlops' command in PATH"
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug in SqlOps install",
"type": "sqlopsExtensionHost",
"request": "launch",
"runtimeExecutable": "sqlops",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
]
},
{
"type": "node",
"request": "attach",
"name": "Attach to Ops Studio",
"protocol": "inspector",
"port": 5870,
"restart": true,
"sourceMaps": true,
"outFiles": [
"${workspaceRoot}/out/**/*.js"
],
"preLaunchTask": "",
"timeout": 25000
},
{
"name": "Debug in enlistment",
"type": "sqlopsExtensionHost",
"request": "launch",
"windows": {
"runtimeExecutable": "${workspaceFolder}/../../scripts/sql.bat"
},
"osx": {
"runtimeExecutable": "${workspaceFolder}/../../scripts/sql.sh"
},
"linux": {
"runtimeExecutable": "${workspaceFolder}/../../scripts/sql.sh"
},
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"timeout": 20000
}
]
}

14
samples/sqlservices/.vscode/tasks.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,14 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"type": "gulp",
"task": "build",
"problemMatcher": [
"$gulp-tsc"
]
}
]
}

Просмотреть файл

@ -0,0 +1,4 @@
.vscode/**
.vscode-test/**
.gitignore
vsc-extension-quickstart.md

Просмотреть файл

@ -0,0 +1 @@
This is a sample extension that will show some basic model-backed UI scenarios. The long-term goal is to use SQL Service querying (e.g. see if Agent and other services are running) and visualize in interesting ways. Additional suggestions for improving this sample are welcome.

Просмотреть файл

@ -0,0 +1,14 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
"use strict";
// NOTE: These are es6 gulpfiles
// Basic build tasks
require('./tasks/buildtasks');
// VSIX generation tasks
require('./tasks/packagetasks');

7118
samples/sqlservices/package-lock.json сгенерированный Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,81 @@
{
"name": "sqlservices",
"displayName": "sqlservices",
"description": "Lists SQL Server service status in the management dashboard for a server",
"version": "0.0.1",
"publisher": "demo",
"engines": {
"vscode": "^1.21.0",
"sqlops": "*"
},
"categories": [
"Other"
],
"activationEvents": [
"*"
],
"main": "./out/src/extension",
"contributes": {
"dashboard.tabs": [
{
"id": "sqlservices.tab",
"title": "sqlservices",
"icon": {
"light": "./out/src/media/insights.svg",
"dark": "./out/src/media/insights_inverse.svg"
},
"description": "Shows available services running in the SQL Server instance",
"container": {
"nav-section": [
{
"id": "sqlservices",
"title": "Services",
"gridItemConfig": {
"sizex": 2,
"sizey": 1
},
"container": {
"modelview-container": null
}
},
{
"id": "splitPanel",
"title": "SplitPanel",
"gridItemConfig": {
"sizex": 2,
"sizey": 1
},
"container": {
"modelview-container": null
}
}
]
}
}
]
},
"scripts": {
"build": "gulp build",
"compile": "gulp compile",
"watch": "gulp watch",
"typings": "gulp copytypings",
"postinstall": "node ./node_modules/vscode/bin/install && node ./node_modules/sqlops/bin/install"
},
"dependencies": {
"vscode-nls": "^3.2.2"
},
"devDependencies": {
"@types/node": "^7.0.43",
"child-process-promise": "^2.2.1",
"del": "^3.0.0",
"gulp": "^4.0.0",
"gulp-color": "0.0.1",
"gulp-sourcemaps": "^2.6.4",
"gulp-tslint": "^6.0.2",
"gulp-typescript": "^3.2.4",
"sqlops": "github:anthonydresser/sqlops-extension-sqlops",
"tslint": "^3.14.0",
"typescript": "^2.6.1",
"vscode": "^1.1.14"
}
}

Просмотреть файл

@ -0,0 +1,9 @@
/*
* This file should contain SQL code that returns a result set
* To get started, build your own queries in SQL Operations Studio and click the
* "View as Chart" button to get the correct chart format. Then choose "Create Insight"
* and update the package.json with the JSON contents, and this file with the query
* used to generate the chart.
*/
select 'My Label' as [Label], 1 as [Value]

Просмотреть файл

@ -0,0 +1,10 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
// CONFIG VALUES ///////////////////////////////////////////////////////////
export const extensionConfigSectionName = 'sqlservices';
export const configLogDebugInfo = 'logDebugInfo';

Просмотреть файл

@ -0,0 +1,104 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
import * as Utils from '../utils';
import * as vscode from 'vscode';
import SplitPropertiesPanel from './splitPropertiesPanel';
/**
* The main controller class that initializes the extension
*/
export default class MainController implements vscode.Disposable {
constructor(protected context: vscode.ExtensionContext) {
}
// PUBLIC METHODS //////////////////////////////////////////////////////
public dispose(): void {
this.deactivate();
}
/**
* Deactivates the extension
*/
public deactivate(): void {
Utils.logDebug('Main controller deactivated');
}
public activate(): Promise<boolean> {
this.registerSqlServicesModelView();
this.registerSplitPanelModelView();
sqlops.tasks.registerTask('sqlservices.clickTask', (profile) => {
vscode.window.showInformationMessage(`Clicked from profile ${profile.serverName}.${profile.databaseName}`);
});
return Promise.resolve(true);
}
private registerSqlServicesModelView(): void {
sqlops.dashboard.registerModelViewProvider('sqlservices', async (view) => {
let flexModel = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row'
}).withItems([
// 1st child panel with N cards
view.modelBuilder.flexContainer()
.withLayout({ flexFlow: 'column' })
.withItems([
view.modelBuilder.card()
.withProperties<sqlops.CardProperties>({
label: 'label1',
value: 'value1',
actions: [{ label: 'action', taskId: 'sqlservices.clickTask' }]
})
.component()
]).component(),
// 2nd child panel with N cards
view.modelBuilder.flexContainer()
.withLayout({ flexFlow: 'column' })
.withItems([
view.modelBuilder.card()
.withProperties<sqlops.CardProperties>({
label: 'label2',
value: 'value2',
actions: [{ label: 'action', taskId: 'sqlservices.clickTask' }]
})
.component()
]).component()
], { flex: '0 1 50%' })
.component();
await view.initializeModel(flexModel);
});
}
private registerSplitPanelModelView(): void {
sqlops.dashboard.registerModelViewProvider('splitPanel', async (view) => {
let numPanels = 3;
let splitPanel = new SplitPropertiesPanel(view, numPanels);
await view.initializeModel(splitPanel.modelBase);
// Add a bunch of cards after an initial timeout
setTimeout(async () => {
for (let i = 0; i < 10; i++) {
let panel = i % numPanels;
let card = view.modelBuilder.card().component();
card.label = `label${i.toString()}`;
splitPanel.addItem(card, panel);
}
}, 0);
});
}
}

Просмотреть файл

@ -0,0 +1,43 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
/**
* The main controller class that initializes the extension
*/
export default class SplitPropertiesPanel {
private panels: sqlops.FlexContainer[];
private _modelBase: sqlops.FlexContainer;
constructor(view: sqlops.ModelView, numPanels: number) {
this.panels = [];
let ratio = Math.round(100 / numPanels);
for (let i = 0; i < numPanels; i++) {
this.panels.push(view.modelBuilder.flexContainer()
.withLayout({ flexFlow: 'column' }).component());
}
this._modelBase = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row'
}).withItems(this.panels, {
flex: `0 1 ${ratio}%`
})
.component();
}
public get modelBase(): sqlops.Component {
return this._modelBase;
}
public addItem(item: sqlops.Component, panel: number): void {
if (panel >= this.panels.length) {
throw new Error(`Cannot add to panel ${panel} as only ${this.panels.length - 1} panels defined`);
}
this.panels[panel].addItem(item, undefined);
}
}

Просмотреть файл

@ -0,0 +1,37 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as vscode from 'vscode';
import MainController from './controllers/mainController';
let mainController: MainController;
export function activate(context: vscode.ExtensionContext): Promise<boolean> {
let activations: Promise<boolean>[] = [];
// Start the main controller
mainController = new MainController(context);
context.subscriptions.push(mainController);
activations.push(mainController.activate());
return Promise.all(activations)
.then((results: boolean[]) => {
for (let result of results) {
if (!result) {
return false;
}
}
return true;
});
}
export function deactivate(): void {
if (mainController) {
mainController.deactivate();
}
}

Просмотреть файл

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#212121;}</style></defs><title>insights</title><path class="cls-1" d="M15,4V8H14V5.71L9.49,10.2l-2-2L2,13.71V14H15v1H1V1H2V12.29L7.49,6.8l2,2L13.28,5H11V4Z"/></svg>

После

Ширина:  |  Высота:  |  Размер: 282 B

Просмотреть файл

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>insights_inverse</title><path class="cls-1" d="M15,4V8H14V5.71L9.49,10.2l-2-2L2,13.71V14H15v1H1V1H2V12.29L7.49,6.8l2,2L13.28,5H11V4Z"/></svg>

После

Ширина:  |  Высота:  |  Размер: 287 B

Просмотреть файл

@ -0,0 +1,52 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as fs from 'fs-extra';
import * as handlebars from 'handlebars';
import * as path from 'path';
import * as vscode from 'vscode';
import * as Constants from './constants';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
/**
* Helper to log messages to the developer console if enabled
* @param msg Message to log to the console
*/
export function logDebug(msg: any): void {
let config = vscode.workspace.getConfiguration(Constants.extensionConfigSectionName);
let logDebugInfo = config[Constants.configLogDebugInfo];
if (logDebugInfo === true) {
let currentTime = new Date().toLocaleTimeString();
let outputMsg = '[' + currentTime + ']: ' + msg ? msg.toString() : '';
console.log(outputMsg);
}
}
export function renderTemplateHtml(extensionPath: string, templateName: string, templateValues: object): Thenable<string> {
let templatePath = path.join(extensionPath, 'resources', templateName);
// 1) Read the template from the disk
// 2) Compile it as a handlebars template and render the HTML
// 3) On failure, return a simple string as an error
return fs.readFile(templatePath, 'utf-8')
.then(templateText => {
let template = handlebars.compile(templateText);
return template(templateValues);
})
.then(
undefined,
error => {
logDebug(error);
return localize('errorLoadingTab', 'An error occurred while loading the tab');
}
);
}

Просмотреть файл

@ -0,0 +1,124 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
"use strict";
let del = require('del');
let gulp = require('gulp');
let srcmap = require('gulp-sourcemaps');
let tslint = require('gulp-tslint');
let ts = require('gulp-typescript');
let cproc = require('child_process');
let os = require('os');
let config = require('./config');
let tsProject = ts.createProject('tsconfig.json');
// GULP TASKS //////////////////////////////////////////////////////////////
gulp.task('clean', function(done) {
return del('out', done);
});
gulp.task('lint', () => {
return gulp.src([
config.paths.project.root + '/src/**/*.ts',
config.paths.project.root + '/test/**/*.ts'
])
.pipe((tslint({
formatter: "verbose"
})))
.pipe(tslint.report());
});
gulp.task('compile:src', function(done) {
gulp.src([
config.paths.project.root + '/src/**/*.sql',
config.paths.project.root + '/src/**/*.svg',
config.paths.project.root + '/src/**/*.html'
]).pipe(gulp.dest('out/src/'));
let srcFiles = [
config.paths.project.root + '/src/**/*.ts',
config.paths.project.root + '/src/**/*.js',
config.paths.project.root + '/typings/**/*.ts'
];
return gulp.src(srcFiles)
.pipe(srcmap.init())
.pipe(tsProject())
.on('error', function() {
if (process.env.BUILDMACHINE) {
done('Extension Tests failed to build. See Above.');
process.exit(1);
}
})
.pipe(srcmap.write('.', {
sourceRoot: function(file) {
return file.cwd + '/src';
}
}))
.pipe(gulp.dest('out/src/'));
});
gulp.task('compile:test', function(done) {
let srcFiles = [
config.paths.project.root + '/test/**/*.ts',
config.paths.project.root + '/typings/**/*.ts'
];
return gulp.src(srcFiles)
.pipe(srcmap.init())
.pipe(tsProject())
.on('error', function() {
if(process.env.BUILDMACHINE) {
done('Failed to compile test source, see above.');
process.exit(1);
}
})
.pipe(srcmap.write('.', {sourceRoot: function(file) { return file.cwd + '/test'; }}))
.pipe(gulp.dest('out/test/'));
});
// COMPOSED GULP TASKS /////////////////////////////////////////////////////
gulp.task("compile", gulp.series("compile:src", "compile:test"));
gulp.task("build", gulp.series("clean", "lint", "compile"));
gulp.task("watch", function() {
gulp.watch([config.paths.project.root + '/src/**/*',
config.paths.project.root + '/test/**/*.ts'],
gulp.series('build'));
});
gulp.task('test', (done) => {
let workspace = process.env['WORKSPACE'];
if (!workspace) {
workspace = process.cwd();
}
process.env.JUNIT_REPORT_PATH = workspace + '/test-reports/ext_xunit.xml';
let sqlopsPath = 'sqlops';
if (process.env['SQLOPS_DEV']) {
let suffix = os.platform === 'win32' ? 'bat' : 'sh';
sqlopsPath = `${process.env['SQLOPS_DEV']}/scripts/sql-cli.${suffix}`;
}
console.log(`Using SQLOPS Path of ${sqlopsPath}`);
cproc.exec(`${sqlopsPath} --extensionDevelopmentPath="${workspace}" --extensionTestsPath="${workspace}/out/test" --verbose`, (error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`);
process.exit(1);
}
console.log(`stdout: ${stdout}`);
console.log(`stderr: ${stderr}`);
done();
});
});
gulp.task('copytypings', function() {
return gulp.src(config.paths.project.root + '/../../src/sql/sqlops.proposed.d.ts')
.pipe(gulp.dest('typings/'));
});

Просмотреть файл

@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
var path = require('path');
var projectRoot = path.resolve(path.dirname(__dirname));
var srcRoot = path.resolve(projectRoot, 'src');
var localization = path.resolve(projectRoot, 'localization');
var config = {
paths: {
project: {
root: projectRoot,
localization: localization
},
extension: {
root: srcRoot
}
}
};
module.exports = config;

Просмотреть файл

@ -0,0 +1,36 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
let gulp = require('gulp');
let exec = require('child-process-promise').exec;
let color = require('gulp-color');
// CONSTANTS ///////////////////////////////////////////////////////////////
let packageVals = require('../package');
// HELPER FUNCTIONS ////////////////////////////////////////////////////////
let buildPackage = function(packageName) {
// Make sure there are
if (!packageVals.repository) {
return Promise.reject("Repository field is not defined in package.json");
}
// Initialize the package command with program and command
let vsceArgs = [];
vsceArgs.push('./node_modules/vsce/out/vsce');
vsceArgs.push('package'); // package command
// Add the package name
vsceArgs.push('-o');
vsceArgs.push(packageName);
// Put it all together and execute the command
let command = vsceArgs.join(' ');
console.log(command);
return exec(command);
};

Просмотреть файл

@ -0,0 +1,16 @@
{
"compileOnSave": true,
"compilerOptions": {
"module": "commonjs",
"target": "ES6",
"lib": [ "es6" ],
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"rootDir": "."
},
"exclude": [
"node_modules",
".vscode-test"
]
}

Просмотреть файл

@ -0,0 +1,123 @@
{
"rules": {
"align": [
true,
"parameters",
"statements"
],
"ban": false,
"class-name": true,
"comment-format": [
true,
"check-space"
],
"curly": true,
"eofline": true,
"forin": true,
"indent": [
true,
"tabs"
],
"interface-name": true,
"jsdoc-format": true,
"label-position": true,
"label-undefined": true,
"max-line-length": [
true,
160
],
"member-access": false,
"member-ordering": false,
"no-any": false,
"no-arg": true,
"no-bitwise": true,
"no-conditional-assignment": true,
"no-consecutive-blank-lines": false,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-construct": true,
"no-constructor-vars": false,
"no-debugger": true,
"no-duplicate-key": true,
"no-duplicate-variable": true,
"no-empty": true,
"no-eval": true,
"no-inferrable-types": false,
"no-internal-module": true,
"no-null-keyword": true,
"no-require-imports": false,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-switch-case-fall-through": false,
"no-trailing-whitespace": true,
"no-unreachable": true,
"no-unused-expression": false,
"no-unused-variable": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"no-var-requires": false,
"object-literal-sort-keys": false,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-finally",
"check-whitespace"
],
"quotemark": [
true,
"single",
"avoid-escape"
],
"radix": true,
"semicolon": true,
"switch-default": true,
"trailing-comma": [
true,
{
"multiline": "never",
"singleline": "never"
}
],
"triple-equals": [
true,
"allow-null-check"
],
"typedef": [
true,
"call-signature",
"property-declaration"
],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"use-strict": false,
"variable-name": [
true,
"allow-leading-underscore",
"ban-keywords"
],
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
]
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -26,7 +26,7 @@ export class RowDetailView {
this._options = mixin(options, this._defaults, false);
}
public init(grid: any) {
public init(grid: any): void {
this._grid = grid;
this._dataView = this._grid.getData();
@ -61,17 +61,17 @@ export class RowDetailView {
this._options = $.extend(true, {}, this._options, options);
}
public handleClick(e: any, args: any) {
public handleClick(e: any, args: any): void {
// 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 (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;
e.preventDefault();
e.stopImmediatePropagation();
return;
}
var item = this._dataView.getItem(args.row);
let item = this._dataView.getItem(args.row);
// trigger an event before toggling
this.onBeforeRowDetailToggle.notify({
@ -100,36 +100,35 @@ export class RowDetailView {
// If we scroll save detail views that go out of cache range
public handleScroll(e, args) {
var range = this._grid.getRenderedRange();
let range = this._grid.getRenderedRange();
var start = (range.top > 0 ? range.top : 0);
var end = (range.bottom > this._dataView.getLength() ? range.bottom : this._dataView.getLength());
let start: number = (range.top > 0 ? range.top : 0);
let end: number = (range.bottom > this._dataView.getLength() ? range.bottom : this._dataView.getLength());
if (end <= 0) {
return;
}
// Get the item at the top of the view
var topMostItem = this._dataView.getItemByIdx(start);
let topMostItem = this._dataView.getItemByIdx(start);
// Check it is a parent item
if (topMostItem._parent === undefined)
{
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)
{
let 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)
{
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);
let 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)
{
if (bottomMostItem._parent !== undefined) {
this.saveDetailView(bottomMostItem._parent);
}
}
@ -143,17 +142,17 @@ export class RowDetailView {
// Collapse all of the open items
public collapseAll() {
for (var i = this._expandedRows.length - 1; i >= 0; i--) {
for (let 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);
let view = $('#innerDetailView_' + item.id);
if (view) {
var html = $('#innerDetailView_' + item.id).html();
if(html !== undefined) {
let html = $('#innerDetailView_' + item.id).html();
if (html !== undefined) {
item._detailContent = html;
}
}
@ -230,7 +229,7 @@ export class RowDetailView {
args.itemDetail._detailViewLoaded = true;
var idxParent = this._dataView.getIdxById(args.itemDetail.id);
let 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
@ -254,7 +253,7 @@ export class RowDetailView {
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
public getPaddingItem(parent, offset) {
var item: any = {};
let item: any = {};
for (let prop in this._grid.getData()) {
item[prop] = null;
@ -284,7 +283,7 @@ export class RowDetailView {
item._height = (item._sizePadding * this._grid.getOptions().rowHeight);
let idxParent = this._dataView.getIdxById(item.id);
for (var idx = 1; idx <= item._sizePadding; idx++) {
for (let idx = 1; idx <= item._sizePadding; idx++) {
this._dataView.insertItem(idxParent + idx, this.getPaddingItem(item, idx));
}
}
@ -307,12 +306,12 @@ export class RowDetailView {
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
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) {
@ -320,9 +319,9 @@ export class RowDetailView {
} else if (dataContext._collapsed) {
return '<div class=\'detailView-toggle expand\'></div>';
} else {
var html = [];
var rowHeight = this._grid.getOptions().rowHeight;
var bottomMargin = 5;
let html = [];
let rowHeight = this._grid.getOptions().rowHeight;
let bottomMargin = 5;
//V313HAX:
//putting in an extra closing div after the closing toggle div and ommiting a
@ -339,7 +338,7 @@ export class RowDetailView {
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>");
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('');
@ -351,33 +350,32 @@ export class RowDetailView {
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);
let mainContainer = document.getElementById('detailViewContainer_' + item.id);
let cellItem = document.getElementById('cellDetailView_' + item.id);
let inner = document.getElementById('innerDetailView_' + item.id);
if (!mainContainer || !cellItem || !inner) return;
for (var idx = 1; idx <= item._sizePadding; idx++) {
for (let 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 ;)
let rowHeight = this._grid.getOptions().rowHeight; // height of a row
let 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;
let itemHeight = inner.clientHeight;
// Now work out how many rows
var rowCount = Math.ceil(itemHeight / rowHeight) + 1;
let 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)
{
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;
this._grid.getOptions().minRowBuffer = item._sizePadding + 3;
}
mainContainer.setAttribute("style", "max-height: " + item._height + "px");
@ -386,7 +384,7 @@ export class RowDetailView {
}
let idxParent = this._dataView.getIdxById(item.id);
for (var idx = 1; idx <= item._sizePadding; idx++) {
for (let idx = 1; idx <= item._sizePadding; idx++) {
this._dataView.insertItem(idxParent + idx, this.getPaddingItem(item, idx));
}
}

Просмотреть файл

@ -14,6 +14,7 @@ import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboar
import { WIDGETS_CONTAINER } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.contribution';
import { GRID_CONTAINER } from 'sql/parts/dashboard/containers/dashboardGridContainer.contribution';
import { WEBVIEW_CONTAINER } from 'sql/parts/dashboard/containers/dashboardWebviewContainer.contribution';
import { MODELVIEW_CONTAINER } from 'sql/parts/dashboard/containers/dashboardModelViewContainer.contribution';
import { CONTROLHOST_CONTAINER } from 'sql/parts/dashboard/containers/dashboardControlHostContainer.contribution';
import { NAV_SECTION } from 'sql/parts/dashboard/containers/dashboardNavSection.contribution';
import { IDashboardContainerRegistry, Extensions as DashboardContainerExtensions, IDashboardContainer, registerContainerType } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
@ -25,6 +26,7 @@ const containerTypes = [
WIDGETS_CONTAINER,
GRID_CONTAINER,
WEBVIEW_CONTAINER,
MODELVIEW_CONTAINER,
CONTROLHOST_CONTAINER,
NAV_SECTION
];

Просмотреть файл

@ -13,6 +13,8 @@
</dashboard-webview-container>
<dashboard-widget-container *ngIf="getContentType(tab) === 'widgets-container'" [tab]="tab">
</dashboard-widget-container>
<dashboard-modelview-container *ngIf="getContentType(tab) === 'modelview-container'" [tab]="tab">
</dashboard-modelview-container>
<dashboard-controlhost-container *ngIf="getContentType(tab) === 'controlhost-container'" [tab]="tab">
</dashboard-controlhost-container>
<dashboard-nav-section *ngIf="getContentType(tab) === 'nav-section'" [tab]="tab">

Просмотреть файл

@ -39,7 +39,7 @@ export class DashboardErrorContainer extends DashboardTab implements AfterViewIn
ngAfterViewInit() {
let errorMessage = this._errorMessageContainer.nativeElement as HTMLElement;
errorMessage.innerHTML = nls.localize('dashboardNavSection_loadTabError', 'The {0} has an invalid content. Please contact extension owner.', this.tab.title);
errorMessage.innerHTML = nls.localize('dashboardNavSection_loadTabError', 'The "{0}" section has invalid content. Please contact extension owner.', this.tab.title);
}
public get id(): string {

Просмотреть файл

@ -0,0 +1,55 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./dashboardWebviewContainer';
import { Component, forwardRef, Input, AfterContentInit, ViewChild } from '@angular/core';
import Event, { Emitter } from 'vs/base/common/event';
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
import { TabConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { ModelViewContent } from 'sql/parts/modelComponents/modelViewContent.component';
@Component({
selector: 'dashboard-modelview-container',
providers: [{ provide: DashboardTab, useExisting: forwardRef(() => DashboardModelViewContainer) }],
template: `
<modelview-content [modelViewId]="tab.id">
</modelview-content>
`
})
export class DashboardModelViewContainer extends DashboardTab implements AfterContentInit {
@Input() private tab: TabConfig;
private _onResize = new Emitter<void>();
public readonly onResize: Event<void> = this._onResize.event;
@ViewChild(ModelViewContent) private _modelViewContent: ModelViewContent;
constructor() {
super();
}
ngAfterContentInit(): void {
this._register(this._modelViewContent.onResize(() => {
this._onResize.fire();
}));
}
public layout(): void {
this._modelViewContent.layout();
}
public get id(): string {
return this.tab.id;
}
public get editable(): boolean {
return this.tab.editable;
}
public refresh(): void {
// no op
}
}

Просмотреть файл

@ -0,0 +1,19 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import * as nls from 'vs/nls';
import { registerContainerType, registerNavSectionContainerType } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
export const MODELVIEW_CONTAINER = 'modelview-container';
let modelviewSchema: IJSONSchema = {
type: 'null',
description: nls.localize('dashboard.container.modelview', "The model-backed view that will be displayed in this tab."),
default: null
};
registerContainerType(MODELVIEW_CONTAINER, modelviewSchema);
registerNavSectionContainerType(MODELVIEW_CONTAINER, modelviewSchema);

Просмотреть файл

@ -0,0 +1,10 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
dashboard-modelview-container {
height: 100%;
width : 100%;
display: block;
}

Просмотреть файл

@ -11,6 +11,8 @@
</dashboard-webview-container>
<dashboard-widget-container *ngIf="getContentType(tab) === 'widgets-container'" [tab]="tab">
</dashboard-widget-container>
<dashboard-modelview-container *ngIf="getContentType(tab) === 'modelview-container'" [tab]="tab">
</dashboard-modelview-container>
<dashboard-grid-container *ngIf="getContentType(tab) === 'grid-container'" [tab]="tab">
</dashboard-grid-container>
<dashboard-error-container *ngIf="getContentType(tab) === 'error-container'" [tab]="tab">

Просмотреть файл

@ -16,7 +16,7 @@ import { memoize } from 'vs/base/common/decorators';
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
import { TabConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { IDashboardWebview } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
import { IDashboardWebview } from 'sql/services/dashboard/common/dashboardViewService';
import { AngularDisposable } from 'sql/base/common/lifecycle';
import * as sqlops from 'sqlops';
@ -46,7 +46,7 @@ export class WebviewContent extends AngularDisposable implements OnInit, IDashbo
}
ngOnInit() {
this._dashboardService.dashboardWebviewService.registerWebview(this);
this._dashboardService.dashboardViewService.registerWebview(this);
this._createWebview();
this._register(addDisposableListener(window, EventType.RESIZE, e => {
this.layout();

Просмотреть файл

@ -14,6 +14,7 @@ import { ChartsModule } from 'ng2-charts/ng2-charts';
import CustomUrlSerializer from 'sql/common/urlSerializer';
import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService';
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
import { Extensions as ComponentExtensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
import { Registry } from 'vs/platform/registry/common/platform';
@ -35,9 +36,12 @@ import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWi
import { DashboardWidgetContainer } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.component';
import { DashboardGridContainer } from 'sql/parts/dashboard/containers/dashboardGridContainer.component';
import { DashboardWebviewContainer } from 'sql/parts/dashboard/containers/dashboardWebviewContainer.component';
import { DashboardModelViewContainer } from 'sql/parts/dashboard/containers/dashboardModelViewContainer.component';
import { DashboardErrorContainer } from 'sql/parts/dashboard/containers/dashboardErrorContainer.component';
import { DashboardNavSection } from 'sql/parts/dashboard/containers/dashboardNavSection.component';
import { WidgetContent } from 'sql/parts/dashboard/contents/widgetContent.component';
import { ModelViewContent } from 'sql/parts/modelComponents/modelViewContent.component';
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
import { WebviewContent } from 'sql/parts/dashboard/contents/webviewContent.component';
import { BreadcrumbComponent } from 'sql/base/browser/ui/breadcrumb/breadcrumb.component';
import { IBreadcrumbService } from 'sql/base/browser/ui/breadcrumb/interfaces';
@ -49,9 +53,9 @@ import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.comp
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
let baseComponents = [DashboardHomeContainer, DashboardComponent, DashboardWidgetWrapper, DashboardWebviewContainer,
DashboardWidgetContainer, DashboardGridContainer, DashboardErrorContainer, DashboardNavSection, WebviewContent, WidgetContent,
DashboardWidgetContainer, DashboardGridContainer, DashboardErrorContainer, DashboardNavSection, ModelViewContent, WebviewContent, WidgetContent,
ComponentHostDirective, BreadcrumbComponent, ControlHostContent, DashboardControlHostContainer,
JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent];
JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent, DashboardModelViewContainer, ModelComponentWrapper];
/* Panel */
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';
@ -81,6 +85,9 @@ let widgetComponents = [
/* Insights */
let insightComponents = Registry.as<IInsightRegistry>(Extensions.InsightContribution).getAllCtors();
/* Model-backed components */
let extensionComponents = Registry.as<IComponentRegistry>(ComponentExtensions.ComponentContribution).getAllCtors();
// Setup routes for various child components
const appRoutes: Routes = [
{ path: 'database-dashboard', component: DatabaseDashboardPage },
@ -99,13 +106,15 @@ const appRoutes: Routes = [
...baseComponents,
...pageComponents,
...widgetComponents,
...insightComponents
...insightComponents,
...extensionComponents
],
// also for widgets
entryComponents: [
DashboardComponent,
...widgetComponents,
...insightComponents
...insightComponents,
...extensionComponents
],
imports: [
CommonModule,

Просмотреть файл

@ -23,7 +23,7 @@ import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { AngularEventType, IAngularEvent, IAngularEventingService } from 'sql/services/angularEventing/angularEventingService';
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
import { TabSettingConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { IDashboardWebviewService } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
import { IDashboardViewService } from 'sql/services/dashboard/common/dashboardViewService';
import { AngularDisposable } from 'sql/base/common/lifecycle';
import { ConnectionContextkey } from 'sql/parts/connection/common/connectionContextKey';
@ -138,7 +138,7 @@ export class DashboardServiceInterface extends AngularDisposable {
private _capabilitiesService = this._bootstrapService.capabilitiesService;
private _configurationEditingService = this._bootstrapService.configurationEditorService;
private _commandService = this._bootstrapService.commandService;
private _dashboardWebviewService = this._bootstrapService.dashboardWebviewService;
private _dashboardViewService = this._bootstrapService.dashboardViewService;
private _partService = this._bootstrapService.partService;
private _angularEventingService = this._bootstrapService.angularEventingService;
private _environmentService = this._bootstrapService.environmentService;
@ -211,8 +211,8 @@ export class DashboardServiceInterface extends AngularDisposable {
return this._instantiationService;
}
public get dashboardWebviewService(): IDashboardWebviewService {
return this._dashboardWebviewService;
public get dashboardViewService(): IDashboardViewService {
return this._dashboardViewService;
}
public get partService(): IPartService {

Просмотреть файл

@ -204,6 +204,8 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
}
componentInstance.data = { columns: result.columnInfo.map(item => item.columnName), rows: result.rows.map(row => row.map(item => item.displayValue)) };
componentInstance.data = { columns: result.columnInfo.map(item => item.columnName), rows: result.rows.map(row => row.map(item => item.displayValue)) };
if (componentInstance.init) {
componentInstance.init();
}

Просмотреть файл

@ -13,7 +13,7 @@ import { memoize } from 'vs/base/common/decorators';
import { DashboardWidget, IDashboardWidget, WidgetConfig, WIDGET_CONFIG } from 'sql/parts/dashboard/common/dashboardWidget';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { IDashboardWebview } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
import { IDashboardWebview } from 'sql/services/dashboard/common/dashboardViewService';
import * as sqlops from 'sqlops';
@ -47,7 +47,7 @@ export class WebviewWidget extends DashboardWidget implements IDashboardWidget,
}
ngOnInit() {
this._dashboardService.dashboardWebviewService.registerWebview(this);
this._dashboardService.dashboardViewService.registerWebview(this);
this._createWebview();
}

Просмотреть файл

@ -0,0 +1,68 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./flexContainer';
import { Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList,
} from '@angular/core';
import * as sqlops from 'sqlops';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
import { IComponent, IComponentDescriptor, IModelStore } from 'sql/parts/modelComponents/interfaces';
@Component({
template: `
<div *ngIf="label" class="cardComponent" style="position: absolute; height: 100%; width: 100%; margin: 10px 0px 10px 0px;">
<span style="margin-left: 10px; display: inline-block;">
<div style="font-size: 11px; font-weight: lighter">{{label}}</div>
<div>{{value}}</div>
</span>
</div>
`
})
export default class CardComponent extends ComponentBase implements IComponent, OnDestroy {
@Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore;
constructor(@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef) {
super(changeRef);
}
ngOnInit(): void {
this.baseInit();
}
ngOnDestroy(): void {
this.baseDestroy();
}
/// IComponent implementation
public layout(): void {
this._changeRef.detectChanges();
}
public setLayout (layout: any): void {
// TODO allow configuring the look and feel
this.layout();
}
// CSS-bound properties
public get label(): string {
return this.getPropertyOrDefault<sqlops.CardProperties, string>((props) => props.label, '');
}
public get value(): string {
return this.getPropertyOrDefault<sqlops.CardProperties, string>((props) => props.value, '');
}
public get actions(): sqlops.ActionDescriptor[] {
return this.getPropertyOrDefault<sqlops.CardProperties, sqlops.ActionDescriptor[]>((props) => props.actions, []);
}
}

Просмотреть файл

@ -0,0 +1,95 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./flexContainer';
import { Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
ViewChild, ElementRef, Injector, OnDestroy, OnInit
} from '@angular/core';
import * as types from 'vs/base/common/types';
import { IComponent, IComponentDescriptor, IModelStore } from 'sql/parts/modelComponents/interfaces';
import { FlexLayout, FlexItemLayout } from 'sqlops';
import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
export class ItemDescriptor<T> {
constructor(public descriptor: IComponentDescriptor, public config: T) {}
}
export abstract class ComponentBase implements IComponent, OnDestroy, OnInit {
protected properties: { [key: string]: any; } = {};
constructor(
protected _changeRef: ChangeDetectorRef) {
}
/// IComponent implementation
abstract descriptor: IComponentDescriptor;
abstract modelStore: IModelStore;
public layout(): void {
this._changeRef.detectChanges();
}
protected baseInit(): void {
if (this.modelStore) {
this.modelStore.registerComponent(this);
}
}
abstract ngOnInit(): void;
protected baseDestroy(): void {
if (this.modelStore) {
this.modelStore.unregisterComponent(this);
}
}
abstract ngOnDestroy(): void;
abstract setLayout (layout: any): void;
public setProperties(properties: { [key: string]: any; }): void {
if (!properties) {
this.properties = {};
}
this.properties = properties;
this.layout();
}
protected getProperties<TPropertyBag>(): TPropertyBag {
return this.properties as TPropertyBag;
}
protected getPropertyOrDefault<TPropertyBag, TValue>(propertyGetter: (TPropertyBag) => TValue, defaultVal: TValue) {
let property = propertyGetter(this.getProperties<TPropertyBag>());
return types.isUndefinedOrNull(property) ? defaultVal : property;
}
}
export abstract class ContainerBase<T> extends ComponentBase {
protected items: ItemDescriptor<T>[];
constructor(
_changeRef: ChangeDetectorRef
) {
super(_changeRef);
this.items = [];
}
/// IComponent container-related implementation
public addToContainer(componentDescriptor: IComponentDescriptor, config: any): void {
this.items.push(new ItemDescriptor(componentDescriptor, config));
this._changeRef.detectChanges();
}
public clearContainer(): void {
this.items = [];
this._changeRef.detectChanges();
}
abstract setLayout (layout: any): void;
}

Просмотреть файл

@ -0,0 +1,15 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import FlexContainer from './flexContainer.component';
import CardComponent from './card.component';
import { registerComponentType } from 'sql/platform/dashboard/common/modelComponentRegistry';
import { ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes';
export const FLEX_CONTAINER = 'flex-container';
registerComponentType(FLEX_CONTAINER, ModelComponentTypes.FlexContainer, FlexContainer);
export const CARD_COMPONENT = 'card-component';
registerComponentType(CARD_COMPONENT, ModelComponentTypes.Card, CardComponent);

Просмотреть файл

@ -0,0 +1,86 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./flexContainer';
import { Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList,
} from '@angular/core';
import { IComponent, IComponentDescriptor, IModelStore } from 'sql/parts/modelComponents/interfaces';
import { FlexLayout, FlexItemLayout } from 'sqlops';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { ContainerBase } from 'sql/parts/modelComponents/componentBase';
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
class FlexItem {
constructor(public descriptor: IComponentDescriptor, public config: FlexItemLayout) {}
}
@Component({
template: `
<div *ngIf="items" class="flexContainer" [style.flexFlow]="flexFlow" [style.justifyContent]="justifyContent">
<div *ngFor="let item of items" [style.flex]="getItemFlex(item)" [style.order]="getItemOrder(item)" >
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore">
</model-component-wrapper>
</div>
</div>
`
})
export default class FlexContainer extends ContainerBase<FlexItemLayout> implements IComponent, OnDestroy {
@Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore;
private _flexFlow: string;
private _justifyContent: string;
@ViewChildren(ModelComponentWrapper) private _componentWrappers: QueryList<ModelComponentWrapper>;
constructor(@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef) {
super(changeRef);
this._flexFlow = ''; // default
this._justifyContent = ''; // default
}
ngOnInit(): void {
this.baseInit();
}
ngOnDestroy(): void {
this.baseDestroy();
}
/// IComponent implementation
public layout(): void {
if (this._componentWrappers) {
this._componentWrappers.forEach(wrapper => {
wrapper.layout();
});
}
}
public setLayout (layout: FlexLayout): void {
this._flexFlow = layout.flexFlow ? layout.flexFlow : '';
this._justifyContent= layout.justifyContent ? layout.justifyContent : '';
this.layout();
}
// CSS-bound properties
public get flexFlow(): string {
return this._flexFlow;
}
public get justifyContent(): string {
return this._justifyContent;
}
private getItemFlex(item: FlexItem): string {
return item.config ? item.config.flex : '';
}
private getItemOrder(item: FlexItem): number {
return item.config ? item.config.order : 0;
}
}

Просмотреть файл

@ -0,0 +1,4 @@
.flexContainer {
display: flex
}

Просмотреть файл

@ -0,0 +1,73 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { InjectionToken } from '@angular/core';
import * as sqlops from 'sqlops';
/**
* An instance of a model-backed component. This will be a UI element
*
* @export
* @interface IComponent
*/
export interface IComponent {
descriptor: IComponentDescriptor;
modelStore: IModelStore;
layout();
clearContainer?: () => void;
addToContainer?: (componentDescriptor: IComponentDescriptor, config: any) => void;
setLayout?: (layout: any) => void;
setProperties?: (properties: { [key: string]: any; }) => void;
}
export const COMPONENT_CONFIG = new InjectionToken<IComponentConfig>('component_config');
export interface IComponentConfig {
descriptor: IComponentDescriptor;
modelStore: IModelStore;
}
/**
* Defines a component and can be used to map from the model-backed version of the
* world to the frontend UI;
*
* @export
* @interface IComponentDescriptor
*/
export interface IComponentDescriptor {
/**
* The type of this component. Used to map to the correct angular selector
* when loading the component
*/
type: string;
/**
* A unique ID for this component
*/
id: string;
}
export interface IModelStore {
/**
* Creates and saves the reference of a component descriptor.
* This can be used during creation of a component later
*/
createComponentDescriptor(type: string, createComponentDescriptor): IComponentDescriptor;
/**
* gets the descriptor for a previously created component ID
*/
getComponentDescriptor(componentId: string): IComponentDescriptor;
registerComponent(component: IComponent): void;
unregisterComponent(component: IComponent): void;
getComponent(componentId: string): IComponent;
/**
* Runs on a component immediately if the component exists, or runs on
* registration of the component otherwise
*
* @param {string} componentId unique identifier of the component
* @param {(component: IComponent) => void} action some action to perform
* @memberof IModelStore
*/
eventuallyRunOnComponent<T>(componentId: string, action: (component: IComponent) => T): Promise<T>;
}

Просмотреть файл

@ -0,0 +1,150 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!sql/media/icons/common-icons';
import {
Component, Input, Inject, forwardRef, ComponentFactoryResolver, AfterContentInit, ViewChild,
ElementRef, OnInit, ChangeDetectorRef, OnDestroy, ReflectiveInjector, Injector, Type, ComponentRef
} from '@angular/core';
import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
import { error } from 'sql/base/common/log';
import { AngularDisposable } from 'sql/base/common/lifecycle';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { IComponent, IComponentConfig, IComponentDescriptor, IModelStore, COMPONENT_CONFIG } from './interfaces';
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
import * as colors from 'vs/platform/theme/common/colorRegistry';
import * as themeColors from 'vs/workbench/common/theme';
import { Action } from 'vs/base/common/actions';
import { Registry } from 'vs/platform/registry/common/platform';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { memoize } from 'vs/base/common/decorators';
import { generateUuid } from 'vs/base/common/uuid';
import * as nls from 'vs/nls';
const componentRegistry = <IComponentRegistry> Registry.as(Extensions.ComponentContribution);
@Component({
selector: 'model-component-wrapper',
template: `
<ng-template component-host>
</ng-template>
`
})
export class ModelComponentWrapper extends AngularDisposable implements OnInit {
@Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore;
@memoize
public get guid(): string {
return generateUuid();
}
private _componentInstance: IComponent;
@ViewChild(ComponentHostDirective) componentHost: ComponentHostDirective;
constructor(
@Inject(forwardRef(() => ComponentFactoryResolver)) private _componentFactoryResolver: ComponentFactoryResolver,
@Inject(forwardRef(() => ElementRef)) private _ref: ElementRef,
@Inject(forwardRef(() => DashboardServiceInterface)) private _bootstrap: DashboardServiceInterface,
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeref: ChangeDetectorRef,
@Inject(forwardRef(() => Injector)) private _injector: Injector
) {
super();
}
ngOnInit() {
let self = this;
this._register(self._bootstrap.themeService.onDidColorThemeChange((event: IColorTheme) => {
self.updateTheme(event);
}));
}
ngAfterViewInit() {
this.updateTheme(this._bootstrap.themeService.getColorTheme());
if (this.componentHost) {
this.loadComponent();
}
this._changeref.detectChanges();
this.layout();
}
public layout(): void {
if (this._componentInstance && this._componentInstance.layout) {
this._componentInstance.layout();
}
}
public get id(): string {
return this._componentInstance.descriptor.id;
}
private get componentConfig(): IComponentConfig {
return {
descriptor: this.descriptor,
modelStore: this.modelStore
};
}
private loadComponent(): void {
if (!this.descriptor || !this.descriptor.type) {
error('No descriptor or type defined for this component');
return;
}
let selector = componentRegistry.getCtorFromId(this.descriptor.type);
if (selector === undefined) {
error('No selector defined for type {0}', this.descriptor.type);
return;
}
let componentFactory = this._componentFactoryResolver.resolveComponentFactory(selector);
let viewContainerRef = this.componentHost.viewContainerRef;
viewContainerRef.clear();
let injector = ReflectiveInjector.resolveAndCreate([{ provide: COMPONENT_CONFIG, useValue: this.componentConfig }], this._injector);
let componentRef: ComponentRef<IComponent>;
try {
componentRef = viewContainerRef.createComponent(componentFactory, 0, injector);
this._componentInstance = componentRef.instance;
this._componentInstance.descriptor = this.descriptor;
this._componentInstance.modelStore = this.modelStore;
this._changeref.detectChanges();
} catch (e) {
error('Error rendering component: {0}', e);
return;
}
let el = <HTMLElement>componentRef.location.nativeElement;
// set widget styles to conform to its box
el.style.overflow = 'hidden';
el.style.position = 'relative';
}
private updateTheme(theme: IColorTheme): void {
// TODO handle theming appropriately
let el = <HTMLElement>this._ref.nativeElement;
let borderColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true);
let backgroundColor = theme.getColor(colors.editorBackground, true);
let foregroundColor = theme.getColor(themeColors.SIDE_BAR_FOREGROUND, true);
let border = theme.getColor(colors.contrastBorder, true);
if (backgroundColor) {
el.style.backgroundColor = backgroundColor.toString();
}
if (foregroundColor) {
el.style.color = foregroundColor.toString();
}
}
}

Просмотреть файл

@ -0,0 +1,89 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Registry } from 'vs/platform/registry/common/platform';
import * as sqlops from 'sqlops';
import { IModelStore, IComponentDescriptor, IComponent } from './interfaces';
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
import { Deferred } from 'sql/base/common/promise';
const componentRegistry = <IComponentRegistry> Registry.as(Extensions.ComponentContribution);
class ComponentDescriptor implements IComponentDescriptor {
constructor(public readonly id: string, public readonly type: string) {
}
}
export class ModelStore implements IModelStore {
private static baseId = 0;
private _descriptorMappings: { [x: string]: IComponentDescriptor } = {};
private _componentMappings: { [x: string]: IComponent } = {};
private _componentActions: { [x: string]: Deferred<IComponent> } = {};
constructor() {
}
public createComponentDescriptor(type: string, id: string): IComponentDescriptor {
let descriptor = new ComponentDescriptor(id, type);
this._descriptorMappings[id] = descriptor;
return descriptor;
}
getComponentDescriptor(id: string): IComponentDescriptor {
return this._descriptorMappings[id];
}
registerComponent(component: IComponent): void {
let id = component.descriptor.id;
this._componentMappings[id] = component;
this.runPendingActions(id, component);
}
unregisterComponent(component: IComponent): void {
let id = component.descriptor.id;
this._componentMappings[id] = undefined;
this._componentActions[id] = undefined;
// TODO notify model for cleanup
}
getComponent(componentId: string): IComponent {
return this._componentMappings[componentId];
}
eventuallyRunOnComponent<T>(componentId: string, action: (component: IComponent) => T): Promise<T> {
let component = this.getComponent(componentId);
if (component) {
return Promise.resolve(action(component));
} else {
return this.addPendingAction(componentId, action);
}
}
private addPendingAction<T>(componentId: string, action: (component: IComponent) => T): Promise<T> {
// We create a promise and chain it onto a tracking promise whose resolve method
// will only be called once the component is created
let deferredPromise = this._componentActions[componentId];
if (!deferredPromise) {
deferredPromise = new Deferred();
this._componentActions[componentId] = deferredPromise;
}
let promise = deferredPromise.promise.then((component) => {
return action(component);
});
return promise;
}
private runPendingActions(componentId: string, component: IComponent) {
let promiseTracker = this._componentActions[componentId];
if (promiseTracker) {
promiseTracker.resolve(component);
}
}
}

Просмотреть файл

@ -0,0 +1,80 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// import 'vs/css!./modelViewContent';
import { Component, forwardRef, Input, OnInit, Inject, ChangeDetectorRef, ElementRef } from '@angular/core';
import Event, { Emitter } from 'vs/base/common/event';
import { Parts } from 'vs/workbench/services/part/common/partService';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { addDisposableListener, EventType } from 'vs/base/browser/dom';
import { memoize } from 'vs/base/common/decorators';
import nls = require('vs/nls');
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
import { TabConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { IModelView } from 'sql/services/model/modelViewService';
import { AngularDisposable } from 'sql/base/common/lifecycle';
import * as sqlops from 'sqlops';
import { ViewBase } from 'sql/parts/modelComponents/viewBase';
@Component({
selector: 'modelview-content',
template: `
<div *ngIf="rootDescriptor">
<model-component-wrapper [descriptor]="rootDescriptor" [modelStore]="modelStore">
</model-component-wrapper>
</div>
`
})
export class ModelViewContent extends ViewBase implements OnInit, IModelView {
@Input() private modelViewId: string;
private _onResize = new Emitter<void>();
public readonly onResize: Event<void> = this._onResize.event;
private _onMessage = new Emitter<string>();
public readonly onMessage: Event<string> = this._onMessage.event;
private _onMessageDisposable: IDisposable;
constructor(
@Inject(forwardRef(() => DashboardServiceInterface)) private _dashboardService: DashboardServiceInterface,
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef
) {
super(changeRef);
}
ngOnInit() {
this._dashboardService.dashboardViewService.registerModelView(this);
this._register(addDisposableListener(window, EventType.RESIZE, e => {
this.layout();
}));
}
public layout(): void {
}
public get id(): string {
return this.modelViewId;
}
@memoize
public get connection(): sqlops.connection.Connection {
let currentConnection = this._dashboardService.connectionManagementService.connectionInfo.connectionProfile;
let connection: sqlops.connection.Connection = {
providerName: currentConnection.providerName,
connectionId: currentConnection.id,
options: currentConnection.options
};
return connection;
}
@memoize
public get serverInfo(): sqlops.ServerInfo {
return this._dashboardService.connectionManagementService.connectionInfo.serverInfo;
}
}

Просмотреть файл

@ -0,0 +1,10 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
modelview-content {
height: 100%;
width : 100%;
display: block;
}

Просмотреть файл

@ -0,0 +1,94 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ChangeDetectorRef } from '@angular/core';
import { Registry } from 'vs/platform/registry/common/platform';
import nls = require('vs/nls');
import * as sqlops from 'sqlops';
import { IModelStore, IComponentDescriptor, IComponent } from './interfaces';
import { IItemConfig, ModelComponentTypes, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
import { IModelView } from 'sql/services/model/modelViewService';
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
import { AngularDisposable } from 'sql/base/common/lifecycle';
import { ModelStore } from 'sql/parts/modelComponents/modelStore';
const componentRegistry = <IComponentRegistry> Registry.as(Extensions.ComponentContribution);
/**
* Provides common logic required for any implementation that hooks to a model provided by
* the extension host
*/
export abstract class ViewBase extends AngularDisposable implements IModelView {
protected readonly modelStore: IModelStore;
protected rootDescriptor: IComponentDescriptor;
constructor(protected changeRef: ChangeDetectorRef) {
super();
this.modelStore = new ModelStore();
}
// Properties needed by the model view code
abstract id: string;
abstract connection: sqlops.connection.Connection;
abstract serverInfo: sqlops.ServerInfo;
initializeModel(rootComponent: IComponentShape): void {
let descriptor = this.defineComponent(rootComponent);
this.rootDescriptor = descriptor;
// Kick off the build by detecting changes to the model
this.changeRef.detectChanges();
}
private defineComponent(component: IComponentShape): IComponentDescriptor {
let typeId = componentRegistry.getIdForTypeMapping(component.type);
if (!typeId) {
// failure case
throw new Error(nls.localize('componentTypeNotRegistered', "Could not find component for type {0}", ModelComponentTypes[component.type]));
}
let descriptor = this.modelStore.createComponentDescriptor(typeId, component.id);
this.setProperties(component.id, component.properties);
this.setLayout(component.id, component.layout);
if (component.itemConfigs) {
for(let item of component.itemConfigs) {
this.addToContainer(component.id, item);
}
}
return descriptor;
}
clearContainer(componentId: string): void {
this.queueAction(componentId, (component) => component.clearContainer());
}
addToContainer(containerId: string, itemConfig: IItemConfig): void {
// Do not return the promise as this should be non-blocking
this.queueAction(containerId, (component) => {
let childDescriptor = this.defineComponent(itemConfig.componentShape);
component.addToContainer(childDescriptor, itemConfig.config);
});
}
setLayout(componentId: string, layout: any): void {
if (!layout) {
return;
}
this.queueAction(componentId, (component) => component.setLayout(layout));
}
setProperties(componentId: string, properties: { [key: string]: any; }): void {
if (!properties) {
return;
}
this.queueAction(componentId, (component) => component.setProperties(properties));
}
private queueAction<T>(componentId: string, action: (component: IComponent) => T): void {
this.modelStore.eventuallyRunOnComponent(componentId, action).catch(err => {
// TODO add error handling
});
}
}

Просмотреть файл

@ -0,0 +1,65 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Type } from '@angular/core';
import { ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes';
import * as platform from 'vs/platform/registry/common/platform';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import * as nls from 'vs/nls';
import { IComponent } from 'sql/parts/modelComponents/interfaces';
export type ComponentIdentifier = string;
export const Extensions = {
ComponentContribution: 'dashboard.contributions.components'
};
export interface IComponentRegistry {
registerComponentType(id: string, typeMapping: ModelComponentTypes, ctor: Type<IComponent>): ComponentIdentifier;
getIdForTypeMapping(typeMapping: ModelComponentTypes): string;
getCtorForType(typeMapping: ModelComponentTypes): Type<IComponent>;
getCtorFromId(id: string): Type<IComponent>;
getAllCtors(): Array<Type<IComponent>>;
getAllIds(): Array<string>;
}
class ComponentRegistry implements IComponentRegistry {
private _idToCtor: { [x: string]: Type<IComponent> } = {};
private _typeNameToId: { [x: string]: string } = {};
registerComponentType(id: string, typeMapping: ModelComponentTypes, ctor: Type<IComponent>): string {
this._idToCtor[id] = ctor;
this._typeNameToId[ModelComponentTypes[typeMapping]] = id;
return id;
}
public getIdForTypeMapping(typeMapping: ModelComponentTypes): string {
return this._typeNameToId[ModelComponentTypes[typeMapping]];
}
public getCtorForType(typeMapping: ModelComponentTypes): Type<IComponent> {
let id = this.getIdForTypeMapping(typeMapping);
return id ? this._idToCtor[id] : undefined;
}
public getCtorFromId(id: string): Type<IComponent> {
return this._idToCtor[id];
}
public getAllCtors(): Array<Type<IComponent>> {
return Object.values(this._idToCtor);
}
public getAllIds(): Array<string> {
return Object.keys(this._idToCtor);
}
}
const componentRegistry = new ComponentRegistry();
platform.Registry.add(Extensions.ComponentContribution, componentRegistry);
export function registerComponentType(id: string, typeMapping: ModelComponentTypes, ctor: Type<IComponent>): ComponentIdentifier {
return componentRegistry.registerComponentType(id, typeMapping, ctor);
}

Просмотреть файл

@ -22,7 +22,7 @@ import { ISqlOAuthService } from 'sql/common/sqlOAuthService';
import { IFileBrowserService, IFileBrowserDialogController } from 'sql/parts/fileBrowser/common/interfaces';
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { IDashboardWebviewService } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
import { IDashboardViewService } from 'sql/services/dashboard/common/dashboardViewService';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
@ -93,7 +93,7 @@ export interface IBootstrapService {
capabilitiesService: ICapabilitiesService;
configurationEditorService: ConfigurationEditingService;
commandService: ICommandService;
dashboardWebviewService: IDashboardWebviewService;
dashboardViewService: IDashboardViewService;
jobManagementService: IJobManagementService;
environmentService: IEnvironmentService;

Просмотреть файл

@ -24,7 +24,7 @@ import { ISqlOAuthService } from 'sql/common/sqlOAuthService';
import { IFileBrowserService, IFileBrowserDialogController } from 'sql/parts/fileBrowser/common/interfaces';
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { IDashboardWebviewService } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
import { IDashboardViewService } from 'sql/services/dashboard/common/dashboardViewService';
import { $ } from 'vs/base/browser/dom';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
@ -102,7 +102,7 @@ export class BootstrapService implements IBootstrapService {
@IClipboardService public clipboardService: IClipboardService,
@ICapabilitiesService public capabilitiesService: ICapabilitiesService,
@ICommandService public commandService: ICommandService,
@IDashboardWebviewService public dashboardWebviewService: IDashboardWebviewService,
@IDashboardViewService public dashboardViewService: IDashboardViewService,
@IJobManagementService public jobManagementService: IJobManagementService,
@IEnvironmentService public environmentService: IEnvironmentService
) {

Просмотреть файл

@ -9,22 +9,23 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
import Event from 'vs/base/common/event';
import * as sqlops from 'sqlops';
import { IItemConfig, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
import { IView, IModelView } from 'sql/services/model/modelViewService';
export const SERVICE_ID = 'dashboardWebviewService';
export const SERVICE_ID = 'dashboardViewService';
export interface IDashboardWebview {
readonly id: string;
readonly connection: sqlops.connection.Connection;
readonly serverInfo: sqlops.ServerInfo;
export interface IDashboardWebview extends IView {
setHtml(html: string): void;
onMessage: Event<string>;
sendMessage(message: string);
}
export interface IDashboardWebviewService {
export interface IDashboardViewService {
_serviceBrand: any;
onRegisteredWebview: Event<IDashboardWebview>;
registerWebview(widget: IDashboardWebview);
onRegisteredModelView: Event<IModelView>;
registerModelView(widget: IModelView);
}
export const IDashboardWebviewService = createDecorator<IDashboardWebviewService>(SERVICE_ID);
export const IDashboardViewService = createDecorator<IDashboardViewService>(SERVICE_ID);

Просмотреть файл

@ -5,17 +5,24 @@
'use strict';
import { IDashboardWebviewService, IDashboardWebview } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
import { IDashboardViewService, IDashboardWebview } from 'sql/services/dashboard/common/dashboardViewService';
import Event, { Emitter } from 'vs/base/common/event';
import { IModelView } from 'sql/services/model/modelViewService';
export class DashboardWebviewService implements IDashboardWebviewService {
export class DashboardViewService implements IDashboardViewService {
_serviceBrand: any;
private _onRegisteredWebview = new Emitter<IDashboardWebview>();
public readonly onRegisteredWebview: Event<IDashboardWebview> = this._onRegisteredWebview.event;
private _onRegisteredModelView = new Emitter<IModelView>();
public readonly onRegisteredModelView: Event<IModelView> = this._onRegisteredModelView.event;
public registerWebview(widget: IDashboardWebview) {
this._onRegisteredWebview.fire(widget);
}
registerModelView(view: IModelView) {
this._onRegisteredModelView.fire(view);
}
}

Просмотреть файл

@ -0,0 +1,22 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
import { IItemConfig, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
export interface IView {
readonly id: string;
readonly connection: sqlops.connection.Connection;
readonly serverInfo: sqlops.ServerInfo;
}
export interface IModelView extends IView {
initializeModel(rootComponent: IComponentShape): void;
clearContainer(componentId: string): void;
addToContainer(containerId: string, item: IItemConfig): void;
setLayout(componentId: string, layout: any): void;
setProperties(componentId: string, properties: { [key: string]: any }): void;
}

206
src/sql/sqlops.proposed.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,206 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// This is the place for API experiments and proposal.
import * as core from 'sqlops';
import * as vscode from 'vscode';
declare module 'sqlops' {
/**
* Supports defining a model that can be instantiated as a view in the UI
* @export
* @interface ModelBuilder
*/
export interface ModelBuilder {
navContainer(): ContainerBuilder<NavContainer, any, any>;
flexContainer(): FlexBuilder;
card(): ComponentBuilder<CardComponent>;
dashboardWidget(widgetId: string): ComponentBuilder<WidgetComponent>;
dashboardWebview(webviewId: string): ComponentBuilder<WebviewComponent>;
}
export interface ComponentBuilder<T extends Component> {
component(): T;
withProperties<U>(properties: U): ComponentBuilder<T>;
}
export interface ContainerBuilder<T extends Component, TLayout,TItemLayout> extends ComponentBuilder<T> {
withLayout(layout: TLayout): ContainerBuilder<T, TLayout, TItemLayout>;
withItems(components: Array<Component>, itemLayout ?: TItemLayout): ContainerBuilder<T, TLayout, TItemLayout>;
}
export interface FlexBuilder extends ContainerBuilder<FlexContainer, FlexLayout, FlexItemLayout> {
}
export interface Component {
readonly id: string;
/**
* Sends any updated properties of the component to the UI
*
* @returns {Thenable<boolean>} Thenable that completes once the update
* has been applied in the UI
* @memberof Component
*/
updateProperties(properties: { [key: string]: any }): Thenable<boolean>;
}
/**
* A component that contains other components
*/
export interface Container<TLayout,TItemLayout> extends Component {
/**
* A copy of the child items array. This cannot be added to directly -
* components must be created using the create methods instead
*/
readonly items: Component[];
/**
* Removes all child items from this container
*/
clearItems(): void;
/**
* Creates a collection of child components and adds them all to this container
*
* @param itemConfigs the definitions
* @param {*} [itemLayout] Optional layout for the child items
*/
addItems(itemConfigs: Array<Component>, itemLayout ?: TItemLayout): void;
/**
* Creates a child component and adds it to this container.
*
* @param {Component} component the component to be added
* @param {*} [itemLayout] Optional layout for this child item
*/
addItem(component: Component, itemLayout ?: TItemLayout): void;
/**
* Defines the layout for this container
*
* @param {TLayout} layout object
*/
setLayout(layout: TLayout): void;
}
export interface NavContainer extends Container<any, any> {
}
/**
* The config for a FlexBox-based container. This supports easy
* addition of content to a container with a flexible layout
* and use of space.
*/
export interface FlexLayout {
/**
* Matches the flex-flow CSS property and its available values.
* To layout as a vertical view use "column", and for horizontal
* use "row".
*/
flexFlow?: string;
/**
* Matches the justify-content CSS property.
*/
justifyContent?: string;
}
export interface FlexItemLayout {
/**
* Matches the order CSS property and its available values.
*/
order?: number;
/**
* Matches the flex CSS property and its available values.
* Default is "0 1 auto".
*/
flex?: string;
}
export interface FlexContainer extends Container<FlexLayout, FlexItemLayout> {
}
/**
* Describes an action to be shown in the UI, with a user-readable label
* and a callback to execute the action
*/
export interface ActionDescriptor {
/**
* User-visible label to display
*/
label: string;
/**
* ID of the task to be called when this is clicked on.
* These should be registered using the {tasks.registerTask} API.
*/
taskId: string;
}
/**
* Properties representing the card component, can be used
* when using ModelBuilder to create the comopnent
*/
export interface CardProperties {
label: string;
value?: string;
actions?: ActionDescriptor[];
}
export interface CardComponent extends Component {
label: string;
value: string;
actions?: ActionDescriptor[];
}
export interface WidgetComponent extends Component {
widgetId: string;
}
export interface WebviewComponent extends Component {
webviewId: string;
}
/**
* A view backed by a model provided by an extension.
* This model contains enough information to lay out the view
*/
export interface ModelView {
/**
* Raised when the view closed.
*/
readonly onClosed: vscode.Event<any>;
/**
* The connection info for the dashboard the webview exists on
*/
readonly connection: connection.Connection;
/**
* The info on the server for the dashboard
*/
readonly serverInfo: ServerInfo;
/**
* The model backing the model-based view
*/
readonly modelBuilder: ModelBuilder;
/**
* Initializes the model with a root component definition.
* Once this has been done, the components will be laid out in the UI and
* can be accessed and altered as needed.
*/
initializeModel<T extends Component>(root: T): Thenable<void>;
}
export namespace dashboard {
/**
* Register a provider for a model-view widget
*/
export function registerModelViewProvider(widgetId: string, handler: (view: ModelView) => void): void;
}
}

Просмотреть файл

@ -62,3 +62,24 @@ export enum ScriptOperation {
Execute = 5,
Alter = 6
}
export enum ModelComponentTypes {
NavContainer,
FlexContainer,
Card,
DashboardWidget,
DashboardWebview
}
export interface IComponentShape {
type: ModelComponentTypes;
id: string;
properties?: { [key: string]: any };
layout?: any;
itemConfigs?: IItemConfig[];
}
export interface IItemConfig {
componentShape: IComponentShape;
config: any;
}

Просмотреть файл

@ -0,0 +1,304 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IMainContext } from 'vs/workbench/api/node/extHost.protocol';
import { Emitter } from 'vs/base/common/event';
import { deepClone } from 'vs/base/common/objects';
import * as nls from 'vs/nls';
import * as vscode from 'vscode';
import * as sqlops from 'sqlops';
import { SqlMainContext, ExtHostModelViewShape, MainThreadModelViewShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
import { IItemConfig, ModelComponentTypes, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
class ModelBuilderImpl implements sqlops.ModelBuilder {
private nextComponentId: number;
constructor(private readonly _proxy: MainThreadModelViewShape, private readonly _handle: number) {
this.nextComponentId = 0;
}
navContainer(): sqlops.ContainerBuilder<sqlops.NavContainer, any, any> {
let id = this.getNextComponentId();
return new ContainerBuilderImpl(this._proxy, this._handle, ModelComponentTypes.NavContainer, id);
}
flexContainer(): sqlops.FlexBuilder {
let id = this.getNextComponentId();
return new ContainerBuilderImpl<sqlops.FlexContainer, sqlops.FlexLayout, sqlops.FlexItemLayout>(this._proxy, this._handle, ModelComponentTypes.FlexContainer, id);
}
card(): sqlops.ComponentBuilder<sqlops.CardComponent> {
let id = this.getNextComponentId();
return new ComponentBuilderImpl(new CardWrapper(this._proxy, this._handle, id));
}
dashboardWidget(widgetId: string): sqlops.ComponentBuilder<sqlops.WidgetComponent> {
let id = this.getNextComponentId();
return new ComponentBuilderImpl<sqlops.WidgetComponent>(new ComponentWrapper(this._proxy, this._handle, ModelComponentTypes.DashboardWidget, id));
}
dashboardWebview(webviewId: string): sqlops.ComponentBuilder<sqlops.WebviewComponent> {
let id = this.getNextComponentId();
return new ComponentBuilderImpl(new ComponentWrapper(this._proxy, this._handle, ModelComponentTypes.DashboardWebview, id));
}
private getNextComponentId(): string {
return `component${this._handle}_${this.nextComponentId++}`;
}
}
class ComponentBuilderImpl<T extends sqlops.Component> implements sqlops.ComponentBuilder<T> {
constructor(protected _component: ComponentWrapper) {
}
component(): T {
return <T><any>this._component;
}
withProperties<U>(properties: U): sqlops.ComponentBuilder<T> {
this._component.properties = properties;
return this;
}
}
class GenericComponentBuilder<T extends sqlops.Component> extends ComponentBuilderImpl<T> {
constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string) {
super(new ComponentWrapper(proxy, handle, type, id));
}
}
class ContainerBuilderImpl<T extends sqlops.Component, TLayout, TItemLayout> extends ComponentBuilderImpl<T> implements sqlops.ContainerBuilder<T, TLayout, TItemLayout> {
constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string) {
super(new ComponentWrapper(proxy, handle, type, id));
}
withLayout(layout: TLayout): sqlops.ContainerBuilder<T, TLayout, TItemLayout> {
this._component.layout = layout;
return this;
}
withItems(components: sqlops.Component[], itemLayout?: TItemLayout): sqlops.ContainerBuilder<T, TLayout, TItemLayout> {
this._component.itemConfigs = components.map(item => {
let componentWrapper = item as ComponentWrapper;
return new InternalItemConfig(componentWrapper, itemLayout);
});
return this;
}
}
class InternalItemConfig {
constructor(private _component: ComponentWrapper, public config: any) {}
public toIItemConfig(): IItemConfig {
return {
config: this.config,
componentShape: this._component.toComponentShape()
};
}
public get component(): sqlops.Component {
return this._component;
}
}
class ComponentWrapper implements sqlops.Component {
public properties: { [key: string]: any } = {};
public layout: any;
public itemConfigs: InternalItemConfig[];
private _onErrorEmitter = new Emitter<Error>();
public readonly onError: vscode.Event<Error> = this._onErrorEmitter.event;
constructor(protected readonly _proxy: MainThreadModelViewShape,
protected readonly _handle: number,
protected _type: ModelComponentTypes,
protected _id: string
) {
this.properties = {};
this.itemConfigs = [];
}
public get id(): string {
return this._id;
}
public get type(): ModelComponentTypes {
return this._type;
}
public get items(): sqlops.Component[] {
return this.itemConfigs.map(itemConfig => itemConfig.component);
}
public toComponentShape(): IComponentShape {
return <IComponentShape> {
id: this.id,
type: this.type,
layout: this.layout,
properties: this.properties,
itemConfigs: this.itemConfigs ? this.itemConfigs.map<IItemConfig>(item => item.toIItemConfig()) : undefined
};
}
public clearItems(): Thenable<void> {
this.itemConfigs = [];
return this._proxy.$clearContainer(this._handle, this.id);
}
public addItems(items: Array<sqlops.Component>, itemLayout ?: any): void {
for(let item of items) {
this.addItem(item, itemLayout);
}
}
public addItem(item: sqlops.Component, itemLayout ?: any): void {
let itemImpl = item as ComponentWrapper;
if (!itemImpl) {
throw new Error(nls.localize('unknownComponentType', 'Unkown component type. Must use ModelBuilder to create objects'));
}
let config = new InternalItemConfig(itemImpl, itemLayout);
this.itemConfigs.push(config);
this._proxy.$addToContainer(this._handle, this.id, config.toIItemConfig()).then(undefined, this.handleError);
}
public setLayout(layout: any): Thenable<void> {
return this._proxy.$setLayout(this._handle, this.id, layout);
}
public updateProperties(): Thenable<boolean> {
return this.notifyPropertyChanged();
}
protected notifyPropertyChanged(): Thenable<boolean> {
return this._proxy.$setProperties(this._handle, this._id, this.properties).then(() => true);
}
protected setProperty(key: string, value: any): Thenable<boolean> {
if (!this.properties[key] || this.properties[key] !== value) {
// Only notify the front end if a value has been updated
this.properties[key] = value;
return this.notifyPropertyChanged();
}
return Promise.resolve(true);
}
private handleError(err: Error): void {
this._onErrorEmitter.fire(err);
}
}
class ContainerWrapper<T, U> extends ComponentWrapper implements sqlops.Container<T, U> {
constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string) {
super(proxy, handle, type, id);
}
}
class CardWrapper extends ComponentWrapper implements sqlops.CardComponent {
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
super(proxy, handle, ModelComponentTypes.Card, id);
this.properties = {};
}
public get label(): string {
return this.properties['label'];
}
public set label(l: string) {
this.setProperty('label', l);
}
public get value(): string {
return this.properties['value'];
}
public set value(v: string) {
this.setProperty('value', v);
}
public get actions(): sqlops.ActionDescriptor[] {
return this.properties['actions'];
}
public set actions(a: sqlops.ActionDescriptor[]) {
this.setProperty('actions', a);
}
}
class ModelViewImpl implements sqlops.ModelView {
public onClosedEmitter = new Emitter<any>();
private _modelBuilder: sqlops.ModelBuilder;
constructor(
private readonly _proxy: MainThreadModelViewShape,
private readonly _handle: number,
private readonly _connection: sqlops.connection.Connection,
private readonly _serverInfo: sqlops.ServerInfo
) {
this._modelBuilder = new ModelBuilderImpl(this._proxy, this._handle);
}
public get onClosed(): vscode.Event<any> {
return this.onClosedEmitter.event;
}
public get connection(): sqlops.connection.Connection {
return deepClone(this._connection);
}
public get serverInfo(): sqlops.ServerInfo {
return deepClone(this._serverInfo);
}
public get modelBuilder(): sqlops.ModelBuilder {
return this._modelBuilder;
}
public initializeModel<T extends sqlops.Component>(component: T): Thenable<void> {
let componentImpl = <any>component as ComponentWrapper;
if (!componentImpl) {
return Promise.reject(nls.localize('unknownConfig', 'Unkown component configuration, must use ModelBuilder to create a configuration object'));
}
return this._proxy.$initializeModel(this._handle, componentImpl.toComponentShape());
}
}
export class ExtHostModelView implements ExtHostModelViewShape {
private readonly _proxy: MainThreadModelViewShape;
private readonly _modelViews = new Map<number, ModelViewImpl>();
private readonly _handlers = new Map<string, (view: sqlops.ModelView) => void>();
constructor(
mainContext: IMainContext
) {
this._proxy = mainContext.getProxy(SqlMainContext.MainThreadModelView);
}
$onClosed(handle: number): void {
const view = this._modelViews.get(handle);
view.onClosedEmitter.fire();
this._modelViews.delete(handle);
}
$registerProvider(widgetId: string, handler: (webview: sqlops.ModelView) => void): void {
this._handlers.set(widgetId, handler);
this._proxy.$registerProvider(widgetId);
}
$registerWidget(handle: number, id: string, connection: sqlops.connection.Connection, serverInfo: sqlops.ServerInfo): void {
let view = new ModelViewImpl(this._proxy, handle, connection, serverInfo);
this._modelViews.set(handle, view);
this._handlers.get(id)(view);
}
}

Просмотреть файл

@ -7,7 +7,7 @@
import { MainThreadDashboardWebviewShape, SqlMainContext, ExtHostDashboardWebviewsShape, SqlExtHostContext } from 'sql/workbench/api/node/sqlExtHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
import { IDashboardWebviewService, IDashboardWebview } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
import { IDashboardViewService, IDashboardWebview } from 'sql/services/dashboard/common/dashboardViewService';
@extHostNamedCustomer(SqlMainContext.MainThreadDashboardWebview)
export class MainThreadDashboardWebview implements MainThreadDashboardWebviewShape {
@ -20,10 +20,10 @@ export class MainThreadDashboardWebview implements MainThreadDashboardWebviewSha
constructor(
context: IExtHostContext,
@IDashboardWebviewService webviewService: IDashboardWebviewService
@IDashboardViewService viewService: IDashboardViewService
) {
this._proxy = context.getProxy(SqlExtHostContext.ExtHostDashboardWebviews);
webviewService.onRegisteredWebview(e => {
viewService.onRegisteredWebview(e => {
if (this.knownWidgets.includes(e.id)) {
let handle = MainThreadDashboardWebview._handlePool++;
this._dialogs.set(handle, e);
@ -36,7 +36,7 @@ export class MainThreadDashboardWebview implements MainThreadDashboardWebviewSha
}
public dispose(): void {
throw new Error("Method not implemented.");
throw new Error('Method not implemented.');
}
$sendMessage(handle: number, message: string) {

Просмотреть файл

@ -0,0 +1,75 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { MainThreadModelViewShape, SqlMainContext, ExtHostModelViewShape, SqlExtHostContext } from 'sql/workbench/api/node/sqlExtHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
import * as sqlops from 'sqlops';
import { IDashboardViewService } from 'sql/services/dashboard/common/dashboardViewService';
import { IItemConfig, ModelComponentTypes, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
import { IModelView } from 'sql/services/model/modelViewService';
@extHostNamedCustomer(SqlMainContext.MainThreadModelView)
export class MainThreadModelView implements MainThreadModelViewShape {
private static _handlePool = 0;
private readonly _proxy: ExtHostModelViewShape;
private readonly _dialogs = new Map<number, IModelView>();
private knownWidgets = new Array<string>();
constructor(
context: IExtHostContext,
@IDashboardViewService viewService: IDashboardViewService
) {
this._proxy = context.getProxy(SqlExtHostContext.ExtHostModelView);
viewService.onRegisteredModelView(view => {
if (this.knownWidgets.includes(view.id)) {
let handle = MainThreadModelView._handlePool++;
this._dialogs.set(handle, view);
this._proxy.$registerWidget(handle, view.id, view.connection, view.serverInfo);
}
});
}
public dispose(): void {
throw new Error('Method not implemented.');
}
$registerProvider(id: string) {
this.knownWidgets.push(id);
}
$initializeModel(handle: number, rootComponent: IComponentShape): Thenable<void> {
return this.execModelViewAction(handle, (modelView) => modelView.initializeModel(rootComponent));
}
$clearContainer(handle: number, componentId: string): Thenable<void> {
return this.execModelViewAction(handle, (modelView) => modelView.clearContainer(componentId));
}
$addToContainer(handle: number, containerId: string, item: IItemConfig): Thenable<void> {
return this.execModelViewAction(handle,
(modelView) => modelView.addToContainer(containerId, item));
}
$setLayout(handle: number, componentId: string, layout: any): Thenable<void> {
return this.execModelViewAction(handle, (modelView) => modelView.setLayout(componentId, layout));
}
$setProperties(handle: number, componentId: string, properties: { [key: string]: any; }): Thenable<void> {
return this.execModelViewAction(handle, (modelView) => modelView.setProperties(componentId, properties));
}
private execModelViewAction<T>(handle: number, action: (m: IModelView) => T): Thenable<T> {
let modelView: IModelView = this._dialogs.get(handle);
let result = action(modelView);
return Promise.resolve(result);
}
}

Просмотреть файл

@ -27,6 +27,7 @@ import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration
import { ExtHostModalDialogs } from 'sql/workbench/api/node/extHostModalDialog';
import { ExtHostTasks } from 'sql/workbench/api/node/extHostTasks';
import { ExtHostDashboardWebviews } from 'sql/workbench/api/node/extHostDashboardWebview';
import { ExtHostModelView } from 'sql/workbench/api/node/extHostModelView';
import { ExtHostConnectionManagement } from 'sql/workbench/api/node/extHostConnectionManagement';
import { ExtHostDashboard } from 'sql/workbench/api/node/extHostDashboard';
import { ExtHostObjectExplorer } from 'sql/workbench/api/node/extHostObjectExplorer';
@ -61,8 +62,10 @@ export function createApiFactory(
const extHostModalDialogs = rpcProtocol.set(SqlExtHostContext.ExtHostModalDialogs, new ExtHostModalDialogs(rpcProtocol));
const extHostTasks = rpcProtocol.set(SqlExtHostContext.ExtHostTasks, new ExtHostTasks(rpcProtocol, logService));
const extHostWebviewWidgets = rpcProtocol.set(SqlExtHostContext.ExtHostDashboardWebviews, new ExtHostDashboardWebviews(rpcProtocol));
const extHostModelView = rpcProtocol.set(SqlExtHostContext.ExtHostModelView, new ExtHostModelView(rpcProtocol));
const extHostDashboard = rpcProtocol.set(SqlExtHostContext.ExtHostDashboard, new ExtHostDashboard(rpcProtocol));
return {
vsCodeFactory: vsCodeFactory,
sqlopsFactory: function (extension: IExtensionDescription): typeof sqlops {
@ -297,6 +300,9 @@ export function createApiFactory(
const dashboard = {
registerWebviewProvider(widgetId: string, handler: (webview: sqlops.DashboardWebview) => void) {
extHostWebviewWidgets.$registerProvider(widgetId, handler);
},
registerModelViewProvider(widgetId: string, handler: (view: sqlops.ModelView) => void): void {
extHostModelView.$registerProvider(widgetId, handler);
}
};

Просмотреть файл

@ -19,6 +19,7 @@ import 'sql/workbench/api/node/mainThreadResourceProvider';
import 'sql/workbench/api/electron-browser/mainThreadTasks';
import 'sql/workbench/api/electron-browser/mainThreadDashboard';
import 'sql/workbench/api/node/mainThreadDashboardWebview';
import 'sql/workbench/api/node/mainThreadModelView';
import './mainThreadAccountManagement';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';

Просмотреть файл

@ -16,6 +16,7 @@ import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import { ITaskHandlerDescription } from 'sql/platform/tasks/common/tasks';
import { IItemConfig, ModelComponentTypes, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
export abstract class ExtHostAccountManagementShape {
$autoOAuthCancelled(handle: number): Thenable<void> { throw ni(); }
@ -444,6 +445,7 @@ export const SqlMainContext = {
MainThreadModalDialog: createMainId<MainThreadModalDialogShape>('MainThreadModalDialog'),
MainThreadTasks: createMainId<MainThreadTasksShape>('MainThreadTasks'),
MainThreadDashboardWebview: createMainId<MainThreadDashboardWebviewShape>('MainThreadDashboardWebview'),
MainThreadModelView: createMainId<MainThreadModelViewShape>('MainThreadModelView'),
MainThreadDashboard: createMainId<MainThreadDashboardShape>('MainThreadDashboard')
};
@ -458,6 +460,7 @@ export const SqlExtHostContext = {
ExtHostModalDialogs: createExtId<ExtHostModalDialogsShape>('ExtHostModalDialogs'),
ExtHostTasks: createExtId<ExtHostTasksShape>('ExtHostTasks'),
ExtHostDashboardWebviews: createExtId<ExtHostDashboardWebviewsShape>('ExtHostDashboardWebviews'),
ExtHostModelView: createExtId<ExtHostModelViewShape>('ExtHostModelView'),
ExtHostDashboard: createExtId<ExtHostDashboardShape>('ExtHostDashboard')
};
@ -507,6 +510,21 @@ export interface MainThreadDashboardWebviewShape extends IDisposable {
$setHtml(handle: number, value: string);
}
export interface ExtHostModelViewShape {
$registerProvider(widgetId: string, handler: (webview: sqlops.ModelView) => void): void;
$onClosed(handle: number): void;
$registerWidget(handle: number, id: string, connection: sqlops.connection.Connection, serverInfo: sqlops.ServerInfo): void;
}
export interface MainThreadModelViewShape extends IDisposable {
$registerProvider(id: string): void;
$initializeModel(handle: number, rootComponent: IComponentShape): Thenable<void>;
$clearContainer(handle: number, componentId: string): Thenable<void>;
$addToContainer(handle: number, containerId: string, item: IItemConfig): Thenable<void>;
$setLayout(handle: number, componentId: string, layout: any): Thenable<void>;
$setProperties(handle: number, componentId: string, properties: { [key: string]: any }): Thenable<void>;
}
export interface ExtHostObjectExplorerShape {
}

Просмотреть файл

@ -154,8 +154,8 @@ import { ClipboardService as sqlClipboardService } from 'sql/platform/clipboard/
import { IResourceProviderService, IAccountPickerService } from 'sql/parts/accountManagement/common/interfaces';
import { ResourceProviderService } from 'sql/parts/accountManagement/common/resourceProviderService';
import { AccountPickerService } from 'sql/parts/accountManagement/accountPicker/accountPickerService';
import { IDashboardWebviewService } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
import { DashboardWebviewService } from 'sql/services/dashboardWebview/common/dashboardWebviewServiceImpl';
import { IDashboardViewService } from 'sql/services/dashboard/common/dashboardViewService';
import { DashboardViewService } from 'sql/services/dashboard/common/dashboardViewServiceImpl';
import { IDashboardService } from 'sql/services/dashboard/common/dashboardService';
import { DashboardService } from 'sql/services/dashboard/common/dashboardServiceImpl';
@ -677,7 +677,7 @@ export class Workbench implements IPartService {
// {{SQL CARBON EDIT}}
// SQL Tools services
serviceCollection.set(IDashboardService, this.instantiationService.createInstance(DashboardService));
serviceCollection.set(IDashboardWebviewService, this.instantiationService.createInstance(DashboardWebviewService));
serviceCollection.set(IDashboardViewService, this.instantiationService.createInstance(DashboardViewService));
serviceCollection.set(IAngularEventingService, this.instantiationService.createInstance(AngularEventingService));
serviceCollection.set(INewDashboardTabDialogService, this.instantiationService.createInstance(NewDashboardTabDialogService));
serviceCollection.set(ISqlOAuthService, this.instantiationService.createInstance(SqlOAuthService));

Просмотреть файл

@ -168,6 +168,8 @@ import 'sql/parts/dashboard/widgets/explorer/explorerWidget.contribution';
import 'sql/parts/dashboard/widgets/tasks/tasksWidget.contribution';
import 'sql/parts/dashboard/widgets/webview/webviewWidget.contribution';
import 'sql/parts/dashboard/dashboardConfig.contribution';
/* Model-based Views */
import 'sql/parts/modelComponents/components.contribution';
/* Containers */
import 'sql/parts/dashboard/containers/dashboardWebviewContainer.contribution';
import 'sql/parts/dashboard/containers/dashboardControlHostContainer.contribution';
@ -175,6 +177,7 @@ import 'sql/parts/dashboard/containers/dashboardGridContainer.contribution';
import 'sql/parts/dashboard/containers/dashboardWidgetContainer.contribution';
import 'sql/parts/dashboard/containers/dashboardContainer.contribution';
import 'sql/parts/dashboard/containers/dashboardNavSection.contribution';
import 'sql/parts/dashboard/containers/dashboardModelViewContainer.contribution';
import 'sql/parts/dashboard/common/dashboardTab.contribution';
/* Extension Host */
import 'sql/workbench/api/electron-browser/sqlExtensionHost.contribution';