From 2fe82e4b2f5613e4c05ce90fd2ccc307f6583ec9 Mon Sep 17 00:00:00 2001 From: Charles Gagnon Date: Tue, 8 Oct 2019 16:02:55 -0700 Subject: [PATCH] Styling updates for HDFS Manage Access Dialog (#7551) --- .../mssql/resources/dark/delete_inverse.svg | 3 + extensions/mssql/resources/light/delete.svg | 3 + .../src/hdfs/ui/hdfsManageAccessDialog.ts | 191 +++++++++++------- extensions/mssql/src/hdfs/ui/uiConstants.ts | 9 +- extensions/mssql/src/iconHelper.ts | 25 +++ extensions/mssql/src/localizedConstants.ts | 2 + extensions/mssql/src/main.ts | 3 + .../modelComponents/button.component.ts | 2 +- 8 files changed, 165 insertions(+), 73 deletions(-) create mode 100644 extensions/mssql/resources/dark/delete_inverse.svg create mode 100644 extensions/mssql/resources/light/delete.svg create mode 100644 extensions/mssql/src/iconHelper.ts diff --git a/extensions/mssql/resources/dark/delete_inverse.svg b/extensions/mssql/resources/dark/delete_inverse.svg new file mode 100644 index 0000000000..548f3729d0 --- /dev/null +++ b/extensions/mssql/resources/dark/delete_inverse.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/extensions/mssql/resources/light/delete.svg b/extensions/mssql/resources/light/delete.svg new file mode 100644 index 0000000000..548f3729d0 --- /dev/null +++ b/extensions/mssql/resources/light/delete.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/extensions/mssql/src/hdfs/ui/hdfsManageAccessDialog.ts b/extensions/mssql/src/hdfs/ui/hdfsManageAccessDialog.ts index a24288a68f..a4998657cb 100644 --- a/extensions/mssql/src/hdfs/ui/hdfsManageAccessDialog.ts +++ b/extensions/mssql/src/hdfs/ui/hdfsManageAccessDialog.ts @@ -8,9 +8,10 @@ import { HdfsModel } from '../hdfsModel'; import { IFileSource } from '../../objectExplorerNodeProvider/fileSources'; import { IAclStatus, AclEntry, AclEntryType } from '../../hdfs/aclEntry'; import { cssStyles } from './uiConstants'; -import { ownersHeader, permissionsHeader, stickyHeader, readHeader, writeHeader, executeHeader, manageAccessTitle, allOthersHeader, addUserOrGroupHeader, enterNamePlaceholder, addLabel, accessHeader, defaultHeader, applyText, applyRecursivelyText, userLabel, groupLabel, errorApplyingAclChanges } from '../../localizedConstants'; +import * as loc from '../../localizedConstants'; import { HdfsError } from '../webhdfs'; import { ApiWrapper } from '../../apiWrapper'; +import { IconPathHelper } from '../../iconHelper'; const permissionsNameColumnWidth = 250; const permissionsStickyColumnWidth = 50; @@ -19,7 +20,8 @@ const permissionsWriteColumnWidth = 50; const permissionsExecuteColumnWidth = 50; const permissionsDeleteColumnWidth = 50; -const permissionsRowHeight = 75; +const permissionsRowHeight = 35; +const locationLabelHeight = 23; // Fits the text size without too much white space export class ManageAccessDialog { @@ -43,15 +45,15 @@ export class ManageAccessDialog { public openDialog(): void { if (!this.dialog) { - this.dialog = this.apiWrapper.createDialog(manageAccessTitle, 'HdfsManageAccess', true); - this.dialog.okButton.label = applyText; + this.dialog = this.apiWrapper.createDialog(loc.manageAccessTitle, 'HdfsManageAccess', true); + this.dialog.okButton.label = loc.applyText; - const applyRecursivelyButton = azdata.window.createButton(applyRecursivelyText); + const applyRecursivelyButton = azdata.window.createButton(loc.applyRecursivelyText); applyRecursivelyButton.onClick(async () => { try { await this.hdfsModel.apply(true); } catch (err) { - this.apiWrapper.showErrorMessage(errorApplyingAclChanges(err instanceof HdfsError ? err.message : err)); + this.apiWrapper.showErrorMessage(loc.errorApplyingAclChanges(err instanceof HdfsError ? err.message : err)); } }); this.dialog.customButtons = [applyRecursivelyButton]; @@ -60,11 +62,11 @@ export class ManageAccessDialog { await this.hdfsModel.apply(); return true; } catch (err) { - this.apiWrapper.showErrorMessage(errorApplyingAclChanges(err instanceof HdfsError ? err.message : err)); + this.apiWrapper.showErrorMessage(loc.errorApplyingAclChanges(err instanceof HdfsError ? err.message : err)); } return false; }); - const tab = azdata.window.createTab(manageAccessTitle); + const tab = azdata.window.createTab(loc.manageAccessTitle); tab.registerContent(async (modelView: azdata.ModelView) => { this.modelBuilder = modelView.modelBuilder; @@ -74,75 +76,107 @@ export class ManageAccessDialog { this.rootLoadingComponent = modelView.modelBuilder.loadingComponent().withItem(rootContainer).component(); - const pathLabel = modelView.modelBuilder.text().withProperties({ value: this.hdfsPath }).component(); - rootContainer.addItem(pathLabel, { flex: '0 0 auto' }); + // We nest the content inside another container for the margins - getting them on the root container isn't supported + const contentContainer = modelView.modelBuilder.flexContainer() + .withLayout({ flexFlow: 'column', width: '100%', height: '100%' }) + .component(); + rootContainer.addItem(contentContainer, { CSSStyles: { 'margin-left': '20px', 'margin-right': '20px' } }); + + const locationContainer = modelView.modelBuilder.flexContainer().withLayout({ flexFlow: 'row', alignItems: 'center' }).component(); + + const locationLabel = modelView.modelBuilder.text() + .withProperties({ + value: loc.locationTitle, + CSSStyles: { ...cssStyles.titleCss } + }).component(); + + const pathLabel = modelView.modelBuilder.text() + .withProperties({ + value: this.hdfsPath, + title: this.hdfsPath, + height: locationLabelHeight, + CSSStyles: { 'user-select': 'text', 'overflow': 'hidden', 'text-overflow': 'ellipsis', ...cssStyles.titleCss } + }).component(); + + locationContainer.addItem(locationLabel, + { + flex: '0 0 auto', + CSSStyles: { 'margin-bottom': '5px' } + }); + locationContainer.addItem(pathLabel, + { + flex: '1 1 auto', + CSSStyles: { 'border': '1px solid #ccc', 'padding': '5px', 'margin-left': '10px', 'min-height': `${locationLabelHeight}px` } + }); + + contentContainer.addItem(locationContainer, { flex: '0 0 auto', CSSStyles: { 'margin-top': '20px' } }); // ===================== // = Permissions Title = // ===================== const permissionsTitle = modelView.modelBuilder.text() - .withProperties({ value: permissionsHeader, CSSStyles: { 'margin-block-start': '0px', 'margin-block-end': '10px' } }) + .withProperties({ value: loc.permissionsHeader }) .component(); - rootContainer.addItem(permissionsTitle, { CSSStyles: { 'margin-top': '15px', 'padding-left': '10px', ...cssStyles.titleCss } }); + contentContainer.addItem(permissionsTitle, { CSSStyles: { 'margin-top': '15px', ...cssStyles.titleCss } }); // ============================== // = Owners permissions section = // ============================== - const ownersPermissionsHeaderRow = createPermissionsHeaderRow(modelView.modelBuilder, ownersHeader, true); - rootContainer.addItem(ownersPermissionsHeaderRow, { CSSStyles: { 'padding-left': '10px', 'box-sizing': 'border-box', 'user-select': 'text' } }); + const ownersPermissionsHeaderRow = createPermissionsHeaderRow(modelView.modelBuilder, loc.ownersHeader, true); + contentContainer.addItem(ownersPermissionsHeaderRow, { CSSStyles: { ...cssStyles.tableHeaderLayoutCss } }); // Empty initially - this is going to eventually be populated with the owner/owning group permissions this.ownersPermissionsContainer = modelView.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component(); - rootContainer.addItem(this.ownersPermissionsContainer); + contentContainer.addItem(this.ownersPermissionsContainer, { flex: '0 0 auto', CSSStyles: { 'margin-bottom': '20px' } }); // ============================== // = Others permissions section = // ============================== const othersPermissionsHeaderRow = modelView.modelBuilder.flexContainer().withLayout({ flexFlow: 'row' }).component(); - const ownersHeaderCell = modelView.modelBuilder.text().withProperties({ value: allOthersHeader }).component(); + const ownersHeaderCell = modelView.modelBuilder.text().withProperties({ value: loc.allOthersHeader }).component(); othersPermissionsHeaderRow.addItem(ownersHeaderCell, { flex: '1 1 auto', CSSStyles: { 'width': `${permissionsNameColumnWidth}px`, 'min-width': `${permissionsNameColumnWidth}px`, ...cssStyles.tableHeaderCss } }); - rootContainer.addItem(othersPermissionsHeaderRow, { CSSStyles: { 'padding-left': '10px', 'box-sizing': 'border-box', 'user-select': 'text' } }); + contentContainer.addItem(othersPermissionsHeaderRow, { CSSStyles: { ...cssStyles.tableHeaderLayoutCss } }); // Empty initially - this is eventually going to be populated with the "Everyone" permissions this.othersPermissionsContainer = modelView.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component(); - rootContainer.addItem(this.othersPermissionsContainer); + contentContainer.addItem(this.othersPermissionsContainer, { flex: '0 0 auto', CSSStyles: { 'margin-bottom': '20px' } }); // =========================== // = Add User Or Group Input = // =========================== const addUserOrGroupTitle = modelView.modelBuilder.text() - .withProperties({ value: addUserOrGroupHeader, CSSStyles: { 'margin-block-start': '0px', 'margin-block-end': '10px' } }) + .withProperties({ value: loc.addUserOrGroupHeader, CSSStyles: { 'margin-block-start': '0px', 'margin-block-end': '10px' } }) .component(); - rootContainer.addItem(addUserOrGroupTitle, { CSSStyles: { 'margin-top': '15px', 'padding-left': '10px', ...cssStyles.titleCss } }); + contentContainer.addItem(addUserOrGroupTitle, { CSSStyles: { 'margin-top': '15px', ...cssStyles.titleCss } }); const typeContainer = modelView.modelBuilder.flexContainer().withProperties({ flexFlow: 'row' }).component(); const aclEntryTypeGroup = 'aclEntryType'; - const userTypeButton = this.createRadioButton(modelView.modelBuilder, userLabel, aclEntryTypeGroup, AclEntryType.user); - const groupTypeButton = this.createRadioButton(modelView.modelBuilder, groupLabel, aclEntryTypeGroup, AclEntryType.group); + const userTypeButton = this.createRadioButton(modelView.modelBuilder, loc.userLabel, aclEntryTypeGroup, AclEntryType.user); + const groupTypeButton = this.createRadioButton(modelView.modelBuilder, loc.groupLabel, aclEntryTypeGroup, AclEntryType.group); userTypeButton.checked = true; this.addUserOrGroupSelectedType = AclEntryType.user; typeContainer.addItems([userTypeButton, groupTypeButton], { flex: '0 0 auto' }); - rootContainer.addItem(typeContainer, { flex: '0 0 auto' }); + contentContainer.addItem(typeContainer, { flex: '0 0 auto' }); const addUserOrGroupInputRow = modelView.modelBuilder.flexContainer().withLayout({ flexFlow: 'row' }).component(); this.addUserOrGroupInput = modelView.modelBuilder.inputBox() - .withProperties( - { - inputType: 'text', - placeHolder: enterNamePlaceholder, - width: 250, - stopEnterPropagation: true - }).component(); + .withProperties({ + inputType: 'text', + placeHolder: loc.enterNamePlaceholder, + width: 250, + stopEnterPropagation: true + }) + .component(); this.addUserOrGroupInput.onEnterKeyPressed((value: string) => { this.hdfsModel.createAndAddAclEntry(value, this.addUserOrGroupSelectedType); this.addUserOrGroupInput.value = ''; }); - const addUserOrGroupButton = modelView.modelBuilder.button().withProperties({ label: addLabel, width: 75 }).component(); + const addUserOrGroupButton = modelView.modelBuilder.button().withProperties({ label: loc.addLabel, width: 75 }).component(); addUserOrGroupButton.onDidClick(() => { this.hdfsModel.createAndAddAclEntry(this.addUserOrGroupInput.value, this.addUserOrGroupSelectedType); this.addUserOrGroupInput.value = ''; @@ -155,21 +189,24 @@ export class ManageAccessDialog { addUserOrGroupButton.enabled = true; } }); + addUserOrGroupInputRow.addItem(this.addUserOrGroupInput, { flex: '0 0 auto' }); addUserOrGroupInputRow.addItem(addUserOrGroupButton, { flex: '0 0 auto', CSSStyles: { 'margin-left': '20px' } }); - rootContainer.addItem(addUserOrGroupInputRow); + contentContainer.addItem(addUserOrGroupInputRow, { flex: '0 0 auto', CSSStyles: { 'margin-bottom': '20px' } }); // ================================================= // = Named Users and Groups permissions header row = // ================================================= - const namedUsersAndGroupsPermissionsHeaderRow = createPermissionsHeaderRow(modelView.modelBuilder, ownersHeader, false); - rootContainer.addItem(namedUsersAndGroupsPermissionsHeaderRow, { CSSStyles: { 'padding-left': '10px', 'box-sizing': 'border-box', 'user-select': 'text' } }); + const namedUsersAndGroupsPermissionsHeaderRow = createPermissionsHeaderRow(modelView.modelBuilder, loc.namedUsersAndGroupsHeader, false); + contentContainer.addItem(namedUsersAndGroupsPermissionsHeaderRow, { CSSStyles: { ...cssStyles.tableHeaderLayoutCss } }); // Empty initially - this is eventually going to be populated with the ACL entries set for this path - this.namedUsersAndGroupsPermissionsContainer = modelView.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component(); - rootContainer.addItem(this.namedUsersAndGroupsPermissionsContainer); + this.namedUsersAndGroupsPermissionsContainer = modelView.modelBuilder.flexContainer() + .withLayout({ flexFlow: 'column' }) + .component(); + contentContainer.addItem(this.namedUsersAndGroupsPermissionsContainer, { flex: '1', CSSStyles: { 'overflow': 'scroll', 'min-height': '200px' } }); this.viewInitialized = true; this.handleAclStatusUpdated(this.hdfsModel.aclStatus); @@ -190,18 +227,18 @@ export class ManageAccessDialog { const ownerPermissionsRow = this.createOwnerPermissionsRow(this.modelBuilder, aclStatus.stickyBit, aclStatus.owner); const owningGroupPermissionsRow = this.createPermissionsRow(this.modelBuilder, aclStatus.group, false); this.ownersPermissionsContainer.clearItems(); - this.ownersPermissionsContainer.addItems([ownerPermissionsRow, owningGroupPermissionsRow], { CSSStyles: { 'border-bottom': cssStyles.tableBorder, 'border-top': cssStyles.tableBorder } }); + this.ownersPermissionsContainer.addItems([ownerPermissionsRow, owningGroupPermissionsRow], { CSSStyles: { 'border-bottom': cssStyles.tableBorderCss, 'border-top': cssStyles.tableBorderCss, 'margin-right': '14px' } }); // Others const otherPermissionsRow = this.createPermissionsRow(this.modelBuilder, aclStatus.other, false); this.othersPermissionsContainer.clearItems(); - this.othersPermissionsContainer.addItem(otherPermissionsRow, { CSSStyles: { 'border-bottom': cssStyles.tableBorder, 'border-top': cssStyles.tableBorder } }); + this.othersPermissionsContainer.addItem(otherPermissionsRow, { CSSStyles: { 'border-bottom': cssStyles.tableBorderCss, 'border-top': cssStyles.tableBorderCss, 'margin-right': '14px' } }); this.namedUsersAndGroupsPermissionsContainer.clearItems(); // Named users and groups aclStatus.entries.forEach(entry => { const namedEntryRow = this.createPermissionsRow(this.modelBuilder, entry, true); - this.namedUsersAndGroupsPermissionsContainer.addItem(namedEntryRow, { CSSStyles: { 'border-bottom': cssStyles.tableBorder, 'border-top': cssStyles.tableBorder } }); + this.namedUsersAndGroupsPermissionsContainer.addItem(namedEntryRow, { CSSStyles: { 'border-bottom': cssStyles.tableBorderCss, 'border-top': cssStyles.tableBorderCss } }); }); this.rootLoadingComponent.loading = false; @@ -217,10 +254,9 @@ export class ManageAccessDialog { private createOwnerPermissionsRow(builder: azdata.ModelBuilder, sticky: boolean, entry: AclEntry): azdata.FlexContainer { const row = this.createPermissionsRow(builder, entry, false); - const stickyCheckbox = builder.checkBox().withProperties({ checked: sticky, height: 25, width: 25 }).component(); - const stickyContainer = builder.flexContainer().withLayout({ width: permissionsReadColumnWidth }).withItems([stickyCheckbox]).component(); + const stickyComponents = createCheckbox(builder, sticky, permissionsReadColumnWidth, permissionsRowHeight); // Insert after name item but before other checkboxes - row.insertItem(stickyContainer, 1, { flex: '0 0 auto' }); + row.insertItem(stickyComponents.container, 1, { flex: '0 0 auto' }); return row; } @@ -245,7 +281,7 @@ export class ManageAccessDialog { // Access - Execute const accessExecuteComponents = createCheckbox(builder, entry.permission.execute, permissionsExecuteColumnWidth, permissionsRowHeight); - rowContainer.addItem(accessExecuteComponents.container, { flex: '0 0 auto', CSSStyles: { 'border-right': cssStyles.tableBorder } }); + rowContainer.addItem(accessExecuteComponents.container, { flex: '0 0 auto', CSSStyles: { 'border-right': cssStyles.tableBorderCss } }); accessExecuteComponents.checkbox.onChanged(() => { entry.permission.execute = accessExecuteComponents.checkbox.checked; }); @@ -271,14 +307,23 @@ export class ManageAccessDialog { // entry.permission.execute = accessReadComponents.checkbox.checked; TODO hook up default logic }); - const deleteContainer = builder.flexContainer().withLayout({ width: permissionsDeleteColumnWidth }).component(); + const deleteContainer = builder.flexContainer().withLayout({ width: permissionsDeleteColumnWidth, height: permissionsRowHeight }).component(); if (includeDelete) { - const deleteButton = builder.button().withProperties({ label: 'Delete' }).component(); + const deleteButton = builder.button() + .withProperties( + { + label: '', + title: loc.deleteTitle, + iconPath: IconPathHelper.delete, + width: 20, + height: 20 + }) + .component(); deleteButton.onDidClick(() => { this.hdfsModel.deleteAclEntry(entry); }); - deleteContainer.addItem(deleteButton, { flex: '0 0 auto' }); + deleteContainer.addItem(deleteButton); } - rowContainer.addItem(deleteContainer, { flex: '0 0 auto' }); + rowContainer.addItem(deleteContainer, { flex: '0 0 auto', CSSStyles: { 'margin-top': '5px', 'margin-left': '5px' } }); return rowContainer; } @@ -296,10 +341,13 @@ function createPermissionsHeaderRow(modelBuilder: azdata.ModelBuilder, nameColum // Section Headers const sectionHeaderContainer = modelBuilder.flexContainer().withLayout({ flexFlow: 'row', justifyContent: 'flex-end' }).component(); - const accessSectionHeader = modelBuilder.text().withProperties({ value: accessHeader }).component(); - sectionHeaderContainer.addItem(accessSectionHeader, { CSSStyles: { 'width': `${permissionsReadColumnWidth + permissionsWriteColumnWidth + permissionsExecuteColumnWidth}px`, 'min-width': `${permissionsReadColumnWidth + permissionsWriteColumnWidth + permissionsExecuteColumnWidth}px`, ...cssStyles.tableHeaderCss } }); - const defaultSectionHeader = modelBuilder.text().withProperties({ value: defaultHeader }).component(); - sectionHeaderContainer.addItem(defaultSectionHeader, { CSSStyles: { 'width': `${permissionsReadColumnWidth + permissionsWriteColumnWidth + permissionsExecuteColumnWidth}px`, 'min-width': `${permissionsReadColumnWidth + permissionsWriteColumnWidth + permissionsExecuteColumnWidth}px`, ...cssStyles.tableHeaderCss } }); + const accessSectionHeader = modelBuilder.text().withProperties({ value: loc.accessHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component(); + sectionHeaderContainer.addItem(accessSectionHeader, { CSSStyles: { 'width': `${permissionsReadColumnWidth + permissionsWriteColumnWidth + permissionsExecuteColumnWidth}px`, 'min-width': `${permissionsReadColumnWidth + permissionsWriteColumnWidth + permissionsExecuteColumnWidth}px` } }); + const defaultSectionHeader = modelBuilder.text().withProperties({ value: loc.defaultHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component(); + sectionHeaderContainer.addItem(defaultSectionHeader, { CSSStyles: { 'width': `${permissionsReadColumnWidth + permissionsWriteColumnWidth + permissionsExecuteColumnWidth}px`, 'min-width': `${permissionsReadColumnWidth + permissionsWriteColumnWidth + permissionsExecuteColumnWidth}px` } }); + // Delete - just used as a spacer + const deleteSectionHeader = modelBuilder.text().component(); + sectionHeaderContainer.addItem(deleteSectionHeader, { CSSStyles: { 'width': `${permissionsDeleteColumnWidth}px`, 'min-width': `${permissionsDeleteColumnWidth}px` } }); rowsContainer.addItem(sectionHeaderContainer); @@ -308,24 +356,24 @@ function createPermissionsHeaderRow(modelBuilder: azdata.ModelBuilder, nameColum const ownersCell = modelBuilder.text().withProperties({ value: nameColumnText }).component(); headerRowContainer.addItem(ownersCell, { flex: '1 1 auto', CSSStyles: { 'width': `${permissionsNameColumnWidth}px`, 'min-width': `${permissionsNameColumnWidth}px`, ...cssStyles.tableHeaderCss } }); if (includeSticky) { - const stickyCell = modelBuilder.text().withProperties({ value: stickyHeader }).component(); - headerRowContainer.addItem(stickyCell, { CSSStyles: { 'width': `${permissionsStickyColumnWidth}px`, 'min-width': `${permissionsStickyColumnWidth}px`, ...cssStyles.tableHeaderCss } }); + const stickyCell = modelBuilder.text().withProperties({ value: loc.stickyHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component(); + headerRowContainer.addItem(stickyCell, { CSSStyles: { 'width': `${permissionsStickyColumnWidth}px`, 'min-width': `${permissionsStickyColumnWidth}px` } }); } // Access Permissions Group - const accessReadCell = modelBuilder.text().withProperties({ value: readHeader }).component(); - headerRowContainer.addItem(accessReadCell, { CSSStyles: { 'width': `${permissionsReadColumnWidth}px`, 'min-width': `${permissionsReadColumnWidth}px`, ...cssStyles.tableHeaderCss } }); - const accessWriteCell = modelBuilder.text().withProperties({ value: writeHeader }).component(); - headerRowContainer.addItem(accessWriteCell, { CSSStyles: { 'width': `${permissionsWriteColumnWidth}px`, 'min-width': `${permissionsWriteColumnWidth}px`, ...cssStyles.tableHeaderCss } }); - const accessExecuteCell = modelBuilder.text().withProperties({ value: executeHeader }).component(); - headerRowContainer.addItem(accessExecuteCell, { CSSStyles: { 'width': `${permissionsExecuteColumnWidth}px`, 'min-width': `${permissionsExecuteColumnWidth}px`, ...cssStyles.tableHeaderCss } }); + const accessReadCell = modelBuilder.text().withProperties({ value: loc.readHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component(); + headerRowContainer.addItem(accessReadCell, { CSSStyles: { 'width': `${permissionsReadColumnWidth}px`, 'min-width': `${permissionsReadColumnWidth}px` } }); + const accessWriteCell = modelBuilder.text().withProperties({ value: loc.writeHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component(); + headerRowContainer.addItem(accessWriteCell, { CSSStyles: { 'width': `${permissionsWriteColumnWidth}px`, 'min-width': `${permissionsWriteColumnWidth}px` } }); + const accessExecuteCell = modelBuilder.text().withProperties({ value: loc.executeHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component(); + headerRowContainer.addItem(accessExecuteCell, { CSSStyles: { 'width': `${permissionsExecuteColumnWidth}px`, 'min-width': `${permissionsExecuteColumnWidth}px`, 'margin-right': '5px' } }); // Default Permissions Group - const defaultReadCell = modelBuilder.text().withProperties({ value: readHeader }).component(); - headerRowContainer.addItem(defaultReadCell, { CSSStyles: { 'width': `${permissionsReadColumnWidth}px`, 'min-width': `${permissionsReadColumnWidth}px`, ...cssStyles.tableHeaderCss } }); - const defaultWriteCell = modelBuilder.text().withProperties({ value: writeHeader }).component(); - headerRowContainer.addItem(defaultWriteCell, { CSSStyles: { 'width': `${permissionsWriteColumnWidth}px`, 'min-width': `${permissionsWriteColumnWidth}px`, ...cssStyles.tableHeaderCss } }); - const defaultExecuteCell = modelBuilder.text().withProperties({ value: executeHeader }).component(); - headerRowContainer.addItem(defaultExecuteCell, { CSSStyles: { 'width': `${permissionsExecuteColumnWidth}px`, 'min-width': `${permissionsExecuteColumnWidth}px`, ...cssStyles.tableHeaderCss } }); + const defaultReadCell = modelBuilder.text().withProperties({ value: loc.readHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component(); + headerRowContainer.addItem(defaultReadCell, { CSSStyles: { 'width': `${permissionsReadColumnWidth}px`, 'min-width': `${permissionsReadColumnWidth}px` } }); + const defaultWriteCell = modelBuilder.text().withProperties({ value: loc.writeHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component(); + headerRowContainer.addItem(defaultWriteCell, { CSSStyles: { 'width': `${permissionsWriteColumnWidth}px`, 'min-width': `${permissionsWriteColumnWidth}px` } }); + const defaultExecuteCell = modelBuilder.text().withProperties({ value: loc.executeHeader, CSSStyles: { ...cssStyles.permissionsTableHeaderCss } }).component(); + headerRowContainer.addItem(defaultExecuteCell, { CSSStyles: { 'width': `${permissionsExecuteColumnWidth}px`, 'min-width': `${permissionsExecuteColumnWidth}px` } }); // Delete const deleteCell = modelBuilder.text().component(); headerRowContainer.addItem(deleteCell, { CSSStyles: { 'width': `${permissionsDeleteColumnWidth}px`, 'min-width': `${permissionsDeleteColumnWidth}px` } }); @@ -336,12 +384,15 @@ function createPermissionsHeaderRow(modelBuilder: azdata.ModelBuilder, nameColum } function createCheckbox(builder: azdata.ModelBuilder, checked: boolean, containerWidth: number, containerHeight: number): { container: azdata.FlexContainer, checkbox: azdata.CheckBoxComponent } { - const checkbox = builder.checkBox().withProperties({ checked: checked, height: 25, width: 25 }).component(); + const checkbox = builder.checkBox() + .withProperties({ checked: checked, height: 20, width: 20 }).component(); + const container = builder.flexContainer() + .withLayout({ width: containerWidth, height: containerHeight }) + //.withItems([checkbox], { CSSStyles: { ...cssStyles.permissionCheckboxCss }}) + .component(); + container.addItem(checkbox, { CSSStyles: { ...cssStyles.permissionCheckboxCss } }); return { - container: builder.flexContainer() - .withLayout({ width: containerWidth, height: containerHeight }) - .withItems([checkbox]) - .component(), + container: container, checkbox: checkbox }; diff --git a/extensions/mssql/src/hdfs/ui/uiConstants.ts b/extensions/mssql/src/hdfs/ui/uiConstants.ts index 8f12dd1cbb..b8e4cf382e 100644 --- a/extensions/mssql/src/hdfs/ui/uiConstants.ts +++ b/extensions/mssql/src/hdfs/ui/uiConstants.ts @@ -4,7 +4,12 @@ *--------------------------------------------------------------------------------------------*/ export namespace cssStyles { - export const tableBorder = '1px solid #ccc'; - export const titleCss = { 'font-size': '20px', 'font-weight': '600' }; + export const tableBorderCss = '1px solid #ccc'; + export const titleCss = { 'font-size': '20px', 'font-weight': '600', 'margin-block-end': '0px', 'margin-block-start': '0px' }; export const tableHeaderCss = { 'font-weight': 'bold', 'text-transform': 'uppercase', 'font-size': '10px', 'user-select': 'text' }; + export const permissionsTableHeaderCss = { ...tableHeaderCss, 'text-align': 'center' }; + export const permissionCheckboxCss = { 'margin-top': '5px', 'margin-left': '13px' }; + // The Named users/groups section potentially has a scrollbar so we shift all the other parts over so the columns still align correctly + export const scrollbarMarginGapCss = { 'margin-right': '17px' }; + export const tableHeaderLayoutCss = { 'padding-left': '10px', 'box-sizing': 'border-box', 'user-select': 'text', ...cssStyles.scrollbarMarginGapCss }; } diff --git a/extensions/mssql/src/iconHelper.ts b/extensions/mssql/src/iconHelper.ts new file mode 100644 index 0000000000..b307fc1cae --- /dev/null +++ b/extensions/mssql/src/iconHelper.ts @@ -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 * as vscode from 'vscode'; + +export interface IconPath { + dark: string; + light: string; +} + +export class IconPathHelper { + private static extensionContext: vscode.ExtensionContext; + + public static delete: IconPath; + + public static setExtensionContext(extensionContext: vscode.ExtensionContext) { + IconPathHelper.extensionContext = extensionContext; + IconPathHelper.delete = { + dark: IconPathHelper.extensionContext.asAbsolutePath('resources/dark/delete_inverse.svg'), + light: IconPathHelper.extensionContext.asAbsolutePath('resources/light/delete.svg') + }; + } +} diff --git a/extensions/mssql/src/localizedConstants.ts b/extensions/mssql/src/localizedConstants.ts index 8d42386b3f..23c36c3934 100644 --- a/extensions/mssql/src/localizedConstants.ts +++ b/extensions/mssql/src/localizedConstants.ts @@ -12,6 +12,7 @@ export const msgMissingNodeContext = localize('msgMissingNodeContext', 'Node Com // HDFS Manage Access Dialog Constants //////////////////////////////////// export const manageAccessTitle = localize('mssql.manageAccessTitle', "Manage Access"); +export const locationTitle = localize('mssql.locationTitle', "Location : "); export const permissionsHeader = localize('mssql.permissionsTitle', "Permissions"); export const ownersHeader = localize('mssql.ownersHeader', "Owners"); export const allOthersHeader = localize('mssql.allOthersHeader', "All Others"); @@ -20,6 +21,7 @@ export const userLabel = localize('mssql.userLabel', "User"); export const groupLabel = localize('mssql.groupLabel', "Group"); export const accessHeader = localize('mssql.accessHeader', "Access"); export const defaultHeader = localize('mssql.defaultHeader', "Default"); +export const deleteTitle = localize('mssql.delete', "Delete"); export const stickyHeader = localize('mssql.stickyHeader', "Sticky"); export const readHeader = localize('mssql.readHeader', "Read"); export const writeHeader = localize('mssql.writeHeader', "Write"); diff --git a/extensions/mssql/src/main.ts b/extensions/mssql/src/main.ts index a7c63c624a..44a0271b64 100644 --- a/extensions/mssql/src/main.ts +++ b/extensions/mssql/src/main.ts @@ -29,6 +29,7 @@ import { createMssqlApi } from './mssqlApiFactory'; import { localize } from './localize'; import { SqlToolsServer } from './sqlToolsServer'; import { promises as fs } from 'fs'; +import { IconPathHelper } from './iconHelper'; const msgSampleCodeDataFrame = localize('msgSampleCodeDataFrame', 'This sample code loads the file into a data frame and shows the first 10 results.'); @@ -47,6 +48,8 @@ export async function activate(context: vscode.ExtensionContext): Promise