diff --git a/src/sql/parts/modelComponents/components.contribution.ts b/src/sql/parts/modelComponents/components.contribution.ts index 69ee3cc684..2c537c2b5e 100644 --- a/src/sql/parts/modelComponents/components.contribution.ts +++ b/src/sql/parts/modelComponents/components.contribution.ts @@ -22,6 +22,7 @@ import TextComponent from './text.component'; import LoadingComponent from './loadingComponent.component'; import FileBrowserTreeComponent from './fileBrowserTree.component'; import EditorComponent from './editor.component'; +import DomComponent from './dom.component'; import { registerComponentType } from 'sql/platform/dashboard/common/modelComponentRegistry'; import { ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes'; @@ -85,3 +86,6 @@ registerComponentType(FILEBROWSERTREE_COMPONENT, ModelComponentTypes.FileBrowser export const EDITOR_COMPONENT = 'editor-component'; registerComponentType(EDITOR_COMPONENT, ModelComponentTypes.Editor, EditorComponent); + +export const DOM_COMPONENT = 'dom-component'; +registerComponentType(DOM_COMPONENT, ModelComponentTypes.Dom, DomComponent); diff --git a/src/sql/parts/modelComponents/dom.component.ts b/src/sql/parts/modelComponents/dom.component.ts new file mode 100644 index 0000000000..20e5f77700 --- /dev/null +++ b/src/sql/parts/modelComponents/dom.component.ts @@ -0,0 +1,92 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./dom'; +import 'vs/css!./highlight'; +import 'vs/css!./markdown'; +import { + Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver, + ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList +} from '@angular/core'; + +import * as sqlops from 'sqlops'; +import * as DOM from 'vs/base/browser/dom'; +import { $, Builder } from 'vs/base/browser/builder'; + +import { ComponentBase } from 'sql/parts/modelComponents/componentBase'; +import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces'; + +@Component({ + template: '', + selector: 'modelview-dom-component' +}) +export default class DomComponent extends ComponentBase implements IComponent, OnDestroy { + @Input() descriptor: IComponentDescriptor; + @Input() modelStore: IModelStore; + private _renderedHtml: string; + private _rootElement: Builder; + private _bodyElement: Builder; + + constructor( + @Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef, + @Inject(forwardRef(() => ElementRef)) el: ElementRef + ) { + super(changeRef, el); + } + + ngOnInit(): void { + this.baseInit(); + this.createDomElement(); + this._register(DOM.addDisposableListener(window, DOM.EventType.RESIZE, e => { + this.layout(); + })); + } + + ngOnDestroy(): void { + this.baseDestroy(); + } + + private createDomElement() { + this._rootElement = new Builder(this._el.nativeElement); + this._bodyElement = $('.dom-body'); + this._rootElement.append(this._bodyElement); + } + + /// Dom Functions + private setHtml(): void { + if (this.html) { + this._renderedHtml = this.html; + this._bodyElement.innerHtml(this._renderedHtml); + } + } + + /// IComponent implementation + public layout(): void { + super.layout(); + let element = this._el.nativeElement; + element.style.width = this.getWidth(); + element.style.height = this.getHeight(); + } + + public setLayout(layout: any): void { + // TODO allow configuring the look and feel + this.layout(); + } + + public setProperties(properties: { [key: string]: any; }): void { + super.setProperties(properties); + if (this.html !== this._renderedHtml) { + this.setHtml(); + } + } + + // CSS-bound properties + public get html(): string { + return this.getPropertyOrDefault((props) => props.html, ''); + } + + public set html(newValue: string) { + this.setPropertyFromUI((properties, html) => { properties.html = html; }, newValue); + } +} diff --git a/src/sql/parts/modelComponents/dom.css b/src/sql/parts/modelComponents/dom.css new file mode 100644 index 0000000000..1f172f166a --- /dev/null +++ b/src/sql/parts/modelComponents/dom.css @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +modelview-dom-component { + display: block; + -webkit-user-select: text; +} \ No newline at end of file diff --git a/src/sql/parts/modelComponents/highlight.css b/src/sql/parts/modelComponents/highlight.css new file mode 100644 index 0000000000..868a03bee3 --- /dev/null +++ b/src/sql/parts/modelComponents/highlight.css @@ -0,0 +1,183 @@ +/* +https://raw.githubusercontent.com/isagalaev/highlight.js/master/src/styles/vs2015.css +*/ +/* + * Visual Studio 2015 dark style + * Author: Nicolas LLOBERA + */ + + +modelview-dom-component .hljs-keyword, +modelview-dom-component .hljs-literal, +modelview-dom-component .hljs-symbol, +modelview-dom-component .hljs-name { + color: #569CD6; +} +modelview-dom-component .hljs-link { + color: #569CD6; + text-decoration: underline; +} + +modelview-dom-component .hljs-built_in, +modelview-dom-component .hljs-type { + color: #4EC9B0; +} + +modelview-dom-component .hljs-number, +modelview-dom-component .hljs-class { + color: #B8D7A3; +} + +modelview-dom-component .hljs-string, +modelview-dom-component .hljs-meta-string { + color: #D69D85; +} + +modelview-dom-component .hljs-regexp, +modelview-dom-component .hljs-template-tag { + color: #9A5334; +} + +modelview-dom-component .hljs-subst, +modelview-dom-component .hljs-function, +modelview-dom-component .hljs-title, +modelview-dom-component .hljs-params, +modelview-dom-component .hljs-formula { + color: #DCDCDC; +} + +modelview-dom-component .hljs-comment, +modelview-dom-component .hljs-quote { + color: #57A64A; + font-style: italic; +} + +modelview-dom-component .hljs-doctag { + color: #608B4E; +} + +modelview-dom-component .hljs-meta, +modelview-dom-component .hljs-meta-keyword, +modelview-dom-component .hljs-tag { + color: #9B9B9B; +} + +modelview-dom-component .hljs-variable, +modelview-dom-component .hljs-template-variable { + color: #BD63C5; +} + +modelview-dom-component .hljs-attr, +modelview-dom-component .hljs-attribute, +modelview-dom-component .hljs-builtin-name { + color: #9CDCFE; +} + +modelview-dom-component .hljs-section { + color: gold; +} + +modelview-dom-component .hljs-emphasis { + font-style: italic; +} + +modelview-dom-component .hljs-strong { + font-weight: bold; +} + +/*.hljs-code { + font-family:'Monospace'; +}*/ + +modelview-dom-component .hljs-bullet, +modelview-dom-component .hljs-selector-tag, +modelview-dom-component .hljs-selector-id, +modelview-dom-component .hljs-selector-class, +modelview-dom-component .hljs-selector-attr, +modelview-dom-component .hljs-selector-pseudo { + color: #D7BA7D; +} + +modelview-dom-component .hljs-addition { + background-color: #144212; + display: inline-block; + width: 100%; +} + +modelview-dom-component .hljs-deletion { + background-color: #600; + display: inline-block; + width: 100%; +} + + +/* +From https://raw.githubusercontent.com/isagalaev/highlight.js/master/src/styles/vs.css +*/ +/* + +Visual Studio-like style based on original C# coloring by Jason Diamond + +*/ +/* +.vscode-light .hljs-function, +.vscode-light .hljs-params { + color: inherit; +} + +.vscode-light .hljs-comment, +.vscode-light .hljs-quote, +.vscode-light .hljs-variable { + color: #008000; +} + +.vscode-light .hljs-keyword, +.vscode-light .hljs-selector-tag, +.vscode-light .hljs-built_in, +.vscode-light .hljs-name, +.vscode-light .hljs-tag { + color: #00f; +} + +.vscode-light .hljs-string, +.vscode-light .hljs-title, +.vscode-light .hljs-section, +.vscode-light .hljs-attribute, +.vscode-light .hljs-literal, +.vscode-light .hljs-template-tag, +.vscode-light .hljs-template-variable, +.vscode-light .hljs-type, +.vscode-light .hljs-addition { + color: #a31515; +} + +.vscode-light .hljs-deletion, +.vscode-light .hljs-selector-attr, +.vscode-light .hljs-selector-pseudo, +.vscode-light .hljs-meta { + color: #2b91af; +} + +.vscode-light .hljs-doctag { + color: #808080; +} + +.vscode-light .hljs-attr { + color: #f00; +} + +.vscode-light .hljs-symbol, +.vscode-light .hljs-bullet, +.vscode-light .hljs-link { + color: #00b0e8; +} + + +.vscode-light .hljs-emphasis { + font-style: italic; +} + +.vscode-light .hljs-strong { + font-weight: bold; +} +*/ \ No newline at end of file diff --git a/src/sql/parts/modelComponents/markdown.css b/src/sql/parts/modelComponents/markdown.css new file mode 100644 index 0000000000..00869deaf2 --- /dev/null +++ b/src/sql/parts/modelComponents/markdown.css @@ -0,0 +1,239 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + modelview-dom-component { + font-family: "Segoe WPC", "Segoe UI", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback"; + font-size: 14px; + padding: 0 26px; + line-height: 22px; + word-wrap: break-word; +} + +modelview-dom-component #code-csp-warning { + position: fixed; + top: 0; + right: 0; + color: white; + margin: 16px; + text-align: center; + font-size: 12px; + font-family: sans-serif; + background-color:#444444; + cursor: pointer; + padding: 6px; + box-shadow: 1px 1px 1px rgba(0,0,0,.25); +} + +modelview-dom-component #code-csp-warning:hover { + text-decoration: none; + background-color:#007acc; + box-shadow: 2px 2px 2px rgba(0,0,0,.25); +} + + +modelview-dom-component .scrollBeyondLastLine { + margin-bottom: calc(100vh - 22px); +} + +modelview-dom-component .showEditorSelection .code-line { + position: relative; +} + +modelview-dom-component .showEditorSelection .code-active-line:before, +modelview-dom-component .showEditorSelection .code-line:hover:before { + content: ""; + display: block; + position: absolute; + top: 0; + left: -12px; + height: 100%; +} + +modelview-dom-component .showEditorSelection li.code-active-line:before, +modelview-dom-component .showEditorSelection li.code-line:hover:before { + left: -30px; +} + +modelview-dom-component .showEditorSelection .code-active-line:before { + border-left: 3px solid rgba(0, 0, 0, 0.15); +} + +modelview-dom-component .showEditorSelection .code-line:hover:before { + border-left: 3px solid rgba(0, 0, 0, 0.40); +} + +modelview-dom-component .showEditorSelection .code-line .code-line:hover:before { + border-left: none; +} + +.vs-dark .monaco-workbench modelview-dom-component .showEditorSelection .code-active-line:before +.hc-black .monaco-workbench modelview-dom-component .showEditorSelection .code-active-line:before { + border-left: 3px solid rgba(255, 255, 255, 0.4); +} + +.vs-dark .monaco-workbench modelview-dom-component .showEditorSelection .code-line:hover:before +.hc-black .monaco-workbench modelview-dom-component .showEditorSelection .code-line:hover:before { + border-left: 3px solid rgba(255, 255, 255, 0.60); +} + +.vs-dark .monaco-workbench modelview-dom-component .showEditorSelection .code-line .code-line:hover:before +.hc-black .monaco-workbench modelview-dom-component .showEditorSelection .code-line .code-line:hover:before { + border-left: none; +} + +.hc-black modelview-dom-component .showEditorSelection .code-active-line:before { + border-left: 3px solid rgba(255, 160, 0, 0.7); +} + +.hc-black modelview-dom-component .showEditorSelection .code-line:hover:before { + border-left: 3px solid rgba(255, 160, 0, 1); +} + +modelview-dom-component .showEditorSelection .code-line .code-line:hover:before { + border-left: none; +} + +modelview-dom-component img { + max-width: 100%; + max-height: 100%; +} + +modelview-dom-component a, a:link{ + text-decoration: none; + color: rgb(0, 0, 238) !important; +} + +modelview-dom-component a:hover, a:link { + text-decoration: underline; +} + +modelview-dom-component a:focus, +modelview-dom-component input:focus, +modelview-dom-component select:focus, +modelview-dom-component textarea:focus { + outline: 1px solid -webkit-focus-ring-color; + outline-offset: -1px; +} + +modelview-dom-component hr { + border: 0; + height: 2px; + border-bottom: 2px solid; +} + +modelview-dom-component h1 { + padding-bottom: 0.3em; + line-height: 1.2; + border-bottom-width: 1px; + border-bottom-style: solid; +} + +modelview-dom-component h1, +modelview-dom-component h2, +modelview-dom-component h3 { + font-weight: normal; +} + +modelview-dom-component h1 code, +modelview-dom-component h2 code, +modelview-dom-component h3 code, +modelview-dom-component h4 code, +modelview-dom-component h5 code, +modelview-dom-component h6 code { + font-size: inherit; + line-height: auto; +} + +modelview-dom-component table { + border-collapse: collapse; +} + +modelview-dom-component table > thead > tr > th { + text-align: left; + border-bottom: 1px solid; +} + +modelview-dom-component table > thead > tr > th, +modelview-dom-component table > thead > tr > td, +modelview-dom-component table > tbody > tr > th, +modelview-dom-component table > tbody > tr > td { + padding: 5px 10px; +} + +modelview-dom-component table > tbody > tr + tr > td { + border-top: 1px solid; +} + +modelview-dom-component blockquote { + margin: 0 7px 0 5px; + padding: 0 16px 0 10px; + border-left-width: 5px; + border-left-style: solid; +} + +modelview-dom-component code { + font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; + font-size: 14px; + line-height: 19px; +} + +modelview-dom-component .wordWrap pre { + white-space: pre-wrap; +} + +modelview-dom-component .mac code { + font-size: 12px; + line-height: 18px; +} + +modelview-dom-component pre:not(.hljs), +modelview-dom-component pre.hljs code > div { + padding: 16px; + border-radius: 3px; + overflow: auto; +} + +/** Theming */ + +modelview-dom-component pre code { + color: var(--vscode-editor-foreground); +} + + +modelview-dom-component pre { + background-color: rgba(220, 220, 220, 0.4); +} + +.vs-dark .monaco-workbench modelview-dom-component pre { + background-color: rgba(10, 10, 10, 0.4); +} + +.hc-black .monaco-workbench modelview-dom-component pre { + background-color: rgb(0, 0, 0); +} + +.hc-black .monaco-workbench modelview-dom-component h1 { + border-color: rgb(0, 0, 0); +} + +modelview-dom-component table > thead > tr > th { + border-color: rgba(0, 0, 0, 0.69); +} + +.vs-dark .monaco-workbench modelview-dom-component table > thead > tr > th { + border-color: rgba(255, 255, 255, 0.69); +} + +modelview-dom-component h1, +modelview-dom-component hr, +modelview-dom-component table > tbody > tr + tr > td { + border-color: rgba(0, 0, 0, 0.18); +} + +.vs-dark .monaco-workbench modelview-dom-component h1, +.vs-dark .monaco-workbench modelview-dom-component hr, +.vs-dark .monaco-workbench modelview-dom-component table > tbody > tr + tr > td { + border-color: rgba(255, 255, 255, 0.18); +} diff --git a/src/sql/sqlops.proposed.d.ts b/src/sql/sqlops.proposed.d.ts index 21f5517914..bd0a4e2b53 100644 --- a/src/sql/sqlops.proposed.d.ts +++ b/src/sql/sqlops.proposed.d.ts @@ -19,6 +19,7 @@ declare module 'sqlops' { navContainer(): ContainerBuilder; divContainer(): DivBuilder; flexContainer(): FlexBuilder; + dom(): ComponentBuilder card(): ComponentBuilder; inputBox(): ComponentBuilder; checkBox(): ComponentBuilder; @@ -575,6 +576,13 @@ declare module 'sqlops' { options?: vscode.WebviewOptions; } + export interface DomProperties extends ComponentProperties { + /** + * Contents of the DOM component. + */ + html?: string; + } + /** * Editor properties for the editor component */ @@ -618,6 +626,10 @@ declare module 'sqlops' { onCardSelectedChanged: vscode.Event; } + export interface DomComponent extends Component, DomProperties { + + } + export interface TextComponent extends Component { value: string; } diff --git a/src/sql/workbench/api/common/sqlExtHostTypes.ts b/src/sql/workbench/api/common/sqlExtHostTypes.ts index 66df5de751..4fc3230da0 100644 --- a/src/sql/workbench/api/common/sqlExtHostTypes.ts +++ b/src/sql/workbench/api/common/sqlExtHostTypes.ts @@ -152,7 +152,8 @@ export enum ModelComponentTypes { LoadingComponent, TreeComponent, FileBrowserTree, - Editor + Editor, + Dom } export interface IComponentShape { diff --git a/src/sql/workbench/api/node/extHostModelView.ts b/src/sql/workbench/api/node/extHostModelView.ts index c11ade049c..b1a3053bac 100644 --- a/src/sql/workbench/api/node/extHostModelView.ts +++ b/src/sql/workbench/api/node/extHostModelView.ts @@ -191,6 +191,13 @@ class ModelBuilderImpl implements sqlops.ModelBuilder { return builder; } + dom(): sqlops.ComponentBuilder { + let id = this.getNextComponentId(); + let builder: ComponentBuilderImpl = this.getComponentBuilder(new DomComponentWrapper(this._proxy, this._handle, id), id); + this._componentBuilders.set(id, builder); + return builder; + } + getComponentBuilder(component: ComponentWrapper, id: string): ComponentBuilderImpl { let componentBuilder: ComponentBuilderImpl = new ComponentBuilderImpl(component); this._componentBuilders.set(id, componentBuilder); @@ -840,8 +847,8 @@ class WebViewWrapper extends ComponentWrapper implements sqlops.WebViewComponent public get html(): string { return this.properties['html']; } - public set html(v: string) { - this.setProperty('html', v); + public set html(html: string) { + this.setProperty('html', html); } public get onMessage(): vscode.Event { @@ -857,6 +864,21 @@ class WebViewWrapper extends ComponentWrapper implements sqlops.WebViewComponent } } +class DomComponentWrapper extends ComponentWrapper implements sqlops.DomComponent { + + constructor(proxy: MainThreadModelViewShape, handle: number, id: string) { + super(proxy, handle, ModelComponentTypes.Dom, id); + this.properties = {}; + } + + public get html(): string { + return this.properties['html']; + } + public set html(html: string) { + this.setProperty('html', html); + } +} + class EditorWrapper extends ComponentWrapper implements sqlops.EditorComponent { constructor(proxy: MainThreadModelViewShape, handle: number, id: string) { super(proxy, handle, ModelComponentTypes.Editor, id);