Feature/bdc create (#4012)
* initial checkin * rename * wizard pages * target cluster radio button group * resource strings * existing cluster picker * revert changes to unwanted file * revert unwanted changes-2 * update cluster icon * settings page * fix group container * hyperlink component * address review comments * comments part 2
@@ -77,9 +77,10 @@ const sqlBuiltInExtensions = [
|
|||||||
'agent',
|
'agent',
|
||||||
'import',
|
'import',
|
||||||
'profiler',
|
'profiler',
|
||||||
'admin-pack'
|
'admin-pack',
|
||||||
|
'big-data-cluster'
|
||||||
];
|
];
|
||||||
var azureExtensions = [ 'azurecore', 'mssql'];
|
var azureExtensions = ['azurecore', 'mssql'];
|
||||||
|
|
||||||
const vscodeEntryPoints = _.flatten([
|
const vscodeEntryPoints = _.flatten([
|
||||||
buildfile.entrypoint('vs/workbench/workbench.main'),
|
buildfile.entrypoint('vs/workbench/workbench.main'),
|
||||||
|
|||||||
17
extensions/big-data-cluster/README.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Microsoft SQL Server big data cluster Extension for Azure Data Studio
|
||||||
|
|
||||||
|
Welcome to Microsoft SQL Server big data cluster Extension for Azure Data Studio!
|
||||||
|
|
||||||
|
## Code of Conduct
|
||||||
|
|
||||||
|
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||||
|
|
||||||
|
## Privacy Statement
|
||||||
|
|
||||||
|
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/en-us/privacystatement) describes the privacy statement of this software.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the [Source EULA](https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt).
|
||||||
1
extensions/big-data-cluster/images/cluster.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>cluster</title><path d="M14,7a1.94,1.94,0,0,1,.78.16,2,2,0,0,1,1.07,1.07,2,2,0,0,1,0,1.55,2,2,0,0,1-1.07,1.07,2,2,0,0,1-1.51,0,2.05,2.05,0,0,1-1.05-1,1.88,1.88,0,0,1-.2-.72L10.84,9a3,3,0,0,1-.56,1,3,3,0,0,1-.87.7L9.86,12H10a1.94,1.94,0,0,1,.78.16,2,2,0,0,1,1.07,1.07,2,2,0,0,1,0,1.55,2,2,0,0,1-1.07,1.07,2,2,0,0,1-1.55,0,2,2,0,0,1-1.07-1.07A2,2,0,0,1,8.25,13a2,2,0,0,1,.67-.72L8.46,11l-.23,0H8a3,3,0,0,1-1.36-.32,3,3,0,0,1-1.07-.9L4,10.58a2,2,0,0,1-.11,1.2,2,2,0,0,1-1.07,1.07A1.94,1.94,0,0,1,2,13a1.94,1.94,0,0,1-.78-.16A2,2,0,0,1,.16,11.78a2,2,0,0,1,0-1.55A2,2,0,0,1,1.22,9.16,1.94,1.94,0,0,1,2,9a2,2,0,0,1,.83.18,2,2,0,0,1,.68.51l1.63-.81A3,3,0,0,1,5.2,6.93,2.91,2.91,0,0,1,5.77,6L4.82,4.82A2,2,0,0,1,4,5a1.94,1.94,0,0,1-.78-.16A2,2,0,0,1,2.16,3.78a2,2,0,0,1,0-1.55A2,2,0,0,1,3.22,1.16a2,2,0,0,1,1.55,0A2,2,0,0,1,5.84,2.22,1.94,1.94,0,0,1,6,3a1.94,1.94,0,0,1-.4,1.2l.94,1.18a3.24,3.24,0,0,1,.71-.28A2.94,2.94,0,0,1,8,5a3,3,0,0,1,1.23.26l1.28-1.92a2,2,0,0,1-.37-.62A2,2,0,0,1,10,2a1.94,1.94,0,0,1,.16-.78A2,2,0,0,1,11.22.16a2,2,0,0,1,1.55,0,2,2,0,0,1,1.07,1.07A1.94,1.94,0,0,1,14,2a1.94,1.94,0,0,1-.16.78,2,2,0,0,1-1.07,1.07A1.94,1.94,0,0,1,12,4a2.06,2.06,0,0,1-.66-.11L10.05,5.82A3,3,0,0,1,11,8l1.17.2a2,2,0,0,1,.74-.86,2.14,2.14,0,0,1,.52-.24A1.92,1.92,0,0,1,14,7ZM2,12a1,1,0,0,0,.39-.08,1,1,0,0,0,.53-.53,1,1,0,0,0,0-.78,1,1,0,0,0-.53-.53,1,1,0,0,0-.78,0,1,1,0,0,0-.53.53,1,1,0,0,0,0,.78,1,1,0,0,0,.53.53A1,1,0,0,0,2,12ZM3,3a1,1,0,0,0,.08.39,1,1,0,0,0,.53.53,1,1,0,0,0,.78,0,1,1,0,0,0,.53-.53,1,1,0,0,0,0-.78,1,1,0,0,0-.53-.53,1,1,0,0,0-.78,0,1,1,0,0,0-.53.53A1,1,0,0,0,3,3Zm5,7a1.94,1.94,0,0,0,.78-.16A2,2,0,0,0,9.84,8.78a2,2,0,0,0,0-1.55A2,2,0,0,0,8.78,6.16a2,2,0,0,0-1.55,0A2,2,0,0,0,6.16,7.22a2,2,0,0,0,0,1.55A2,2,0,0,0,7.22,9.84,1.94,1.94,0,0,0,8,10Zm3,4a1,1,0,0,0-.08-.39,1,1,0,0,0-.53-.53,1,1,0,0,0-.78,0,1,1,0,0,0-.53.53,1,1,0,0,0,0,.78,1,1,0,0,0,.53.53,1,1,0,0,0,.78,0,1,1,0,0,0,.53-.53A1,1,0,0,0,11,14ZM12,1a1,1,0,0,0-.39.08,1,1,0,0,0-.53.53,1,1,0,0,0,0,.78,1,1,0,0,0,.53.53,1,1,0,0,0,.78,0,1,1,0,0,0,.53-.53,1,1,0,0,0,0-.78,1,1,0,0,0-.53-.53A1,1,0,0,0,12,1Zm2,9a1,1,0,0,0,.39-.08,1,1,0,0,0,.53-.53,1,1,0,0,0,0-.78,1,1,0,0,0-.53-.53,1,1,0,0,0-.78,0,1,1,0,0,0-.53.53,1,1,0,0,0,0,.78,1,1,0,0,0,.53.53A1,1,0,0,0,14,10Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 2.3 KiB |
1
extensions/big-data-cluster/images/cluster_inverse.svg
Normal file
@@ -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>cluster_inverse</title><path class="cls-1" d="M14,7a1.94,1.94,0,0,1,.78.16,2,2,0,0,1,1.07,1.07,2,2,0,0,1,0,1.55,2,2,0,0,1-1.07,1.07,2,2,0,0,1-1.51,0,2.05,2.05,0,0,1-1.05-1,1.88,1.88,0,0,1-.2-.72L10.84,9a3,3,0,0,1-.56,1,3,3,0,0,1-.87.7L9.86,12H10a1.94,1.94,0,0,1,.78.16,2,2,0,0,1,1.07,1.07,2,2,0,0,1,0,1.55,2,2,0,0,1-1.07,1.07,2,2,0,0,1-1.55,0,2,2,0,0,1-1.07-1.07A2,2,0,0,1,8.25,13a2,2,0,0,1,.67-.72L8.46,11l-.23,0H8a3,3,0,0,1-1.36-.32,3,3,0,0,1-1.07-.9L4,10.58a2,2,0,0,1-.11,1.2,2,2,0,0,1-1.07,1.07A1.94,1.94,0,0,1,2,13a1.94,1.94,0,0,1-.78-.16A2,2,0,0,1,.16,11.78a2,2,0,0,1,0-1.55A2,2,0,0,1,1.22,9.16,1.94,1.94,0,0,1,2,9a2,2,0,0,1,.83.18,2,2,0,0,1,.68.51l1.63-.81A3,3,0,0,1,5.2,6.93,2.91,2.91,0,0,1,5.77,6L4.82,4.82A2,2,0,0,1,4,5a1.94,1.94,0,0,1-.78-.16A2,2,0,0,1,2.16,3.78a2,2,0,0,1,0-1.55A2,2,0,0,1,3.22,1.16a2,2,0,0,1,1.55,0A2,2,0,0,1,5.84,2.22,1.94,1.94,0,0,1,6,3a1.94,1.94,0,0,1-.4,1.2l.94,1.18a3.24,3.24,0,0,1,.71-.28A2.94,2.94,0,0,1,8,5a3,3,0,0,1,1.23.26l1.28-1.92a2,2,0,0,1-.37-.62A2,2,0,0,1,10,2a1.94,1.94,0,0,1,.16-.78A2,2,0,0,1,11.22.16a2,2,0,0,1,1.55,0,2,2,0,0,1,1.07,1.07A1.94,1.94,0,0,1,14,2a1.94,1.94,0,0,1-.16.78,2,2,0,0,1-1.07,1.07A1.94,1.94,0,0,1,12,4a2.06,2.06,0,0,1-.66-.11L10.05,5.82A3,3,0,0,1,11,8l1.17.2a2,2,0,0,1,.74-.86,2.14,2.14,0,0,1,.52-.24A1.92,1.92,0,0,1,14,7ZM2,12a1,1,0,0,0,.39-.08,1,1,0,0,0,.53-.53,1,1,0,0,0,0-.78,1,1,0,0,0-.53-.53,1,1,0,0,0-.78,0,1,1,0,0,0-.53.53,1,1,0,0,0,0,.78,1,1,0,0,0,.53.53A1,1,0,0,0,2,12ZM3,3a1,1,0,0,0,.08.39,1,1,0,0,0,.53.53,1,1,0,0,0,.78,0,1,1,0,0,0,.53-.53,1,1,0,0,0,0-.78,1,1,0,0,0-.53-.53,1,1,0,0,0-.78,0,1,1,0,0,0-.53.53A1,1,0,0,0,3,3Zm5,7a1.94,1.94,0,0,0,.78-.16A2,2,0,0,0,9.84,8.78a2,2,0,0,0,0-1.55A2,2,0,0,0,8.78,6.16a2,2,0,0,0-1.55,0A2,2,0,0,0,6.16,7.22a2,2,0,0,0,0,1.55A2,2,0,0,0,7.22,9.84,1.94,1.94,0,0,0,8,10Zm3,4a1,1,0,0,0-.08-.39,1,1,0,0,0-.53-.53,1,1,0,0,0-.78,0,1,1,0,0,0-.53.53,1,1,0,0,0,0,.78,1,1,0,0,0,.53.53,1,1,0,0,0,.78,0,1,1,0,0,0,.53-.53A1,1,0,0,0,11,14ZM12,1a1,1,0,0,0-.39.08,1,1,0,0,0-.53.53,1,1,0,0,0,0,.78,1,1,0,0,0,.53.53,1,1,0,0,0,.78,0,1,1,0,0,0,.53-.53,1,1,0,0,0,0-.78,1,1,0,0,0-.53-.53A1,1,0,0,0,12,1Zm2,9a1,1,0,0,0,.39-.08,1,1,0,0,0,.53-.53,1,1,0,0,0,0-.78,1,1,0,0,0-.53-.53,1,1,0,0,0-.78,0,1,1,0,0,0-.53.53,1,1,0,0,0,0,.78,1,1,0,0,0,.53.53A1,1,0,0,0,14,10Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 2.4 KiB |
BIN
extensions/big-data-cluster/images/sqlserver.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
41
extensions/big-data-cluster/package.json
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "big-data-cluster",
|
||||||
|
"displayName": "SQL Server big data cluster",
|
||||||
|
"description": "SQL Server big data cluster",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"publisher": "Microsoft",
|
||||||
|
"preview": true,
|
||||||
|
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt",
|
||||||
|
"icon": "images/sqlserver.png",
|
||||||
|
"aiKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412",
|
||||||
|
"engines": {
|
||||||
|
"vscode": "0.10.x"
|
||||||
|
},
|
||||||
|
"activationEvents": [
|
||||||
|
"*"
|
||||||
|
],
|
||||||
|
"main": "./out/main",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/Microsoft/azuredatastudio.git"
|
||||||
|
},
|
||||||
|
"extensionDependencies": [
|
||||||
|
"Microsoft.mssql"
|
||||||
|
],
|
||||||
|
"contributes": {
|
||||||
|
"commands": [
|
||||||
|
{
|
||||||
|
"command": "mssql.cluster.create",
|
||||||
|
"title": "Create SQL Server big data cluster",
|
||||||
|
"category": "SQL Server"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vscode-nls": "^3.2.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"mocha-junit-reporter": "^1.17.0",
|
||||||
|
"mocha-multi-reporters": "^1.1.7"
|
||||||
|
}
|
||||||
|
}
|
||||||
35
extensions/big-data-cluster/src/data/kubeConfigParser.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 { ClusterInfo } from '../interfaces';
|
||||||
|
|
||||||
|
export interface IKubeConfigParser {
|
||||||
|
parse(configPath: string): ClusterInfo[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TestKubeConfigParser implements IKubeConfigParser {
|
||||||
|
parse(configPath: string): ClusterInfo[] {
|
||||||
|
let clusters = [];
|
||||||
|
for (let i = 0; i < 18; i++) {
|
||||||
|
let name;
|
||||||
|
if (i % 2 === 0) {
|
||||||
|
name = `kubernetes cluster ${i}`;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
name = 'cluster dev ' + i;
|
||||||
|
}
|
||||||
|
clusters.push(
|
||||||
|
{
|
||||||
|
displayName: name,
|
||||||
|
name: `kub-dev-xxxx-cluster-${i}`,
|
||||||
|
user: 'root'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return clusters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
17
extensions/big-data-cluster/src/interfaces.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
export interface ClusterInfo {
|
||||||
|
name: string;
|
||||||
|
displayName: string;
|
||||||
|
user: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TargetClusterType {
|
||||||
|
ExistingKubernetesCluster,
|
||||||
|
NewLocalCluster,
|
||||||
|
NewAksCluster
|
||||||
|
}
|
||||||
21
extensions/big-data-cluster/src/main.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 vscode = require('vscode');
|
||||||
|
import { MainController } from './mainController';
|
||||||
|
export let controller: MainController;
|
||||||
|
|
||||||
|
export function activate(context: vscode.ExtensionContext) {
|
||||||
|
controller = new MainController(context);
|
||||||
|
controller.activate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// this method is called when your extension is deactivated
|
||||||
|
export function deactivate(): void {
|
||||||
|
if (controller) {
|
||||||
|
controller.deactivate();
|
||||||
|
}
|
||||||
|
}
|
||||||
34
extensions/big-data-cluster/src/mainController.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 { CreateClusterWizard } from './wizards/create-cluster/createClusterWizard';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main controller class that initializes the extension
|
||||||
|
*/
|
||||||
|
export class MainController {
|
||||||
|
protected _context: vscode.ExtensionContext;
|
||||||
|
|
||||||
|
public constructor(context: vscode.ExtensionContext) {
|
||||||
|
this._context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activates the extension
|
||||||
|
*/
|
||||||
|
public activate(): void {
|
||||||
|
vscode.commands.registerCommand('mssql.cluster.create', () => {
|
||||||
|
let wizard = new CreateClusterWizard(this._context);
|
||||||
|
wizard.open();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deactivates the extension
|
||||||
|
*/
|
||||||
|
public deactivate(): void { }
|
||||||
|
}
|
||||||
9
extensions/big-data-cluster/src/typings/ref.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
||||||
|
/// <reference path='../../../../src/sql/sqlops.d.ts'/>
|
||||||
|
/// <reference path='../../../../src/sql/sqlops.proposed.d.ts'/>
|
||||||
|
/// <reference types='@types/node'/>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 { IKubeConfigParser } from '../../data/kubeConfigParser';
|
||||||
|
import { ClusterInfo, TargetClusterType } from '../../interfaces';
|
||||||
|
|
||||||
|
export class CreateClusterModel {
|
||||||
|
|
||||||
|
constructor(private _kubeConfigParser: IKubeConfigParser) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public loadClusters(configPath: string): ClusterInfo[] {
|
||||||
|
return this._kubeConfigParser.parse(configPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public targetClusterType: TargetClusterType;
|
||||||
|
|
||||||
|
public selectedCluster: ClusterInfo;
|
||||||
|
|
||||||
|
public adminUserName: string;
|
||||||
|
|
||||||
|
public adminPassword: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 { CreateClusterModel } from './createClusterModel';
|
||||||
|
import { SelectTargetClusterPage } from './pages/targetClusterPage';
|
||||||
|
import { SummaryPage } from './pages/summaryPage';
|
||||||
|
import { SettingsPage } from './pages/settingsPage';
|
||||||
|
import { ClusterProfilePage } from './pages/clusterProfilePage';
|
||||||
|
import { TestKubeConfigParser } from '../../data/kubeConfigParser';
|
||||||
|
import { ExtensionContext } from 'vscode';
|
||||||
|
import { WizardBase } from '../wizardBase';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
export class CreateClusterWizard extends WizardBase<CreateClusterModel> {
|
||||||
|
constructor(context: ExtensionContext) {
|
||||||
|
let configParser = new TestKubeConfigParser();
|
||||||
|
let model = new CreateClusterModel(configParser);
|
||||||
|
super(model, context, localize('bdc-create.wizardTitle', 'Create a big data cluster'));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected initialize(): void {
|
||||||
|
let settingsPage = new SettingsPage(this.model, this);
|
||||||
|
let clusterProfilePage = new ClusterProfilePage(this.model, this);
|
||||||
|
let selectTargetClusterPage = new SelectTargetClusterPage(this.model, this);
|
||||||
|
let summaryPage = new SummaryPage(this.model, this);
|
||||||
|
|
||||||
|
this.wizard.pages = [
|
||||||
|
settingsPage.page,
|
||||||
|
clusterProfilePage.page,
|
||||||
|
selectTargetClusterPage.page,
|
||||||
|
summaryPage.page
|
||||||
|
];
|
||||||
|
|
||||||
|
this.wizard.generateScriptButton.label = localize('bdc-create.generateScriptsButtonText', 'Generate Scripts');
|
||||||
|
this.wizard.doneButton.label = localize('bdc-create.createClusterButtonText', 'Create');
|
||||||
|
|
||||||
|
this.wizard.generateScriptButton.onClick(() => { });
|
||||||
|
this.wizard.doneButton.onClick(() => { });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 { WizardPageBase } from '../../wizardPageBase';
|
||||||
|
import { CreateClusterModel } from '../createClusterModel';
|
||||||
|
import { WizardBase } from '../../wizardBase';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
export class ClusterProfilePage extends WizardPageBase<CreateClusterModel> {
|
||||||
|
constructor(model: CreateClusterModel, wizard: WizardBase<CreateClusterModel>) {
|
||||||
|
super(localize('bdc-create.clusterProfilePageTitle', 'Select a cluster profile'),
|
||||||
|
localize('bdc-create.clusterProfilePageDescription', 'Select your requirement and we will provide you a pre-defined default scaling. You can later go to cluster configuration and customize it.'),
|
||||||
|
model, wizard);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected initialize(view: sqlops.ModelView): Thenable<void> {
|
||||||
|
let formBuilder = view.modelBuilder.formContainer();
|
||||||
|
let form = formBuilder.component();
|
||||||
|
return view.initializeModel(form);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 { WizardPageBase } from '../../wizardPageBase';
|
||||||
|
import { CreateClusterModel } from '../createClusterModel';
|
||||||
|
import { WizardBase } from '../../wizardBase';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
export class SettingsPage extends WizardPageBase<CreateClusterModel> {
|
||||||
|
constructor(model: CreateClusterModel, wizard: WizardBase<CreateClusterModel>) {
|
||||||
|
super(localize('bdc-create.settingsPageTitle', 'Settings'),
|
||||||
|
localize('bdc-create.settingsPageDescription', 'Configure the settings required for deploying SQL Server big data cluster'),
|
||||||
|
model, wizard);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected initialize(view: sqlops.ModelView): Thenable<void> {
|
||||||
|
let formBuilder = view.modelBuilder.formContainer();
|
||||||
|
let adminUserNameInput = this.createInputWithLabel(view, localize('bdc-create.AdminUsernameText', 'Admin username'), true, (inputBox: sqlops.InputBoxComponent) => {
|
||||||
|
this.model.adminUserName = inputBox.value;
|
||||||
|
});
|
||||||
|
let adminPasswordInput = this.createInputWithLabel(view, localize('bdc-create.AdminUserPasswordText', 'Password'), true, (inputBox: sqlops.InputBoxComponent) => {
|
||||||
|
this.model.adminPassword = inputBox.value;
|
||||||
|
}, 'password');
|
||||||
|
|
||||||
|
let basicSettingsGroup = view.modelBuilder.groupContainer().withItems([adminUserNameInput, adminPasswordInput]).withLayout({ header: localize('bdc-create.BasicSettingsText', 'Basic Settings'), collapsible: true }).component();
|
||||||
|
let dockerSettingsGroup = view.modelBuilder.groupContainer().withItems([]).withLayout({ header: localize('bdc-create.DockerSettingsText', 'Docker Settings'), collapsible: true }).component();
|
||||||
|
|
||||||
|
let acceptEulaCheckbox = view.modelBuilder.checkBox().component();
|
||||||
|
acceptEulaCheckbox.label = localize('bdc-create.AcceptEulaText', 'I accept the SQL Server EULA');
|
||||||
|
acceptEulaCheckbox.checked = false;
|
||||||
|
|
||||||
|
let eulaHyperlink = view.modelBuilder.hyperlink().withProperties({
|
||||||
|
label: localize('bdc-create.ViewEulaText', 'View Eula'),
|
||||||
|
url: 'https://docs.microsoft.com/en-us/sql/getting-started/about-the-sql-server-license-terms?view=sql-server-2014'
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
let eulaContainer = this.createRow(view, [acceptEulaCheckbox, eulaHyperlink]);
|
||||||
|
|
||||||
|
let form = formBuilder.withFormItems([
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
component: basicSettingsGroup
|
||||||
|
}, {
|
||||||
|
title: '',
|
||||||
|
component: dockerSettingsGroup
|
||||||
|
}, {
|
||||||
|
title: '',
|
||||||
|
component: eulaContainer
|
||||||
|
}]).component();
|
||||||
|
return view.initializeModel(form);
|
||||||
|
}
|
||||||
|
|
||||||
|
private createInputWithLabel(view: sqlops.ModelView, label: string, isRequiredField: boolean, textChangedHandler: (inputBox: sqlops.InputBoxComponent) => void, inputType: string = 'text'): sqlops.FlexContainer {
|
||||||
|
let input = view.modelBuilder.inputBox().withProperties({
|
||||||
|
required: isRequiredField,
|
||||||
|
inputType: inputType
|
||||||
|
}).component();
|
||||||
|
let text = view.modelBuilder.text().withProperties({ value: label }).component();
|
||||||
|
input.width = '300px';
|
||||||
|
text.width = '150px';
|
||||||
|
input.onTextChanged(() => {
|
||||||
|
textChangedHandler(input);
|
||||||
|
});
|
||||||
|
return this.createRow(view, [text, input]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private createRow(view: sqlops.ModelView, items: sqlops.Component[]): sqlops.FlexContainer {
|
||||||
|
return view.modelBuilder.flexContainer().withItems(items, { CSSStyles: { 'margin-right': '10px' } }).withLayout({ flexFlow: 'row', alignItems: 'baseline' }).component();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 { WizardPageBase } from '../../wizardPageBase';
|
||||||
|
import { CreateClusterModel } from '../createClusterModel';
|
||||||
|
import { WizardBase } from '../../wizardBase';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
export class SummaryPage extends WizardPageBase<CreateClusterModel> {
|
||||||
|
constructor(model: CreateClusterModel, wizard: WizardBase<CreateClusterModel>) {
|
||||||
|
super(localize('bdc-create.summaryPageTitle', 'Summary'), '', model, wizard);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected initialize(view: sqlops.ModelView): Thenable<void> {
|
||||||
|
let formBuilder = view.modelBuilder.formContainer();
|
||||||
|
let form = formBuilder.component();
|
||||||
|
return view.initializeModel(form);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 vscode from 'vscode';
|
||||||
|
import * as os from 'os';
|
||||||
|
import { WizardPageBase } from '../../wizardPageBase';
|
||||||
|
import { CreateClusterModel } from '../createClusterModel';
|
||||||
|
import { WizardBase } from '../../wizardBase';
|
||||||
|
import { TargetClusterType } from '../../../interfaces';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
const ClusterTypeRadioButtonGroupName = 'SelectClusterType';
|
||||||
|
|
||||||
|
export class SelectTargetClusterPage extends WizardPageBase<CreateClusterModel> {
|
||||||
|
constructor(model: CreateClusterModel, wizard: WizardBase<CreateClusterModel>) {
|
||||||
|
super(localize('bdc-create.selectTargetClusterPageTitle', 'Where do you want to deploy this SQL Server big data cluster?'),
|
||||||
|
localize('bdc-create.selectTargetClusterPageDescription', 'Select an existing Kubernetes cluster or choose a cluster type you want to deploy'),
|
||||||
|
model, wizard);
|
||||||
|
}
|
||||||
|
|
||||||
|
private existingClusterOption: sqlops.RadioButtonComponent;
|
||||||
|
private createLocalClusterOption: sqlops.RadioButtonComponent;
|
||||||
|
private createAksClusterOption: sqlops.RadioButtonComponent;
|
||||||
|
private pageContainer: sqlops.DivContainer;
|
||||||
|
private existingClusterControl: sqlops.FlexContainer;
|
||||||
|
private createLocalClusterControl: sqlops.FlexContainer;
|
||||||
|
private createAksClusterControl: sqlops.FlexContainer;
|
||||||
|
private clusterContextsLabel: sqlops.TextComponent;
|
||||||
|
private errorLoadingClustersLabel: sqlops.TextComponent;
|
||||||
|
private clusterContextContainer: sqlops.DivContainer;
|
||||||
|
|
||||||
|
private cards: sqlops.CardComponent[];
|
||||||
|
|
||||||
|
protected initialize(view: sqlops.ModelView): Thenable<void> {
|
||||||
|
let self = this;
|
||||||
|
this.model.targetClusterType = TargetClusterType.ExistingKubernetesCluster;
|
||||||
|
this.existingClusterOption = this.createTargetTypeRadioButton(view, localize('bdc-create.existingK8sCluster', 'Existing Kubernetes cluster'), true);
|
||||||
|
this.createLocalClusterOption = this.createTargetTypeRadioButton(view, localize('bdc-create.createLocalCluster', 'Create new local cluster'));
|
||||||
|
this.createAksClusterOption = this.createTargetTypeRadioButton(view, localize('bdc-create.createAksCluster', 'Create new Azure Kubernetes Service cluster'));
|
||||||
|
|
||||||
|
this.existingClusterOption.onDidClick(() => {
|
||||||
|
self.pageContainer.clearItems();
|
||||||
|
self.pageContainer.addItem(self.existingClusterControl);
|
||||||
|
self.model.targetClusterType = TargetClusterType.ExistingKubernetesCluster;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.createLocalClusterOption.onDidClick(() => {
|
||||||
|
self.pageContainer.clearItems();
|
||||||
|
self.pageContainer.addItem(self.createLocalClusterControl);
|
||||||
|
self.model.targetClusterType = TargetClusterType.NewLocalCluster;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.createAksClusterOption.onDidClick(() => {
|
||||||
|
self.pageContainer.clearItems();
|
||||||
|
self.pageContainer.addItem(self.createAksClusterControl);
|
||||||
|
self.model.targetClusterType = TargetClusterType.NewAksCluster;
|
||||||
|
});
|
||||||
|
|
||||||
|
let optionGroup = view.modelBuilder.divContainer().withItems([this.existingClusterOption, this.createLocalClusterOption, this.createAksClusterOption],
|
||||||
|
{ CSSStyles: { 'margin-right': '30px' } }).withLayout({ width: 'auto' }).component();
|
||||||
|
this.initExistingClusterControl(view);
|
||||||
|
this.initLocalClusterControl(view);
|
||||||
|
this.initAksClusterControl(view);
|
||||||
|
this.pageContainer = view.modelBuilder.divContainer().withItems([this.existingClusterControl]).withLayout({ width: '100%' }).component();
|
||||||
|
let container = view.modelBuilder.flexContainer().withItems([optionGroup, this.pageContainer], { flex: '0 0 auto' }).withLayout({ flexFlow: 'row', alignItems: 'left' }).component();
|
||||||
|
|
||||||
|
let formBuilder = view.modelBuilder.formContainer().withFormItems(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
component: container,
|
||||||
|
title: ''
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{
|
||||||
|
horizontal: true
|
||||||
|
}
|
||||||
|
).withLayout({ width: '100%', height: '100%' });
|
||||||
|
|
||||||
|
let form = formBuilder.component();
|
||||||
|
return view.initializeModel(form);
|
||||||
|
}
|
||||||
|
|
||||||
|
private createTargetTypeRadioButton(view: sqlops.ModelView, label: string, checked: boolean = false): sqlops.RadioButtonComponent {
|
||||||
|
return view.modelBuilder.radioButton().withProperties({ label: label, name: ClusterTypeRadioButtonGroupName, checked: checked }).component();
|
||||||
|
}
|
||||||
|
|
||||||
|
private initExistingClusterControl(view: sqlops.ModelView): void {
|
||||||
|
let self = this;
|
||||||
|
let sectionDescription = view.modelBuilder.text().withProperties({ value: localize('bdc-create.existingClusterSectionDescription', 'Select the cluster context you want to install the SQL Server big data cluster') }).component();
|
||||||
|
let configFileLabel = view.modelBuilder.text().withProperties({ value: localize('bdc-create.kubeConfigFileLabelText', 'KubeConfig File') }).component();
|
||||||
|
let configFileInput = view.modelBuilder.inputBox().withProperties({ width: '300px' }).component();
|
||||||
|
let browseFileButton = view.modelBuilder.button().withProperties({ label: localize('bdc-browseText', 'Browse'), width: '100px' }).component();
|
||||||
|
let configFileContainer = view.modelBuilder.flexContainer()
|
||||||
|
.withLayout({ flexFlow: 'row', alignItems: 'baseline' })
|
||||||
|
.withItems([configFileLabel, configFileInput, browseFileButton], { CSSStyles: { 'margin-right': '10px' } }).component();
|
||||||
|
this.clusterContextsLabel = view.modelBuilder.text().withProperties({ value: localize('bdc-clusterContextsLabelText', 'Cluster Contexts') }).component();
|
||||||
|
this.errorLoadingClustersLabel = view.modelBuilder.text().withProperties({ value: localize('bdc-errorLoadingClustersText', 'No cluster information is found in the config file or an error ocurred while loading the config file') }).component();
|
||||||
|
this.clusterContextContainer = view.modelBuilder.divContainer().component();
|
||||||
|
this.existingClusterControl = view.modelBuilder.divContainer().withItems([sectionDescription, configFileContainer, this.clusterContextContainer], { CSSStyles: { 'margin-top': '0px' } }).component();
|
||||||
|
|
||||||
|
browseFileButton.onDidClick(async () => {
|
||||||
|
let fileUris = await vscode.window.showOpenDialog(
|
||||||
|
{
|
||||||
|
canSelectFiles: true,
|
||||||
|
canSelectFolders: false,
|
||||||
|
canSelectMany: false,
|
||||||
|
defaultUri: vscode.Uri.file(os.homedir()),
|
||||||
|
openLabel: localize('bdc-selectKubeConfigFileText', 'Select'),
|
||||||
|
filters: {
|
||||||
|
'KubeConfig Files': ['kubeconfig'],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!fileUris || fileUris.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.clusterContextContainer.clearItems();
|
||||||
|
|
||||||
|
let fileUri = fileUris[0];
|
||||||
|
|
||||||
|
configFileInput.value = fileUri.fsPath;
|
||||||
|
|
||||||
|
let clusters = self.model.loadClusters(fileUri.fsPath);
|
||||||
|
|
||||||
|
self.cards = [];
|
||||||
|
if (clusters.length !== 0) {
|
||||||
|
self.model.selectedCluster = clusters[0];
|
||||||
|
for (let i = 0; i < clusters.length; i++) {
|
||||||
|
let cluster = clusters[i];
|
||||||
|
let card = view.modelBuilder.card().withProperties({
|
||||||
|
selected: i === 0,
|
||||||
|
label: cluster.name,
|
||||||
|
descriptions: [cluster.displayName, cluster.user],
|
||||||
|
cardType: sqlops.CardType.ListItem,
|
||||||
|
iconPath: {
|
||||||
|
dark: self.wizard.context.asAbsolutePath('images/cluster_inverse.svg'),
|
||||||
|
light: self.wizard.context.asAbsolutePath('images/cluster.svg')
|
||||||
|
},
|
||||||
|
}).component();
|
||||||
|
card.onCardSelectedChanged(() => {
|
||||||
|
if (card.selected) {
|
||||||
|
self.cards.forEach(c => {
|
||||||
|
if (c !== card) {
|
||||||
|
c.selected = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.model.selectedCluster = cluster;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.cards.push(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.clusterContextContainer.addItem(self.clusterContextsLabel);
|
||||||
|
self.clusterContextContainer.addItems(self.cards);
|
||||||
|
} else {
|
||||||
|
self.clusterContextContainer.addItem(this.errorLoadingClustersLabel);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private initLocalClusterControl(view: sqlops.ModelView): void {
|
||||||
|
let placeholder = view.modelBuilder.text().withProperties({ value: 'create local cluster place holder' }).component();
|
||||||
|
this.createLocalClusterControl = view.modelBuilder.divContainer().withItems([placeholder]).component();
|
||||||
|
}
|
||||||
|
|
||||||
|
private initAksClusterControl(view: sqlops.ModelView): void {
|
||||||
|
let placeholder = view.modelBuilder.text().withProperties({ value: 'AKS cluster place holder' }).component();
|
||||||
|
this.createAksClusterControl = view.modelBuilder.divContainer().withItems([placeholder]).component();
|
||||||
|
}
|
||||||
|
}
|
||||||
24
extensions/big-data-cluster/src/wizards/wizardBase.ts
Normal file
@@ -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.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import * as sqlops from 'sqlops';
|
||||||
|
import { ExtensionContext } from 'vscode';
|
||||||
|
|
||||||
|
export abstract class WizardBase<T> {
|
||||||
|
|
||||||
|
protected wizard: sqlops.window.modelviewdialog.Wizard;
|
||||||
|
|
||||||
|
constructor(public model: T, public context: ExtensionContext, private title: string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public open(): Thenable<void> {
|
||||||
|
this.wizard = sqlops.window.modelviewdialog.createWizard(this.title);
|
||||||
|
this.initialize();
|
||||||
|
return this.wizard.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract initialize(): void;
|
||||||
|
}
|
||||||
30
extensions/big-data-cluster/src/wizards/wizardPageBase.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 { WizardBase } from './wizardBase';
|
||||||
|
|
||||||
|
export abstract class WizardPageBase<T> {
|
||||||
|
private _page: sqlops.window.modelviewdialog.WizardPage;
|
||||||
|
|
||||||
|
public get page(): sqlops.window.modelviewdialog.WizardPage {
|
||||||
|
return this._page;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get wizard(): WizardBase<T> {
|
||||||
|
return this._wizard;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(title: string, description: string, protected model: T, private _wizard: WizardBase<T>) {
|
||||||
|
this._page = sqlops.window.modelviewdialog.createWizardPage(title);
|
||||||
|
this._page.description = description;
|
||||||
|
this._page.registerContent((view: sqlops.ModelView) => {
|
||||||
|
return this.initialize(view);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract initialize(view: sqlops.ModelView): Thenable<void>;
|
||||||
|
}
|
||||||
19
extensions/big-data-cluster/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compileOnSave": true,
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es6",
|
||||||
|
"outDir": "./out",
|
||||||
|
"lib": [
|
||||||
|
"es6", "es2015.promise"
|
||||||
|
],
|
||||||
|
"sourceMap": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"declaration": true
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
|
}
|
||||||
109
extensions/big-data-cluster/yarn.lock
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
ansi-regex@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
|
||||||
|
integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
|
||||||
|
|
||||||
|
charenc@~0.0.1:
|
||||||
|
version "0.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
|
||||||
|
integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=
|
||||||
|
|
||||||
|
crypt@~0.0.1:
|
||||||
|
version "0.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
|
||||||
|
integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=
|
||||||
|
|
||||||
|
debug@^2.2.0:
|
||||||
|
version "2.6.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||||
|
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||||
|
dependencies:
|
||||||
|
ms "2.0.0"
|
||||||
|
|
||||||
|
debug@^3.1.0:
|
||||||
|
version "3.2.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
|
||||||
|
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
|
||||||
|
dependencies:
|
||||||
|
ms "^2.1.1"
|
||||||
|
|
||||||
|
is-buffer@~1.1.1:
|
||||||
|
version "1.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
||||||
|
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
|
||||||
|
|
||||||
|
lodash@^4.16.4:
|
||||||
|
version "4.17.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
|
||||||
|
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
|
||||||
|
|
||||||
|
md5@^2.1.0:
|
||||||
|
version "2.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9"
|
||||||
|
integrity sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=
|
||||||
|
dependencies:
|
||||||
|
charenc "~0.0.1"
|
||||||
|
crypt "~0.0.1"
|
||||||
|
is-buffer "~1.1.1"
|
||||||
|
|
||||||
|
minimist@0.0.8:
|
||||||
|
version "0.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
|
||||||
|
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
|
||||||
|
|
||||||
|
mkdirp@~0.5.1:
|
||||||
|
version "0.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
|
||||||
|
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
|
||||||
|
dependencies:
|
||||||
|
minimist "0.0.8"
|
||||||
|
|
||||||
|
mocha-junit-reporter@^1.17.0:
|
||||||
|
version "1.18.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.18.0.tgz#9209a3fba30025ae3ae5e6bfe7f9c5bc3c2e8ee2"
|
||||||
|
integrity sha512-y3XuqKa2+HRYtg0wYyhW/XsLm2Ps+pqf9HaTAt7+MVUAKFJaNAHOrNseTZo9KCxjfIbxUWwckP5qCDDPUmjSWA==
|
||||||
|
dependencies:
|
||||||
|
debug "^2.2.0"
|
||||||
|
md5 "^2.1.0"
|
||||||
|
mkdirp "~0.5.1"
|
||||||
|
strip-ansi "^4.0.0"
|
||||||
|
xml "^1.0.0"
|
||||||
|
|
||||||
|
mocha-multi-reporters@^1.1.7:
|
||||||
|
version "1.1.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/mocha-multi-reporters/-/mocha-multi-reporters-1.1.7.tgz#cc7f3f4d32f478520941d852abb64d9988587d82"
|
||||||
|
integrity sha1-zH8/TTL0eFIJQdhSq7ZNmYhYfYI=
|
||||||
|
dependencies:
|
||||||
|
debug "^3.1.0"
|
||||||
|
lodash "^4.16.4"
|
||||||
|
|
||||||
|
ms@2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||||
|
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
||||||
|
|
||||||
|
ms@^2.1.1:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
|
||||||
|
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
|
||||||
|
|
||||||
|
strip-ansi@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
|
||||||
|
integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
|
||||||
|
dependencies:
|
||||||
|
ansi-regex "^3.0.0"
|
||||||
|
|
||||||
|
vscode-nls@^3.2.1:
|
||||||
|
version "3.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.5.tgz#25520c1955108036dec607c85e00a522f247f1a4"
|
||||||
|
integrity sha512-ITtoh3V4AkWXMmp3TB97vsMaHRgHhsSFPsUdzlueSL+dRZbSNTZeOmdQv60kjCV306ghPxhDeoNUEm3+EZMuyw==
|
||||||
|
|
||||||
|
xml@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5"
|
||||||
|
integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=
|
||||||
1
src/sql/media/icons/collapsed.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#646465" d="M6 4v8l4-4-4-4zm1 2.414L8.586 8 7 9.586V6.414z"/></svg>
|
||||||
|
After Width: | Height: | Size: 139 B |
1
src/sql/media/icons/collapsed_inverse.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#E8E8E8" d="M6 4v8l4-4-4-4zm1 2.414L8.586 8 7 9.586V6.414z"/></svg>
|
||||||
|
After Width: | Height: | Size: 139 B |
1
src/sql/media/icons/expanded.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#646465" d="M11 10H5.344L11 4.414V10z"/></svg>
|
||||||
|
After Width: | Height: | Size: 118 B |
1
src/sql/media/icons/expanded_inverse.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#E8E8E8" d="M11 10H5.344L11 4.414V10z"/></svg>
|
||||||
|
After Width: | Height: | Size: 118 B |
@@ -1,4 +1,6 @@
|
|||||||
<div *ngIf="label" [class]="getClass()" (click)="onCardClick()" (mouseover)="onCardHoverChanged($event)" (mouseout)="onCardHoverChanged($event)">
|
<div *ngIf="label" [class]="getClass()" (click)="onCardClick()" (mouseover)="onCardHoverChanged($event)" (mouseout)="onCardHoverChanged($event)"
|
||||||
|
tabIndex="0">
|
||||||
|
<ng-container *ngIf="isVerticalButton || isDetailsCard">
|
||||||
<span *ngIf="hasStatus" class="card-status">
|
<span *ngIf="hasStatus" class="card-status">
|
||||||
<div class="status-content" [style.backgroundColor]="statusColor"></div>
|
<div class="status-content" [style.backgroundColor]="statusColor"></div>
|
||||||
</span>
|
</span>
|
||||||
@@ -30,4 +32,18 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="isListItemCard">
|
||||||
|
<div class="list-item-content">
|
||||||
|
<div>
|
||||||
|
<div [class]="iconClass">{{label}}</div>
|
||||||
|
<div *ngFor="let desc of descriptions">
|
||||||
|
<div class="list-item-description">{{desc}}</div>
|
||||||
|
</div>
|
||||||
|
<span *ngIf="showRadioButton" class="selection-indicator-container">
|
||||||
|
<div *ngIf="showAsSelected" class="selection-indicator"></div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
@@ -70,8 +70,9 @@ export default class CardComponent extends ComponentWithIconBase implements ICom
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getClass(): string {
|
public getClass(): string {
|
||||||
return (this.selectable && this.selected || this._hasFocus) ? 'model-card selected' :
|
let cardClass = this.isListItemCard ? 'model-card-list-item' : 'model-card';
|
||||||
'model-card unselected';
|
return (this.selectable && this.selected || this._hasFocus) ? `${cardClass} selected` :
|
||||||
|
`${cardClass} unselected`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public onCardHoverChanged(event: any) {
|
public onCardHoverChanged(event: any) {
|
||||||
@@ -93,11 +94,16 @@ export default class CardComponent extends ComponentWithIconBase implements ICom
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get iconClass(): string {
|
public get iconClass(): string {
|
||||||
|
if (this.isListItemCard) {
|
||||||
|
return this._iconClass + ' icon' + ' list-item-icon';
|
||||||
|
}
|
||||||
|
else {
|
||||||
return this._iconClass + ' icon' + ' cardIcon';
|
return this._iconClass + ' icon' + ' cardIcon';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private get selectable(): boolean {
|
private get selectable(): boolean {
|
||||||
return this.cardType === 'VerticalButton';
|
return this.cardType === 'VerticalButton' || this.cardType === 'ListItem';
|
||||||
}
|
}
|
||||||
|
|
||||||
// CSS-bound properties
|
// CSS-bound properties
|
||||||
@@ -126,11 +132,15 @@ export default class CardComponent extends ComponentWithIconBase implements ICom
|
|||||||
return !this.cardType || this.cardType === 'Details';
|
return !this.cardType || this.cardType === 'Details';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get isListItemCard(): boolean {
|
||||||
|
return !this.cardType || this.cardType === 'ListItem';
|
||||||
|
}
|
||||||
|
|
||||||
public get isVerticalButton(): boolean {
|
public get isVerticalButton(): boolean {
|
||||||
return this.cardType === 'VerticalButton';
|
return this.cardType === 'VerticalButton';
|
||||||
}
|
}
|
||||||
|
|
||||||
public get showRadioButton():boolean{
|
public get showRadioButton(): boolean {
|
||||||
return this.selectable && (this.selected || this._hasFocus);
|
return this.selectable && (this.selected || this._hasFocus);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,6 +148,9 @@ export default class CardComponent extends ComponentWithIconBase implements ICom
|
|||||||
return this.selectable && this.selected;
|
return this.selectable && this.selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get descriptions(): string[] {
|
||||||
|
return this.getPropertyOrDefault<CardProperties, string[]>((props) => props.descriptions, []);
|
||||||
|
}
|
||||||
|
|
||||||
public get actions(): ActionDescriptor[] {
|
public get actions(): ActionDescriptor[] {
|
||||||
return this.getPropertyOrDefault<CardProperties, ActionDescriptor[]>((props) => props.actions, []);
|
return this.getPropertyOrDefault<CardProperties, ActionDescriptor[]>((props) => props.actions, []);
|
||||||
|
|||||||
@@ -10,11 +10,13 @@
|
|||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.model-card-list-item.selected,
|
||||||
.model-card.selected {
|
.model-card.selected {
|
||||||
border-color: rgb(0, 120, 215);
|
border-color: rgb(0, 120, 215);
|
||||||
box-shadow: rgba(0, 120, 215, 0.75) 0px 0px 6px;
|
box-shadow: rgba(0, 120, 215, 0.75) 0px 0px 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.model-card-list-item.unselected,
|
||||||
.model-card.unselected {
|
.model-card.unselected {
|
||||||
border-color: rgb(214, 214, 214);
|
border-color: rgb(214, 214, 214);
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
@@ -96,6 +98,7 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.model-card-list-item .selection-indicator-container,
|
||||||
.model-card .selection-indicator-container {
|
.model-card .selection-indicator-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 5px;
|
top: 5px;
|
||||||
@@ -110,6 +113,30 @@
|
|||||||
border-style: solid;
|
border-style: solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.model-card-list-item .selection-indicator-container,
|
||||||
|
.model-card .selection-indicator-container {
|
||||||
|
position: absolute;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: white;
|
||||||
|
border-width: 1px;
|
||||||
|
border-color: rgb(0, 120, 215);
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-card-list-item .selection-indicator-container {
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-card .selection-indicator-container {
|
||||||
|
top: 5px;
|
||||||
|
right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-card-list-item .selection-indicator,
|
||||||
.model-card .selection-indicator {
|
.model-card .selection-indicator {
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
width: 8px;
|
width: 8px;
|
||||||
@@ -136,3 +163,33 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-decoration: underline
|
text-decoration: underline
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.model-card-list-item {
|
||||||
|
display: inline-block;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
margin: 5px 0px 5px 0px;
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-card-list-item .list-item-content {
|
||||||
|
height: auto;
|
||||||
|
padding: 5px 26px 5px 5px;
|
||||||
|
min-height: 30px;
|
||||||
|
min-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-card-list-item .list-item-icon {
|
||||||
|
background-position: 2px 2px;
|
||||||
|
padding-left:22px;
|
||||||
|
font-size: 15px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 16px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-card-list-item .list-item-description {
|
||||||
|
padding-left:22px;
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ import EditorComponent from './editor.component';
|
|||||||
import DomComponent from './dom.component';
|
import DomComponent from './dom.component';
|
||||||
import { registerComponentType } from 'sql/platform/dashboard/common/modelComponentRegistry';
|
import { registerComponentType } from 'sql/platform/dashboard/common/modelComponentRegistry';
|
||||||
import { ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes';
|
import { ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
|
import HyperlinkComponent from 'sql/parts/modelComponents/hyperlink.component';
|
||||||
|
|
||||||
export const DIV_CONTAINER = 'div-container';
|
export const DIV_CONTAINER = 'div-container';
|
||||||
registerComponentType(DIV_CONTAINER, ModelComponentTypes.DivContainer, DivContainer);
|
registerComponentType(DIV_CONTAINER, ModelComponentTypes.DivContainer, DivContainer);
|
||||||
@@ -89,3 +90,6 @@ registerComponentType(EDITOR_COMPONENT, ModelComponentTypes.Editor, EditorCompon
|
|||||||
|
|
||||||
export const DOM_COMPONENT = 'dom-component';
|
export const DOM_COMPONENT = 'dom-component';
|
||||||
registerComponentType(DOM_COMPONENT, ModelComponentTypes.Dom, DomComponent);
|
registerComponentType(DOM_COMPONENT, ModelComponentTypes.Dom, DomComponent);
|
||||||
|
|
||||||
|
export const HYPERLINK_COMPONENT = 'hyperlink-component';
|
||||||
|
registerComponentType(HYPERLINK_COMPONENT, ModelComponentTypes.Hyperlink, HyperlinkComponent);
|
||||||
@@ -20,10 +20,10 @@ import { CommonServiceInterface } from 'sql/services/common/commonServiceInterfa
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'modelview-groupContainer',
|
selector: 'modelview-groupContainer',
|
||||||
template: `
|
template: `
|
||||||
<div *ngIf="hasHeader()" class="modelview-group-header" >
|
<div *ngIf="hasHeader()" [class]="getHeaderClass()" (click)="changeState()">
|
||||||
{{_containerLayout.header}}
|
{{_containerLayout.header}}
|
||||||
</div>
|
</div>
|
||||||
<div #container *ngIf="items" class="modelview-group-container" [style.width]="getContainerWidth()">
|
<div #container *ngIf="items" class="modelview-group-container" [style.width]="getContainerWidth()" [style.display]="getContainerDisplayStyle()">
|
||||||
<ng-container *ngFor="let item of items">
|
<ng-container *ngFor="let item of items">
|
||||||
<div class="modelview-group-row" >
|
<div class="modelview-group-row" >
|
||||||
<div class="modelview-group-cell">
|
<div class="modelview-group-cell">
|
||||||
@@ -40,6 +40,7 @@ export default class GroupContainer extends ContainerBase<GroupLayout> implement
|
|||||||
@Input() modelStore: IModelStore;
|
@Input() modelStore: IModelStore;
|
||||||
|
|
||||||
private _containerLayout: GroupLayout;
|
private _containerLayout: GroupLayout;
|
||||||
|
private _collapsed: boolean;
|
||||||
|
|
||||||
@ViewChild('container', { read: ElementRef }) private _container: ElementRef;
|
@ViewChild('container', { read: ElementRef }) private _container: ElementRef;
|
||||||
|
|
||||||
@@ -48,6 +49,7 @@ export default class GroupContainer extends ContainerBase<GroupLayout> implement
|
|||||||
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
|
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
|
||||||
@Inject(forwardRef(() => ElementRef)) el: ElementRef) {
|
@Inject(forwardRef(() => ElementRef)) el: ElementRef) {
|
||||||
super(changeRef, el);
|
super(changeRef, el);
|
||||||
|
this._collapsed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@@ -65,6 +67,7 @@ export default class GroupContainer extends ContainerBase<GroupLayout> implement
|
|||||||
|
|
||||||
public setLayout(layout: GroupLayout): void {
|
public setLayout(layout: GroupLayout): void {
|
||||||
this._containerLayout = layout;
|
this._containerLayout = layout;
|
||||||
|
this._collapsed = !!layout.collapsed;
|
||||||
this.layout();
|
this.layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,6 +75,10 @@ export default class GroupContainer extends ContainerBase<GroupLayout> implement
|
|||||||
return this._containerLayout && this._containerLayout && this._containerLayout.header !== undefined;
|
return this._containerLayout && this._containerLayout && this._containerLayout.header !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private isCollapsible(): boolean {
|
||||||
|
return this.hasHeader() && this._containerLayout.collapsible === true;
|
||||||
|
}
|
||||||
|
|
||||||
private getContainerWidth(): string {
|
private getContainerWidth(): string {
|
||||||
if (this._containerLayout && this._containerLayout.width) {
|
if (this._containerLayout && this._containerLayout.width) {
|
||||||
let width: string = this._containerLayout.width.toString();
|
let width: string = this._containerLayout.width.toString();
|
||||||
@@ -83,4 +90,24 @@ export default class GroupContainer extends ContainerBase<GroupLayout> implement
|
|||||||
return '100%';
|
return '100%';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getContainerDisplayStyle(): string {
|
||||||
|
return !this.isCollapsible() || !this._collapsed ? 'block' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
private getHeaderClass(): string {
|
||||||
|
if (this.isCollapsible()) {
|
||||||
|
let modifier = this._collapsed ? 'collapsed' : 'expanded';
|
||||||
|
return `modelview-group-header-collapsible ${modifier}`;
|
||||||
|
} else {
|
||||||
|
return 'modelview-group-header';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private changeState(): void {
|
||||||
|
if (this.isCollapsible()) {
|
||||||
|
this._collapsed = !this._collapsed;
|
||||||
|
this._changeRef.detectChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,14 +7,40 @@
|
|||||||
|
|
||||||
.modelview-group-row {
|
.modelview-group-row {
|
||||||
display: table-row;
|
display: table-row;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modelview-group-header-collapsible,
|
||||||
.modelview-group-header {
|
.modelview-group-header {
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modelview-group-header-collapsible {
|
||||||
|
padding-left: 20px;
|
||||||
|
background-position: 2px 2px;
|
||||||
|
background-size: 16px 16px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vs .modelview-group-header-collapsible.expanded {
|
||||||
|
background-image: url("../../media/icons/expanded.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
.vs-dark .modelview-group-header-collapsible.expanded,
|
||||||
|
.hc-black .modelview-group-header-collapsible.expanded {
|
||||||
|
background-image: url("../../media/icons/expanded_inverse.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
.vs .modelview-group-header-collapsible.collapsed {
|
||||||
|
background-image: url("../../media/icons/collapsed.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
.vs-dark .modelview-group-header-collapsible.collapsed,
|
||||||
|
.hc-black .modelview-group-header-collapsible.collapsed {
|
||||||
|
background-image: url("../../media/icons/collapsed_inverse.svg");
|
||||||
|
}
|
||||||
|
|
||||||
.modelview-group-cell {
|
.modelview-group-cell {
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
|
|||||||
70
src/sql/parts/modelComponents/hyperlink.component.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
Component, Input, Inject, ChangeDetectorRef, forwardRef,
|
||||||
|
OnDestroy, AfterViewInit, ElementRef
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import * as sqlops from 'sqlops';
|
||||||
|
|
||||||
|
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
|
||||||
|
import { IComponent, IComponentDescriptor, IModelStore } from 'sql/parts/modelComponents/interfaces';
|
||||||
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'modelview-hyperlink',
|
||||||
|
template: `<a [href]="getUrl()" target="blank">{{getLabel()}}</a>`
|
||||||
|
})
|
||||||
|
export default class HyperlinkComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
||||||
|
@Input() descriptor: IComponentDescriptor;
|
||||||
|
@Input() modelStore: IModelStore;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(forwardRef(() => CommonServiceInterface)) private _commonService: CommonServiceInterface,
|
||||||
|
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
|
||||||
|
@Inject(forwardRef(() => ElementRef)) el: ElementRef) {
|
||||||
|
super(changeRef, el);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.baseInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.baseDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public setLayout(layout: any): void {
|
||||||
|
this.layout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public set label(newValue: string) {
|
||||||
|
this.setPropertyFromUI<sqlops.HyperlinkComponentProperties, string>((properties, value) => { properties.label = value; }, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get label(): string {
|
||||||
|
return this.getPropertyOrDefault<sqlops.HyperlinkComponentProperties, string>((props) => props.label, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLabel(): string {
|
||||||
|
return this.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set url(newValue: string) {
|
||||||
|
this.setPropertyFromUI<sqlops.HyperlinkComponentProperties, string>((properties, value) => { properties.url = value; }, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get url(): string {
|
||||||
|
return this.getPropertyOrDefault<sqlops.HyperlinkComponentProperties, string>((props) => props.url, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
public getUrl(): string {
|
||||||
|
return this.url;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@ import { CommonServiceInterface } from 'sql/services/common/commonServiceInterfa
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'modelview-text',
|
selector: 'modelview-text',
|
||||||
template: `
|
template: `
|
||||||
<p>{{getValue()}}</p>`
|
<p [style.width]="getWidth()">{{getValue()}}</p>`
|
||||||
})
|
})
|
||||||
export default class TextComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
export default class TextComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
||||||
@Input() descriptor: IComponentDescriptor;
|
@Input() descriptor: IComponentDescriptor;
|
||||||
|
|||||||
19
src/sql/sqlops.proposed.d.ts
vendored
@@ -40,6 +40,7 @@ declare module 'sqlops' {
|
|||||||
toolbarContainer(): ToolbarBuilder;
|
toolbarContainer(): ToolbarBuilder;
|
||||||
loadingComponent(): LoadingComponentBuilder;
|
loadingComponent(): LoadingComponentBuilder;
|
||||||
fileBrowserTree(): ComponentBuilder<FileBrowserTreeComponent>;
|
fileBrowserTree(): ComponentBuilder<FileBrowserTreeComponent>;
|
||||||
|
hyperlink(): ComponentBuilder<HyperlinkComponent>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TreeComponentDataProvider<T> extends vscode.TreeDataProvider<T> {
|
export interface TreeComponentDataProvider<T> extends vscode.TreeDataProvider<T> {
|
||||||
@@ -348,6 +349,8 @@ declare module 'sqlops' {
|
|||||||
export interface GroupLayout {
|
export interface GroupLayout {
|
||||||
width?: number | string;
|
width?: number | string;
|
||||||
header?: string;
|
header?: string;
|
||||||
|
collapsible?: boolean;
|
||||||
|
collapsed?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GroupItemLayout {
|
export interface GroupItemLayout {
|
||||||
@@ -433,7 +436,8 @@ declare module 'sqlops' {
|
|||||||
|
|
||||||
export enum CardType {
|
export enum CardType {
|
||||||
VerticalButton = 'VerticalButton',
|
VerticalButton = 'VerticalButton',
|
||||||
Details = 'Details'
|
Details = 'Details',
|
||||||
|
ListItem = 'ListItem'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -444,6 +448,7 @@ declare module 'sqlops' {
|
|||||||
label: string;
|
label: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
actions?: ActionDescriptor[];
|
actions?: ActionDescriptor[];
|
||||||
|
descriptions?: string[];
|
||||||
status?: StatusIndicator;
|
status?: StatusIndicator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -540,6 +545,11 @@ declare module 'sqlops' {
|
|||||||
value?: string;
|
value?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface HyperlinkComponentProperties extends ComponentProperties {
|
||||||
|
label: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface DropDownProperties extends ComponentProperties {
|
export interface DropDownProperties extends ComponentProperties {
|
||||||
value?: string | CategoryValue;
|
value?: string | CategoryValue;
|
||||||
values?: string[] | CategoryValue[];
|
values?: string[] | CategoryValue[];
|
||||||
@@ -638,10 +648,15 @@ declare module 'sqlops' {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TextComponent extends Component {
|
export interface TextComponent extends Component, ComponentProperties {
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface HyperlinkComponent extends Component, HyperlinkComponentProperties {
|
||||||
|
label: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface InputBoxComponent extends Component, InputBoxProperties {
|
export interface InputBoxComponent extends Component, InputBoxProperties {
|
||||||
onTextChanged: vscode.Event<any>;
|
onTextChanged: vscode.Event<any>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,7 +164,8 @@ export enum ModelComponentTypes {
|
|||||||
TreeComponent,
|
TreeComponent,
|
||||||
FileBrowserTree,
|
FileBrowserTree,
|
||||||
Editor,
|
Editor,
|
||||||
Dom
|
Dom,
|
||||||
|
Hyperlink
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IComponentShape {
|
export interface IComponentShape {
|
||||||
@@ -262,6 +263,7 @@ export interface CardProperties {
|
|||||||
label: string;
|
label: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
actions?: ActionDescriptor[];
|
actions?: ActionDescriptor[];
|
||||||
|
descriptions?: string[];
|
||||||
status?: StatusIndicator;
|
status?: StatusIndicator;
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
cardType: CardType;
|
cardType: CardType;
|
||||||
@@ -300,7 +302,8 @@ export enum DeclarativeDataType {
|
|||||||
|
|
||||||
export enum CardType {
|
export enum CardType {
|
||||||
VerticalButton = 'VerticalButton',
|
VerticalButton = 'VerticalButton',
|
||||||
Details = 'Details'
|
Details = 'Details',
|
||||||
|
ListItem = 'ListItem'
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Orientation {
|
export enum Orientation {
|
||||||
@@ -492,7 +495,7 @@ export class CellRange {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(start: number, end: number) {
|
constructor(start: number, end: number) {
|
||||||
if (typeof(start) !== 'number' || typeof(start) !== 'number' || start < 0 || end < 0) {
|
if (typeof (start) !== 'number' || typeof (start) !== 'number' || start < 0 || end < 0) {
|
||||||
throw new Error('Invalid arguments');
|
throw new Error('Invalid arguments');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -198,6 +198,13 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
|
|||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hyperlink(): sqlops.ComponentBuilder<sqlops.HyperlinkComponent> {
|
||||||
|
let id = this.getNextComponentId();
|
||||||
|
let builder: ComponentBuilderImpl<sqlops.HyperlinkComponent> = this.getComponentBuilder(new HyperlinkComponentWrapper(this._proxy, this._handle, id), id);
|
||||||
|
this._componentBuilders.set(id, builder);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
getComponentBuilder<T extends sqlops.Component>(component: ComponentWrapper, id: string): ComponentBuilderImpl<T> {
|
getComponentBuilder<T extends sqlops.Component>(component: ComponentWrapper, id: string): ComponentBuilderImpl<T> {
|
||||||
let componentBuilder: ComponentBuilderImpl<T> = new ComponentBuilderImpl<T>(component);
|
let componentBuilder: ComponentBuilderImpl<T> = new ComponentBuilderImpl<T>(component);
|
||||||
this._componentBuilders.set(id, componentBuilder);
|
this._componentBuilders.set(id, componentBuilder);
|
||||||
@@ -1248,6 +1255,28 @@ class TreeComponentWrapper<T> extends ComponentWrapper implements sqlops.TreeCom
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class HyperlinkComponentWrapper extends ComponentWrapper implements sqlops.HyperlinkComponentProperties {
|
||||||
|
|
||||||
|
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
|
||||||
|
super(proxy, handle, ModelComponentTypes.Hyperlink, id);
|
||||||
|
this.properties = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
public get label(): string {
|
||||||
|
return this.properties['label'];
|
||||||
|
}
|
||||||
|
public set label(v: string) {
|
||||||
|
this.setProperty('label', v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get url(): string {
|
||||||
|
return this.properties['url'];
|
||||||
|
}
|
||||||
|
public set url(v: string) {
|
||||||
|
this.setProperty('url', v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ModelViewImpl implements sqlops.ModelView {
|
class ModelViewImpl implements sqlops.ModelView {
|
||||||
|
|
||||||
public onClosedEmitter = new Emitter<any>();
|
public onClosedEmitter = new Emitter<any>();
|
||||||
|
|||||||