mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-12 19:18:32 -05:00
Use remote file dialog for database and server properties dialogs (#24390)
This commit is contained in:
7
src/sql/azdata.proposed.d.ts
vendored
7
src/sql/azdata.proposed.d.ts
vendored
@@ -2068,9 +2068,14 @@ declare module 'azdata' {
|
||||
* @param connectionUri The URI of the connection to the target server
|
||||
* @param targetPath The file path on the server machine to open by default in the dialog
|
||||
* @param fileFilters The filters used to limit which files are displayed in the file browser
|
||||
* @param showFoldersOnly Optional argument to specify whether the browser should only show folders
|
||||
* @returns The path of the file chosen from the dialog, and undefined if the dialog is closed without selecting anything.
|
||||
*/
|
||||
export function openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: FileFilters[]): Thenable<string | undefined>;
|
||||
export function openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: FileFilters[], showFoldersOnly?: boolean): Thenable<string | undefined>;
|
||||
}
|
||||
|
||||
export interface FileBrowserProvider extends DataProvider {
|
||||
openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean, showFoldersOnly?: boolean): Thenable<boolean>;
|
||||
}
|
||||
|
||||
export interface TableComponent {
|
||||
|
||||
@@ -338,8 +338,8 @@ export class MainThreadDataProtocol extends Disposable implements MainThreadData
|
||||
public $registerFileBrowserProvider(providerId: string, handle: number): Promise<any> {
|
||||
const self = this;
|
||||
this._fileBrowserService.registerProvider(providerId, <azdata.FileBrowserProvider>{
|
||||
openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean): Thenable<boolean> {
|
||||
return self._proxy.$openFileBrowser(handle, ownerUri, expandPath, fileFilters, changeFilter);
|
||||
openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean, showFoldersOnly?: boolean): Thenable<boolean> {
|
||||
return self._proxy.$openFileBrowser(handle, ownerUri, expandPath, fileFilters, changeFilter, showFoldersOnly);
|
||||
},
|
||||
expandFolderNode(ownerUri: string, expandPath: string): Thenable<boolean> {
|
||||
return self._proxy.$expandFolderNode(handle, ownerUri, expandPath);
|
||||
|
||||
@@ -20,13 +20,13 @@ export class MainThreadWindow extends Disposable implements MainThreadWindowShap
|
||||
super();
|
||||
}
|
||||
|
||||
public async $openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Promise<string | undefined> {
|
||||
public async $openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[], showFoldersOnly?: boolean): Promise<string | undefined> {
|
||||
let completion = new Promise<string | undefined>((resolve, reject) => {
|
||||
try {
|
||||
const handleOnClosed = (path: string | undefined) => {
|
||||
resolve(path);
|
||||
};
|
||||
this._fileBrowserDialogService.showDialog(connectionUri, targetPath, fileFilters, '', true, handleOnClosed);
|
||||
this._fileBrowserDialogService.showDialog(connectionUri, targetPath, fileFilters, '', true, handleOnClosed, showFoldersOnly);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
|
||||
@@ -632,8 +632,8 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
|
||||
/**
|
||||
* Open a file browser
|
||||
*/
|
||||
public override $openFileBrowser(handle: number, ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean): Thenable<boolean> {
|
||||
return this._resolveProvider<azdata.FileBrowserProvider>(handle).openFileBrowser(ownerUri, expandPath, fileFilters, changeFilter);
|
||||
public override $openFileBrowser(handle: number, ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean, showFoldersOnly?: boolean): Thenable<boolean> {
|
||||
return this._resolveProvider<azdata.FileBrowserProvider>(handle).openFileBrowser(ownerUri, expandPath, fileFilters, changeFilter, showFoldersOnly);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,7 +17,7 @@ export class ExtHostWindow implements ExtHostWindowShape {
|
||||
this._proxy = _mainContext.getProxy(SqlMainContext.MainThreadWindow);
|
||||
}
|
||||
|
||||
$openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Promise<string | undefined> {
|
||||
return this._proxy.$openServerFileBrowserDialog(connectionUri, targetPath, fileFilters);
|
||||
$openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[], showFoldersOnly?: boolean): Promise<string | undefined> {
|
||||
return this._proxy.$openServerFileBrowserDialog(connectionUri, targetPath, fileFilters, showFoldersOnly);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,8 +488,8 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
|
||||
openCustomErrorDialog(options: sqlExtHostTypes.IErrorDialogOptions): Thenable<string | undefined> {
|
||||
return extHostModelViewDialog.openCustomErrorDialog(options);
|
||||
},
|
||||
openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Thenable<string | undefined> {
|
||||
return extHostWindow.$openServerFileBrowserDialog(connectionUri, targetPath, fileFilters);
|
||||
openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[], showFoldersOnly?: boolean): Thenable<string | undefined> {
|
||||
return extHostWindow.$openServerFileBrowserDialog(connectionUri, targetPath, fileFilters, showFoldersOnly);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -355,7 +355,7 @@ export abstract class ExtHostDataProtocolShape {
|
||||
/**
|
||||
* Open a file browser
|
||||
*/
|
||||
$openFileBrowser(handle: number, ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean): Thenable<boolean> { throw ni(); }
|
||||
$openFileBrowser(handle: number, ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean, showFoldersOnly?: boolean): Thenable<boolean> { throw ni(); }
|
||||
|
||||
|
||||
/**
|
||||
@@ -833,7 +833,7 @@ export interface ExtHostWorkspaceShape {
|
||||
}
|
||||
|
||||
export interface ExtHostWindowShape {
|
||||
$openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Promise<string | undefined>;
|
||||
$openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[], showFoldersOnly?: boolean): Promise<string | undefined>;
|
||||
}
|
||||
|
||||
export interface MainThreadWorkspaceShape {
|
||||
@@ -843,7 +843,7 @@ export interface MainThreadWorkspaceShape {
|
||||
}
|
||||
|
||||
export interface MainThreadWindowShape {
|
||||
$openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Promise<string | undefined>;
|
||||
$openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[], showFoldersOnly?: boolean): Promise<string | undefined>;
|
||||
}
|
||||
|
||||
export interface MainThreadBackgroundTaskManagementShape extends IDisposable {
|
||||
|
||||
@@ -38,6 +38,8 @@ export class FileBrowserDialog extends Modal {
|
||||
private _body: HTMLElement;
|
||||
private _filePathInputBox: InputBox;
|
||||
private _fileFilterSelectBox: SelectBox;
|
||||
private _fileFilterRow: HTMLElement;
|
||||
private _originalFilterDisplay: string;
|
||||
private _okButton: Button;
|
||||
private _onOk = new Emitter<string>();
|
||||
public onOk: Event<string> = this._onOk.event;
|
||||
@@ -99,6 +101,8 @@ export class FileBrowserDialog extends Modal {
|
||||
this._fileFilterSelectBox.setAriaLabel(filterLabel);
|
||||
let filterBuilder = DialogHelper.appendRow(tableContainer, filterLabel, 'file-input-label', 'file-input-box');
|
||||
DialogHelper.appendInputSelectBox(filterBuilder, this._fileFilterSelectBox);
|
||||
this._fileFilterRow = tableContainer.childNodes[1] as HTMLElement;
|
||||
this._originalFilterDisplay = this._fileFilterRow.style.display;
|
||||
|
||||
this._okButton = this.addFooterButton(localize('fileBrowser.ok', "OK"), () => this.ok());
|
||||
this._okButton.enabled = false;
|
||||
@@ -112,10 +116,17 @@ export class FileBrowserDialog extends Modal {
|
||||
expandPath: string,
|
||||
fileFilters: [{ label: string, filters: string[] }],
|
||||
fileValidationServiceType: string,
|
||||
showFoldersOnly?: boolean
|
||||
): void {
|
||||
this._viewModel.initialize(ownerUri, expandPath, fileFilters, fileValidationServiceType);
|
||||
this._viewModel.initialize(ownerUri, expandPath, fileFilters, fileValidationServiceType, showFoldersOnly);
|
||||
this._viewModel.openFileBrowser(0, false).catch(err => onUnexpectedError(err));
|
||||
this._fileFilterSelectBox.setOptions(this._viewModel.formattedFileFilters, 0);
|
||||
if (showFoldersOnly) {
|
||||
this._fileFilterSelectBox.setOptions([]);
|
||||
this._fileFilterRow.style.display = 'none';
|
||||
} else {
|
||||
this._fileFilterSelectBox.setOptions(this._viewModel.formattedFileFilters, 0);
|
||||
this._fileFilterRow.style.display = this._originalFilterDisplay;
|
||||
}
|
||||
this._filePathInputBox.value = expandPath;
|
||||
this._isFolderSelected = true;
|
||||
this.enableOkButton();
|
||||
@@ -129,7 +140,7 @@ export class FileBrowserDialog extends Modal {
|
||||
|
||||
/* enter key */
|
||||
protected override onAccept() {
|
||||
if (this._okButton.enabled === true) {
|
||||
if (this._okButton.enabled) {
|
||||
this.ok();
|
||||
}
|
||||
}
|
||||
@@ -140,7 +151,7 @@ export class FileBrowserDialog extends Modal {
|
||||
}
|
||||
|
||||
private enableOkButton() {
|
||||
if (strings.isFalsyOrWhitespace(this._selectedFilePath) || this._isFolderSelected === true) {
|
||||
if (strings.isFalsyOrWhitespace(this._selectedFilePath) || (this._isFolderSelected && !this._viewModel.showFoldersOnly)) {
|
||||
this._okButton.enabled = false;
|
||||
} else {
|
||||
this._okButton.enabled = true;
|
||||
@@ -215,9 +226,11 @@ export class FileBrowserDialog extends Modal {
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this._fileFilterSelectBox.onDidSelect(selectData => {
|
||||
this.onFilterSelectChanged(selectData.index).catch(err => onUnexpectedError(err));
|
||||
}));
|
||||
if (this._fileFilterSelectBox) {
|
||||
this._register(this._fileFilterSelectBox.onDidSelect(selectData => {
|
||||
this.onFilterSelectChanged(selectData.index).catch(err => onUnexpectedError(err));
|
||||
}));
|
||||
}
|
||||
this._register(this._filePathInputBox.onDidChange(e => {
|
||||
this.onFilePathChange(e);
|
||||
}));
|
||||
|
||||
@@ -25,7 +25,8 @@ export class FileBrowserDialogController implements IFileBrowserDialogController
|
||||
fileFilters: [{ label: string, filters: string[] }],
|
||||
fileValidationServiceType: string,
|
||||
isWide: boolean,
|
||||
handleOnClosed: (path: string | undefined) => void
|
||||
handleOnClosed: (path: string | undefined) => void,
|
||||
showFoldersOnly?: boolean
|
||||
): void {
|
||||
if (!this._fileBrowserDialog) {
|
||||
this._fileBrowserDialog = this._instantiationService.createInstance(FileBrowserDialog, localize('filebrowser.selectFile', "Select a file"));
|
||||
@@ -44,6 +45,6 @@ export class FileBrowserDialogController implements IFileBrowserDialogController
|
||||
this._fileBrowserDialog = undefined;
|
||||
});
|
||||
|
||||
this._fileBrowserDialog.open(ownerUri, expandPath, fileFilters, fileValidationServiceType);
|
||||
this._fileBrowserDialog.open(ownerUri, expandPath, fileFilters, fileValidationServiceType, showFoldersOnly);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
.file-browser-dialog .tree-view {
|
||||
height: calc(100% - 90px);
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.file-table-content {
|
||||
@@ -21,8 +23,8 @@
|
||||
|
||||
.file-browser-dialog .option-section {
|
||||
padding-top: 10px;
|
||||
height: 90px;
|
||||
box-sizing: border-box;
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
|
||||
.file-input-label {
|
||||
|
||||
@@ -16,5 +16,6 @@ export interface IFileBrowserDialogController {
|
||||
fileFilters: { label: string, filters: string[] }[],
|
||||
fileValidationServiceType: string,
|
||||
isWide: boolean,
|
||||
handleOnOk: (path: string | undefined) => void): void;
|
||||
handleOnOk: (path: string | undefined) => void,
|
||||
showFoldersOnly?: boolean): void;
|
||||
}
|
||||
|
||||
@@ -46,11 +46,11 @@ export class FileBrowserService implements IFileBrowserService {
|
||||
return this._onPathValidate.event;
|
||||
}
|
||||
|
||||
public openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean): Promise<boolean> {
|
||||
public openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean, showFoldersOnly?: boolean): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
const provider = this.getProvider(ownerUri);
|
||||
if (provider) {
|
||||
provider.openFileBrowser(ownerUri, expandPath, fileFilters, changeFilter).then(result => {
|
||||
provider.openFileBrowser(ownerUri, expandPath, fileFilters, changeFilter, showFoldersOnly).then(result => {
|
||||
resolve(result);
|
||||
}, error => {
|
||||
reject(error);
|
||||
|
||||
@@ -15,6 +15,7 @@ export class FileBrowserViewModel {
|
||||
private _expandPath: string;
|
||||
private _fileFilters: [{ label: string, filters: string[] }];
|
||||
private _fileValidationServiceType: string;
|
||||
private _showFoldersOnly: boolean;
|
||||
public formattedFileFilters: string[];
|
||||
|
||||
constructor(@IFileBrowserService private _fileBrowserService: IFileBrowserService) {
|
||||
@@ -28,14 +29,20 @@ export class FileBrowserViewModel {
|
||||
this._fileBrowserService.onPathValidate(args => onPathValidateCallback(args));
|
||||
}
|
||||
|
||||
public get showFoldersOnly(): boolean {
|
||||
return this._showFoldersOnly;
|
||||
}
|
||||
|
||||
public initialize(ownerUri: string,
|
||||
expandPath: string,
|
||||
fileFilters: [{ label: string, filters: string[] }],
|
||||
fileValidationServiceType: string,
|
||||
showFoldersOnly?: boolean
|
||||
) {
|
||||
this._ownerUri = ownerUri;
|
||||
this._expandPath = expandPath;
|
||||
this._fileValidationServiceType = fileValidationServiceType;
|
||||
this._showFoldersOnly = !!showFoldersOnly;
|
||||
|
||||
if (!fileFilters) {
|
||||
this._fileFilters = [{ label: localize('allFiles', "All files"), filters: ['*'] }];
|
||||
@@ -55,7 +62,7 @@ export class FileBrowserViewModel {
|
||||
|
||||
public async openFileBrowser(filterIndex: number, changeFilter: boolean): Promise<void> {
|
||||
if (this._fileFilters[filterIndex]) {
|
||||
await this._fileBrowserService.openFileBrowser(this._ownerUri, this._expandPath, this._fileFilters[filterIndex].filters, changeFilter);
|
||||
await this._fileBrowserService.openFileBrowser(this._ownerUri, this._expandPath, this._fileFilters[filterIndex].filters, changeFilter, this._showFoldersOnly);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ export interface IFileBrowserService {
|
||||
/**
|
||||
* Open file browser
|
||||
*/
|
||||
openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean): Promise<boolean>;
|
||||
openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean, showFoldersOnly?: boolean): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Event called when file browser is opened
|
||||
|
||||
Reference in New Issue
Block a user