mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-14 01:25:37 -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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import 'vs/css!./media/calloutDialog';
|
||||
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");
|
||||
export abstract class CalloutDialog<T> extends Modal {
|
||||
|
||||
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
|
||||
@@ -103,224 +44,17 @@ export class CalloutDialog extends Modal {
|
||||
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;
|
||||
}
|
||||
protected abstract renderBody(container: HTMLElement): void;
|
||||
|
||||
public render() {
|
||||
super.render();
|
||||
public abstract open(): Promise<T>;
|
||||
|
||||
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));
|
||||
}
|
||||
public cancel(): void {
|
||||
this.hide();
|
||||
this.dispose();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
.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;
|
||||
@@ -189,17 +89,6 @@
|
||||
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;
|
||||
|
||||
@@ -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 { 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 { 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 { DialogWidth } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
@@ -130,7 +131,8 @@ export class TransformMarkdownAction extends Action {
|
||||
}
|
||||
|
||||
export class MarkdownTextTransformer {
|
||||
private _callout: CalloutDialog;
|
||||
private _imageCallout: ImageCalloutDialog;
|
||||
private _linkCallout: LinkCalloutDialog;
|
||||
private readonly insertLinkHeading = localize('callout.insertLinkHeading', "Insert link");
|
||||
private readonly insertImageHeading = localize('callout.insertImageHeading', "Insert image");
|
||||
|
||||
@@ -208,24 +210,28 @@ export class MarkdownTextTransformer {
|
||||
const triggerPosY = triggerElement.getBoundingClientRect().top;
|
||||
const triggerHeight = triggerElement.offsetHeight;
|
||||
const triggerWidth = triggerElement.offsetWidth;
|
||||
const dialogProperties = { xPos: triggerPosX, yPos: triggerPosY, width: triggerWidth, height: triggerHeight };
|
||||
let calloutOptions;
|
||||
/**
|
||||
* 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();
|
||||
if (type === MarkdownButtonType.IMAGE_PREVIEW) {
|
||||
if (!this._imageCallout) {
|
||||
this._imageCallout = this._instantiationService.createInstance(ImageCalloutDialog, this.insertImageHeading, width, dialogProperties);
|
||||
this._imageCallout.render();
|
||||
calloutOptions = await this._imageCallout.open();
|
||||
calloutOptions.insertTitle = this.insertImageHeading;
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user