mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
calloutDialog refactor - new superclasses for insert image and insert link (#14385)
* calloutDialog refactor - split code specific to image and link into their own super classes. Moved callout styles into a new stylesheet. * Image and Link inserts working. * Stylesheets cleanup. Refactor cleanup. * Removed CSS comment. Added missing image callout style. Revised generic open and cancel classes. Moved all remaining localized strings into shared constants file.
This commit is contained in:
@@ -3,86 +3,27 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { URI } from 'vs/base/common/uri';
|
import 'vs/css!./media/calloutDialog';
|
||||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
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 { IDialogProperties, Modal, DialogWidth } from 'sql/workbench/browser/modal/modal';
|
||||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
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 { 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 { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||||
import { ILogService } from 'vs/platform/log/common/log';
|
import { ILogService } from 'vs/platform/log/common/log';
|
||||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
|
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
||||||
import { attachModalDialogStyler } from 'sql/workbench/common/styler';
|
|
||||||
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
|
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 abstract class CalloutDialog<T> extends Modal {
|
||||||
|
|
||||||
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(
|
constructor(
|
||||||
calloutType: CalloutType,
|
|
||||||
title: string,
|
title: string,
|
||||||
width: DialogWidth,
|
width: DialogWidth,
|
||||||
dialogProperties: IDialogProperties,
|
dialogProperties: IDialogProperties,
|
||||||
@IPathService private readonly _pathService: IPathService,
|
|
||||||
@IFileDialogService private readonly _fileDialogService: IFileDialogService,
|
|
||||||
@IThemeService themeService: IThemeService,
|
@IThemeService themeService: IThemeService,
|
||||||
@ILayoutService layoutService: ILayoutService,
|
@ILayoutService layoutService: ILayoutService,
|
||||||
@IAdsTelemetryService telemetryService: IAdsTelemetryService,
|
@IAdsTelemetryService telemetryService: IAdsTelemetryService,
|
||||||
@IContextKeyService contextKeyService: IContextKeyService,
|
@IContextKeyService contextKeyService: IContextKeyService,
|
||||||
@IContextViewService private _contextViewService: IContextViewService,
|
|
||||||
@IClipboardService clipboardService: IClipboardService,
|
@IClipboardService clipboardService: IClipboardService,
|
||||||
@ILogService logService: ILogService,
|
@ILogService logService: ILogService,
|
||||||
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService
|
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService
|
||||||
@@ -103,224 +44,17 @@ export class CalloutDialog extends Modal {
|
|||||||
dialogProperties: dialogProperties,
|
dialogProperties: dialogProperties,
|
||||||
width: width
|
width: width
|
||||||
});
|
});
|
||||||
|
|
||||||
this._selectionComplete = new Deferred<ICalloutDialogOptions>();
|
|
||||||
this._calloutType = calloutType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected abstract renderBody(container: HTMLElement): void;
|
||||||
* 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() {
|
public abstract open(): Promise<T>;
|
||||||
super.render();
|
|
||||||
|
|
||||||
attachModalDialogStyler(this, this._themeService);
|
public cancel(): void {
|
||||||
|
this.hide();
|
||||||
this.addFooterButton(this.insertButtonText, () => this.insert());
|
this.dispose();
|
||||||
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 {
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
98
src/sql/workbench/browser/modal/media/calloutDialog.css
Normal file
98
src/sql/workbench/browser/modal/media/calloutDialog.css
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.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 p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.modal.callout-dialog .modal-content .button-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
.modal.callout-dialog .modal-content .row {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.hc-black .modal.callout-dialog .modal-dialog {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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.callout-dialog .modal-header {
|
||||||
|
padding: 18px 24px 8px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.callout-dialog .modal-footer {
|
||||||
|
padding: 15px 24px 15px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
@@ -24,113 +24,13 @@
|
|||||||
height: 480px;
|
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 {
|
.modal .modal-header {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal.callout-dialog .modal-header {
|
|
||||||
padding: 18px 24px 8px 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal .modal-footer {
|
.modal .modal-footer {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
}
|
}
|
||||||
.modal.callout-dialog .modal-footer {
|
|
||||||
padding: 15px 24px 15px 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal .codicon.in-progress {
|
.modal .codicon.in-progress {
|
||||||
width: 25px;
|
width: 25px;
|
||||||
@@ -189,17 +89,6 @@
|
|||||||
overflow: hidden;
|
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 */
|
/* modl body content style(excluding dialogErrorMessage section) for angulr component dialog */
|
||||||
.angular-modal-body-content {
|
.angular-modal-body-content {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
import { localize } from 'vs/nls';
|
||||||
|
|
||||||
|
// Localized texts
|
||||||
|
export const insertButtonText = localize('callout.insertButton', "Insert");
|
||||||
|
export const cancelButtonText = localize('callout.cancelButton', "Cancel");
|
||||||
|
// Insert Image
|
||||||
|
export const locationLabel = localize('imageCallout.locationLabel', "Image location");
|
||||||
|
export const localImageLabel = localize('imageCallout.localImageLabel', "This computer");
|
||||||
|
export const remoteImageLabel = localize('imageCallout.remoteImageLabel', "Online");
|
||||||
|
export const pathInputLabel = localize('imageCallout.pathInputLabel', "Image URL");
|
||||||
|
export const pathPlaceholder = localize('imageCallout.pathPlaceholder', "Enter image path");
|
||||||
|
export const urlPlaceholder = localize('imageCallout.urlPlaceholder', "Enter image URL");
|
||||||
|
export const browseAltText = localize('imageCallout.browseAltText', "Browse");
|
||||||
|
export const embedImageLabel = localize('imageCallout.embedImageLabel', "Attach image to notebook");
|
||||||
|
export const locationLocal = localize('imageCallout.local', "Local");
|
||||||
|
export const locationRemote = localize('imageCallout.remote', "Remote");
|
||||||
|
// Insert Link
|
||||||
|
export const linkTextLabel = localize('linkCallout.linkTextLabel', "Text to display");
|
||||||
|
export const linkTextPlaceholder = localize('linkCallout.linkTextPlaceholder', "Text to display");
|
||||||
|
export const linkAddressLabel = localize('linkCallout.linkAddressLabel', "Address");
|
||||||
|
export const linkAddressPlaceholder = localize('linkCallout.linkAddressPlaceholder', "Link to an existing file or web page");
|
||||||
@@ -0,0 +1,227 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import 'vs/css!./media/imageCalloutDialog';
|
||||||
|
import * as DOM from 'vs/base/browser/dom';
|
||||||
|
import * as strings from 'vs/base/common/strings';
|
||||||
|
import * as styler from 'vs/platform/theme/common/styler';
|
||||||
|
import { URI } from 'vs/base/common/uri';
|
||||||
|
import * as constants from 'sql/workbench/contrib/notebook/browser/calloutDialog/common/constants';
|
||||||
|
import { CalloutDialog } from 'sql/workbench/browser/modal/calloutDialog';
|
||||||
|
import { IFileDialogService, IOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs';
|
||||||
|
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||||
|
import { IDialogProperties } from 'sql/workbench/browser/modal/modal';
|
||||||
|
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||||
|
import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||||
|
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||||
|
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 { 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 { DialogWidth } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
|
import { attachModalDialogStyler } from 'sql/workbench/common/styler';
|
||||||
|
|
||||||
|
export interface IImageCalloutDialogOptions {
|
||||||
|
insertTitle?: string,
|
||||||
|
insertMarkup?: string,
|
||||||
|
imagePath?: string,
|
||||||
|
embedImage?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ImageCalloutDialog extends CalloutDialog<IImageCalloutDialogOptions> {
|
||||||
|
private _selectionComplete: Deferred<IImageCalloutDialogOptions> = new Deferred<IImageCalloutDialogOptions>();
|
||||||
|
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;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
title: string,
|
||||||
|
width: DialogWidth,
|
||||||
|
dialogProperties: IDialogProperties,
|
||||||
|
@IPathService private readonly _pathService: IPathService,
|
||||||
|
@IFileDialogService private readonly _fileDialogService: IFileDialogService,
|
||||||
|
@IContextViewService private readonly _contextViewService: IContextViewService,
|
||||||
|
@IThemeService themeService: IThemeService,
|
||||||
|
@ILayoutService layoutService: ILayoutService,
|
||||||
|
@IAdsTelemetryService telemetryService: IAdsTelemetryService,
|
||||||
|
@IContextKeyService contextKeyService: IContextKeyService,
|
||||||
|
@IClipboardService clipboardService: IClipboardService,
|
||||||
|
@ILogService logService: ILogService,
|
||||||
|
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
title,
|
||||||
|
width,
|
||||||
|
dialogProperties,
|
||||||
|
themeService,
|
||||||
|
layoutService,
|
||||||
|
telemetryService,
|
||||||
|
contextKeyService,
|
||||||
|
clipboardService,
|
||||||
|
logService,
|
||||||
|
textResourcePropertiesService
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the dialog and returns a promise for what options the user chooses.
|
||||||
|
*/
|
||||||
|
public open(): Promise<IImageCalloutDialogOptions> {
|
||||||
|
this.show();
|
||||||
|
return this._selectionComplete.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): void {
|
||||||
|
super.render();
|
||||||
|
attachModalDialogStyler(this, this._themeService);
|
||||||
|
this.addFooterButton(constants.insertButtonText, () => this.insert());
|
||||||
|
this.addFooterButton(constants.cancelButtonText, () => this.cancel(), undefined, true);
|
||||||
|
this.registerListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected renderBody(container: HTMLElement) {
|
||||||
|
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 = constants.locationLabel;
|
||||||
|
DOM.append(locationRow, this._imageLocationLabel);
|
||||||
|
|
||||||
|
let radioButtonGroup = DOM.$('.radio-group');
|
||||||
|
this._imageLocalRadioButton = new RadioButton(radioButtonGroup, {
|
||||||
|
label: constants.localImageLabel,
|
||||||
|
enabled: true,
|
||||||
|
checked: true
|
||||||
|
});
|
||||||
|
this._imageRemoteRadioButton = new RadioButton(radioButtonGroup, {
|
||||||
|
label: constants.remoteImageLabel,
|
||||||
|
enabled: true,
|
||||||
|
checked: false
|
||||||
|
});
|
||||||
|
this._imageLocalRadioButton.name = this._editorImageLocationGroup;
|
||||||
|
this._imageLocalRadioButton.value = constants.locationLocal;
|
||||||
|
this._imageRemoteRadioButton.name = this._editorImageLocationGroup;
|
||||||
|
this._imageRemoteRadioButton.value = constants.locationRemote;
|
||||||
|
|
||||||
|
DOM.append(locationRow, radioButtonGroup);
|
||||||
|
|
||||||
|
let pathRow = DOM.$('.row');
|
||||||
|
DOM.append(imageContentColumn, pathRow);
|
||||||
|
this._imageUrlLabel = DOM.$('p');
|
||||||
|
if (this._imageLocalRadioButton.checked === true) {
|
||||||
|
this._imageUrlLabel.innerText = constants.pathPlaceholder;
|
||||||
|
} else {
|
||||||
|
this._imageUrlLabel.innerText = constants.urlPlaceholder;
|
||||||
|
}
|
||||||
|
DOM.append(pathRow, this._imageUrlLabel);
|
||||||
|
|
||||||
|
let inputContainer = DOM.$('.flex-container');
|
||||||
|
this._imageUrlInputBox = new InputBox(
|
||||||
|
inputContainer,
|
||||||
|
this._contextViewService,
|
||||||
|
{
|
||||||
|
placeholder: constants.pathPlaceholder,
|
||||||
|
ariaLabel: constants.pathInputLabel
|
||||||
|
});
|
||||||
|
let browseButtonContainer = DOM.$('.button-icon');
|
||||||
|
this._imageBrowseButton = DOM.$('a.codicon.masked-icon.browse-local');
|
||||||
|
this._imageBrowseButton.title = constants.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 = constants.urlPlaceholder;
|
||||||
|
this._imageUrlInputBox.setPlaceHolder(constants.urlPlaceholder);
|
||||||
|
}));
|
||||||
|
this._register(this._imageLocalRadioButton.onClicked(e => {
|
||||||
|
this._imageBrowseButton.style.display = 'block';
|
||||||
|
this._imageUrlLabel.innerText = constants.pathPlaceholder;
|
||||||
|
this._imageUrlInputBox.setPlaceHolder(constants.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: constants.embedImageLabel,
|
||||||
|
checked: false,
|
||||||
|
onChange: (viaKeyboard) => { },
|
||||||
|
ariaLabel: constants.embedImageLabel
|
||||||
|
});
|
||||||
|
DOM.append(embedRow, this._imageEmbedLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private registerListeners(): void {
|
||||||
|
this._register(styler.attachInputBoxStyler(this._imageUrlInputBox, this._themeService));
|
||||||
|
this._register(styler.attachCheckboxStyler(this._imageEmbedCheckbox, this._themeService));
|
||||||
|
}
|
||||||
|
|
||||||
|
public insert(): void {
|
||||||
|
this.hide();
|
||||||
|
this._selectionComplete.resolve({
|
||||||
|
insertMarkup: `<img src="${strings.escape(this._imageUrlInputBox.value)}">`,
|
||||||
|
imagePath: this._imageUrlInputBox.value,
|
||||||
|
embedImage: this._imageEmbedCheckbox.checked
|
||||||
|
});
|
||||||
|
this.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public cancel(): void {
|
||||||
|
super.cancel();
|
||||||
|
this._selectionComplete.resolve({
|
||||||
|
insertMarkup: '',
|
||||||
|
imagePath: undefined,
|
||||||
|
embedImage: undefined
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import 'vs/css!./media/linkCalloutDialog';
|
||||||
|
import * as DOM from 'vs/base/browser/dom';
|
||||||
|
import * as strings from 'vs/base/common/strings';
|
||||||
|
import * as styler from 'vs/platform/theme/common/styler';
|
||||||
|
import * as constants from 'sql/workbench/contrib/notebook/browser/calloutDialog/common/constants';
|
||||||
|
import { CalloutDialog } from 'sql/workbench/browser/modal/calloutDialog';
|
||||||
|
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||||
|
import { IDialogProperties } from 'sql/workbench/browser/modal/modal';
|
||||||
|
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||||
|
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||||
|
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 { ILayoutService } from 'vs/platform/layout/browser/layoutService';
|
||||||
|
import { Deferred } from 'sql/base/common/promise';
|
||||||
|
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
|
||||||
|
import { DialogWidth } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
|
import { attachModalDialogStyler } from 'sql/workbench/common/styler';
|
||||||
|
|
||||||
|
export interface ILinkCalloutDialogOptions {
|
||||||
|
insertTitle?: string,
|
||||||
|
insertMarkup?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LinkCalloutDialog extends CalloutDialog<ILinkCalloutDialogOptions> {
|
||||||
|
private _selectionComplete: Deferred<ILinkCalloutDialogOptions> = new Deferred<ILinkCalloutDialogOptions>();
|
||||||
|
private _linkTextLabel: HTMLElement;
|
||||||
|
private _linkTextInputBox: InputBox;
|
||||||
|
private _linkAddressLabel: HTMLElement;
|
||||||
|
private _linkUrlInputBox: InputBox;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
title: string,
|
||||||
|
width: DialogWidth,
|
||||||
|
dialogProperties: IDialogProperties,
|
||||||
|
@IContextViewService private readonly _contextViewService: IContextViewService,
|
||||||
|
@IThemeService themeService: IThemeService,
|
||||||
|
@ILayoutService layoutService: ILayoutService,
|
||||||
|
@IAdsTelemetryService telemetryService: IAdsTelemetryService,
|
||||||
|
@IContextKeyService contextKeyService: IContextKeyService,
|
||||||
|
@IClipboardService clipboardService: IClipboardService,
|
||||||
|
@ILogService logService: ILogService,
|
||||||
|
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
title,
|
||||||
|
width,
|
||||||
|
dialogProperties,
|
||||||
|
themeService,
|
||||||
|
layoutService,
|
||||||
|
telemetryService,
|
||||||
|
contextKeyService,
|
||||||
|
clipboardService,
|
||||||
|
logService,
|
||||||
|
textResourcePropertiesService
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the dialog and returns a promise for what options the user chooses.
|
||||||
|
*/
|
||||||
|
public open(): Promise<ILinkCalloutDialogOptions> {
|
||||||
|
this.show();
|
||||||
|
return this._selectionComplete.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): void {
|
||||||
|
super.render();
|
||||||
|
attachModalDialogStyler(this, this._themeService);
|
||||||
|
this.addFooterButton(constants.insertButtonText, () => this.insert());
|
||||||
|
this.addFooterButton(constants.cancelButtonText, () => this.cancel(), undefined, true);
|
||||||
|
this.registerListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected renderBody(container: HTMLElement) {
|
||||||
|
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 = constants.linkTextLabel;
|
||||||
|
DOM.append(linkTextRow, this._linkTextLabel);
|
||||||
|
|
||||||
|
const linkTextInputContainer = DOM.$('.input-field');
|
||||||
|
this._linkTextInputBox = new InputBox(
|
||||||
|
linkTextInputContainer,
|
||||||
|
this._contextViewService,
|
||||||
|
{
|
||||||
|
placeholder: constants.linkTextPlaceholder,
|
||||||
|
ariaLabel: constants.linkTextLabel
|
||||||
|
});
|
||||||
|
DOM.append(linkTextRow, linkTextInputContainer);
|
||||||
|
|
||||||
|
let linkAddressRow = DOM.$('.row');
|
||||||
|
DOM.append(linkContentColumn, linkAddressRow);
|
||||||
|
this._linkAddressLabel = DOM.$('p');
|
||||||
|
this._linkAddressLabel.innerText = constants.linkAddressLabel;
|
||||||
|
DOM.append(linkAddressRow, this._linkAddressLabel);
|
||||||
|
|
||||||
|
const linkAddressInputContainer = DOM.$('.input-field');
|
||||||
|
this._linkUrlInputBox = new InputBox(
|
||||||
|
linkAddressInputContainer,
|
||||||
|
this._contextViewService,
|
||||||
|
{
|
||||||
|
placeholder: constants.linkAddressPlaceholder,
|
||||||
|
ariaLabel: constants.linkAddressLabel
|
||||||
|
});
|
||||||
|
DOM.append(linkAddressRow, linkAddressInputContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private registerListeners(): void {
|
||||||
|
this._register(styler.attachInputBoxStyler(this._linkTextInputBox, this._themeService));
|
||||||
|
this._register(styler.attachInputBoxStyler(this._linkUrlInputBox, this._themeService));
|
||||||
|
}
|
||||||
|
|
||||||
|
public insert(): void {
|
||||||
|
this.hide();
|
||||||
|
this._selectionComplete.resolve({
|
||||||
|
insertMarkup: `<a href="${strings.escape(this._linkUrlInputBox.value)}">${strings.escape(this._linkTextInputBox.value)}</a>`,
|
||||||
|
});
|
||||||
|
this.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public cancel(): void {
|
||||||
|
super.cancel();
|
||||||
|
this._selectionComplete.resolve({
|
||||||
|
insertMarkup: ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.modal.callout-dialog .modal-content .insert-image .flex-container {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.modal.callout-dialog .modal-content .insert-image .flex-container > div {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 380px;
|
||||||
|
}
|
||||||
|
.modal.callout-dialog .modal-content .radio-group input {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
.modal.callout-dialog .modal-content .radio-group span {
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
@@ -16,7 +16,8 @@ import { Selection } from 'vs/editor/common/core/selection';
|
|||||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||||
import { Position } from 'vs/editor/common/core/position';
|
import { Position } from 'vs/editor/common/core/position';
|
||||||
import { MarkdownToolbarComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.component';
|
import { MarkdownToolbarComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/markdownToolbar.component';
|
||||||
import { CalloutDialog, CalloutType } from 'sql/workbench/browser/modal/calloutDialog';
|
import { ImageCalloutDialog } from 'sql/workbench/contrib/notebook/browser/calloutDialog/imageCalloutDialog';
|
||||||
|
import { LinkCalloutDialog } from 'sql/workbench/contrib/notebook/browser/calloutDialog/linkCalloutDialog';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { DialogWidth } from 'sql/workbench/api/common/sqlExtHostTypes';
|
import { DialogWidth } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
|
|
||||||
@@ -130,7 +131,8 @@ export class TransformMarkdownAction extends Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class MarkdownTextTransformer {
|
export class MarkdownTextTransformer {
|
||||||
private _callout: CalloutDialog;
|
private _imageCallout: ImageCalloutDialog;
|
||||||
|
private _linkCallout: LinkCalloutDialog;
|
||||||
private readonly insertLinkHeading = localize('callout.insertLinkHeading', "Insert link");
|
private readonly insertLinkHeading = localize('callout.insertLinkHeading', "Insert link");
|
||||||
private readonly insertImageHeading = localize('callout.insertImageHeading', "Insert image");
|
private readonly insertImageHeading = localize('callout.insertImageHeading', "Insert image");
|
||||||
|
|
||||||
@@ -208,24 +210,28 @@ export class MarkdownTextTransformer {
|
|||||||
const triggerPosY = triggerElement.getBoundingClientRect().top;
|
const triggerPosY = triggerElement.getBoundingClientRect().top;
|
||||||
const triggerHeight = triggerElement.offsetHeight;
|
const triggerHeight = triggerElement.offsetHeight;
|
||||||
const triggerWidth = triggerElement.offsetWidth;
|
const triggerWidth = triggerElement.offsetWidth;
|
||||||
|
const dialogProperties = { xPos: triggerPosX, yPos: triggerPosY, width: triggerWidth, height: triggerHeight };
|
||||||
|
let calloutOptions;
|
||||||
/**
|
/**
|
||||||
* Width value here reflects designs for Notebook callouts.
|
* Width value here reflects designs for Notebook callouts.
|
||||||
*/
|
*/
|
||||||
const width: DialogWidth = 452;
|
const width: DialogWidth = 452;
|
||||||
|
|
||||||
const calloutType: CalloutType = type === MarkdownButtonType.IMAGE_PREVIEW ? 'IMAGE' : 'LINK';
|
if (type === MarkdownButtonType.IMAGE_PREVIEW) {
|
||||||
|
if (!this._imageCallout) {
|
||||||
let title = type === MarkdownButtonType.IMAGE_PREVIEW ? this.insertImageHeading : this.insertLinkHeading;
|
this._imageCallout = this._instantiationService.createInstance(ImageCalloutDialog, this.insertImageHeading, width, dialogProperties);
|
||||||
|
this._imageCallout.render();
|
||||||
if (!this._callout) {
|
calloutOptions = await this._imageCallout.open();
|
||||||
const dialogProperties = { xPos: triggerPosX, yPos: triggerPosY, width: triggerWidth, height: triggerHeight };
|
calloutOptions.insertTitle = this.insertImageHeading;
|
||||||
this._callout = this._instantiationService.createInstance(CalloutDialog, calloutType, title, width, dialogProperties);
|
}
|
||||||
this._callout.render();
|
} else {
|
||||||
|
if (!this._linkCallout) {
|
||||||
|
this._linkCallout = this._instantiationService.createInstance(LinkCalloutDialog, this.insertLinkHeading, width, dialogProperties);
|
||||||
|
this._linkCallout.render();
|
||||||
|
calloutOptions = await this._linkCallout.open();
|
||||||
|
calloutOptions.insertTitle = this.insertLinkHeading;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let calloutOptions = await this._callout.open();
|
|
||||||
calloutOptions.insertTitle = title;
|
|
||||||
calloutOptions.calloutType = calloutType;
|
|
||||||
|
|
||||||
return calloutOptions.insertMarkup;
|
return calloutOptions.insertMarkup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user