mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-27 17:23:21 -05:00
New feature for Notebooks - Callout (#13078)
* New feature for Notebooks - callout. This utilizes a modifed modal.ts that renders a small modal set to appear at the trigger event and disappear when user clicks off of it. This is intended only for micro-interactions such as inserting links, tables and images into Notebooks. Error dialogs dependent on modal have been updated to implement the modified width property correctlty. * Modified dialogStyle names in interface. Applied updates to files dependent on this. Wired up disposable listener for browsing local files in image select callout. Renamed callout to calloutDialog. * Converted double quotes in CSS to single quotes. Revised run method, removing the promise wrapping from the conditional. * Passing click target to modal for positioning. Created custom buttons and click event handlers for Insert image and Insert link. Set feature behind preview flag. * Revised structure and styles. * Updated component for use with ML extension. Updated ML extension code with changes for custom dialog creation. * Fixed async context for handleBrowse. Passing the new modal properties into Dialog implementation for ML. * Added option to suppress callout dialog header and footer from the machine learning view file. Added a compact variant of callout. Corrected Dialog tab initialization. * Maddy/callout modifications (#13586) * initial changes * canSelectFolders false * change label, placeholder text on radiobtn click * Added support for custom XY offset to account for modal size and to provide for fine-tuning of unique modal instances. * Updated Image icon with latest from Design. * Replaced node process with IPathService for retreiving image file. * Added theme color lookups to provide default colors when none are provided. * Added async/await to tests calling transformText. * textCell, modal - swapped out HostListener for a member method which listens for the Esc keyup. Updated templates with method call on keyup. This cleaned up the double event call whenever the Esc key was pressed. Added property to modal Cancel button call to make it a secondary button. * Cleaned up callout styles. * Removed color use for input fields because component code delivers theme-specific values. * Added check for CSS class `mac` on the body tag. Added conditionals to supply alternate dialogXYOffset in the case of non-Mac environment. * Cleanup: Simplified DialogPosition. Renamed telemtry references and dialog related methods. Corrected spelling. Added missing signatures and types. Updated warning callout link URLs. * Removed async and await as there are no promise dependencies. Added a signature. * Revert "Fix windows insiders icons (#13579)" (#13630) This reverts commita0ef594792. * Changed cores validation message (#13617) * Changed cores validation message * Missed validation * Remove cores validation message * Applied verification for cores change to miaa c+s page * WYSIWYG Improvements to highlight (#13032) * Improvements to highlight * wip * Tests pass * Leverage escaping mechanism * Tweak highlight logic * PR comments * add await to thenable method (#13635) * Delete ConnectionDialogue.ipynb (#13634) this nb was an attempt at creating a connection dialog. removing not found in toc * Added engine version argument to edit command. (#13610) * Added engine version argument to edit command. Neccessary for not using pg12 * Included for changing password in overview page * Updated fakeazdataapi test * Fix empty column issue (#13641) Co-authored-by: Monica Gupta <mogupt@microsoft.com> * add right padding to notebook toolbar action item (#13640) * add right padding to action item * remove extra line and add space * Adding SQL Edge project template (#13558) * Checkpoint * removing flag for not creating subfolder * Adding Edge template * Removing janky map function * Adding templates for additional objects * Updating tests, fixing bug * Added Edge project icon * Updating strings to Drew-approved text * Cleaning up template scripts and Edge project template names * Update package.json (#13626) * Update Import UI to match other UIs (#13637) * Update Import UI to match other UIs * Fixed another bug * Fix notebook unordered grid values after papermill execution (#13614) * Fix unordered table * check entire first row schema: * SQL Notebooks should not get affected * delete unused variable and edit comments * refactor for efficient table ordering * nit naming * Normalize path to change (#13660) * vbump asde deployment extension (#13662) do a patch version update, will adjust if the next change is a major one. * Add test for dynamic enablement (#13602) * Add test for dynamic enablement * update names * Remove debug console log (#13669) * Remove placeholder on deployment wizards when field is disabled dynamically (#13658) * Bump highlight.js in /extensions/markdown-language-features (#13675) Bumps [highlight.js](https://github.com/highlightjs/highlight.js) from 9.15.10 to 10.4.1. - [Release notes](https://github.com/highlightjs/highlight.js/releases) - [Changelog](https://github.com/highlightjs/highlight.js/blob/master/CHANGES.md) - [Commits](https://github.com/highlightjs/highlight.js/compare/9.15.10...10.4.1) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * add ability to control the enabled state of checkbox cells (#13644) * control enabled state of checkbox cells * add more check * Prevent Table from Disappearing due to exception when looking for tHead (#13680) * Prevent exception when tHead doesn't exist at node * Add test for no thead * Fix issue with pasting results in Teams (#13673) * Fix issue with pasting results in Teams * Addressed comment to change header tag to th Co-authored-by: Monica Gupta <mogupt@microsoft.com> * Add descriptions and validation to connected mode (#13676) * Add dependent field provider to resource deployment (#13664) * Add dependent field provider to resource deployment * Change name to value provider service * Add error handling * providerId -> id * Set dropdown value correctly * missed id * back to providerId * fix updating missed id * remove placeholder * Have resource deployment providers return disposables (#13690) * Add dependent field provider to resource deployment * Change name to value provider service * Add error handling * providerId -> id * Set dropdown value correctly * missed id * back to providerId * fix updating missed id * Make resource deployment providers disposable * Retry publish and always try adding asset (#13700) * Retry publish and always try adding asset * Undo asset upload change * Add logging * Notebooks: Remove result set summary from saved metadata (#13616) * remove result set summary from metadata * remove batchId and id from celloutputmetadata * remove extra line * Add scan suppressions (#13705) * Add action for responding to Needs Logs label (#13707) * Fix action name (#13708) * Add action for responding to Needs Logs label * Fix action name * Rename action config file (#13709) * Add action for responding to Needs Logs label * Fix action name * Rename config file * remove quotes * Adding unit tests for schema compare service (#13642) * Retry getConfig (#13712) * Retry getConfig * Add logging * vBump Arc and Azdata (#13717) * switch schema compare to use inputbox instead of table headers (#13715) * Added Accounts and Database Backup Page to Migration wizard (#13548) * Added localized strings Created a db backup page added radio buttons * created components for database backup page * Added account selection page * Added accounts page * Some more work done - Added page validations - Almost done with db backup except for a few api calls. * Some more progress added graph api for storage account * Finished hooking up all the endpoints on db page. * Some code fixed and refactoring * Fixed a ton of validation bugs * Added common localized strings to the constants file * some code cleanup * changed method name to makeHttpGetRequest * change http result class name * Added return types and return values to the functions * removed void returns * Added more return types and values * Storing accounts in the map with ids as key Fixed a bug in case of no subscriptions found * cleaning up the code * Fixed localized strings * Added comments to get request api Added validation logic to database backup page removed unnecessary page validations. * Added some get resource functions in azure core * Changed thenable to promise * Added arm calls for file shares and blob storage * Added field specific validation error message * Added examples in validation error message. * Fixed some typings and localized string * Added live validations to dropdowns * Fixed method name to getSQLVMservers * Use console.log for retry logging (#13722) * Fixed Schema compare integration tests by adding retry (#13649) * Add workspace information in Import UI (#13648) * Add workspace information in Import UI * Addressed comments * Reduced space between Workspace heading and the label * Lint azdata.d.ts (#13728) * Added developer name to the list of developers. (#13725) onboarding commit: Added developer name to the list. * Fix environment variables for controller create (#13732) * vbump schema compare and sql database projects (#13730) * December release readme (#13733) * Change server group look (#13608) * change server group look * remove dead code * add top padding * add bot padding as well * fix heights to account for padding * fix arrow alignment * fix ellipses and node length parity * fix alignment * Make loading components not valid and improve RD radio group (#13738) * Revert "Added Accounts and Database Backup Page to Migration wizard (#13548)" (#13742) This reverts commite169005571. * Add loading text properties for option sources (#13743) * Add loading text to deployment radio options * Fix loading race condition * Update text * Add kube config and kube cluster to arc data controller screens (#13551) * Un-skip and fix a few of the db projects tests (#13726) * Un-skip and fix a few of the db projects tests * Addressed comments * Fix one test failure on Linux/Mac * Update STS to revert SqlClient update (#13758) * Update required azdata versions (#13762) * fix the recent list (#13770) * Adding base classes for data dev extension telemetry (#13763) * adding telemetry dependencies for data-workspaces and sql-database-projects * Adding telemetry dependencies for dacpac extension * Adding telemetry base to data workspaces and projects * Adding telemetry base code to the dacpac extension Co-authored-by: Benjin Dubishar <benjin@Largo.local> Co-authored-by: Sai Avishkar Sreerama <ssreerama@microsoft.com> * Update changelog (#13773) * Notebook Extension: First logging improvements (#13729) * First logging improvements * PR feedback for err output * Add BEGIN/END to snippet (#13784) * Adding database backup and accounts page to migration wizard (#13764) * Added localized strings Created a db backup page added radio buttons * created components for database backup page * Added account selection page * Added accounts page * Some more work done - Added page validations - Almost done with db backup except for a few api calls. * Some more progress added graph api for storage account * Finished hooking up all the endpoints on db page. * Some code fixed and refactoring * Fixed a ton of validation bugs * Added common localized strings to the constants file * some code cleanup * changed method name to makeHttpGetRequest * change http result class name * Added return types and return values to the functions * removed void returns * Added more return types and values * Storing accounts in the map with ids as key Fixed a bug in case of no subscriptions found * cleaning up the code * Fixed localized strings * Added comments to get request api Added validation logic to database backup page removed unnecessary page validations. * Added some get resource functions in azure core * Changed thenable to promise * Added arm calls for file shares and blob storage * Added field specific validation error message * Added examples in validation error message. * Fixed some typings and localized string * Added live validations to dropdowns * Fixed method name to getSQLVMservers * Using older storage package * Update typings/namings (#13767) * Update typings * more typings fixes * switched fileshares and blobcontainers api to http requests * removed the extra line * Adding resource graph documentation link as a comment * remove makeHttpRequest api from azurecore Co-authored-by: Charles Gagnon <chgagnon@microsoft.com> * change afterClean to beforeBuild for removing the generated assests.json (#13736) * change afterClean to beforeBuild for removing the generated assests.json * fix merge conflict * rename files * Clean up Loading Component typings (#13785) * Clean up Loading Component typings * add properties to impl * Log active element when notebook smoke test fails (#13724) * add error log for active element * fix active element selector * await isActiveElement call * call waitforactiveleement * Bump ini from 1.3.5 to 1.3.7 in /samples/sqlservices (#13776) Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.7. - [Release notes](https://github.com/isaacs/ini/releases) - [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.7) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump ini from 1.3.5 to 1.3.7 in /build (#13765) Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.7. - [Release notes](https://github.com/isaacs/ini/releases) - [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.7) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump ini from 1.3.4 to 1.3.8 (#13792) Bumps [ini](https://github.com/isaacs/ini) from 1.3.4 to 1.3.8. - [Release notes](https://github.com/isaacs/ini/releases) - [Commits](https://github.com/isaacs/ini/compare/v1.3.4...v1.3.8) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump ini from 1.3.5 to 1.3.8 in /extensions/markdown-language-features (#13791) Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.8. - [Release notes](https://github.com/isaacs/ini/releases) - [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.8) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * renable kernels dropdown test (#13727) Verify no skipped core tests * Remove redundant parameter in test scripts (#13755) * Fire onDidSelect event when selecting event from code (#13691) * Fire onDidSelect event when selecting event from code * Fix import tests * fix typo * Use correct Azure graph endpoint & cleanup (#13786) * Use correct Azure graph endpoint * Add enum * Update CODEOWNERS (#13798) * Make project workspace selectable if no workspace is open yet (#13508) * allow new workspace location to be editable * fix workspace inputbox not showing up after toggling open workspace radio buttons * add a few tests * cleanup * fix errors * addressing comments * fix filter for windows * add error message if existing workspace file is selected and change picker to be folder only * address comments * fix typos and update tests * vbump 2018 -> 2019 (#13800) * Fix "not externalized correctly" warnings (#13806) * Notebook Deep Link to Section (#13795) * Notebook deep link to section * fragment wip * table component improvement (#13801) * hyperlink column * fixed width for image only button - old behavior * Server Reports extension: fix for start and stop xevent sessions (#13565) * fix for start and stop xevent sessions * vscode.open for help URL * setup info messages for localization @kburtram - I could use an assist on updating the v# and publishing the vsix, but there's no rush this can wait until after the holidays * Arc - Update Postgres name length limit (#13807) Arc - Update Postgres name length limit. It was recently reduced from 12 to 11. * Fix whitespace differences in sqlproj (#13805) * add whiteSpaceAtEndOfSelfclosingTag * update test baselines * Fix paths for tests (#13816) * Fixed the stray validation error message in Resource Deployment wizard (#13747) * Fixed the stray validation error message * Removed not working ' with validation Adding back cancel button disabling * mark a couple data workspace tests as unstable (#13822) * cosmetic changes (#13820) * cosmetic changes * moved limitLongName function to the utils * add . as trigger character (#13811) * Remove hardcoded search box height (#13823) * Use azdata-test modelview stubs (#13818) * Filter vscode delegate command events (#13832) * Fix duplicate SVG rendering (#13828) * Fix select box event ordering (#13831) * Fix select box event ordering * more fixes * Fix page * Revert typing change * Undo param * Fix compile error * Completely remove typings * Dacpac - Showing error message to user if operation fails (#13830) * Showing error message to user if operation fails. * Added more tests * Fix unstable data-workspace tests (#13824) * stub file existing validation * add error message * change back to calling dialog.validate() * move tests to separate dialogbase file and add more error message validation * comment out the unstable unit test step (#13834) * remove --build flag * comment out unstable test * add build tag back * Removed padding-top / bottom declarations for text cell notebook-preview. This compacts the text cell by 14px on top and bottom. (#13815) * Stop forcing left text align on tables (#13840) * Have same connection logic for all nb int tests (#13844) * Added fix for the infinite page refresh in resource Deployment tools page (#13813) * Added fixed for the infinite loop in resource Deployment tools page Generating events for select boxes only when the select box value is changed. * Fixed the check logic in select method * Reverted to old code and fixed some bugs * Fixed event generation check logic * Make new workspace inputbox editable in Create project from database dialog (#13842) * update create project from database dialog to have editable new workspace * add validation * add test * add error message * Remove test for now * cleanup * add periods * throw errors * change return type to void * Removal of Components folder, moving ADP notebook to its own notebook. (#13848) * adp folder removed, notebook moved. * moved ADP notebook to own folder. * Passing click target to modal for positioning. Created custom buttons and click event handlers for Insert image and Insert link. Set feature behind preview flag. * Revert "Revert "Fix windows insiders icons (#13579)" (#13630)" This reverts commit 111dcb4c6885d9ab1a24398b13f103835789e6e0. * Corrected button style and declared dialogStyle for ManageModelsDialog. This fixes broken layout see when user clicks: Import or view models. * Revised suppressHeader/Footer to renderHeader/Footer and fixed logic. Code cleanup. Changed how calloutType: IMAGE and LINK are implemented. Added comments where neeed. * Fixed callout separator: Swapped out transparent for notebookToolbarLines * Removed promise context from image callout logic. * Moved calloutDialog into modal folder. * Code and style adjustments per feedback. Removed dependency on mac body class. Cleaned up dialog theme. Revsied modal callout logic. * Corrected CSS for notebook toolbar. Removed unused code. Added code to ensure that renderFooter would occur whether true or undefined. * Code cleanup. Clarified more details in my code comments. Added comments to values that needed identification. Escaping user-supplied text before it is rendered. * Renamed TriggerProperties interface and implementations to DialogProperties. Added default value for DialogStyle to sqlExtHost.api.impl so existing dialogs take flyout by default. * Replaced null value with undefined. Revised theme logic to account for undefined color value. * Fixed top 30px offset rule so callout dialogs do not get this added. * Revised undefined check for modal theme color. Removed calloutCompact. Moved callout dialog widths into DialogWidth property when calling methods to create callout or dialog. Added comments. Revised CSS. * Providing fallback value in the case of null. Hex: FFFFFF00 is white with no transparency. * Removed fallback hex color for foreground. Added check for foregroundRgb before attempting to grab rgba values. Added footer top border color. * Added formatting after resolving conflicts. * Corrected implementation after taking changes from main. Co-authored-by: Maddy <12754347+MaddyDev@users.noreply.github.com> Co-authored-by: Charles Gagnon <chgagnon@microsoft.com> Co-authored-by: nasc17 <69922333+nasc17@users.noreply.github.com> Co-authored-by: Chris LaFreniere <40371649+chlafreniere@users.noreply.github.com> Co-authored-by: Barbara Valdez <34872381+barbaravaldez@users.noreply.github.com> Co-authored-by: Christopher C <37060219+cavonac@users.noreply.github.com> Co-authored-by: Monica Gupta <scorpio90m@gmail.com> Co-authored-by: Monica Gupta <mogupt@microsoft.com> Co-authored-by: Benjin Dubishar <benjin.dubishar@gmail.com> Co-authored-by: Karl Burtram <karlb@microsoft.com> Co-authored-by: Sakshi Sharma <57200045+SakshiS-harma@users.noreply.github.com> Co-authored-by: Vasu Bhog <vabhog@microsoft.com> Co-authored-by: Alan Ren <alanren@microsoft.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Lucy Zhang <luczhan@microsoft.com> Co-authored-by: Leila Lali <llali@microsoft.com> Co-authored-by: Kim Santiago <31145923+kisantia@users.noreply.github.com> Co-authored-by: Aasim Khan <aasimkhan30@gmail.com> Co-authored-by: Sai Avishkar Sreerama <74571829+ssreerama@users.noreply.github.com> Co-authored-by: Aditya Bist <adbist@microsoft.com> Co-authored-by: Arvind Ranasaria <ranasaria@outlook.com> Co-authored-by: Benjin Dubishar <benjin@Largo.local> Co-authored-by: Sai Avishkar Sreerama <ssreerama@microsoft.com> Co-authored-by: Drew Skwiers-Koballa <dzsquared@users.noreply.github.com> Co-authored-by: Brian Bergeron <brian.e.bergeron@gmail.com> Co-authored-by: Vladimir Chernov <v-chvlad@microsoft.com> Co-authored-by: Alex Ma <alma1@microsoft.com>
This commit is contained in:
@@ -75,6 +75,11 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
|
||||
let dialog = this.getDialog(handle);
|
||||
const options = assign({}, DefaultDialogOptions);
|
||||
options.width = dialog.width;
|
||||
options.dialogStyle = dialog.dialogStyle;
|
||||
options.dialogPosition = dialog.dialogPosition;
|
||||
options.renderHeader = dialog.renderHeader;
|
||||
options.renderFooter = dialog.renderFooter;
|
||||
options.dialogProperties = dialog.dialogProperties;
|
||||
this._dialogService.showDialog(dialog, dialogName, options);
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -88,11 +93,16 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
|
||||
public $setDialogDetails(handle: number, details: IModelViewDialogDetails): Thenable<void> {
|
||||
let dialog = this._dialogs.get(handle);
|
||||
if (!dialog) {
|
||||
dialog = new Dialog(details.title, details.width);
|
||||
let okButton = this.getButton(details.okButton);
|
||||
let cancelButton = this.getButton(details.cancelButton);
|
||||
dialog.okButton = okButton;
|
||||
dialog.cancelButton = cancelButton;
|
||||
dialog = new Dialog(details.title, details.width, details.dialogStyle, details.dialogPosition, details.renderHeader, details.renderFooter, details.dialogProperties);
|
||||
|
||||
/**
|
||||
* Only peform actions on footer if it is shown.
|
||||
*/
|
||||
if (details.renderFooter !== false) {
|
||||
dialog.okButton = this.getButton(details.okButton);
|
||||
dialog.cancelButton = this.getButton(details.cancelButton);
|
||||
}
|
||||
|
||||
dialog.onValidityChanged(valid => this._proxy.$onPanelValidityChanged(handle, valid));
|
||||
dialog.registerCloseValidator(() => this.validateDialogClose(handle));
|
||||
this._dialogs.set(handle, dialog);
|
||||
@@ -110,7 +120,6 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
|
||||
if (details.customButtons) {
|
||||
dialog.customButtons = details.customButtons.map(buttonHandle => this.getButton(buttonHandle));
|
||||
}
|
||||
|
||||
dialog.message = details.message;
|
||||
|
||||
return Promise.resolve();
|
||||
|
||||
@@ -13,7 +13,7 @@ import * as azdata from 'azdata';
|
||||
|
||||
import { SqlMainContext, ExtHostModelViewDialogShape, MainThreadModelViewDialogShape, ExtHostModelViewShape, ExtHostBackgroundTaskManagementShape } from 'sql/workbench/api/common/sqlExtHost.protocol';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { TabOrientation, DialogWidth } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { TabOrientation, DialogWidth, DialogStyle, DialogPosition, IDialogProperties } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
const DONE_LABEL = nls.localize('dialogDoneLabel', "Done");
|
||||
const CANCEL_LABEL = nls.localize('dialogCancelLabel', "Cancel");
|
||||
@@ -127,6 +127,11 @@ class DialogImpl extends ModelViewPanelImpl implements azdata.window.Dialog {
|
||||
private _dialogName: string;
|
||||
private _isWide: boolean;
|
||||
private _width: DialogWidth;
|
||||
private _dialogStyle: DialogStyle;
|
||||
private _dialogPosition: DialogPosition;
|
||||
private _renderHeader: boolean;
|
||||
private _renderFooter: boolean;
|
||||
private _dialogProperties: IDialogProperties;
|
||||
|
||||
constructor(extHostModelViewDialog: ExtHostModelViewDialog,
|
||||
extHostModelView: ExtHostModelViewShape,
|
||||
@@ -141,6 +146,46 @@ class DialogImpl extends ModelViewPanelImpl implements azdata.window.Dialog {
|
||||
});
|
||||
}
|
||||
|
||||
public get dialogStyle(): azdata.window.DialogStyle {
|
||||
return this._dialogStyle;
|
||||
}
|
||||
|
||||
public set dialogStyle(value: azdata.window.DialogStyle) {
|
||||
this._dialogStyle = value;
|
||||
}
|
||||
|
||||
public get dialogPosition(): azdata.window.DialogPosition {
|
||||
return this._dialogPosition;
|
||||
}
|
||||
|
||||
public set dialogPosition(value: azdata.window.DialogPosition) {
|
||||
this._dialogPosition = value;
|
||||
}
|
||||
|
||||
public get renderHeader(): boolean {
|
||||
return this._renderHeader;
|
||||
}
|
||||
|
||||
public set renderHeader(value: boolean) {
|
||||
this._renderHeader = value;
|
||||
}
|
||||
|
||||
public get renderFooter(): boolean {
|
||||
return this._renderFooter;
|
||||
}
|
||||
|
||||
public set renderFooter(value: boolean) {
|
||||
this._renderFooter = value;
|
||||
}
|
||||
|
||||
public get dialogProperties(): IDialogProperties {
|
||||
return this._dialogProperties;
|
||||
}
|
||||
|
||||
public set dialogProperties(value: IDialogProperties) {
|
||||
this._dialogProperties = value;
|
||||
}
|
||||
|
||||
public get width(): azdata.window.DialogWidth {
|
||||
return this._width;
|
||||
}
|
||||
@@ -674,20 +719,49 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
|
||||
}
|
||||
|
||||
public updateDialogContent(dialog: azdata.window.Dialog): void {
|
||||
let dialogWidth: DialogWidth = 'narrow';
|
||||
let dialogStyle: DialogStyle;
|
||||
let dialogPosition: DialogPosition;
|
||||
let renderHeader: boolean;
|
||||
let renderFooter: boolean;
|
||||
let dialogProperties: IDialogProperties;
|
||||
let handle = this.getHandle(dialog);
|
||||
let tabs = dialog.content;
|
||||
|
||||
if (dialog.dialogStyle) {
|
||||
dialogStyle = dialog.dialogStyle;
|
||||
}
|
||||
if (dialog.dialogPosition) {
|
||||
dialogPosition = dialog.dialogPosition;
|
||||
}
|
||||
if (dialog.renderHeader) {
|
||||
renderHeader = dialog.renderHeader;
|
||||
}
|
||||
if (dialog.renderFooter) {
|
||||
renderFooter = dialog.renderFooter;
|
||||
}
|
||||
if (dialog.dialogProperties) {
|
||||
dialogProperties = dialog.dialogProperties;
|
||||
}
|
||||
if (tabs && typeof tabs !== 'string') {
|
||||
tabs.forEach(tab => this.updateTabContent(tab));
|
||||
}
|
||||
|
||||
if (dialog.customButtons) {
|
||||
dialog.customButtons.forEach(button => {
|
||||
button.secondary = true;
|
||||
this.updateButton(button);
|
||||
});
|
||||
}
|
||||
this.updateButton(dialog.okButton);
|
||||
this.updateButton(dialog.cancelButton);
|
||||
let dialogWidth: DialogWidth = 'narrow';
|
||||
|
||||
/**
|
||||
* Only peform actions on footer if it is shown.
|
||||
*/
|
||||
if (dialog.renderFooter !== false) {
|
||||
this.updateButton(dialog.okButton);
|
||||
this.updateButton(dialog.cancelButton);
|
||||
}
|
||||
|
||||
if (dialog.isWide !== undefined) {
|
||||
dialogWidth = dialog.isWide ? 'wide' : 'narrow';
|
||||
} else if (dialog.width !== undefined) {
|
||||
@@ -698,6 +772,11 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
|
||||
this._proxy.$setDialogDetails(handle, {
|
||||
title: dialog.title,
|
||||
width: dialogWidth,
|
||||
dialogStyle: dialogStyle,
|
||||
dialogPosition: dialogPosition,
|
||||
renderHeader: renderHeader,
|
||||
renderFooter: renderFooter,
|
||||
dialogProperties: dialogProperties,
|
||||
okButton: this.getHandle(dialog.okButton),
|
||||
cancelButton: this.getHandle(dialog.cancelButton),
|
||||
content: dialog.content && typeof dialog.content !== 'string' ? dialog.content.map(tab => this.getHandle(tab)) : dialog.content as string,
|
||||
@@ -731,12 +810,27 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
|
||||
this._onClickCallbacks.set(handle, callback);
|
||||
}
|
||||
|
||||
public createDialog(title: string, dialogName?: string, extension?: IExtensionDescription, width?: azdata.window.DialogWidth): azdata.window.Dialog {
|
||||
public createDialog(title: string, dialogName?: string, extension?: IExtensionDescription, width?: DialogWidth, dialogStyle?: DialogStyle, dialogPosition?: DialogPosition, renderHeader?: boolean, renderFooter?: boolean, dialogProperties?: IDialogProperties): azdata.window.Dialog {
|
||||
|
||||
let dialog = new DialogImpl(this, this._extHostModelView, this._extHostTaskManagement, extension);
|
||||
|
||||
if (dialogName) {
|
||||
dialog.dialogName = dialogName;
|
||||
}
|
||||
dialog.title = title;
|
||||
if (dialogStyle) {
|
||||
dialog.dialogStyle = dialogStyle;
|
||||
}
|
||||
if (dialogPosition) {
|
||||
dialog.dialogPosition = dialogPosition;
|
||||
}
|
||||
if (dialogProperties) {
|
||||
dialog.dialogProperties = dialogProperties;
|
||||
}
|
||||
dialog.renderHeader = renderHeader;
|
||||
dialog.renderFooter = renderFooter;
|
||||
if (title) {
|
||||
dialog.title = title;
|
||||
}
|
||||
dialog.width = width ?? 'narrow';
|
||||
dialog.handle = this.getHandle(dialog);
|
||||
return dialog;
|
||||
|
||||
@@ -141,7 +141,7 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
|
||||
},
|
||||
connect(connectionProfile: azdata.IConnectionProfile, saveConnection: boolean, showDashboard: boolean): Thenable<azdata.ConnectionResult> {
|
||||
return extHostConnectionManagement.$connect(connectionProfile, saveConnection, showDashboard);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// Backcompat "sqlops" APIs
|
||||
@@ -416,14 +416,17 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
|
||||
return extHostModalDialogs.createDialog(name);
|
||||
},
|
||||
// the 'width' parameter used to be boolean type named 'isWide', the optional boolean type for 'width' parameter is added for backward compatibility support of 'isWide' parameter.
|
||||
createModelViewDialog(title: string, dialogName?: string, width?: boolean | azdata.window.DialogWidth): azdata.window.Dialog {
|
||||
createModelViewDialog(title: string, dialogName?: string, width?: boolean | sqlExtHostTypes.DialogWidth, dialogStyle?: sqlExtHostTypes.DialogStyle, dialogPosition?: sqlExtHostTypes.DialogPosition, renderHeader?: boolean, renderFooter?: boolean, dialogProperties?: sqlExtHostTypes.IDialogProperties): azdata.window.Dialog {
|
||||
let dialogWidth: azdata.window.DialogWidth;
|
||||
if (typeof width === 'boolean') {
|
||||
dialogWidth = width === true ? 'wide' : 'narrow';
|
||||
} else {
|
||||
dialogWidth = width;
|
||||
}
|
||||
return extHostModelViewDialog.createDialog(title, dialogName, extension, dialogWidth);
|
||||
if (dialogStyle === undefined) {
|
||||
dialogStyle = 'flyout';
|
||||
}
|
||||
return extHostModelViewDialog.createDialog(title, dialogName, extension, dialogWidth, dialogStyle, dialogPosition, renderHeader, renderFooter, dialogProperties);
|
||||
},
|
||||
createTab(title: string): azdata.window.DialogTab {
|
||||
return extHostModelViewDialog.createTab(title, extension);
|
||||
|
||||
@@ -259,6 +259,11 @@ export interface IModelViewDialogDetails {
|
||||
customButtons: number[];
|
||||
message: DialogMessage;
|
||||
width: DialogWidth;
|
||||
dialogStyle: DialogStyle;
|
||||
dialogPosition: DialogPosition;
|
||||
renderHeader: boolean;
|
||||
renderFooter: boolean;
|
||||
dialogProperties: IDialogProperties;
|
||||
}
|
||||
|
||||
export interface IModelViewTabDetails {
|
||||
@@ -302,6 +307,17 @@ export interface IModelViewWizardDetails {
|
||||
|
||||
export type DialogWidth = 'narrow' | 'medium' | 'wide' | number;
|
||||
|
||||
export type DialogStyle = 'normal' | 'flyout' | 'callout';
|
||||
|
||||
export type DialogPosition = 'left' | 'below';
|
||||
|
||||
export interface IDialogProperties {
|
||||
xPos: number,
|
||||
yPos: number,
|
||||
width: number,
|
||||
height: number
|
||||
}
|
||||
|
||||
export enum MessageLevel {
|
||||
Error = 0,
|
||||
Warning = 1,
|
||||
|
||||
326
src/sql/workbench/browser/modal/calloutDialog.ts
Normal file
326
src/sql/workbench/browser/modal/calloutDialog.ts
Normal file
@@ -0,0 +1,326 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import * as styler from 'vs/platform/theme/common/styler';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { IDialogProperties, Modal, DialogWidth } from 'sql/workbench/browser/modal/modal';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { IFileDialogService, IOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
||||
import { attachModalDialogStyler } from 'sql/workbench/common/styler';
|
||||
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
|
||||
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox';
|
||||
import { RadioButton } from 'sql/base/browser/ui/radioButton/radioButton';
|
||||
import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||
|
||||
export type CalloutType = 'IMAGE' | 'LINK';
|
||||
|
||||
export interface ICalloutDialogOptions {
|
||||
insertTitle?: string,
|
||||
calloutType?: CalloutType,
|
||||
insertMarkup?: string,
|
||||
imagePath?: string,
|
||||
embedImage?: boolean
|
||||
}
|
||||
export class CalloutDialog extends Modal {
|
||||
private _calloutType: CalloutType;
|
||||
private _selectionComplete: Deferred<ICalloutDialogOptions>;
|
||||
// Link
|
||||
private _linkTextLabel: HTMLElement;
|
||||
private _linkTextInputBox: InputBox;
|
||||
private _linkAddressLabel: HTMLElement;
|
||||
private _linkUrlInputBox: InputBox;
|
||||
// Image
|
||||
private _imageLocationLabel: HTMLElement;
|
||||
private _imageLocalRadioButton: RadioButton;
|
||||
private _editorImageLocationGroup: string = 'editorImageLocationGroup';
|
||||
private _imageRemoteRadioButton: RadioButton;
|
||||
private _imageUrlLabel: HTMLElement;
|
||||
private _imageUrlInputBox: InputBox;
|
||||
private _imageBrowseButton: HTMLAnchorElement;
|
||||
private _imageEmbedLabel: HTMLElement;
|
||||
private _imageEmbedCheckbox: Checkbox;
|
||||
|
||||
private readonly insertButtonText = localize('callout.insertButton', "Insert");
|
||||
private readonly cancelButtonText = localize('callout.cancelButton', "Cancel");
|
||||
// Link
|
||||
private readonly linkTextLabel = localize('callout.linkTextLabel', "Text to display");
|
||||
private readonly linkTextPlaceholder = localize('callout.linkTextPlaceholder', "Text to display");
|
||||
private readonly linkAddressLabel = localize('callout.linkAddressLabel', "Address");
|
||||
private readonly linkAddressPlaceholder = localize('callout.linkAddressPlaceholder', "Link to an existing file or web page");
|
||||
// Image
|
||||
private readonly locationLabel = localize('callout.locationLabel', "Image location");
|
||||
private readonly localImageLabel = localize('callout.localImageLabel', "This computer");
|
||||
private readonly remoteImageLabel = localize('callout.remoteImageLabel', "Online");
|
||||
private readonly pathInputLabel = localize('callout.pathInputLabel', "Image URL");
|
||||
private readonly pathPlaceholder = localize('callout.pathPlaceholder', "Enter image path");
|
||||
private readonly urlPlaceholder = localize('callout.urlPlaceholder', "Enter image URL");
|
||||
private readonly browseAltText = localize('callout.browseAltText', "Browse");
|
||||
private readonly embedImageLabel = localize('callout.embedImageLabel', "Attach image to notebook");
|
||||
|
||||
constructor(
|
||||
calloutType: CalloutType,
|
||||
title: string,
|
||||
width: DialogWidth,
|
||||
dialogProperties: IDialogProperties,
|
||||
@IPathService private readonly _pathService: IPathService,
|
||||
@IFileDialogService private readonly _fileDialogService: IFileDialogService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@ILayoutService layoutService: ILayoutService,
|
||||
@IAdsTelemetryService telemetryService: IAdsTelemetryService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IContextViewService private _contextViewService: IContextViewService,
|
||||
@IClipboardService clipboardService: IClipboardService,
|
||||
@ILogService logService: ILogService,
|
||||
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService
|
||||
) {
|
||||
super(
|
||||
title,
|
||||
TelemetryKeys.CalloutDialog,
|
||||
telemetryService,
|
||||
layoutService,
|
||||
clipboardService,
|
||||
themeService,
|
||||
logService,
|
||||
textResourcePropertiesService,
|
||||
contextKeyService,
|
||||
{
|
||||
dialogStyle: 'callout',
|
||||
dialogPosition: 'below',
|
||||
dialogProperties: dialogProperties,
|
||||
width: width
|
||||
});
|
||||
|
||||
this._selectionComplete = new Deferred<ICalloutDialogOptions>();
|
||||
this._calloutType = calloutType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the dialog and returns a promise for what options the user chooses.
|
||||
*/
|
||||
public open(): Promise<ICalloutDialogOptions> {
|
||||
this.show();
|
||||
return this._selectionComplete.promise;
|
||||
}
|
||||
|
||||
public render() {
|
||||
super.render();
|
||||
|
||||
attachModalDialogStyler(this, this._themeService);
|
||||
|
||||
this.addFooterButton(this.insertButtonText, () => this.insert());
|
||||
this.addFooterButton(this.cancelButtonText, () => this.cancel(), undefined, true);
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
protected renderBody(container: HTMLElement) {
|
||||
if (this._calloutType === 'IMAGE') {
|
||||
this.buildInsertImageCallout(container);
|
||||
}
|
||||
|
||||
if (this._calloutType === 'LINK') {
|
||||
this.buildInsertLinkCallout(container);
|
||||
}
|
||||
}
|
||||
|
||||
private buildInsertImageCallout(container: HTMLElement): void {
|
||||
let imageContentColumn = DOM.$('.column.insert-image');
|
||||
DOM.append(container, imageContentColumn);
|
||||
|
||||
let locationRow = DOM.$('.row');
|
||||
DOM.append(imageContentColumn, locationRow);
|
||||
|
||||
this._imageLocationLabel = DOM.$('p');
|
||||
this._imageLocationLabel.innerText = this.locationLabel;
|
||||
DOM.append(locationRow, this._imageLocationLabel);
|
||||
|
||||
let radioButtonGroup = DOM.$('.radio-group');
|
||||
this._imageLocalRadioButton = new RadioButton(radioButtonGroup, {
|
||||
label: this.localImageLabel,
|
||||
enabled: true,
|
||||
checked: true
|
||||
});
|
||||
this._imageRemoteRadioButton = new RadioButton(radioButtonGroup, {
|
||||
label: this.remoteImageLabel,
|
||||
enabled: true,
|
||||
checked: false
|
||||
});
|
||||
this._imageLocalRadioButton.value = localize('local', "Local");
|
||||
this._imageLocalRadioButton.name = this._editorImageLocationGroup;
|
||||
this._imageRemoteRadioButton.value = localize('remote', "Remote");
|
||||
this._imageRemoteRadioButton.name = this._editorImageLocationGroup;
|
||||
DOM.append(locationRow, radioButtonGroup);
|
||||
|
||||
let pathRow = DOM.$('.row');
|
||||
DOM.append(imageContentColumn, pathRow);
|
||||
this._imageUrlLabel = DOM.$('p');
|
||||
if (this._imageLocalRadioButton.checked === true) {
|
||||
this._imageUrlLabel.innerText = this.pathPlaceholder;
|
||||
} else {
|
||||
this._imageUrlLabel.innerText = this.urlPlaceholder;
|
||||
}
|
||||
DOM.append(pathRow, this._imageUrlLabel);
|
||||
|
||||
let inputContainer = DOM.$('.flex-container');
|
||||
this._imageUrlInputBox = new InputBox(
|
||||
inputContainer,
|
||||
this._contextViewService,
|
||||
{
|
||||
placeholder: this.pathPlaceholder,
|
||||
ariaLabel: this.pathInputLabel
|
||||
});
|
||||
let browseButtonContainer = DOM.$('.button-icon');
|
||||
this._imageBrowseButton = DOM.$('a.codicon.masked-icon.browse-local');
|
||||
this._imageBrowseButton.title = this.browseAltText;
|
||||
DOM.append(inputContainer, browseButtonContainer);
|
||||
DOM.append(browseButtonContainer, this._imageBrowseButton);
|
||||
|
||||
this._register(DOM.addDisposableListener(this._imageBrowseButton, DOM.EventType.CLICK, async () => {
|
||||
let selectedUri = await this.handleBrowse();
|
||||
if (selectedUri) {
|
||||
this._imageUrlInputBox.value = selectedUri.fsPath;
|
||||
}
|
||||
}, true));
|
||||
|
||||
this._register(this._imageRemoteRadioButton.onClicked(e => {
|
||||
this._imageBrowseButton.style.display = 'none';
|
||||
this._imageUrlLabel.innerText = this.urlPlaceholder;
|
||||
this._imageUrlInputBox.setPlaceHolder(this.urlPlaceholder);
|
||||
}));
|
||||
this._register(this._imageLocalRadioButton.onClicked(e => {
|
||||
this._imageBrowseButton.style.display = 'block';
|
||||
this._imageUrlLabel.innerText = this.pathPlaceholder;
|
||||
this._imageUrlInputBox.setPlaceHolder(this.pathPlaceholder);
|
||||
}));
|
||||
DOM.append(pathRow, inputContainer);
|
||||
|
||||
let embedRow = DOM.$('.row');
|
||||
DOM.append(imageContentColumn, embedRow);
|
||||
this._imageEmbedLabel = DOM.append(embedRow, DOM.$('.checkbox'));
|
||||
this._imageEmbedCheckbox = new Checkbox(
|
||||
this._imageEmbedLabel,
|
||||
{
|
||||
label: this.embedImageLabel,
|
||||
checked: false,
|
||||
onChange: (viaKeyboard) => { },
|
||||
ariaLabel: this.embedImageLabel
|
||||
});
|
||||
DOM.append(embedRow, this._imageEmbedLabel);
|
||||
}
|
||||
|
||||
private buildInsertLinkCallout(container: HTMLElement): void {
|
||||
let linkContentColumn = DOM.$('.column.insert-link');
|
||||
DOM.append(container, linkContentColumn);
|
||||
|
||||
let linkTextRow = DOM.$('.row');
|
||||
DOM.append(linkContentColumn, linkTextRow);
|
||||
|
||||
this._linkTextLabel = DOM.$('p');
|
||||
this._linkTextLabel.innerText = this.linkTextLabel;
|
||||
DOM.append(linkTextRow, this._linkTextLabel);
|
||||
|
||||
const linkTextInputContainer = DOM.$('.input-field');
|
||||
this._linkTextInputBox = new InputBox(
|
||||
linkTextInputContainer,
|
||||
this._contextViewService,
|
||||
{
|
||||
placeholder: this.linkTextPlaceholder,
|
||||
ariaLabel: this.linkTextLabel
|
||||
});
|
||||
DOM.append(linkTextRow, linkTextInputContainer);
|
||||
|
||||
let linkAddressRow = DOM.$('.row');
|
||||
DOM.append(linkContentColumn, linkAddressRow);
|
||||
this._linkAddressLabel = DOM.$('p');
|
||||
this._linkAddressLabel.innerText = this.linkAddressLabel;
|
||||
DOM.append(linkAddressRow, this._linkAddressLabel);
|
||||
|
||||
const linkAddressInputContainer = DOM.$('.input-field');
|
||||
this._linkUrlInputBox = new InputBox(
|
||||
linkAddressInputContainer,
|
||||
this._contextViewService,
|
||||
{
|
||||
placeholder: this.linkAddressPlaceholder,
|
||||
ariaLabel: this.linkAddressLabel
|
||||
});
|
||||
DOM.append(linkAddressRow, linkAddressInputContainer);
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
// Theme styler
|
||||
if (this._calloutType === 'IMAGE') {
|
||||
this._register(styler.attachInputBoxStyler(this._imageUrlInputBox, this._themeService));
|
||||
this._register(styler.attachCheckboxStyler(this._imageEmbedCheckbox, this._themeService));
|
||||
}
|
||||
if (this._calloutType === 'LINK') {
|
||||
this._register(styler.attachInputBoxStyler(this._linkTextInputBox, this._themeService));
|
||||
this._register(styler.attachInputBoxStyler(this._linkUrlInputBox, this._themeService));
|
||||
}
|
||||
}
|
||||
|
||||
protected layout(height?: number): void {
|
||||
}
|
||||
|
||||
public insert() {
|
||||
this.hide();
|
||||
if (this._calloutType === 'IMAGE') {
|
||||
this._selectionComplete.resolve({
|
||||
insertMarkup: `<img src="${strings.escape(this._imageUrlInputBox.value)}">`,
|
||||
imagePath: this._imageUrlInputBox.value,
|
||||
embedImage: this._imageEmbedCheckbox.checked
|
||||
});
|
||||
}
|
||||
if (this._calloutType === 'LINK') {
|
||||
this._selectionComplete.resolve({
|
||||
insertMarkup: `<a href="${strings.escape(this._linkUrlInputBox.value)}">${strings.escape(this._linkTextInputBox.value)}</a>`,
|
||||
});
|
||||
}
|
||||
this.dispose();
|
||||
}
|
||||
|
||||
public cancel() {
|
||||
this.hide();
|
||||
this._selectionComplete.resolve({
|
||||
insertMarkup: '',
|
||||
imagePath: undefined,
|
||||
embedImage: undefined
|
||||
});
|
||||
this.dispose();
|
||||
}
|
||||
|
||||
private async getUserHome(): Promise<string> {
|
||||
const userHomeUri = await this._pathService.userHome();
|
||||
return userHomeUri.path;
|
||||
}
|
||||
|
||||
private async handleBrowse(): Promise<URI | undefined> {
|
||||
let options: IOpenDialogOptions = {
|
||||
openLabel: undefined,
|
||||
canSelectFiles: true,
|
||||
canSelectFolders: false,
|
||||
canSelectMany: false,
|
||||
defaultUri: URI.file(await this.getUserHome()),
|
||||
title: undefined
|
||||
};
|
||||
let imageUri: URI[] = await this._fileDialogService.showOpenDialog(options);
|
||||
if (imageUri.length > 0) {
|
||||
return imageUri[0];
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
4
src/sql/workbench/browser/modal/media/browse-local.svg
Normal file
4
src/sql/workbench/browser/modal/media/browse-local.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20 8V16C20 16.1406 19.974 16.2708 19.9219 16.3906C19.8698 16.5104 19.7969 16.6172 19.7031 16.7109C19.6146 16.7995 19.5104 16.8698 19.3906 16.9219C19.2708 16.974 19.1406 17 19 17H5C4.85938 17 4.72917 16.974 4.60938 16.9219C4.48958 16.8698 4.38281 16.7995 4.28906 16.7109C4.20052 16.6172 4.13021 16.5104 4.07812 16.3906C4.02604 16.2708 4 16.1406 4 16V6C4 5.85938 4.02604 5.72917 4.07812 5.60938C4.13021 5.48958 4.20052 5.38542 4.28906 5.29688C4.38281 5.20312 4.48958 5.13021 4.60938 5.07812C4.72917 5.02604 4.85938 5 5 5H10.75C10.9427 5 11.1224 5.03646 11.2891 5.10938C11.4557 5.17708 11.6068 5.27083 11.7422 5.39062C11.8828 5.50521 12.0078 5.63802 12.1172 5.78906C12.2318 5.9349 12.3359 6.08594 12.4297 6.24219C12.4974 6.36198 12.5625 6.46875 12.625 6.5625C12.6927 6.65625 12.7656 6.73698 12.8438 6.80469C12.9271 6.86719 13.0182 6.91667 13.1172 6.95312C13.2214 6.98438 13.349 7 13.5 7H19C19.1406 7 19.2708 7.02604 19.3906 7.07812C19.5104 7.13021 19.6146 7.20312 19.7031 7.29688C19.7969 7.38542 19.8698 7.48958 19.9219 7.60938C19.974 7.72917 20 7.85938 20 8ZM10.75 6H5V8H10.75C10.8906 8 11.0078 7.97656 11.1016 7.92969C11.2005 7.88281 11.2917 7.82552 11.375 7.75781C11.4635 7.6901 11.5521 7.61719 11.6406 7.53906C11.7292 7.45573 11.8307 7.38281 11.9453 7.32031C11.8672 7.23177 11.7839 7.11458 11.6953 6.96875C11.612 6.81771 11.5208 6.67188 11.4219 6.53125C11.3229 6.38542 11.2161 6.26042 11.1016 6.15625C10.9922 6.05208 10.875 6 10.75 6ZM19 16V8H13.5C13.2083 8 12.9714 8.02604 12.7891 8.07812C12.612 8.125 12.4609 8.1849 12.3359 8.25781C12.2161 8.33073 12.112 8.41146 12.0234 8.5C11.9349 8.58854 11.8359 8.66927 11.7266 8.74219C11.6224 8.8151 11.4948 8.8776 11.3438 8.92969C11.1927 8.97656 10.9948 9 10.75 9H5V16H19Z" fill="#0078D4"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -13,25 +13,131 @@
|
||||
z-index: 500;
|
||||
}
|
||||
|
||||
.modal:not(.flyout-dialog) .modal-dialog {
|
||||
.modal .btn-secondary {
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
.modal:not(.flyout-dialog):not(.callout-dialog) .modal-dialog {
|
||||
margin: auto;
|
||||
width: 640px;
|
||||
height: 480px;
|
||||
}
|
||||
|
||||
.modal.callout-dialog {
|
||||
background-color: transparent;
|
||||
}
|
||||
.modal.callout-dialog .modal-dialog {
|
||||
border-radius: 2px;
|
||||
box-shadow: 0px 3px 8px rgba(var(--foreground));
|
||||
max-height: 300px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.modal.callout-dialog .modal-content .insert-image .flex-container {
|
||||
display: flex;
|
||||
}
|
||||
.modal.callout-dialog .modal-content .insert-image .flex-container > div {
|
||||
flex: 1;
|
||||
}
|
||||
.modal.callout-dialog .modal-content p {
|
||||
margin: 0;
|
||||
}
|
||||
.modal.callout-dialog .modal-content .button-icon {
|
||||
cursor: pointer;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.modal.callout-dialog .modal-content .insert-image .monaco-inputbox {
|
||||
min-width: 380px;
|
||||
}
|
||||
.modal.callout-dialog .modal-content .row {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.modal.callout-dialog .modal-content .radio-group input {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.modal.callout-dialog .modal-content .radio-group span {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.hc-black .modal.callout-dialog .modal-dialog {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* Correct the arrow appearance for HC theme */
|
||||
.callout-arrow:before {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color:
|
||||
transparent
|
||||
transparent
|
||||
var(--bodybackground)
|
||||
var(--bodybackground);
|
||||
box-shadow: -3px 3px 3px 0 rgba(var(--foreground));
|
||||
content: '';
|
||||
display: block;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
}
|
||||
.callout-arrow.from-below:before {
|
||||
border-width: 0.5em;
|
||||
left: 2em;
|
||||
top: -0.2em;
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
.callout-arrow.from-left:before {
|
||||
background-color: var(--bodybackground);
|
||||
height: 26px;
|
||||
right: -13px;
|
||||
top: 26px;
|
||||
transform: rotate(-135deg);
|
||||
width: 26px;
|
||||
}
|
||||
|
||||
.hc-black .callout-arrow:before {
|
||||
background-color: var(--bodybackground);
|
||||
border-color:
|
||||
transparent
|
||||
transparent
|
||||
var(--border)
|
||||
var(--border);
|
||||
border-width: 0.1em;
|
||||
box-shadow: none;
|
||||
height: 0.8em;
|
||||
width: 0.8em;
|
||||
}
|
||||
.hc-black .callout-arrow.from-below:before {
|
||||
top: -0.4em;
|
||||
}
|
||||
.hc-black .callout-arrow.from-left:before {
|
||||
height: 2em;
|
||||
right: -1.2em;
|
||||
width: 2em;
|
||||
}
|
||||
|
||||
|
||||
.modal .modal-header {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.modal.callout-dialog .modal-header {
|
||||
padding: 18px 24px 8px 24px;
|
||||
}
|
||||
|
||||
.modal .modal-footer {
|
||||
padding: 15px;
|
||||
}
|
||||
.modal.callout-dialog .modal-footer {
|
||||
padding: 15px 24px 15px 24px;
|
||||
}
|
||||
|
||||
.modal .codicon.in-progress {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
/** FLYOUT **/
|
||||
.modal.flyout-dialog .modal-dialog {
|
||||
margin: auto auto auto auto;
|
||||
height: 100%;
|
||||
@@ -83,6 +189,17 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal.callout-dialog .modal-body {
|
||||
padding: 8px 24px;
|
||||
}
|
||||
|
||||
.modal.callout-dialog.compact .modal-header {
|
||||
padding: 16px 24px 4px 24px;
|
||||
}
|
||||
.modal.callout-dialog.compact .modal-body {
|
||||
padding: 4px 24px 16px 24px;
|
||||
}
|
||||
|
||||
/* modl body content style(excluding dialogErrorMessage section) for angulr component dialog */
|
||||
.angular-modal-body-content {
|
||||
overflow-x: hidden;
|
||||
@@ -114,25 +231,16 @@
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.vs-dark .modal.flyout-dialog .input {
|
||||
background-color: #3C3C3C;
|
||||
}
|
||||
|
||||
.vs-dark .modal.flyout-dialog input:disabled {
|
||||
background-color: #E1E1E1;
|
||||
color: #3C3C3C;
|
||||
}
|
||||
|
||||
.modal .select-box,
|
||||
.modal .monaco-select-box {
|
||||
width: 100%;
|
||||
height: 25px;
|
||||
color: #6C6C6C;
|
||||
font-size: 11px;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.modal .modal-footer {
|
||||
border-top: 1px solid #E1E1E1;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@@ -149,9 +257,11 @@
|
||||
}
|
||||
|
||||
.modal .footer-button a.monaco-button.monaco-text-button {
|
||||
min-width: 100px;
|
||||
border-radius: 2px;
|
||||
height: 24px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.vs .monaco-text-button:focus {
|
||||
@@ -159,14 +269,13 @@
|
||||
}
|
||||
|
||||
.modal .footer-button {
|
||||
margin-left: 5px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.modal .right-footer .footer-button:last-of-type {
|
||||
margin-right: none;
|
||||
}
|
||||
|
||||
|
||||
.modal.flyout-dialog .dialog-message {
|
||||
padding: 6px 10px 10px 10px;
|
||||
font-size: 13px;
|
||||
@@ -176,20 +285,20 @@
|
||||
|
||||
.vs .modal.flyout-dialog .dialog-message.error,
|
||||
.vs-dark .modal.flyout-dialog .dialog-message.error {
|
||||
background-color:#B62E00 !important;
|
||||
color:#FFFFFF !important;
|
||||
background-color: #B62E00 !important;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.vs .modal.flyout-dialog .dialog-message.warning,
|
||||
.vs-dark .modal.flyout-dialog .dialog-message.warning {
|
||||
background-color:#F9E385 !important;
|
||||
color:#4A4A4A !important;
|
||||
background-color: #F9E385 !important;
|
||||
color: #4A4A4A !important;
|
||||
}
|
||||
|
||||
.vs .modal.flyout-dialog .dialog-message.info,
|
||||
.vs-dark .modal.flyout-dialog .dialog-message.info {
|
||||
background-color:#096CC9 !important;
|
||||
color:#FFFFFF !important;
|
||||
background-color: #096CC9 !important;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.modal.flyout-dialog .dialog-message-header {
|
||||
@@ -226,18 +335,18 @@
|
||||
}
|
||||
|
||||
.modal.flyout-dialog .dialog-message.info .dialog-message-button > a:focus,
|
||||
.modal.flyout-dialog .dialog-message.error .dialog-message-button > a:focus{
|
||||
.modal.flyout-dialog .dialog-message.error .dialog-message-button > a:focus {
|
||||
outline-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.modal.flyout-dialog .dialog-message.warning .dialog-message-button > a:focus{
|
||||
.modal.flyout-dialog .dialog-message.warning .dialog-message-button > a:focus {
|
||||
outline-color: #000000;
|
||||
}
|
||||
|
||||
.modal.flyout-dialog .dialog-message-button > a {
|
||||
background-position-x: 2px !important;
|
||||
background-color: inherit !important;
|
||||
color:inherit !important;
|
||||
color: inherit !important;
|
||||
padding-left: 22px !important;
|
||||
background-size: 16px 16px !important;
|
||||
text-align: left !important;
|
||||
@@ -288,7 +397,7 @@
|
||||
background: url('show_details.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.dialog-message.info .dialog-message-icon {
|
||||
.dialog-message.info .dialog-message-icon {
|
||||
background: url('info_notification_inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
@@ -303,3 +412,18 @@
|
||||
.dialog-message.error .dialog-message-icon {
|
||||
background: url('error_notification_inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.codicon.masked-icon.browse-local {
|
||||
display: inline-block;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
}
|
||||
.codicon.masked-icon.browse-local:before {
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
background-image: none;
|
||||
-webkit-mask-image: url('browse-local.svg');
|
||||
mask-image: url('browse-local.svg');
|
||||
-webkit-mask-size: 100%;
|
||||
mask-size: 100%;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
|
||||
import { Button } from 'sql/base/browser/ui/button/button';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
@@ -27,6 +26,10 @@ import { IThemable } from 'vs/base/common/styler';
|
||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
||||
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
|
||||
import { alert } from 'vs/base/browser/ui/aria/aria';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { editorWidgetForeground, editorBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { notebookToolbarLines } from 'sql/platform/theme/common/colorRegistry';
|
||||
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
|
||||
export enum MessageLevel {
|
||||
Error = 0,
|
||||
@@ -56,9 +59,21 @@ export interface IModalDialogStyles {
|
||||
}
|
||||
|
||||
export type DialogWidth = 'narrow' | 'medium' | 'wide' | number;
|
||||
export type DialogStyle = 'normal' | 'flyout' | 'callout';
|
||||
export type DialogPosition = 'left' | 'below';
|
||||
|
||||
export interface IDialogProperties {
|
||||
xPos: number,
|
||||
yPos: number,
|
||||
width: number,
|
||||
height: number
|
||||
}
|
||||
|
||||
export interface IModalOptions {
|
||||
isFlyout?: boolean;
|
||||
dialogStyle?: DialogStyle;
|
||||
dialogPosition?: DialogPosition;
|
||||
positionX?: number;
|
||||
positionY?: number;
|
||||
width?: DialogWidth;
|
||||
isAngular?: boolean;
|
||||
hasBackButton?: boolean;
|
||||
@@ -66,16 +81,25 @@ export interface IModalOptions {
|
||||
hasErrors?: boolean;
|
||||
hasSpinner?: boolean;
|
||||
spinnerTitle?: string;
|
||||
renderHeader?: boolean;
|
||||
renderFooter?: boolean;
|
||||
dialogProperties?: IDialogProperties;
|
||||
}
|
||||
|
||||
const defaultOptions: IModalOptions = {
|
||||
isFlyout: true,
|
||||
dialogStyle: 'flyout',
|
||||
dialogPosition: undefined,
|
||||
positionX: undefined,
|
||||
positionY: undefined,
|
||||
width: 'narrow',
|
||||
isAngular: false,
|
||||
hasBackButton: false,
|
||||
hasTitleIcon: false,
|
||||
hasErrors: false,
|
||||
hasSpinner: false
|
||||
hasSpinner: false,
|
||||
renderHeader: true,
|
||||
renderFooter: true,
|
||||
dialogProperties: undefined
|
||||
};
|
||||
|
||||
const tabbableElementsQuerySelector = 'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex="0"]';
|
||||
@@ -106,6 +130,7 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
private _dialogBorder?: Color;
|
||||
private _dialogHeaderAndFooterBackground?: Color;
|
||||
private _dialogBodyBackground?: Color;
|
||||
private _footerBorderTopColor?: Color;
|
||||
|
||||
private _modalDialog?: HTMLElement;
|
||||
private _modalContent?: HTMLElement;
|
||||
@@ -168,18 +193,31 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
|
||||
/**
|
||||
* Build and render the modal, will call {@link Modal#renderBody}
|
||||
*
|
||||
*/
|
||||
public render() {
|
||||
let top: number;
|
||||
let builderClass = '.modal.fade';
|
||||
if (this._modalOptions.isFlyout) {
|
||||
builderClass += '.flyout-dialog';
|
||||
}
|
||||
builderClass += this._modalOptions.dialogStyle === 'flyout' ? '.flyout-dialog'
|
||||
: this._modalOptions.dialogStyle === 'callout' ? '.callout-dialog'
|
||||
: '';
|
||||
|
||||
this._bodyContainer = DOM.$(`${builderClass}`, { role: 'dialog', 'aria-label': this._title });
|
||||
const top = this.layoutService.offset?.top ?? 0;
|
||||
|
||||
if (this._modalOptions.dialogStyle === 'callout') {
|
||||
top = 0;
|
||||
} else {
|
||||
top = this.layoutService.offset?.top ?? 0;
|
||||
}
|
||||
this._bodyContainer.style.top = `${top}px`;
|
||||
this._modalDialog = DOM.append(this._bodyContainer, DOM.$('.modal-dialog'));
|
||||
this._modalContent = DOM.append(this._modalDialog, DOM.$('.modal-content'));
|
||||
|
||||
if (this._modalOptions.dialogStyle === 'callout') {
|
||||
let arrowClass = `.callout-arrow.from-${this._modalOptions.dialogPosition}`;
|
||||
this._modalContent = DOM.append(this._modalDialog, DOM.$(`.modal-content${arrowClass}`));
|
||||
} else {
|
||||
this._modalContent = DOM.append(this._modalDialog, DOM.$('.modal-content'));
|
||||
}
|
||||
|
||||
if (typeof this._modalOptions.width === 'number') {
|
||||
this._modalDialog.style.width = `${this._modalOptions.width}px`;
|
||||
@@ -187,23 +225,29 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
this._modalDialog.classList.add(`${this._modalOptions.width}-dialog`);
|
||||
}
|
||||
|
||||
if (this._modalOptions.dialogStyle === 'callout') {
|
||||
this._register(DOM.addDisposableListener(this._bodyContainer, DOM.EventType.CLICK, (e) => this.handleClickOffModal(e)));
|
||||
}
|
||||
|
||||
if (!isUndefinedOrNull(this._title)) {
|
||||
this._modalHeaderSection = DOM.append(this._modalContent, DOM.$('.modal-header'));
|
||||
if (this._modalOptions.hasBackButton) {
|
||||
const container = DOM.append(this._modalHeaderSection, DOM.$('.modal-go-back'));
|
||||
this._backButton = new Button(container, { secondary: true });
|
||||
this._backButton.icon = {
|
||||
classNames: 'backButtonIcon'
|
||||
};
|
||||
this._backButton.title = localize('modal.back', "Back");
|
||||
}
|
||||
if (this._modalOptions.renderHeader || this._modalOptions.renderHeader === undefined) {
|
||||
this._modalHeaderSection = DOM.append(this._modalContent, DOM.$('.modal-header'));
|
||||
if (this._modalOptions.hasBackButton) {
|
||||
const container = DOM.append(this._modalHeaderSection, DOM.$('.modal-go-back'));
|
||||
this._backButton = new Button(container, { secondary: true });
|
||||
this._backButton.icon = {
|
||||
classNames: 'backButtonIcon'
|
||||
};
|
||||
this._backButton.title = localize('modal.back', "Back");
|
||||
}
|
||||
|
||||
if (this._modalOptions.hasTitleIcon) {
|
||||
this._modalTitleIcon = DOM.append(this._modalHeaderSection, DOM.$('.modal-title-icon'));
|
||||
}
|
||||
if (this._modalOptions.hasTitleIcon) {
|
||||
this._modalTitleIcon = DOM.append(this._modalHeaderSection, DOM.$('.modal-title-icon'));
|
||||
}
|
||||
|
||||
this._modalTitle = DOM.append(this._modalHeaderSection, DOM.$('h1.modal-title'));
|
||||
this._modalTitle.innerText = this._title;
|
||||
this._modalTitle = DOM.append(this._modalHeaderSection, DOM.$('h1.modal-title'));
|
||||
this._modalTitle.innerText = this._title;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this._modalOptions.isAngular && this._modalOptions.hasErrors) {
|
||||
@@ -249,16 +293,17 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
this._modalBodySection = DOM.append(this._modalContent, DOM.$(`.${modalBodyClass}`));
|
||||
this.renderBody(this._modalBodySection);
|
||||
|
||||
// This modal footer section refers to the footer of of the dialog
|
||||
if (!this._modalOptions.isAngular) {
|
||||
this._modalFooterSection = DOM.append(this._modalContent, DOM.$('.modal-footer'));
|
||||
if (this._modalOptions.hasSpinner) {
|
||||
this._spinnerElement = DOM.append(this._modalFooterSection, DOM.$('.codicon.in-progress'));
|
||||
this._spinnerElement.setAttribute('title', this._modalOptions.spinnerTitle ?? '');
|
||||
DOM.hide(this._spinnerElement);
|
||||
if (this._modalOptions.renderFooter !== false) {
|
||||
if (!this._modalOptions.isAngular) {
|
||||
this._modalFooterSection = DOM.append(this._modalContent, DOM.$('.modal-footer'));
|
||||
if (this._modalOptions.hasSpinner) {
|
||||
this._spinnerElement = DOM.append(this._modalFooterSection, DOM.$('.codicon.in-progress'));
|
||||
this._spinnerElement.setAttribute('title', this._modalOptions.spinnerTitle ?? '');
|
||||
DOM.hide(this._spinnerElement);
|
||||
}
|
||||
this._leftFooter = DOM.append(this._modalFooterSection, DOM.$('.left-footer'));
|
||||
this._rightFooter = DOM.append(this._modalFooterSection, DOM.$('.right-footer'));
|
||||
}
|
||||
this._leftFooter = DOM.append(this._modalFooterSection, DOM.$('.left-footer'));
|
||||
this._rightFooter = DOM.append(this._modalFooterSection, DOM.$('.right-footer'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,6 +320,20 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
this.hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to close modal when a click occurs outside the modal.
|
||||
* This is exclusive to the Callout.
|
||||
* @param e The Callout modal click event
|
||||
*/
|
||||
private handleClickOffModal(e: MouseEvent): void {
|
||||
const target = e.target as HTMLElement;
|
||||
if (target.closest('.modal-content')) {
|
||||
return;
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridable to change behavior of enter key
|
||||
*/
|
||||
@@ -357,16 +416,56 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tasks to perform before dialog is shown
|
||||
* Includes: positioning of dialog
|
||||
*/
|
||||
protected positionDialog(): void {
|
||||
/**
|
||||
* In the case of 'below', dialog will be positioned beneath the trigger and arrow aligned with trigger.
|
||||
* In the case of 'left', dialog will be positioned left of the trigger and arrow aligned with trigger.
|
||||
*/
|
||||
if (this._modalOptions.dialogStyle === 'callout') {
|
||||
let dialogWidth;
|
||||
if (typeof this._modalOptions.width === 'number') {
|
||||
dialogWidth = this._modalOptions.width;
|
||||
}
|
||||
|
||||
if (this._modalOptions.dialogPosition === 'below') {
|
||||
if (this._modalOptions.dialogProperties) {
|
||||
this._modalDialog.style.left = `${this._modalOptions.dialogProperties.xPos - this._modalOptions.dialogProperties.width}px`;
|
||||
this._modalDialog.style.top = `${this._modalOptions.dialogProperties.yPos + (this._modalOptions.dialogProperties.height)}px`;
|
||||
} else {
|
||||
this._modalDialog.style.left = `${this._modalOptions.positionX}px`;
|
||||
this._modalDialog.style.top = `${this._modalOptions.positionY}px`;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._modalOptions.dialogPosition === 'left') {
|
||||
if (this._modalOptions.dialogProperties) {
|
||||
this._modalDialog.style.left = `${this._modalOptions.positionX - (dialogWidth + this._modalOptions.dialogProperties.width)}px`;
|
||||
this._modalDialog.style.top = `${this._modalOptions.positionY - this._modalOptions.dialogProperties.height * 2}px`;
|
||||
} else {
|
||||
this._modalDialog.style.left = `${this._modalOptions.positionX - (dialogWidth)}px`;
|
||||
this._modalDialog.style.top = `${this._modalOptions.positionY}px`;
|
||||
}
|
||||
}
|
||||
this._modalDialog.style.width = `${dialogWidth}px`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the modal and attaches key listeners
|
||||
*/
|
||||
protected show() {
|
||||
this.positionDialog();
|
||||
this._focusedElementBeforeOpen = <HTMLElement>document.activeElement;
|
||||
this._modalShowingContext.get()!.push(this._staticKey);
|
||||
DOM.append(this.layoutService.container, this._bodyContainer!);
|
||||
this.setInitialFocusedElement();
|
||||
|
||||
this.disposableStore.add(DOM.addDisposableListener(document, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => {
|
||||
this.disposableStore.add(DOM.addDisposableListener(document, DOM.EventType.KEY_UP, (e: KeyboardEvent) => {
|
||||
let context = this._modalShowingContext.get()!;
|
||||
if (context[context.length - 1] === this._staticKey) {
|
||||
let event = new StandardKeyboardEvent(e);
|
||||
@@ -423,6 +522,7 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
* Adds a button to the footer of the modal
|
||||
* @param label Label to show on the button
|
||||
* @param onSelect The callback to call when the button is selected
|
||||
* @param isSecondary Set the css class if true
|
||||
*/
|
||||
protected addFooterButton(label: string, onSelect: () => void, orientation: 'left' | 'right' = 'right', isSecondary: boolean = false): Button {
|
||||
let footerButton = DOM.$('.footer-button');
|
||||
@@ -434,6 +534,7 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
} else {
|
||||
DOM.append(this._rightFooter!, footerButton);
|
||||
}
|
||||
|
||||
this._footerButtons.push(button);
|
||||
return button;
|
||||
}
|
||||
@@ -541,8 +642,8 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
/**
|
||||
* Return background color of header and footer
|
||||
*/
|
||||
protected get headerAndFooterBackground(): string | null {
|
||||
return this._dialogHeaderAndFooterBackground ? this._dialogHeaderAndFooterBackground.toString() : null;
|
||||
protected get headerAndFooterBackground(): string | undefined {
|
||||
return this._dialogHeaderAndFooterBackground ? this._dialogHeaderAndFooterBackground.toString() : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -575,10 +676,15 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
* Called by the theme registry on theme change to style the component
|
||||
*/
|
||||
public style(styles: IModalDialogStyles): void {
|
||||
this._dialogForeground = styles.dialogForeground;
|
||||
this._dialogBorder = styles.dialogBorder;
|
||||
this._dialogHeaderAndFooterBackground = styles.dialogHeaderAndFooterBackground;
|
||||
this._dialogBodyBackground = styles.dialogBodyBackground;
|
||||
this._dialogForeground = styles.dialogForeground ? styles.dialogForeground : this._themeService.getColorTheme().getColor(editorWidgetForeground);
|
||||
this._dialogBorder = styles.dialogBorder ? styles.dialogBorder : this._themeService.getColorTheme().getColor(notebookToolbarLines);
|
||||
if (this._modalOptions.dialogStyle === 'callout') {
|
||||
this._dialogHeaderAndFooterBackground = styles.dialogBodyBackground ? styles.dialogBodyBackground : this._themeService.getColorTheme().getColor(SIDE_BAR_BACKGROUND);
|
||||
} else {
|
||||
this._dialogHeaderAndFooterBackground = styles.dialogHeaderAndFooterBackground ? styles.dialogHeaderAndFooterBackground : this._themeService.getColorTheme().getColor(SIDE_BAR_BACKGROUND);
|
||||
}
|
||||
this._dialogBodyBackground = styles.dialogBodyBackground ? styles.dialogBodyBackground : this._themeService.getColorTheme().getColor(editorBackground);
|
||||
this._footerBorderTopColor = styles.footerBorderTopColor ? styles.footerBorderTopColor : this._themeService.getColorTheme().getColor(notebookToolbarLines);
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
@@ -587,8 +693,10 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
const border = this._dialogBorder ? this._dialogBorder.toString() : '';
|
||||
const headerAndFooterBackground = this._dialogHeaderAndFooterBackground ? this._dialogHeaderAndFooterBackground.toString() : '';
|
||||
const bodyBackground = this._dialogBodyBackground ? this._dialogBodyBackground.toString() : '';
|
||||
const footerBorderTopWidth = border ? '1px' : '';
|
||||
const footerBorderTopStyle = border ? 'solid' : '';
|
||||
const calloutStyle: CSSStyleDeclaration = this._modalDialog.style;
|
||||
const footerTopBorderColor = this._footerBorderTopColor ? this._footerBorderTopColor.toString() : '';
|
||||
|
||||
const foregroundRgb: Color = Color.Format.CSS.parseHex(foreground);
|
||||
|
||||
if (this._closeButtonInHeader) {
|
||||
this._closeButtonInHeader.style.color = foreground;
|
||||
@@ -598,12 +706,25 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
this._modalDialog.style.borderWidth = border ? '1px' : '';
|
||||
this._modalDialog.style.borderStyle = border ? 'solid' : '';
|
||||
this._modalDialog.style.borderColor = border;
|
||||
|
||||
calloutStyle.setProperty('--border', `${border}`);
|
||||
calloutStyle.setProperty('--bodybackground', `${bodyBackground}`);
|
||||
if (foregroundRgb) {
|
||||
calloutStyle.setProperty('--foreground', `
|
||||
${foregroundRgb.rgba.r},
|
||||
${foregroundRgb.rgba.g},
|
||||
${foregroundRgb.rgba.b},
|
||||
0.08
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._modalHeaderSection) {
|
||||
this._modalHeaderSection.style.backgroundColor = headerAndFooterBackground;
|
||||
this._modalHeaderSection.style.borderBottomWidth = border ? '1px' : '';
|
||||
this._modalHeaderSection.style.borderBottomStyle = border ? 'solid' : '';
|
||||
if (!(this._modalOptions.dialogStyle === 'callout')) {
|
||||
this._modalHeaderSection.style.borderBottomWidth = border ? '1px' : '';
|
||||
this._modalHeaderSection.style.borderBottomStyle = border ? 'solid' : '';
|
||||
}
|
||||
this._modalHeaderSection.style.borderBottomColor = border;
|
||||
}
|
||||
|
||||
@@ -620,9 +741,9 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
|
||||
if (this._modalFooterSection) {
|
||||
this._modalFooterSection.style.backgroundColor = headerAndFooterBackground;
|
||||
this._modalFooterSection.style.borderTopWidth = footerBorderTopWidth;
|
||||
this._modalFooterSection.style.borderTopStyle = footerBorderTopStyle;
|
||||
this._modalFooterSection.style.borderTopColor = border;
|
||||
this._modalFooterSection.style.borderTopWidth = border ? '1px' : '';
|
||||
this._modalFooterSection.style.borderTopStyle = border ? 'solid' : '';
|
||||
this._modalFooterSection.style.borderTopColor = footerTopBorderColor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,12 +58,12 @@ export class CellToolbarComponent {
|
||||
this._actionBar = new Taskbar(taskbar);
|
||||
this._actionBar.context = context;
|
||||
|
||||
let addCellsButton = this.instantiationService.createInstance(AddCellAction, 'notebook.AddCodeCell', localize('codeCellsPreview', "Add cell"), 'notebook-button masked-pseudo code');
|
||||
let addCellsButton = this.instantiationService.createInstance(AddCellAction, 'notebook.AddCodeCell', localize('codeCellsPreview', "Add cell"), 'masked-pseudo code');
|
||||
|
||||
let addCodeCellButton = this.instantiationService.createInstance(AddCellAction, 'notebook.AddCodeCell', localize('codePreview', "Code cell"), 'notebook-button masked-pseudo code');
|
||||
let addCodeCellButton = this.instantiationService.createInstance(AddCellAction, 'notebook.AddCodeCell', localize('codePreview', "Code cell"), 'masked-pseudo code');
|
||||
addCodeCellButton.cellType = CellTypes.Code;
|
||||
|
||||
let addTextCellButton = this.instantiationService.createInstance(AddCellAction, 'notebook.AddTextCell', localize('textPreview', "Text cell"), 'notebook-button masked-pseudo markdown');
|
||||
let addTextCellButton = this.instantiationService.createInstance(AddCellAction, 'notebook.AddTextCell', localize('textPreview', "Text cell"), 'masked-pseudo markdown');
|
||||
addTextCellButton.cellType = CellTypes.Markdown;
|
||||
|
||||
let moveCellDownButton = this.instantiationService.createInstance(MoveCellAction, 'notebook.MoveCellDown', 'masked-icon move-down', this.buttonMoveDown);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div style="width: 100%; height: 100%; display: flex; flex-flow: column">
|
||||
<div style="width: 100%; height: 100%; display: flex; flex-flow: column" (keyup)="onKey($event)">
|
||||
<div style="flex: 0 0 auto;">
|
||||
<code-component [cellModel]="cellModel" [model]="model" [activeCellId]="activeCellId"></code-component>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { nb } from 'azdata';
|
||||
import { OnInit, Component, Input, Inject, forwardRef, ChangeDetectorRef, SimpleChange, OnChanges, HostListener, ViewChildren, QueryList } from '@angular/core';
|
||||
import { OnInit, Component, Input, Inject, forwardRef, ChangeDetectorRef, SimpleChange, OnChanges, ViewChildren, QueryList } from '@angular/core';
|
||||
import { CellView } from 'sql/workbench/contrib/notebook/browser/cellViews/interfaces';
|
||||
import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel';
|
||||
@@ -12,6 +12,8 @@ import { Deferred } from 'sql/base/common/promise';
|
||||
import { ICellEditorProvider } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { CodeComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/code.component';
|
||||
import { OutputComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/output.component';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
|
||||
|
||||
export const CODE_SELECTOR: string = 'code-cell-component';
|
||||
@@ -32,12 +34,6 @@ export class CodeCellComponent extends CellView implements OnInit, OnChanges {
|
||||
this._activeCellId = value;
|
||||
}
|
||||
|
||||
@HostListener('document:keydown.escape', ['$event'])
|
||||
handleKeyboardEvent() {
|
||||
this.cellModel.active = false;
|
||||
this._model.updateActiveCell(undefined);
|
||||
}
|
||||
|
||||
private _model: NotebookModel;
|
||||
private _activeCellId: string;
|
||||
|
||||
@@ -133,4 +129,12 @@ export class CodeCellComponent extends CellView implements OnInit, OnChanges {
|
||||
public cellGuid(): string {
|
||||
return this.cellModel.cellGuid;
|
||||
}
|
||||
|
||||
public onKey(e: KeyboardEvent) {
|
||||
let event = new StandardKeyboardEvent(e);
|
||||
if (event.equals(KeyCode.Escape)) {
|
||||
this.cellModel.active = false;
|
||||
this._model.updateActiveCell(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,18 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import 'vs/css!./markdownToolbar';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { Button, IButtonStyles } from 'sql/base/browser/ui/button/button';
|
||||
import { Component, Input, Inject, ViewChild, ElementRef } from '@angular/core';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { ITaskbarContent, Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { TransformMarkdownAction, MarkdownButtonType, ToggleViewAction } from 'sql/workbench/contrib/notebook/browser/markdownToolbarActions';
|
||||
import { TransformMarkdownAction, MarkdownTextTransformer, MarkdownButtonType, ToggleViewAction } from 'sql/workbench/contrib/notebook/browser/markdownToolbarActions';
|
||||
import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { DropdownMenuActionViewItem } from 'sql/base/browser/ui/buttonMenu/buttonMenu';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { AngularDisposable } from 'sql/base/browser/lifecycle';
|
||||
|
||||
export const MARKDOWN_TOOLBAR_SELECTOR: string = 'markdown-toolbar-component';
|
||||
|
||||
@@ -19,9 +23,11 @@ export const MARKDOWN_TOOLBAR_SELECTOR: string = 'markdown-toolbar-component';
|
||||
selector: MARKDOWN_TOOLBAR_SELECTOR,
|
||||
templateUrl: decodeURI(require.toUrl('./markdownToolbar.component.html'))
|
||||
})
|
||||
export class MarkdownToolbarComponent {
|
||||
export class MarkdownToolbarComponent extends AngularDisposable {
|
||||
@ViewChild('mdtoolbar', { read: ElementRef }) private mdtoolbar: ElementRef;
|
||||
|
||||
public previewFeaturesEnabled: boolean = false;
|
||||
|
||||
public buttonBold = localize('buttonBold', "Bold");
|
||||
public buttonItalic = localize('buttonItalic', "Italic");
|
||||
public buttonUnderline = localize('buttonUnderline', "Underline");
|
||||
@@ -44,6 +50,7 @@ export class MarkdownToolbarComponent {
|
||||
|
||||
private _taskbarContent: Array<ITaskbarContent>;
|
||||
private _wysiwygTaskbarContent: Array<ITaskbarContent>;
|
||||
private _previewModeTaskbarContent: Array<ITaskbarContent>;
|
||||
|
||||
@Input() public cellModel: ICellModel;
|
||||
private _actionBar: Taskbar;
|
||||
@@ -52,25 +59,65 @@ export class MarkdownToolbarComponent {
|
||||
_toggleMarkdownViewAction: ToggleViewAction;
|
||||
|
||||
constructor(
|
||||
@Inject(INotebookService) private _notebookService: INotebookService,
|
||||
@Inject(IInstantiationService) private _instantiationService: IInstantiationService,
|
||||
@Inject(IContextMenuService) private contextMenuService: IContextMenuService
|
||||
) { }
|
||||
@Inject(IContextMenuService) private _contextMenuService: IContextMenuService,
|
||||
@Inject(IConfigurationService) private _configurationService: IConfigurationService
|
||||
) {
|
||||
super();
|
||||
this._register(this._configurationService.onDidChangeConfiguration(e => {
|
||||
this.previewFeaturesEnabled = this._configurationService.getValue('workbench.enablePreviewFeatures');
|
||||
}));
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.initActionBar();
|
||||
}
|
||||
|
||||
private initActionBar() {
|
||||
this.previewFeaturesEnabled = this._configurationService.getValue('workbench.enablePreviewFeatures');
|
||||
|
||||
let linkButton: TransformMarkdownAction;
|
||||
let imageButton: TransformMarkdownAction;
|
||||
let linkButtonContainer: HTMLElement;
|
||||
let imageButtonContainer: HTMLElement;
|
||||
|
||||
if (this.previewFeaturesEnabled) {
|
||||
linkButtonContainer = DOM.$('li.action-item');
|
||||
linkButtonContainer.setAttribute('role', 'presentation');
|
||||
let linkButton = new Button(linkButtonContainer);
|
||||
linkButton.element.setAttribute('class', 'action-label codicon insert-link masked-icon');
|
||||
let buttonStyle: IButtonStyles = {
|
||||
buttonBackground: null
|
||||
};
|
||||
linkButton.style(buttonStyle);
|
||||
|
||||
this._register(DOM.addDisposableListener(linkButtonContainer, DOM.EventType.CLICK, e => {
|
||||
this.onInsertButtonClick(e, MarkdownButtonType.LINK_PREVIEW);
|
||||
}));
|
||||
|
||||
imageButtonContainer = DOM.$('li.action-item');
|
||||
imageButtonContainer.setAttribute('role', 'presentation');
|
||||
let imageButton = new Button(imageButtonContainer);
|
||||
imageButton.element.setAttribute('class', 'action-label codicon insert-image masked-icon');
|
||||
|
||||
imageButton.style(buttonStyle);
|
||||
|
||||
this._register(DOM.addDisposableListener(imageButtonContainer, DOM.EventType.CLICK, e => {
|
||||
this.onInsertButtonClick(e, MarkdownButtonType.IMAGE_PREVIEW);
|
||||
}));
|
||||
} else {
|
||||
linkButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.linkText', '', 'insert-link masked-icon', this.buttonLink, this.cellModel, MarkdownButtonType.LINK);
|
||||
imageButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.imageText', '', 'insert-image masked-icon', this.buttonImage, this.cellModel, MarkdownButtonType.IMAGE);
|
||||
}
|
||||
|
||||
let boldButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.boldText', '', 'bold masked-icon', this.buttonBold, this.cellModel, MarkdownButtonType.BOLD);
|
||||
let italicButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.italicText', '', 'italic masked-icon', this.buttonItalic, this.cellModel, MarkdownButtonType.ITALIC);
|
||||
let underlineButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.underlineText', '', 'underline masked-icon', this.buttonUnderline, this.cellModel, MarkdownButtonType.UNDERLINE);
|
||||
let highlightButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.highlightText', '', 'highlight masked-icon', this.buttonHighlight, this.cellModel, MarkdownButtonType.HIGHLIGHT);
|
||||
let codeButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.codeText', '', 'code masked-icon', this.buttonCode, this.cellModel, MarkdownButtonType.CODE);
|
||||
let linkButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.linkText', '', 'insert-link masked-icon', this.buttonLink, this.cellModel, MarkdownButtonType.LINK);
|
||||
let listButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.listText', '', 'list masked-icon', this.buttonList, this.cellModel, MarkdownButtonType.UNORDERED_LIST);
|
||||
let orderedListButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.orderedText', '', 'ordered-list masked-icon', this.buttonOrderedList, this.cellModel, MarkdownButtonType.ORDERED_LIST);
|
||||
let imageButton = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.imageText', '', 'insert-image masked-icon', this.buttonImage, this.cellModel, MarkdownButtonType.IMAGE);
|
||||
|
||||
let headingDropdown = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.heading', '', 'heading', this.dropdownHeading, this.cellModel, null);
|
||||
let heading1 = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.heading1', this.optionHeading1, 'heading 1', this.optionHeading1, this.cellModel, MarkdownButtonType.HEADING1);
|
||||
let heading2 = this._instantiationService.createInstance(TransformMarkdownAction, 'notebook.heading2', this.optionHeading2, 'heading 2', this.optionHeading2, this.cellModel, MarkdownButtonType.HEADING2);
|
||||
@@ -90,11 +137,11 @@ export class MarkdownToolbarComponent {
|
||||
let dropdownMenuActionViewItem = new DropdownMenuActionViewItem(
|
||||
headingDropdown,
|
||||
[heading1, heading2, heading3, paragraph],
|
||||
this.contextMenuService,
|
||||
this._contextMenuService,
|
||||
undefined,
|
||||
this._actionBar.actionRunner,
|
||||
undefined,
|
||||
'notebook-button masked-pseudo-after dropdown-arrow',
|
||||
'masked-pseudo-after dropdown-arrow',
|
||||
this.optionParagraph,
|
||||
undefined
|
||||
);
|
||||
@@ -129,20 +176,51 @@ export class MarkdownToolbarComponent {
|
||||
{ action: this._toggleSplitViewAction },
|
||||
{ action: this._toggleMarkdownViewAction }
|
||||
];
|
||||
|
||||
this._previewModeTaskbarContent = [
|
||||
{ action: boldButton },
|
||||
{ action: italicButton },
|
||||
{ action: underlineButton },
|
||||
{ action: highlightButton },
|
||||
{ action: codeButton },
|
||||
{ element: linkButtonContainer },
|
||||
{ action: listButton },
|
||||
{ action: orderedListButton },
|
||||
{ element: imageButtonContainer },
|
||||
{ element: buttonDropdownContainer },
|
||||
{ action: this._toggleTextViewAction },
|
||||
{ action: this._toggleSplitViewAction },
|
||||
{ action: this._toggleMarkdownViewAction }
|
||||
];
|
||||
|
||||
// Hide link and image buttons in WYSIWYG mode
|
||||
if (this.cellModel.showPreview && !this.cellModel.showMarkdown) {
|
||||
this._actionBar.setContent(this._wysiwygTaskbarContent);
|
||||
} else {
|
||||
this._actionBar.setContent(this._taskbarContent);
|
||||
if (this.previewFeaturesEnabled) {
|
||||
this._actionBar.setContent(this._previewModeTaskbarContent);
|
||||
} else {
|
||||
this._actionBar.setContent(this._taskbarContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public onInsertButtonClick(event: MouseEvent, type: MarkdownButtonType): void {
|
||||
let go = new MarkdownTextTransformer(this._notebookService, this.cellModel, this._instantiationService);
|
||||
let trigger = event.target as HTMLElement;
|
||||
go.transformText(type, trigger);
|
||||
}
|
||||
|
||||
public hideLinkAndImageButtons() {
|
||||
this._actionBar.setContent(this._wysiwygTaskbarContent);
|
||||
}
|
||||
|
||||
public showLinkAndImageButtons() {
|
||||
this._actionBar.setContent(this._taskbarContent);
|
||||
if (this.previewFeaturesEnabled) {
|
||||
this._actionBar.setContent(this._previewModeTaskbarContent);
|
||||
} else {
|
||||
this._actionBar.setContent(this._taskbarContent);
|
||||
}
|
||||
}
|
||||
|
||||
public removeActiveClassFromModeActions() {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div style="width: 100%; height: 100%; display: flex; flex-flow: column" (mouseover)="hover=true" (mouseleave)="hover=false">
|
||||
<div style="width: 100%; height: 100%; display: flex; flex-flow: column" (keyup)="onKey($event)" (mouseover)="hover=true" (mouseleave)="hover=false">
|
||||
<markdown-toolbar-component #markdownToolbar *ngIf="isEditMode" [cellModel]="cellModel"></markdown-toolbar-component>
|
||||
<div class="notebook-text" [class.show-markdown]="markdownMode" [class.show-preview]="previewMode">
|
||||
<code-component *ngIf="markdownMode" [cellModel]="cellModel" (onContentChanged)="handleContentChanged()" [model]="model" [activeCellId]="activeCellId">
|
||||
|
||||
@@ -29,6 +29,8 @@ import { CodeComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/
|
||||
import { NotebookRange, ICellEditorProvider, INotebookService } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { HTMLMarkdownConverter } from 'sql/workbench/contrib/notebook/browser/htmlMarkdownConverter';
|
||||
import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/notebookInput';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
|
||||
export const TEXT_SELECTOR: string = 'text-cell-component';
|
||||
const USER_SELECT_CLASS = 'actionselect';
|
||||
@@ -52,15 +54,6 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
|
||||
this._activeCellId = value;
|
||||
}
|
||||
|
||||
@HostListener('document:keydown.escape', ['$event'])
|
||||
handleKeyboardEvent() {
|
||||
if (this.isEditMode) {
|
||||
this.toggleEditMode(false);
|
||||
}
|
||||
this.cellModel.active = false;
|
||||
this._model.updateActiveCell(undefined);
|
||||
}
|
||||
|
||||
// Double click to edit text cell in notebook
|
||||
@HostListener('dblclick', ['$event']) onDblClick() {
|
||||
this.enableActiveCellEditOnDoubleClick();
|
||||
@@ -457,6 +450,17 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
|
||||
this.cellModel.active = true;
|
||||
this._model.updateActiveCell(this.cellModel);
|
||||
}
|
||||
|
||||
public onKey(e: KeyboardEvent) {
|
||||
let event = new StandardKeyboardEvent(e);
|
||||
if (event.equals(KeyCode.Escape)) {
|
||||
if (this.isEditMode) {
|
||||
this.toggleEditMode(false);
|
||||
}
|
||||
this.cellModel.active = false;
|
||||
this._model.updateActiveCell(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function preventDefaultAndExecCommand(e: KeyboardEvent, commandId: string) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { localize } from 'vs/nls';
|
||||
import { INotebookEditor, INotebookService } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
@@ -15,7 +16,9 @@ import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { MarkdownToolbarComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.component';
|
||||
|
||||
import { CalloutDialog, CalloutType } from 'sql/workbench/browser/modal/calloutDialog';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { DialogWidth } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
export class TransformMarkdownAction extends Action {
|
||||
|
||||
@@ -26,25 +29,20 @@ export class TransformMarkdownAction extends Action {
|
||||
tooltip: string,
|
||||
private _cellModel: ICellModel,
|
||||
private _type: MarkdownButtonType,
|
||||
@INotebookService private _notebookService: INotebookService
|
||||
@INotebookService private _notebookService: INotebookService,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService
|
||||
) {
|
||||
super(id, label, cssClass);
|
||||
this._tooltip = tooltip;
|
||||
}
|
||||
public run(context: any): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
try {
|
||||
if (!context?.cellModel?.showMarkdown && context?.cellModel?.showPreview) {
|
||||
this.transformDocumentCommand();
|
||||
} else {
|
||||
let markdownTextTransformer = new MarkdownTextTransformer(this._notebookService, this._cellModel);
|
||||
markdownTextTransformer.transformText(this._type);
|
||||
}
|
||||
resolve(true);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
public async run(context: any): Promise<boolean> {
|
||||
if (!context?.cellModel?.showMarkdown && context?.cellModel?.showPreview) {
|
||||
this.transformDocumentCommand();
|
||||
} else {
|
||||
let markdownTextTransformer = new MarkdownTextTransformer(this._notebookService, this._cellModel, this._instantiationService);
|
||||
await markdownTextTransformer.transformText(this._type);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private transformDocumentCommand() {
|
||||
@@ -105,12 +103,14 @@ export class TransformMarkdownAction extends Action {
|
||||
}
|
||||
break;
|
||||
case MarkdownButtonType.IMAGE:
|
||||
case MarkdownButtonType.IMAGE_PREVIEW:
|
||||
// TODO
|
||||
break;
|
||||
case MarkdownButtonType.ITALIC:
|
||||
document.execCommand('italic');
|
||||
break;
|
||||
case MarkdownButtonType.LINK:
|
||||
case MarkdownButtonType.LINK_PREVIEW:
|
||||
document.execCommand('createLink', false, window.getSelection()?.focusNode?.textContent);
|
||||
break;
|
||||
case MarkdownButtonType.ORDERED_LIST:
|
||||
@@ -130,17 +130,21 @@ export class TransformMarkdownAction extends Action {
|
||||
}
|
||||
|
||||
export class MarkdownTextTransformer {
|
||||
private _callout: CalloutDialog;
|
||||
private readonly insertLinkHeading = localize('callout.insertLinkHeading', "Insert link");
|
||||
private readonly insertImageHeading = localize('callout.insertImageHeading', "Insert image");
|
||||
|
||||
constructor(
|
||||
private _notebookService: INotebookService,
|
||||
private _cellModel: ICellModel,
|
||||
private _instantiationService: IInstantiationService,
|
||||
private _notebookEditor?: INotebookEditor) { }
|
||||
|
||||
public get notebookEditor(): INotebookEditor {
|
||||
return this._notebookEditor;
|
||||
}
|
||||
|
||||
public transformText(type: MarkdownButtonType): void {
|
||||
public async transformText(type: MarkdownButtonType, triggerElement?: HTMLElement): Promise<void> {
|
||||
let editorControl = this.getEditorControl();
|
||||
if (editorControl) {
|
||||
let selections = editorControl.getSelections();
|
||||
@@ -154,8 +158,15 @@ export class MarkdownTextTransformer {
|
||||
endLineNumber: selection.startLineNumber
|
||||
};
|
||||
|
||||
let beginInsertedText = getStartTextToInsert(type);
|
||||
let endInsertedText = getEndTextToInsert(type);
|
||||
let beginInsertedText: string;
|
||||
let endInsertedText: string;
|
||||
|
||||
if (type === MarkdownButtonType.IMAGE_PREVIEW || type === MarkdownButtonType.LINK_PREVIEW) {
|
||||
beginInsertedText = await this.createCallout(type, triggerElement);
|
||||
} else {
|
||||
beginInsertedText = getStartTextToInsert(type);
|
||||
endInsertedText = getEndTextToInsert(type);
|
||||
}
|
||||
|
||||
let endRange: IRange = {
|
||||
startColumn: selection.endColumn,
|
||||
@@ -187,6 +198,37 @@ export class MarkdownTextTransformer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate modal for use as callout when inserting Link or Image into markdown.
|
||||
* @param calloutStyle Style of callout passed in to determine which callout is rendered.
|
||||
* Returns markup created after user enters values and submits the callout.
|
||||
*/
|
||||
private async createCallout(type: MarkdownButtonType, triggerElement: HTMLElement): Promise<string> {
|
||||
const triggerPosX = triggerElement.getBoundingClientRect().left;
|
||||
const triggerPosY = triggerElement.getBoundingClientRect().top;
|
||||
const triggerHeight = triggerElement.offsetHeight;
|
||||
const triggerWidth = triggerElement.offsetWidth;
|
||||
/**
|
||||
* Width value here reflects designs for Notebook callouts.
|
||||
*/
|
||||
const width: DialogWidth = 452;
|
||||
|
||||
const calloutType: CalloutType = type === MarkdownButtonType.IMAGE_PREVIEW ? 'IMAGE' : 'LINK';
|
||||
|
||||
let title = type === MarkdownButtonType.IMAGE_PREVIEW ? this.insertImageHeading : this.insertLinkHeading;
|
||||
|
||||
if (!this._callout) {
|
||||
const dialogProperties = { xPos: triggerPosX, yPos: triggerPosY, width: triggerWidth, height: triggerHeight };
|
||||
this._callout = this._instantiationService.createInstance(CalloutDialog, calloutType, title, width, dialogProperties);
|
||||
this._callout.render();
|
||||
}
|
||||
let calloutOptions = await this._callout.open();
|
||||
calloutOptions.insertTitle = title;
|
||||
calloutOptions.calloutType = calloutType;
|
||||
|
||||
return calloutOptions.insertMarkup;
|
||||
}
|
||||
|
||||
private getEditorControl(): CodeEditorWidget | undefined {
|
||||
if (!this._notebookEditor) {
|
||||
this._notebookEditor = this._notebookService.findNotebookEditor(this._cellModel?.notebookModel?.notebookUri);
|
||||
@@ -398,9 +440,11 @@ export enum MarkdownButtonType {
|
||||
CODE,
|
||||
HIGHLIGHT,
|
||||
LINK,
|
||||
LINK_PREVIEW,
|
||||
UNORDERED_LIST,
|
||||
ORDERED_LIST,
|
||||
IMAGE,
|
||||
IMAGE_PREVIEW,
|
||||
HEADING1,
|
||||
HEADING2,
|
||||
HEADING3,
|
||||
@@ -469,12 +513,14 @@ function getStartTextToInsert(type: MarkdownButtonType): string {
|
||||
case MarkdownButtonType.CODE:
|
||||
return '```\n';
|
||||
case MarkdownButtonType.LINK:
|
||||
case MarkdownButtonType.LINK_PREVIEW:
|
||||
return '[';
|
||||
case MarkdownButtonType.UNORDERED_LIST:
|
||||
return '- ';
|
||||
case MarkdownButtonType.ORDERED_LIST:
|
||||
return '1. ';
|
||||
case MarkdownButtonType.IMAGE:
|
||||
case MarkdownButtonType.IMAGE_PREVIEW:
|
||||
return '![';
|
||||
case MarkdownButtonType.HIGHLIGHT:
|
||||
return '<mark>';
|
||||
@@ -504,7 +550,9 @@ function getEndTextToInsert(type: MarkdownButtonType): string {
|
||||
case MarkdownButtonType.CODE:
|
||||
return '\n```';
|
||||
case MarkdownButtonType.LINK:
|
||||
case MarkdownButtonType.LINK_PREVIEW:
|
||||
case MarkdownButtonType.IMAGE:
|
||||
case MarkdownButtonType.IMAGE_PREVIEW:
|
||||
return ']()';
|
||||
case MarkdownButtonType.HIGHLIGHT:
|
||||
return '</mark>';
|
||||
@@ -552,8 +600,10 @@ function getColumnOffsetForSelection(type: MarkdownButtonType, nothingSelected:
|
||||
}
|
||||
switch (type) {
|
||||
case MarkdownButtonType.LINK:
|
||||
case MarkdownButtonType.LINK_PREVIEW:
|
||||
return 2;
|
||||
case MarkdownButtonType.IMAGE:
|
||||
case MarkdownButtonType.IMAGE_PREVIEW:
|
||||
return 2;
|
||||
// -1 is considered as having no explicit offset, so do not do anything with selection
|
||||
default: return -1;
|
||||
|
||||
@@ -370,15 +370,15 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
let spacerElement = document.createElement('li');
|
||||
spacerElement.style.marginLeft = 'auto';
|
||||
|
||||
let addCellsButton = this.instantiationService.createInstance(AddCellAction, 'notebook.AddCodeCell', localize('codeCellsPreview', "Add cell"), 'notebook-button masked-pseudo code');
|
||||
let addCellsButton = this.instantiationService.createInstance(AddCellAction, 'notebook.AddCodeCell', localize('codeCellsPreview', "Add cell"), 'masked-pseudo code');
|
||||
|
||||
let addCodeCellButton = this.instantiationService.createInstance(AddCellAction, 'notebook.AddCodeCell', localize('codePreview', "Code cell"), 'notebook-button masked-pseudo code');
|
||||
let addCodeCellButton = this.instantiationService.createInstance(AddCellAction, 'notebook.AddCodeCell', localize('codePreview', "Code cell"), 'masked-pseudo code');
|
||||
addCodeCellButton.cellType = CellTypes.Code;
|
||||
|
||||
let addTextCellButton = this.instantiationService.createInstance(AddCellAction, 'notebook.AddTextCell', localize('textPreview', "Text cell"), 'notebook-button masked-pseudo markdown');
|
||||
let addTextCellButton = this.instantiationService.createInstance(AddCellAction, 'notebook.AddTextCell', localize('textPreview', "Text cell"), 'masked-pseudo markdown');
|
||||
addTextCellButton.cellType = CellTypes.Markdown;
|
||||
|
||||
this._runAllCellsAction = this.instantiationService.createInstance(RunAllCellsAction, 'notebook.runAllCells', localize('runAllPreview', "Run all"), 'notebook-button masked-pseudo start-outline');
|
||||
this._runAllCellsAction = this.instantiationService.createInstance(RunAllCellsAction, 'notebook.runAllCells', localize('runAllPreview', "Run all"), 'masked-pseudo start-outline');
|
||||
|
||||
let collapseCellsAction = this.instantiationService.createInstance(CollapseCellsAction, 'notebook.collapseCells', true);
|
||||
|
||||
@@ -401,7 +401,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
undefined,
|
||||
this._actionBar.actionRunner,
|
||||
undefined,
|
||||
'codicon notebook-button masked-pseudo masked-pseudo-after add-new dropdown-arrow',
|
||||
'codicon masked-pseudo masked-pseudo-after add-new dropdown-arrow',
|
||||
localize('addCell', "Cell"),
|
||||
undefined
|
||||
);
|
||||
@@ -431,13 +431,13 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
attachToDropdown.render(attachToContainer);
|
||||
attachSelectBoxStyler(attachToDropdown, this.themeService);
|
||||
|
||||
let addCodeCellButton = this.instantiationService.createInstance(AddCellAction, 'notebook.AddCodeCell', localize('code', "Code"), 'notebook-button icon-add');
|
||||
let addCodeCellButton = this.instantiationService.createInstance(AddCellAction, 'notebook.AddCodeCell', localize('code', "Code"), 'icon-add');
|
||||
addCodeCellButton.cellType = CellTypes.Code;
|
||||
|
||||
let addTextCellButton = this.instantiationService.createInstance(AddCellAction, 'notebook.AddTextCell', localize('text', "Text"), 'notebook-button icon-add');
|
||||
let addTextCellButton = this.instantiationService.createInstance(AddCellAction, 'notebook.AddTextCell', localize('text', "Text"), 'icon-add');
|
||||
addTextCellButton.cellType = CellTypes.Markdown;
|
||||
|
||||
this._runAllCellsAction = this.instantiationService.createInstance(RunAllCellsAction, 'notebook.runAllCells', localize('runAll', "Run Cells"), 'notebook-button icon-run-cells');
|
||||
this._runAllCellsAction = this.instantiationService.createInstance(RunAllCellsAction, 'notebook.runAllCells', localize('runAll', "Run Cells"), 'icon-run-cells');
|
||||
|
||||
let clearResultsButton = this.instantiationService.createInstance(ClearAllOutputsAction, 'notebook.ClearAllOutputs', false);
|
||||
|
||||
|
||||
@@ -37,28 +37,29 @@
|
||||
min-height: 21px;
|
||||
}
|
||||
|
||||
.notebookEditor .editor-toolbar .actions-container .action-item .notebook-button.masked-pseudo {
|
||||
.notebookEditor .editor-toolbar .actions-container .action-item .codicon.masked-pseudo {
|
||||
padding-right: 18px;
|
||||
}
|
||||
.notebookEditor .editor-toolbar .actions-container .action-item .notebook-button {
|
||||
|
||||
.notebookEditor .editor-toolbar .actions-container .action-item .codicon {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
padding: 0 18px;
|
||||
padding: 0 0 0 18px;
|
||||
background-size: 13px;
|
||||
font-size: 13px;
|
||||
height: 21px;
|
||||
}
|
||||
.notebookEditor .editor-toolbar .actions-container .action-item .notebook-button.masked-icon {
|
||||
.notebookEditor .editor-toolbar .actions-container .action-item .codicon.masked-icon {
|
||||
padding: 0;
|
||||
width: 28px;
|
||||
}
|
||||
|
||||
.notebookEditor .in-preview .actions-container .action-item .notebook-button,
|
||||
.notebookEditor .in-preview .actions-container .action-item .notebook-button:before {
|
||||
.notebookEditor .in-preview .actions-container .action-item .codicon,
|
||||
.notebookEditor .in-preview .actions-container .action-item .codicon:before {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
padding-right: 0px;
|
||||
padding-right: 0;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
@@ -66,14 +67,14 @@
|
||||
.in-preview
|
||||
.actions-container
|
||||
.action-item
|
||||
.notebook-button.masked-pseudo {
|
||||
.codicon.masked-pseudo {
|
||||
padding-left: 30px;
|
||||
}
|
||||
.notebookEditor
|
||||
.in-preview
|
||||
.actions-container
|
||||
.action-item
|
||||
.notebook-button.masked-icon:before {
|
||||
.codicon.masked-icon:before {
|
||||
margin-right: 0;
|
||||
padding-left: 18px;
|
||||
width: 16px;
|
||||
@@ -85,11 +86,11 @@
|
||||
.in-preview
|
||||
.actions-container
|
||||
.action-item:last-child
|
||||
.notebook-button {
|
||||
.codicon {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.notebookEditor .in-preview .actions-container .action-item:last-child .notebook-button.fixed-width {
|
||||
.notebookEditor .in-preview .actions-container .action-item:last-child .codicon.fixed-width {
|
||||
background-size: contain;
|
||||
margin-left: 8px;
|
||||
padding: 0 0 0 18px;
|
||||
@@ -109,71 +110,71 @@
|
||||
}
|
||||
|
||||
/* non-preview */
|
||||
.notebookEditor :not(.in-preview) .notebook-button.icon-add {
|
||||
.notebookEditor :not(.in-preview) .codicon.icon-add {
|
||||
background-image: url("./media/light/add.svg");
|
||||
}
|
||||
|
||||
.vs-dark .notebookEditor :not(.in-preview) .notebook-button.icon-add,
|
||||
.hc-black .notebookEditor :not(.in-preview) .notebook-button.icon-add {
|
||||
.vs-dark .notebookEditor :not(.in-preview) .codicon.icon-add,
|
||||
.hc-black .notebookEditor :not(.in-preview) .codicon.icon-add {
|
||||
background-image: url("./media/dark/add_inverse.svg");
|
||||
}
|
||||
|
||||
.notebookEditor :not(.in-preview) .notebook-button.icon-run-cells {
|
||||
.notebookEditor :not(.in-preview) .codicon.icon-run-cells {
|
||||
background-image: url("./media/light/run_cells.svg");
|
||||
}
|
||||
|
||||
.vs-dark .notebookEditor :not(.in-preview) .notebook-button.icon-run-cells,
|
||||
.hc-black .notebookEditor :not(.in-preview) .notebook-button.icon-run-cells {
|
||||
.vs-dark .notebookEditor :not(.in-preview) .codicon.icon-run-cells,
|
||||
.hc-black .notebookEditor :not(.in-preview) .codicon.icon-run-cells {
|
||||
background-image: url("./media/dark/run_cells_inverse.svg");
|
||||
}
|
||||
|
||||
.notebookEditor :not(.in-preview) .notebook-button.icon-trusted {
|
||||
.notebookEditor :not(.in-preview) .codicon.icon-trusted {
|
||||
background-image: url("./media/light/trusted.svg");
|
||||
}
|
||||
|
||||
.vs-dark .notebookEditor :not(.in-preview) .notebook-button.icon-trusted,
|
||||
.hc-black .notebookEditor :not(.in-preview) .notebook-button.icon-trusted {
|
||||
.vs-dark .notebookEditor :not(.in-preview) .codicon.icon-trusted,
|
||||
.hc-black .notebookEditor :not(.in-preview) .codicon.icon-trusted {
|
||||
background-image: url("./media/dark/trusted_inverse.svg");
|
||||
}
|
||||
.notebookEditor :not(.in-preview) .notebook-button.icon-notTrusted {
|
||||
.notebookEditor :not(.in-preview) .codicon.icon-notTrusted {
|
||||
background-image: url("./media/light/nottrusted.svg");
|
||||
}
|
||||
|
||||
.vs-dark .notebookEditor :not(.in-preview) .notebook-button.icon-notTrusted,
|
||||
.hc-black .notebookEditor :not(.in-preview) .notebook-button.icon-notTrusted {
|
||||
.vs-dark .notebookEditor :not(.in-preview) .codicon.icon-notTrusted,
|
||||
.hc-black .notebookEditor :not(.in-preview) .codicon.icon-notTrusted {
|
||||
background-image: url("./media/dark/nottrusted_inverse.svg");
|
||||
}
|
||||
|
||||
.notebookEditor :not(.in-preview) .notebook-button.icon-show-cells {
|
||||
.notebookEditor :not(.in-preview) .codicon.icon-show-cells {
|
||||
background-image: url("./media/light/show_code.svg");
|
||||
}
|
||||
|
||||
.vs-dark .notebookEditor :not(.in-preview) .notebook-button.icon-show-cells,
|
||||
.hc-black .notebookEditor :not(.in-preview) .notebook-button.icon-show-cells {
|
||||
.vs-dark .notebookEditor :not(.in-preview) .codicon.icon-show-cells,
|
||||
.hc-black .notebookEditor :not(.in-preview) .codicon.icon-show-cells {
|
||||
background-image: url("./media/dark/show_code_inverse.svg");
|
||||
}
|
||||
|
||||
.notebookEditor :not(.in-preview) .notebook-button.icon-hide-cells {
|
||||
.notebookEditor :not(.in-preview) .codicon.icon-hide-cells {
|
||||
background-image: url("./media/light/hide_code.svg");
|
||||
}
|
||||
|
||||
.vs-dark .notebookEditor :not(.in-preview) .notebook-button.icon-hide-cells,
|
||||
.hc-black .notebookEditor :not(.in-preview) .notebook-button.icon-hide-cells {
|
||||
.vs-dark .notebookEditor :not(.in-preview) .codicon.icon-hide-cells,
|
||||
.hc-black .notebookEditor :not(.in-preview) .codicon.icon-hide-cells {
|
||||
background-image: url("./media/dark/hide_code_inverse.svg");
|
||||
}
|
||||
|
||||
.notebookEditor :not(.in-preview) .notebook-button.icon-clear-results {
|
||||
.notebookEditor :not(.in-preview) .codicon.icon-clear-results {
|
||||
background-image: url("./media/light/clear_results.svg");
|
||||
}
|
||||
|
||||
.vs-dark .notebookEditor :not(.in-preview) .notebook-button.icon-clear-results,
|
||||
.hc-black .notebookEditor :not(.in-preview) .notebook-button.icon-clear-results {
|
||||
.vs-dark .notebookEditor :not(.in-preview) .codicon.icon-clear-results,
|
||||
.hc-black .notebookEditor :not(.in-preview) .codicon.icon-clear-results {
|
||||
background-image: url("./media/dark/clear_results_inverse.svg");
|
||||
}
|
||||
|
||||
.notebookEditor .in-preview .notebook-button.masked-icon,
|
||||
.vs-dark .notebookEditor .in-preview .notebook-button.icon-clear-results,
|
||||
.hc-black .notebookEditor .in-preview .notebook-button.icon-clear-results {
|
||||
.notebookEditor .in-preview .codicon.masked-icon,
|
||||
.vs-dark .notebookEditor .in-preview .codicon.icon-clear-results,
|
||||
.hc-black .notebookEditor .in-preview .codicon.icon-clear-results {
|
||||
background-image: none;
|
||||
}
|
||||
/* non-preview */
|
||||
|
||||
@@ -101,7 +101,7 @@ export abstract class TooltipFromLabelAction extends Action {
|
||||
// Action to clear outputs of all code cells.
|
||||
export class ClearAllOutputsAction extends TooltipFromLabelAction {
|
||||
private static readonly label = localize('clearResults', "Clear Results");
|
||||
private static readonly baseClass = 'notebook-button';
|
||||
private static readonly baseClass = 'codicon';
|
||||
private static readonly iconClass = 'icon-clear-results';
|
||||
private static readonly maskedIconClass = 'masked-icon';
|
||||
|
||||
@@ -170,7 +170,7 @@ export class TrustedAction extends ToggleableAction {
|
||||
// Constants
|
||||
private static readonly trustedLabel = localize('trustLabel', "Trusted");
|
||||
private static readonly notTrustedLabel = localize('untrustLabel', "Not Trusted");
|
||||
private static readonly baseClass = 'notebook-button';
|
||||
private static readonly baseClass = 'codicon';
|
||||
private static readonly previewTrustedCssClass = 'icon-shield';
|
||||
private static readonly trustedCssClass = 'icon-trusted';
|
||||
private static readonly previewNotTrustedCssClass = 'icon-shield-x';
|
||||
@@ -232,7 +232,7 @@ export class RunAllCellsAction extends Action {
|
||||
export class CollapseCellsAction extends ToggleableAction {
|
||||
private static readonly collapseCells = localize('collapseAllCells', "Collapse Cells");
|
||||
private static readonly expandCells = localize('expandAllCells', "Expand Cells");
|
||||
private static readonly baseClass = 'notebook-button';
|
||||
private static readonly baseClass = 'codicon';
|
||||
private static readonly previewCollapseCssClass = 'icon-collapse-cells';
|
||||
private static readonly collapseCssClass = 'icon-hide-cells';
|
||||
private static readonly previewExpandCssClass = 'icon-expand-cells';
|
||||
|
||||
@@ -150,8 +150,8 @@ export function registerNotebookThemes(overrideEditorThemeSetting: boolean, conf
|
||||
//Notebook toolbar masked icons
|
||||
const notebookToolbarIconColor = theme.getColor(notebookToolbarIcon);
|
||||
if (notebookToolbarIconColor) {
|
||||
collector.addRule(`.notebookEditor .notebook-button.masked-icon:before { background-color: ${notebookToolbarIconColor};}`);
|
||||
collector.addRule(`.notebookEditor .notebook-button.masked-pseudo:before { background-color: ${notebookToolbarIconColor};}`);
|
||||
collector.addRule(`.masked-icon:before { background-color: ${notebookToolbarIconColor};}`);
|
||||
collector.addRule(`.masked-pseudo:before { background-color: ${notebookToolbarIconColor};}`);
|
||||
}
|
||||
const notebookToolbarLinesColor = theme.getColor(notebookToolbarLines);
|
||||
if (notebookToolbarLinesColor) {
|
||||
@@ -164,7 +164,7 @@ export function registerNotebookThemes(overrideEditorThemeSetting: boolean, conf
|
||||
}
|
||||
const buttonMenuArrowColor = theme.getColor(buttonMenuArrow);
|
||||
if (buttonMenuArrowColor) {
|
||||
collector.addRule(`.notebookEditor .notebook-button.masked-pseudo-after:after { background-color: ${buttonMenuArrowColor};}`);
|
||||
collector.addRule(`.notebookEditor .masked-pseudo-after:after { background-color: ${buttonMenuArrowColor};}`);
|
||||
}
|
||||
|
||||
// Active cell border, cell toolbar border, cell toolbar icons, view toggle active button bottom border
|
||||
|
||||
@@ -73,7 +73,7 @@ suite('MarkdownTextTransformer', () => {
|
||||
|
||||
cellModel = new CellModel(undefined, undefined, mockNotebookService.object);
|
||||
notebookEditor = new NotebookEditorStub({ cellGuid: cellModel.cellGuid, instantiationService: instantiationService });
|
||||
markdownTextTransformer = new MarkdownTextTransformer(mockNotebookService.object, cellModel, notebookEditor);
|
||||
markdownTextTransformer = new MarkdownTextTransformer(mockNotebookService.object, cellModel, instantiationService, notebookEditor);
|
||||
mockNotebookService.setup(s => s.findNotebookEditor(TypeMoq.It.isAny())).returns(() => notebookEditor);
|
||||
|
||||
let editor = notebookEditor.cellEditors[0].getEditor();
|
||||
@@ -91,87 +91,87 @@ suite('MarkdownTextTransformer', () => {
|
||||
assert(!isUndefinedOrNull(widget.getModel()), 'Text model is undefined');
|
||||
});
|
||||
|
||||
test('Transform text with no previous selection', () => {
|
||||
testWithNoSelection(MarkdownButtonType.BOLD, '****', true);
|
||||
testWithNoSelection(MarkdownButtonType.BOLD, '');
|
||||
testWithNoSelection(MarkdownButtonType.ITALIC, '__', true);
|
||||
testWithNoSelection(MarkdownButtonType.ITALIC, '');
|
||||
testWithNoSelection(MarkdownButtonType.CODE, '```\n\n```', true);
|
||||
testWithNoSelection(MarkdownButtonType.CODE, '');
|
||||
testWithNoSelection(MarkdownButtonType.HIGHLIGHT, '<mark></mark>', true);
|
||||
testWithNoSelection(MarkdownButtonType.HIGHLIGHT, '');
|
||||
testWithNoSelection(MarkdownButtonType.LINK, '[]()', true);
|
||||
testWithNoSelection(MarkdownButtonType.LINK, '');
|
||||
testWithNoSelection(MarkdownButtonType.UNORDERED_LIST, '- ', true);
|
||||
testWithNoSelection(MarkdownButtonType.UNORDERED_LIST, '');
|
||||
testWithNoSelection(MarkdownButtonType.ORDERED_LIST, '1. ', true);
|
||||
testWithNoSelection(MarkdownButtonType.ORDERED_LIST, '');
|
||||
testWithNoSelection(MarkdownButtonType.IMAGE, '![]()', true);
|
||||
testWithNoSelection(MarkdownButtonType.IMAGE, '');
|
||||
testWithNoSelection(MarkdownButtonType.HEADING1, '# ', true);
|
||||
testWithNoSelection(MarkdownButtonType.HEADING1, '');
|
||||
testWithNoSelection(MarkdownButtonType.HEADING2, '## ', true);
|
||||
testWithNoSelection(MarkdownButtonType.HEADING2, '');
|
||||
testWithNoSelection(MarkdownButtonType.HEADING3, '### ', true);
|
||||
testWithNoSelection(MarkdownButtonType.HEADING3, '');
|
||||
test('Transform text with no previous selection', async () => {
|
||||
await testWithNoSelection(MarkdownButtonType.BOLD, '****', true);
|
||||
await testWithNoSelection(MarkdownButtonType.BOLD, '');
|
||||
await testWithNoSelection(MarkdownButtonType.ITALIC, '__', true);
|
||||
await testWithNoSelection(MarkdownButtonType.ITALIC, '');
|
||||
await testWithNoSelection(MarkdownButtonType.CODE, '```\n\n```', true);
|
||||
await testWithNoSelection(MarkdownButtonType.CODE, '');
|
||||
await testWithNoSelection(MarkdownButtonType.HIGHLIGHT, '<mark></mark>', true);
|
||||
await testWithNoSelection(MarkdownButtonType.HIGHLIGHT, '');
|
||||
await testWithNoSelection(MarkdownButtonType.LINK, '[]()', true);
|
||||
await testWithNoSelection(MarkdownButtonType.LINK, '');
|
||||
await testWithNoSelection(MarkdownButtonType.UNORDERED_LIST, '- ', true);
|
||||
await testWithNoSelection(MarkdownButtonType.UNORDERED_LIST, '');
|
||||
await testWithNoSelection(MarkdownButtonType.ORDERED_LIST, '1. ', true);
|
||||
await testWithNoSelection(MarkdownButtonType.ORDERED_LIST, '');
|
||||
await testWithNoSelection(MarkdownButtonType.IMAGE, '![]()', true);
|
||||
await testWithNoSelection(MarkdownButtonType.IMAGE, '');
|
||||
await testWithNoSelection(MarkdownButtonType.HEADING1, '# ', true);
|
||||
await testWithNoSelection(MarkdownButtonType.HEADING1, '');
|
||||
await testWithNoSelection(MarkdownButtonType.HEADING2, '## ', true);
|
||||
await testWithNoSelection(MarkdownButtonType.HEADING2, '');
|
||||
await testWithNoSelection(MarkdownButtonType.HEADING3, '### ', true);
|
||||
await testWithNoSelection(MarkdownButtonType.HEADING3, '');
|
||||
});
|
||||
|
||||
test('Transform text with one word selected', () => {
|
||||
testWithSingleWordSelected(MarkdownButtonType.CODE, '```\nWORD\n```');
|
||||
test('Transform text with one word selected', async () => {
|
||||
await testWithSingleWordSelected(MarkdownButtonType.CODE, '```\nWORD\n```');
|
||||
});
|
||||
|
||||
test('Transform text with multiple words selected', () => {
|
||||
testWithMultipleWordsSelected(MarkdownButtonType.BOLD, '**Multi Words**');
|
||||
testWithMultipleWordsSelected(MarkdownButtonType.ITALIC, '_Multi Words_');
|
||||
testWithMultipleWordsSelected(MarkdownButtonType.CODE, '```\nMulti Words\n```');
|
||||
testWithMultipleWordsSelected(MarkdownButtonType.HIGHLIGHT, '<mark>Multi Words</mark>');
|
||||
testWithMultipleWordsSelected(MarkdownButtonType.LINK, '[Multi Words]()');
|
||||
testWithMultipleWordsSelected(MarkdownButtonType.UNORDERED_LIST, '- Multi Words');
|
||||
testWithMultipleWordsSelected(MarkdownButtonType.ORDERED_LIST, '1. Multi Words');
|
||||
testWithMultipleWordsSelected(MarkdownButtonType.IMAGE, '![Multi Words]()');
|
||||
test('Transform text with multiple words selected', async () => {
|
||||
await testWithMultipleWordsSelected(MarkdownButtonType.BOLD, '**Multi Words**');
|
||||
await testWithMultipleWordsSelected(MarkdownButtonType.ITALIC, '_Multi Words_');
|
||||
await testWithMultipleWordsSelected(MarkdownButtonType.CODE, '```\nMulti Words\n```');
|
||||
await testWithMultipleWordsSelected(MarkdownButtonType.HIGHLIGHT, '<mark>Multi Words</mark>');
|
||||
await testWithMultipleWordsSelected(MarkdownButtonType.LINK, '[Multi Words]()');
|
||||
await testWithMultipleWordsSelected(MarkdownButtonType.UNORDERED_LIST, '- Multi Words');
|
||||
await testWithMultipleWordsSelected(MarkdownButtonType.ORDERED_LIST, '1. Multi Words');
|
||||
await testWithMultipleWordsSelected(MarkdownButtonType.IMAGE, '![Multi Words]()');
|
||||
});
|
||||
|
||||
test('Transform text with multiple lines selected', () => {
|
||||
testWithMultipleLinesSelected(MarkdownButtonType.BOLD, '**Multi\nLines\nSelected**');
|
||||
testWithMultipleLinesSelected(MarkdownButtonType.ITALIC, '_Multi\nLines\nSelected_');
|
||||
testWithMultipleLinesSelected(MarkdownButtonType.CODE, '```\nMulti\nLines\nSelected\n```');
|
||||
testWithMultipleLinesSelected(MarkdownButtonType.HIGHLIGHT, '<mark>Multi\nLines\nSelected</mark>');
|
||||
testWithMultipleLinesSelected(MarkdownButtonType.LINK, '[Multi\nLines\nSelected]()');
|
||||
testWithMultipleLinesSelected(MarkdownButtonType.UNORDERED_LIST, '- Multi\n- Lines\n- Selected');
|
||||
testWithMultipleLinesSelected(MarkdownButtonType.ORDERED_LIST, '1. Multi\n1. Lines\n1. Selected');
|
||||
testWithMultipleLinesSelected(MarkdownButtonType.IMAGE, '![Multi\nLines\nSelected]()');
|
||||
testWithMultipleLinesSelected(MarkdownButtonType.HEADING1, '# Multi\n# Lines\n# Selected');
|
||||
testWithMultipleLinesSelected(MarkdownButtonType.HEADING2, '## Multi\n## Lines\n## Selected');
|
||||
testWithMultipleLinesSelected(MarkdownButtonType.HEADING3, '### Multi\n### Lines\n### Selected');
|
||||
test('Transform text with multiple lines selected', async () => {
|
||||
await testWithMultipleLinesSelected(MarkdownButtonType.BOLD, '**Multi\nLines\nSelected**');
|
||||
await testWithMultipleLinesSelected(MarkdownButtonType.ITALIC, '_Multi\nLines\nSelected_');
|
||||
await testWithMultipleLinesSelected(MarkdownButtonType.CODE, '```\nMulti\nLines\nSelected\n```');
|
||||
await testWithMultipleLinesSelected(MarkdownButtonType.HIGHLIGHT, '<mark>Multi\nLines\nSelected</mark>');
|
||||
await testWithMultipleLinesSelected(MarkdownButtonType.LINK, '[Multi\nLines\nSelected]()');
|
||||
await testWithMultipleLinesSelected(MarkdownButtonType.UNORDERED_LIST, '- Multi\n- Lines\n- Selected');
|
||||
await testWithMultipleLinesSelected(MarkdownButtonType.ORDERED_LIST, '1. Multi\n1. Lines\n1. Selected');
|
||||
await testWithMultipleLinesSelected(MarkdownButtonType.IMAGE, '![Multi\nLines\nSelected]()');
|
||||
await testWithMultipleLinesSelected(MarkdownButtonType.HEADING1, '# Multi\n# Lines\n# Selected');
|
||||
await testWithMultipleLinesSelected(MarkdownButtonType.HEADING2, '## Multi\n## Lines\n## Selected');
|
||||
await testWithMultipleLinesSelected(MarkdownButtonType.HEADING3, '### Multi\n### Lines\n### Selected');
|
||||
});
|
||||
|
||||
test('Ensure notebook editor returns expected object', () => {
|
||||
test('Ensure notebook editor returns expected object', async () => {
|
||||
assert.deepEqual(notebookEditor, markdownTextTransformer.notebookEditor, 'Notebook editor does not match expected value');
|
||||
// Set markdown text transformer to not have a notebook editor passed in
|
||||
markdownTextTransformer = new MarkdownTextTransformer(mockNotebookService.object, cellModel);
|
||||
markdownTextTransformer = new MarkdownTextTransformer(mockNotebookService.object, cellModel, instantiationService);
|
||||
assert.equal(markdownTextTransformer.notebookEditor, undefined, 'No notebook editor should be returned');
|
||||
// Even after text is attempted to be transformed, there should be no editor, and therefore nothing on the text model
|
||||
markdownTextTransformer.transformText(MarkdownButtonType.BOLD);
|
||||
await markdownTextTransformer.transformText(MarkdownButtonType.BOLD);
|
||||
assert.equal(markdownTextTransformer.notebookEditor, undefined, 'Notebook model does not have a valid uri, so no editor should be returned');
|
||||
assert.equal(textModel.getValue(), '', 'No text should exist on the textModel');
|
||||
});
|
||||
|
||||
function testWithNoSelection(type: MarkdownButtonType, expectedValue: string, setValue = false): void {
|
||||
async function testWithNoSelection(type: MarkdownButtonType, expectedValue: string, setValue = false): Promise<void> {
|
||||
if (setValue) {
|
||||
textModel.setValue('');
|
||||
}
|
||||
markdownTextTransformer.transformText(type);
|
||||
await markdownTextTransformer.transformText(type);
|
||||
assert.equal(textModel.getValue(), expectedValue, `${MarkdownButtonType[type]} with no selection failed (setValue ${setValue})`);
|
||||
}
|
||||
|
||||
function testWithSingleWordSelected(type: MarkdownButtonType, expectedValue: string): void {
|
||||
async function testWithSingleWordSelected(type: MarkdownButtonType, expectedValue: string): Promise<void> {
|
||||
let value = 'WORD';
|
||||
textModel.setValue(value);
|
||||
|
||||
// Test transformation (adding text)
|
||||
widget.setSelection({ startColumn: 1, startLineNumber: 1, endColumn: value.length + 1, endLineNumber: 1 });
|
||||
assert.equal(textModel.getValueInRange(widget.getSelection()), value, 'Expected selection is not found');
|
||||
markdownTextTransformer.transformText(type);
|
||||
await markdownTextTransformer.transformText(type);
|
||||
const textModelValue = textModel.getValue();
|
||||
assert.equal(textModelValue, expectedValue, `${MarkdownButtonType[type]} with single word selection failed`);
|
||||
|
||||
@@ -179,32 +179,32 @@ suite('MarkdownTextTransformer', () => {
|
||||
const valueRange = getValueRange(textModel, value);
|
||||
assert.notEqual(valueRange, undefined, 'Could not find value in model after transformation');
|
||||
widget.setSelection(valueRange);
|
||||
markdownTextTransformer.transformText(type);
|
||||
await markdownTextTransformer.transformText(type);
|
||||
assert.equal(textModel.getValue(), value, `Undo operation for ${MarkdownButtonType[type]} with single word selection failed`);
|
||||
}
|
||||
|
||||
function testWithMultipleWordsSelected(type: MarkdownButtonType, expectedValue: string): void {
|
||||
async function testWithMultipleWordsSelected(type: MarkdownButtonType, expectedValue: string): Promise<void> {
|
||||
let value = 'Multi Words';
|
||||
textModel.setValue(value);
|
||||
widget.setSelection({ startColumn: 1, startLineNumber: 1, endColumn: 12, endLineNumber: 1 });
|
||||
assert.equal(textModel.getValueInRange(widget.getSelection()), value, 'Expected multi-word selection is not found');
|
||||
markdownTextTransformer.transformText(type);
|
||||
await markdownTextTransformer.transformText(type);
|
||||
assert.equal(textModel.getValue(), expectedValue, `${MarkdownButtonType[type]} with multiple word selection failed`);
|
||||
|
||||
// Test undo (removing text)
|
||||
const valueRange = getValueRange(textModel, value);
|
||||
assert.notEqual(valueRange, undefined, 'Could not find value in model after transformation');
|
||||
widget.setSelection(valueRange);
|
||||
markdownTextTransformer.transformText(type);
|
||||
await markdownTextTransformer.transformText(type);
|
||||
assert.equal(textModel.getValue(), value, `Undo operation for ${MarkdownButtonType[type]} with multiple word selection failed`);
|
||||
}
|
||||
|
||||
function testWithMultipleLinesSelected(type: MarkdownButtonType, expectedValue: string): void {
|
||||
async function testWithMultipleLinesSelected(type: MarkdownButtonType, expectedValue: string): Promise<void> {
|
||||
let value = 'Multi\nLines\nSelected';
|
||||
textModel.setValue(value);
|
||||
widget.setSelection({ startColumn: 1, startLineNumber: 1, endColumn: 9, endLineNumber: 3 });
|
||||
assert.equal(textModel.getValueInRange(widget.getSelection()), value, 'Expected multi-line selection is not found');
|
||||
markdownTextTransformer.transformText(type);
|
||||
await markdownTextTransformer.transformText(type);
|
||||
assert.equal(textModel.getValue(), expectedValue, `${MarkdownButtonType[type]} with multiple line selection failed`);
|
||||
|
||||
// Test undo (removing text)
|
||||
@@ -213,7 +213,7 @@ suite('MarkdownTextTransformer', () => {
|
||||
valueRange = new Range(valueRange.startLineNumber, valueRange.startColumn, valueRange.endLineNumber + 2, 9);
|
||||
assert.notEqual(valueRange, undefined, 'Could not find value in model after transformation');
|
||||
widget.setSelection(valueRange);
|
||||
markdownTextTransformer.transformText(type);
|
||||
await markdownTextTransformer.transformText(type);
|
||||
assert.equal(textModel.getValue(), value, `Undo operation for ${MarkdownButtonType[type]} with multiple line selection failed`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -49,7 +49,7 @@ export class WebViewDialog extends Modal {
|
||||
@IWebviewService private readonly webviewService: IWebviewService,
|
||||
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService
|
||||
) {
|
||||
super('', TelemetryKeys.WebView, telemetryService, layoutService, clipboardService, themeService, logService, textResourcePropertiesService, contextKeyService, { isFlyout: false, hasTitleIcon: true });
|
||||
super('', TelemetryKeys.WebView, telemetryService, layoutService, clipboardService, themeService, logService, textResourcePropertiesService, contextKeyService, { dialogStyle: 'normal', hasTitleIcon: true });
|
||||
this._okLabel = localize('webViewDialog.ok', "OK");
|
||||
this._closeLabel = localize('webViewDialog.close', "Close");
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ export class AutoOAuthDialog extends Modal {
|
||||
textResourcePropertiesService,
|
||||
contextKeyService,
|
||||
{
|
||||
isFlyout: true,
|
||||
dialogStyle: 'flyout',
|
||||
hasBackButton: true,
|
||||
hasSpinner: true
|
||||
}
|
||||
|
||||
@@ -20,6 +20,12 @@ export class CustomDialogService {
|
||||
|
||||
public showDialog(dialog: Dialog, dialogName?: string, options?: IModalOptions): void {
|
||||
let name = dialogName ? dialogName : 'CustomDialog';
|
||||
|
||||
if (options && (options.dialogStyle === 'callout')) {
|
||||
options.positionX = document.activeElement.getBoundingClientRect().left;
|
||||
options.positionY = document.activeElement.getBoundingClientRect().top;
|
||||
options.renderFooter = false;
|
||||
}
|
||||
let dialogModal = this._instantiationService.createInstance(DialogModal, dialog, name, options || DefaultDialogOptions);
|
||||
this._dialogModals.set(dialog, dialogModal);
|
||||
dialogModal.render();
|
||||
|
||||
@@ -58,25 +58,31 @@ export class DialogModal extends Modal {
|
||||
super.render();
|
||||
attachModalDialogStyler(this, this._themeService);
|
||||
|
||||
if (this.backButton) {
|
||||
if (this._modalOptions.renderFooter !== false) {
|
||||
this._modalOptions.renderFooter = true;
|
||||
}
|
||||
|
||||
if (this._modalOptions.renderFooter && this.backButton) {
|
||||
this.backButton.onDidClick(() => this.cancel());
|
||||
attachButtonStyler(this.backButton, this._themeService, { buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND });
|
||||
}
|
||||
|
||||
if (this._dialog.customButtons) {
|
||||
if (this._modalOptions.renderFooter && this._dialog.customButtons) {
|
||||
this._dialog.customButtons.forEach(button => {
|
||||
let buttonElement = this.addDialogButton(button);
|
||||
this.updateButtonElement(buttonElement, button);
|
||||
});
|
||||
}
|
||||
|
||||
this._doneButton = this.addDialogButton(this._dialog.okButton, () => this.done(), false, true);
|
||||
this._dialog.okButton.registerClickEvent(this._onDone.event);
|
||||
this._dialog.onValidityChanged(valid => {
|
||||
this._doneButton.enabled = valid && this._dialog.okButton.enabled;
|
||||
});
|
||||
this.addDialogButton(this._dialog.cancelButton, () => this.cancel(), false);
|
||||
this._dialog.cancelButton.registerClickEvent(this._onCancel.event);
|
||||
if (this._modalOptions.renderFooter) {
|
||||
this._doneButton = this.addDialogButton(this._dialog.okButton, () => this.done(), false, true);
|
||||
this._dialog.okButton.registerClickEvent(this._onDone.event);
|
||||
this._dialog.onValidityChanged(valid => {
|
||||
this._doneButton.enabled = valid && this._dialog.okButton.enabled;
|
||||
});
|
||||
this.addDialogButton(this._dialog.cancelButton, () => this.cancel(), false);
|
||||
this._dialog.cancelButton.registerClickEvent(this._onCancel.event);
|
||||
}
|
||||
|
||||
let messageChangeHandler = (message: DialogMessage) => {
|
||||
if (message && message.text) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as azdata from 'azdata';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { DialogMessage, DialogWidth } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { DialogMessage, DialogWidth, DialogStyle, DialogPosition, IDialogProperties } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
export class ModelViewPane {
|
||||
private _valid: boolean = true;
|
||||
@@ -43,6 +43,11 @@ export class Dialog extends ModelViewPane {
|
||||
private static readonly CANCEL_BUTTON_LABEL = localize('dialogModalCancelButtonLabel', "Cancel");
|
||||
|
||||
public content: string | DialogTab[] = '';
|
||||
public dialogStyle: DialogStyle;
|
||||
public dialogPosition: DialogPosition;
|
||||
public renderHeader: boolean;
|
||||
public renderFooter: boolean;
|
||||
public dialogProperties: IDialogProperties;
|
||||
public okButton: DialogButton = new DialogButton(Dialog.DONE_BUTTON_LABEL, true);
|
||||
public cancelButton: DialogButton = new DialogButton(Dialog.CANCEL_BUTTON_LABEL, true);
|
||||
public customButtons: DialogButton[] = [];
|
||||
@@ -51,11 +56,26 @@ export class Dialog extends ModelViewPane {
|
||||
private _message: DialogMessage | undefined;
|
||||
private _closeValidator: CloseValidator | undefined;
|
||||
|
||||
constructor(public title: string, public width: DialogWidth, content?: string | DialogTab[]) {
|
||||
constructor(public title: string, public width: DialogWidth, dialogStyle?: DialogStyle, dialogPosition?: DialogPosition, renderHeader?: boolean, renderFooter?: boolean, dialogProperties?: IDialogProperties, content?: string | DialogTab[]) {
|
||||
super();
|
||||
if (content) {
|
||||
this.content = content;
|
||||
}
|
||||
if (dialogStyle) {
|
||||
this.dialogStyle = dialogStyle;
|
||||
}
|
||||
if (dialogPosition) {
|
||||
this.dialogPosition = dialogPosition;
|
||||
}
|
||||
if (renderHeader) {
|
||||
this.renderHeader = renderHeader;
|
||||
}
|
||||
if (renderFooter) {
|
||||
this.renderFooter = renderFooter;
|
||||
}
|
||||
if (dialogProperties) {
|
||||
this.dialogProperties = dialogProperties;
|
||||
}
|
||||
}
|
||||
|
||||
public get message(): DialogMessage | undefined {
|
||||
|
||||
@@ -52,7 +52,7 @@ export class ErrorMessageDialog extends Modal {
|
||||
@ILogService logService: ILogService,
|
||||
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService
|
||||
) {
|
||||
super('', TelemetryKeys.ErrorMessage, telemetryService, layoutService, clipboardService, themeService, logService, textResourcePropertiesService, contextKeyService, { isFlyout: false, hasTitleIcon: true });
|
||||
super('', TelemetryKeys.ErrorMessage, telemetryService, layoutService, clipboardService, themeService, logService, textResourcePropertiesService, contextKeyService, { dialogStyle: 'normal', hasTitleIcon: true });
|
||||
this._okLabel = localize('errorMessageDialog.ok', "OK");
|
||||
this._closeLabel = localize('errorMessageDialog.close', "Close");
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ export class FileBrowserDialog extends Modal {
|
||||
@ILogService logService: ILogService,
|
||||
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService
|
||||
) {
|
||||
super(title, TelemetryKeys.Backup, telemetryService, layoutService, clipboardService, themeService, logService, textResourcePropertiesService, contextKeyService, { isFlyout: true, hasTitleIcon: false, hasBackButton: true, hasSpinner: true });
|
||||
super(title, TelemetryKeys.Backup, telemetryService, layoutService, clipboardService, themeService, logService, textResourcePropertiesService, contextKeyService, { dialogStyle: 'flyout', hasTitleIcon: false, hasBackButton: true, hasSpinner: true });
|
||||
this._viewModel = this._instantiationService.createInstance(FileBrowserViewModel);
|
||||
this._viewModel.onAddFileTree(args => this.handleOnAddFileTree(args.rootNode, args.selectedNode, args.expandedNodes).catch(err => onUnexpectedError(err)));
|
||||
this._viewModel.onPathValidate(args => this.handleOnValidate(args.succeeded, args.message));
|
||||
|
||||
@@ -83,7 +83,7 @@ export class ProfilerFilterDialog extends Modal {
|
||||
@IProfilerService private profilerService: IProfilerService,
|
||||
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService
|
||||
) {
|
||||
super('', TelemetryKeys.ProfilerFilter, telemetryService, layoutService, clipboardService, themeService, logService, textResourcePropertiesService, contextKeyService, { isFlyout: false, hasTitleIcon: true });
|
||||
super('', TelemetryKeys.ProfilerFilter, telemetryService, layoutService, clipboardService, themeService, logService, textResourcePropertiesService, contextKeyService, { dialogStyle: 'normal', hasTitleIcon: true });
|
||||
}
|
||||
|
||||
public open(input: ProfilerInput) {
|
||||
|
||||
@@ -87,7 +87,7 @@ export class FirewallRuleDialog extends Modal {
|
||||
textResourcePropertiesService,
|
||||
contextKeyService,
|
||||
{
|
||||
isFlyout: true,
|
||||
dialogStyle: 'flyout',
|
||||
hasBackButton: true,
|
||||
hasSpinner: true
|
||||
}
|
||||
|
||||
@@ -112,6 +112,11 @@ suite('MainThreadModelViewDialog Tests', () => {
|
||||
dialogDetails = {
|
||||
title: 'dialog1',
|
||||
width: 'narrow',
|
||||
dialogStyle: 'callout',
|
||||
dialogPosition: 'left',
|
||||
renderHeader: true,
|
||||
renderFooter: true,
|
||||
dialogProperties: { xPos: 1200, yPos: 100, width: 20, height: 20 },
|
||||
content: [tab1Handle, tab2Handle],
|
||||
okButton: okButtonHandle,
|
||||
cancelButton: cancelButtonHandle,
|
||||
|
||||
Reference in New Issue
Block a user