diff --git a/src/sql/azdata.proposed.d.ts b/src/sql/azdata.proposed.d.ts index 5ae8a8a563..6709eabff6 100644 --- a/src/sql/azdata.proposed.d.ts +++ b/src/sql/azdata.proposed.d.ts @@ -767,6 +767,13 @@ declare module 'azdata' { delete?: boolean; } + export enum CardType { + /** + * Card with the icon as a background image + */ + Image = 'Image' + } + export namespace workspace { /** * Creates and enters a workspace at the specified location diff --git a/src/sql/workbench/api/common/sqlExtHostTypes.ts b/src/sql/workbench/api/common/sqlExtHostTypes.ts index 484d642a29..2830c8001e 100644 --- a/src/sql/workbench/api/common/sqlExtHostTypes.ts +++ b/src/sql/workbench/api/common/sqlExtHostTypes.ts @@ -396,7 +396,8 @@ export enum DeclarativeDataType { export enum CardType { VerticalButton = 'VerticalButton', Details = 'Details', - ListItem = 'ListItem' + ListItem = 'ListItem', + Image = 'Image' } export enum Orientation { diff --git a/src/sql/workbench/browser/modelComponents/card.component.html b/src/sql/workbench/browser/modelComponents/card.component.html index 67a5da4ccc..5bcf6a4fa7 100644 --- a/src/sql/workbench/browser/modelComponents/card.component.html +++ b/src/sql/workbench/browser/modelComponents/card.component.html @@ -2,7 +2,7 @@
- +
@@ -47,6 +47,15 @@
+ + +
+
+
+

{{label}}

+
+
+
diff --git a/src/sql/workbench/browser/modelComponents/card.component.ts b/src/sql/workbench/browser/modelComponents/card.component.ts index 4545c4003a..5acaa23786 100644 --- a/src/sql/workbench/browser/modelComponents/card.component.ts +++ b/src/sql/workbench/browser/modelComponents/card.component.ts @@ -18,8 +18,9 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import * as DOM from 'vs/base/browser/dom'; import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/platform/dashboard/browser/interfaces'; -import { IColorTheme } from 'vs/platform/theme/common/themeService'; +import { IColorTheme, ICssStyleCollector, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ILogService } from 'vs/platform/log/common/log'; +import { CARD_OVERLAY_BACKGROUND, CARD_OVERLAY_FOREGROUND } from 'vs/workbench/common/theme'; export interface ActionDescriptor { label: string; @@ -52,7 +53,8 @@ export interface CardDescriptionItem { export enum CardType { VerticalButton = 'VerticalButton', Details = 'Details', - ListItem = 'ListItem' + ListItem = 'ListItem', + Image = 'Image' } @Component({ @@ -119,6 +121,11 @@ export default class CardComponent extends ComponentWithIconBase { + const backgroundColor = theme.getColor(CARD_OVERLAY_BACKGROUND); + const foregroundColor = theme.getColor(CARD_OVERLAY_FOREGROUND); + if (backgroundColor) { + collector.addRule(` + .model-card-legacy .card-label-overlay { + background-color: ${backgroundColor.toString()}; + } + `); + } + + if (foregroundColor) { + collector.addRule(` + .model-card-legacy.image-card .card-label { + color: ${foregroundColor.toString()}; + } + `); + } +}); diff --git a/src/sql/workbench/browser/modelComponents/media/legacycard.css b/src/sql/workbench/browser/modelComponents/media/legacycard.css index 86a4aa9998..6c72141091 100644 --- a/src/sql/workbench/browser/modelComponents/media/legacycard.css +++ b/src/sql/workbench/browser/modelComponents/media/legacycard.css @@ -26,6 +26,47 @@ min-width: 30px; } +.model-card-legacy.image-card { + height: auto; + margin: 0; + display: block; +} + +.model-card-legacy.image-card .card-content { + display: block; + width: 100%; + height: 100%; + padding: 0; + min-height: 100px; +} + +.model-card-legacy.image-card .card-image { + position: absolute; + height: 100%; + width: 100%; + background-size: contain; + background-position: initial; + background-repeat: no-repeat; + max-width: unset; + max-height: unset; +} + +.model-card-legacy.image-card .card-label-overlay { + position: absolute; + bottom: 0; + width: 100%; + height: 25px; + line-height: 25px; + padding: 0; + margin: 0; + display: block; +} + +.model-card-legacy.image-card .card-label { + margin: 0; + padding: 0px 10px +} + .model-card-legacy .card-vertical-button { position: relative; display: flex; @@ -103,6 +144,7 @@ border-width: 1px; border-color: rgb(0, 120, 215); border-style: solid; + z-index: 1; } .model-card-list-item-legacy .selection-indicator-container, .model-card-legacy .selection-indicator-container { diff --git a/src/sql/workbench/contrib/notebook/browser/notebookViews/insertCellsModal.module.ts b/src/sql/workbench/contrib/notebook/browser/notebookViews/insertCellsModal.module.ts new file mode 100644 index 0000000000..7575334b6d --- /dev/null +++ b/src/sql/workbench/contrib/notebook/browser/notebookViews/insertCellsModal.module.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { + ApplicationRef, ComponentFactoryResolver, NgModule, + Inject, forwardRef, Type +} from '@angular/core'; +import { APP_BASE_HREF, CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { BrowserModule } from '@angular/platform-browser'; +import { providerIterator } from 'sql/workbench/services/bootstrap/browser/bootstrapService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IBootstrapParams, ISelector } from 'sql/workbench/services/bootstrap/common/bootstrapParams'; +import CardComponent from 'sql/workbench/browser/modelComponents/card.component'; +import DivContainer from 'sql/workbench/browser/modelComponents/divContainer.component'; +import { ModelComponentWrapper } from 'sql/workbench/browser/modelComponents/modelComponentWrapper.component'; +import { InsertCellsScreenshots } from 'sql/workbench/contrib/notebook/browser/notebookViews/insertCellsScreenshots.component'; +import ImageComponent from 'sql/workbench/browser/modelComponents/image.component'; + +// Insertcells main angular module +export const InsertCellsModule = (params: IBootstrapParams, selector: string, instantiationService: IInstantiationService): Type => { + @NgModule({ + declarations: [ + CardComponent, + DivContainer, + ModelComponentWrapper, + InsertCellsScreenshots, + ImageComponent, + ], + entryComponents: [InsertCellsScreenshots, CardComponent, ImageComponent], + imports: [ + FormsModule, + CommonModule, + BrowserModule + ], + providers: [ + { provide: APP_BASE_HREF, useValue: '/' }, + { provide: IBootstrapParams, useValue: params }, + { provide: ISelector, useValue: selector }, + ...providerIterator(instantiationService) + ] + }) + class ModuleClass { + constructor( + @Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver, + @Inject(ISelector) private selector: string + ) { + } + ngDoBootstrap(appRef: ApplicationRef) { + const factory = this._resolver.resolveComponentFactory(InsertCellsScreenshots); + (factory).factory.selector = this.selector; + appRef.bootstrap(factory); + } + } + return ModuleClass; +}; diff --git a/src/sql/workbench/contrib/notebook/browser/notebookViews/insertCellsModal.ts b/src/sql/workbench/contrib/notebook/browser/notebookViews/insertCellsModal.ts index 4a9d0c3522..c3bd15c4a1 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebookViews/insertCellsModal.ts +++ b/src/sql/workbench/contrib/notebook/browser/notebookViews/insertCellsModal.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import 'vs/css!./insertCellsModal'; -import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox'; import { Button } from 'sql/base/browser/ui/button/button'; import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService'; import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; @@ -13,22 +12,24 @@ import { ITextResourcePropertiesService } from 'vs/editor/common/services/textRe import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { ILogService } from 'vs/platform/log/common/log'; -import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import * as DOM from 'vs/base/browser/dom'; -import { attachCheckboxStyler } from 'sql/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ServiceOptionType } from 'sql/platform/connection/common/interfaces'; import { ServiceOption } from 'azdata'; import * as DialogHelper from 'sql/workbench/browser/modal/dialogHelper'; import { TextCellComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/textCell.component'; -import { ComponentFactoryResolver, ViewContainerRef } from '@angular/core'; +import { NgModuleRef, ComponentFactoryResolver, ViewContainerRef } from '@angular/core'; import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces'; import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel'; -import { inputBorder, inputValidationInfoBorder } from 'vs/platform/theme/common/colorRegistry'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { bootstrapAngular } from 'sql/workbench/services/bootstrap/browser/bootstrapService'; import { localize } from 'vs/nls'; import { NotebookViewsExtension } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewsExtension'; +import { InsertCellsModule } from 'sql/workbench/contrib/notebook/browser/notebookViews/insertCellsModal.module'; import { attachButtonStyler } from 'vs/platform/theme/common/styler'; import { truncate } from 'vs/base/common/strings'; import { toJpeg } from 'html-to-image'; +import { IComponentEventArgs } from 'sql/platform/dashboard/browser/interfaces'; +import { Thumbnail } from 'sql/workbench/contrib/notebook/browser/notebookViews/insertCellsScreenshots.component'; type CellOption = { optionMetadata: ServiceOption, @@ -54,6 +55,10 @@ export class CellOptionsModel { }); } + public get checkedOptions(): CellOption[] { + return Object.values(this._optionsMap).filter(o => o.currentValue === true); + } + private getDisplayValue(optionMetadata: ServiceOption, optionValue: string): boolean { let displayValue: boolean = false; switch (optionMetadata.valueType) { @@ -93,8 +98,8 @@ export class InsertCellsModal extends Modal { private _submitButton: Button; private _cancelButton: Button; - private _optionsMap: { [name: string]: Checkbox } = {}; private _maxTitleLength: number = 20; + private _moduleRef?: NgModuleRef; constructor( private onInsert: (cell: ICellModel) => void, @@ -107,6 +112,7 @@ export class InsertCellsModal extends Modal { @IClipboardService clipboardService: IClipboardService, @IContextKeyService contextKeyService: IContextKeyService, @IAdsTelemetryService telemetryService: IAdsTelemetryService, + @IInstantiationService private _instantiationService: IInstantiationService, @ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService, ) { super( @@ -127,21 +133,7 @@ export class InsertCellsModal extends Modal { } protected renderBody(container: HTMLElement): void { - const grid = DOM.$('div#insert-dialog-cell-grid'); - - grid.style.display = 'grid'; - grid.style.gridTemplateColumns = '1fr 1fr'; - grid.style.gap = '10px'; - grid.style.padding = '10px'; - grid.style.overflowY = 'auto'; - grid.style.maxHeight = 'calc(100% - 40px)'; - - const gridTitle = DOM.$('h2.grid-title'); - gridTitle.title = localize("insertCellsModal.selectCells", "Select cell sources"); - - DOM.append(container, grid); - - this.createOptions(grid) + this.createOptions(container) .catch((e) => { this.setError(localize("insertCellsModal.thumbnailError", "Error: Unable to generate thumbnails.")); }); } @@ -153,59 +145,48 @@ export class InsertCellsModal extends Modal { const activeView = this._context.getActiveView(); const cellsAvailableToInsert = activeView.hiddenCells; - cellsAvailableToInsert.forEach(async (cell) => { - const optionWidget = this.createCheckBoxHelper( - container, - '
', - false, - () => this.onOptionChecked(cell.cellGuid) - ); + const thumbnails = await Promise.all( + cellsAvailableToInsert.map(async (cell) => { + return { + id: cell.cellGuid, + path: await this.generateScreenshot(cell), + title: localize("insertCellsModal.cellTitle", "Cell {0}", Number.parseInt(cell.id) + 1) + } as Thumbnail; + }) + ); - const img = await this.generateScreenshot(cell); - const wrapper = DOM.$('div.thumnail-wrapper'); - const thumbnail = DOM.$('img.thumbnail'); - - thumbnail.src = img; - thumbnail.style.maxWidth = '100%'; - DOM.append(wrapper, thumbnail); - optionWidget.label = wrapper.outerHTML; - - this._optionsMap[cell.cellGuid] = optionWidget; - }); + this.bootstrapAngular(container, thumbnails); } - private createCheckBoxHelper(container: HTMLElement, label: string, isChecked: boolean, onCheck: (viaKeyboard: boolean) => void): Checkbox { - const checkbox = new Checkbox(DOM.append(container, DOM.$('.dialog-input-section')), { - label: label, - checked: isChecked, - onChange: onCheck, - ariaLabel: label - }); - this._register(attachCheckboxStyler(checkbox, this._themeService)); - return checkbox; - } - - public onOptionChecked(optionName: string) { - this.viewModel.setOptionValue(optionName, (this._optionsMap[optionName]).checked); - this.validate(); + public onOptionChecked(e: IComponentEventArgs) { + if (e.args?.value) { + let optionName: string = e.args.value; + this.viewModel.setOptionValue(optionName, e.args.selected); + this.validate(); + } } public async generateScreenshot(cell: ICellModel, screenshotWidth: number = 300, screenshowHeight: number = 300, backgroundColor: string = '#ffffff'): Promise { - let componentFactory = this._componentFactoryResolver.resolveComponentFactory(TextCellComponent); - let component = this._containerRef.createComponent(componentFactory); + try { + let componentFactory = this._componentFactoryResolver.resolveComponentFactory(TextCellComponent); + let component = this._containerRef.createComponent(componentFactory); - component.instance.model = this._context.notebook as NotebookModel; - component.instance.cellModel = cell; + component.instance.model = this._context.notebook as NotebookModel; + component.instance.cellModel = cell; - component.instance.handleContentChanged(); + component.instance.handleContentChanged(); - const element: HTMLElement = component.instance.outputRef.nativeElement; + const element: HTMLElement = component.instance.outputRef.nativeElement; - const scale = element.clientWidth / screenshotWidth; - const canvasWidth = element.clientWidth / scale; - const canvasHeight = element.clientHeight / scale; + const scale = element.clientWidth / screenshotWidth; + const canvasWidth = element.clientWidth / scale; + const canvasHeight = element.clientHeight / scale; - return toJpeg(component.instance.outputRef.nativeElement, { quality: .6, canvasWidth, canvasHeight, backgroundColor }); + return toJpeg(component.instance.outputRef.nativeElement, { quality: .6, canvasWidth, canvasHeight, backgroundColor }); + } catch (e) { + this.logService.error(`Error generating screenshot: ${e}`); + return ''; + } } private getOptions(): ServiceOption[] { @@ -239,7 +220,7 @@ export class InsertCellsModal extends Modal { } private validate() { - if (Object.keys(this._optionsMap).length) { + if (this.viewModel.checkedOptions.length) { this._submitButton.enabled = true; } else { this._submitButton.enabled = false; @@ -265,34 +246,25 @@ export class InsertCellsModal extends Modal { public override dispose(): void { super.dispose(); - for (let key in this._optionsMap) { - let widget = this._optionsMap[key]; - widget.dispose(); - delete this._optionsMap[key]; + + if (this._moduleRef) { + this._moduleRef.destroy(); } } + + /** + * Get the bootstrap params and perform the bootstrap + */ + private bootstrapAngular(bodyContainer: HTMLElement, thumbnails: Thumbnail[]) { + this._instantiationService.invokeFunction(bootstrapAngular, + InsertCellsModule, + bodyContainer, + 'insert-cells-screenshots-component', + { + thumbnails, + onClick: (e: IComponentEventArgs) => { this.onOptionChecked(e); } + }, + undefined, + (moduleRef: NgModuleRef) => this._moduleRef = moduleRef); + } } - -registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { - const inputBorderColor = theme.getColor(inputBorder); - if (inputBorderColor) { - collector.addRule(` - #insert-dialog-cell-grid input[type="checkbox"] + label { - border: 2px solid; - border-color: ${inputBorderColor.toString()}; - display: flex; - height: 125px; - overflow: hidden; - } - `); - } - - const inputActiveOptionBorderColor = theme.getColor(inputValidationInfoBorder); - if (inputActiveOptionBorderColor) { - collector.addRule(` - #insert-dialog-cell-grid input[type="checkbox"]:checked + label { - border-color: ${inputActiveOptionBorderColor.toString()}; - } - `); - } -}); diff --git a/src/sql/workbench/contrib/notebook/browser/notebookViews/insertCellsScreenshots.component.ts b/src/sql/workbench/contrib/notebook/browser/notebookViews/insertCellsScreenshots.component.ts new file mode 100644 index 0000000000..56fb5dc857 --- /dev/null +++ b/src/sql/workbench/contrib/notebook/browser/notebookViews/insertCellsScreenshots.component.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./insertCellsScreenshots'; +import { Component, AfterViewInit, forwardRef, Inject, ComponentFactoryResolver, ViewContainerRef, ViewChild } from '@angular/core'; +import { IBootstrapParams } from 'sql/workbench/services/bootstrap/common/bootstrapParams'; +import { IComponentEventArgs } from 'sql/platform/dashboard/browser/interfaces'; +import CardComponent, { CardType } from 'sql/workbench/browser/modelComponents/card.component'; +import { URI } from 'vs/base/common/uri'; + + +export interface LayoutRequestParams { + modelViewId?: string; + alwaysRefresh?: boolean; +} + +export interface Thumbnail { + id: string, + path: string, + title: string +} + +export interface InsertCellsComponentParams extends IBootstrapParams { + thumbnails: Thumbnail[], + onClick: (e: IComponentEventArgs) => void +} + +@Component({ + selector: 'insert-cells-screenshots-component', + template: '
' +}) +export class InsertCellsScreenshots implements AfterViewInit { + @ViewChild('divContainer', { read: ViewContainerRef }) _containerRef: ViewContainerRef; + + constructor( + @Inject(IBootstrapParams) private _params: InsertCellsComponentParams, + @Inject(forwardRef(() => ComponentFactoryResolver)) private _componentFactoryResolver: ComponentFactoryResolver + ) { } + + ngAfterViewInit(): void { + this._params.thumbnails.forEach((thumbnail: Thumbnail, idx: number) => { + const cellImageUri = URI.parse(thumbnail.path); + + let cardComponentFactory = this._componentFactoryResolver.resolveComponentFactory(CardComponent); + let cardComponent = this._containerRef.createComponent(cardComponentFactory); + + cardComponent.instance.setProperties({ iconPath: cellImageUri, label: thumbnail.title, value: thumbnail.id, cardType: CardType.Image }); + + cardComponent.instance.enabled = true; + cardComponent.instance.registerEventHandler(e => this._params.onClick(e)); + }); + } +} diff --git a/src/sql/workbench/contrib/notebook/browser/notebookViews/insertCellsScreenshots.css b/src/sql/workbench/contrib/notebook/browser/notebookViews/insertCellsScreenshots.css new file mode 100644 index 0000000000..af19905403 --- /dev/null +++ b/src/sql/workbench/contrib/notebook/browser/notebookViews/insertCellsScreenshots.css @@ -0,0 +1,8 @@ +.insert-cells-screenshot-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 10px; + padding: 10px; + overflow-y: auto; + max-height: calc(100% - 40px); +} diff --git a/src/sql/workbench/contrib/notebook/browser/notebookViews/notebookViews.component.ts b/src/sql/workbench/contrib/notebook/browser/notebookViews/notebookViews.component.ts index c2a94d72ca..9836851175 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebookViews/notebookViews.component.ts +++ b/src/sql/workbench/contrib/notebook/browser/notebookViews/notebookViews.component.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Component, Input, ViewChildren, QueryList, ChangeDetectorRef, forwardRef, Inject, ViewChild, ElementRef, ViewContainerRef, ComponentFactoryResolver } from '@angular/core'; import { ICellModel, INotebookModel, ISingleNotebookEditOperation, NotebookContentChange } from 'sql/workbench/services/notebook/browser/models/modelInterfaces'; +import 'vs/css!./notebookViewsGrid'; import { CodeCellComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/codeCell.component'; import { TextCellComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/textCell.component'; import { ICellEditorProvider, INotebookParams, INotebookService, INotebookEditor, NotebookRange, INotebookSection, DEFAULT_NOTEBOOK_PROVIDER, SQL_NOTEBOOK_PROVIDER } from 'sql/workbench/services/notebook/browser/notebookService'; @@ -260,6 +261,10 @@ export class NotebookViewComponent extends AngularDisposable implements INoteboo let insertCellsAction = this._instantiationService.createInstance(InsertCellAction, this.insertCell.bind(this), this.views, this._containerRef, this._componentFactoryResolver); + // Hide the insert cell action button if no cells can be added + insertCellsAction.enabled = this.activeView?.hiddenCells.length > 0; + this.activeView.onCellVisibilityChanged(e => { insertCellsAction.enabled = this.activeView?.hiddenCells.length > 0; }); + this._runAllCellsAction = this._instantiationService.createInstance(RunAllCellsAction, 'notebook.runAllCells', localize('runAllPreview', "Run all"), 'notebook-button masked-pseudo start-outline'); let spacerElement = document.createElement('li'); diff --git a/src/sql/workbench/contrib/notebook/test/stubs.ts b/src/sql/workbench/contrib/notebook/test/stubs.ts index 2e42d30735..1d64e258d1 100644 --- a/src/sql/workbench/contrib/notebook/test/stubs.ts +++ b/src/sql/workbench/contrib/notebook/test/stubs.ts @@ -770,6 +770,7 @@ export class NotebookViewStub implements INotebookView { displayedCells: readonly ICellModel[]; onDeleted: vsEvent.Event; + onCellVisibilityChanged: vsEvent.Event; initialize(): void { throw new Error('Method not implemented.'); } diff --git a/src/sql/workbench/services/notebook/browser/notebookViews/notebookViewModel.ts b/src/sql/workbench/services/notebook/browser/notebookViews/notebookViewModel.ts index 56961133fc..b7cb649afe 100644 --- a/src/sql/workbench/services/notebook/browser/notebookViews/notebookViewModel.ts +++ b/src/sql/workbench/services/notebook/browser/notebookViews/notebookViewModel.ts @@ -21,10 +21,12 @@ function cellCollides(c1: INotebookViewCell, c2: INotebookViewCell): boolean { export class NotebookViewModel implements INotebookView { private _onDeleted = new Emitter(); + private _onCellVisibilityChanged = new Emitter(); private _isNew: boolean = false; public readonly guid: string; public readonly onDeleted = this._onDeleted.event; + public readonly onCellVisibilityChanged = this._onCellVisibilityChanged.event; constructor( protected _name: string, @@ -119,10 +121,12 @@ export class NotebookViewModel implements INotebookView { public insertCell(cell: ICellModel) { this.updateCell(cell, this, { hidden: false }); + this._onCellVisibilityChanged.fire(cell); } public hideCell(cell: ICellModel) { this.updateCell(cell, this, { hidden: true }); + this._onCellVisibilityChanged.fire(cell); } public moveCell(cell: ICellModel, x: number, y: number) { diff --git a/src/sql/workbench/services/notebook/browser/notebookViews/notebookViews.d.ts b/src/sql/workbench/services/notebook/browser/notebookViews/notebookViews.d.ts index d115891a54..1861e78e08 100644 --- a/src/sql/workbench/services/notebook/browser/notebookViews/notebookViews.d.ts +++ b/src/sql/workbench/services/notebook/browser/notebookViews/notebookViews.d.ts @@ -26,8 +26,9 @@ export interface INotebookViews { export interface INotebookView { readonly guid: string; readonly onDeleted: Event; - isNew: boolean; + readonly onCellVisibilityChanged: Event; + isNew: boolean; cells: Readonly; hiddenCells: Readonly; displayedCells: Readonly; diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index 07b267f341..8c0fe5ef7b 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -696,3 +696,17 @@ export const WINDOW_INACTIVE_BORDER = registerColor('window.inactiveBorder', { light: null, hc: contrastBorder }, localize('windowInactiveBorder', "The color used for the border of the window when it is inactive. Only supported in the desktop client when using the custom title bar.")); + + +// < --- Card --- > +export const CARD_OVERLAY_FOREGROUND = registerColor('card.overlayForeground', { + dark: null, + light: null, + hc: null +}, localize('cardOverlayForeground', "The card overlay foreground color.")); + +export const CARD_OVERLAY_BACKGROUND = registerColor('card.overlayBackground', { + dark: '#252526', + light: '#F3F3F3', + hc: '#000000' +}, localize('cardOverlayBackground', "The card overlay background color."));