Rework message panel (#10177)

* wip

* fix compile

* fix look

* fix hygiene errors

* add back functionality

* fix some issues with accessibility

* proper dispose template

* handle state properly in message panel
This commit is contained in:
Anthony Dresser
2020-04-30 13:49:18 -07:00
committed by GitHub
parent 28230b67d4
commit 92e0b2e130
4 changed files with 240 additions and 276 deletions

View File

@@ -3,8 +3,15 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface IMessageTreeState {
readonly focus: string[];
readonly selection: string[];
readonly expanded: string[];
readonly scrollTop: number;
}
export class MessagePanelState {
public scrollPosition?: number;
public viewState?: IMessageTreeState;
dispose() {

View File

@@ -5,14 +5,10 @@
import { Action } from 'vs/base/common/actions';
import { localize } from 'vs/nls';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { Table } from 'sql/base/browser/ui/table/table';
import { QueryEditor } from './queryEditor';
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
import { isWindows } from 'vs/base/common/platform';
import { removeAnsiEscapeCodes } from 'vs/base/common/strings';
import { IGridDataProvider } from 'sql/workbench/services/query/common/gridDataProvider';
import { INotificationService } from 'vs/platform/notification/common/notification';
import QueryRunner from 'sql/workbench/services/query/common/queryRunner';
@@ -35,11 +31,6 @@ export interface IGridActionContext {
resultId: number;
}
export interface IMessagesActionContext {
selection: Selection;
tree: ITree;
}
function mapForNumberColumn(ranges: Slick.Range[]): Slick.Range[] {
if (ranges) {
return ranges.map(e => new Slick.Range(e.fromRow, e.fromCell - 1, e.toRow, e.toCell ? e.toCell - 1 : undefined));
@@ -132,49 +123,6 @@ export class SelectAllGridAction extends Action {
}
}
export class CopyMessagesAction extends Action {
public static ID = 'grid.messages.copy';
public static LABEL = localize('copyMessages', "Copy");
constructor(
@IClipboardService private clipboardService: IClipboardService
) {
super(CopyMessagesAction.ID, CopyMessagesAction.LABEL);
}
public run(context: IMessagesActionContext): Promise<boolean> {
this.clipboardService.writeText(context.selection.toString());
return Promise.resolve(true);
}
}
const lineDelimiter = isWindows ? '\r\n' : '\n';
export class CopyAllMessagesAction extends Action {
public static ID = 'grid.messages.copyAll';
public static LABEL = localize('copyAll', "Copy All");
constructor(
private tree: ITree,
@IClipboardService private clipboardService: IClipboardService) {
super(CopyAllMessagesAction.ID, CopyAllMessagesAction.LABEL);
}
public run(): Promise<any> {
let text = '';
const navigator = this.tree.getNavigator();
// skip first navigator element - the root node
while (navigator.next()) {
if (text) {
text += lineDelimiter;
}
text += (navigator.current()).message;
}
this.clipboardService.writeText(removeAnsiEscapeCodes(text));
return Promise.resolve(null);
}
}
export class MaximizeTableAction extends Action {
public static ID = 'grid.maximize';
public static LABEL = localize('maximize', "Maximize");

View File

@@ -3,22 +3,84 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/* Disable repl hover highlight in tree. */
.monaco-workbench .message-tree .monaco-tree .monaco-tree-rows > .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) {
background-color: inherit;
/* Debug repl */
.message-tree {
height: 100%;
box-sizing: border-box;
overflow: hidden;
}
/* Disable repl hover highlight in tree. */
.monaco-workbench .message-tree .monaco-tree .monaco-tree-row > .content {
line-height: 18px;
.message-tree .monaco-tl-contents {
user-select: text;
-webkit-user-select: text;
white-space: pre;
}
.message-tree.word-wrap .monaco-tl-contents {
/* Wrap words but also do not trim whitespace #6275 */
word-wrap: break-word;
/* white-space: pre-wrap; */
white-space: pre-wrap;
/* Break on all #7533 */
word-break: break-all;
}
.monaco-workbench .message-tree .monaco-tree .monaco-tree-rows>.monaco-tree-row {
cursor: default;
.monaco-workbench.mac .message-tree .monaco-tl-twistie.collapsible + .monaco-tl-contents,
.monaco-workbench.mac .message-tree .monaco-tl-twistie {
cursor: pointer;
}
.message-tree .output.expression.value-and-source {
display: flex;
}
.message-tree .output.expression.value-and-source .value {
flex: 1;
}
.message-tree .monaco-tl-contents .arrow {
position:absolute;
left: 2px;
opacity: 0.25;
}
.vs-dark .message-tree .monaco-tl-contents .arrow {
opacity: 0.4;
}
.message-tree .output.expression.value-and-source .source {
margin-left: 4px;
margin-right: 8px;
cursor: pointer;
text-decoration: underline;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 150px;
}
.message-tree .monaco-list-row {
cursor: text;
}
.message-tree .output.expression > .value,
.message-tree .evaluation-result.expression > .value {
margin-left: 0px;
}
.message-tree .output.expression > .annotation,
.message-tree .evaluation-result.expression > .annotation {
font-size: inherit;
padding-left: 6px;
}
.message-tree .output.expression .name:not(:empty) {
margin-right: 6px;
}
/* Only show 'stale expansion' info when the element gets expanded. */
.message-tree .evaluation-result > .annotation::before {
content: '';
}
.message-tree .time-stamp {

View File

@@ -4,31 +4,34 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/messagePanel';
import { IMessagesActionContext, CopyMessagesAction, CopyAllMessagesAction } from './actions';
import QueryRunner, { IQueryMessage } from 'sql/workbench/services/query/common/queryRunner';
import { IExpandableTree } from 'sql/workbench/services/objectExplorer/browser/treeUpdateUtils';
import { ISelectionData } from 'azdata';
import { IDataSource, ITree, IRenderer, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree';
import { ITreeRenderer, IDataSource, ITreeNode, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree';
import { generateUuid } from 'vs/base/common/uuid';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { OpenMode, ClickBehavior, ICancelableEvent, IControllerOptions } from 'vs/base/parts/tree/browser/treeDefaults';
import { WorkbenchTreeController } from 'vs/platform/list/browser/listService';
import { WorkbenchDataTree } from 'vs/platform/list/browser/listService';
import { isArray, isString } from 'vs/base/common/types';
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { $, Dimension, createStyleSheet } from 'vs/base/browser/dom';
import { QueryEditor } from 'sql/workbench/contrib/query/browser/queryEditor';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Disposable, DisposableStore, dispose } from 'vs/base/common/lifecycle';
import { $, Dimension, createStyleSheet, addStandardDisposableGenericMouseDownListner } from 'vs/base/browser/dom';
import { resultsErrorColor } from 'sql/platform/theme/common/colors';
import { MessagePanelState } from 'sql/workbench/common/editor/query/messagePanelState';
import { CachedListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { FuzzyScore } from 'vs/base/common/filters';
import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
import { localize } from 'vs/nls';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IAction, Action } from 'vs/base/common/actions';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
import { removeAnsiEscapeCodes } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { QueryEditor } from 'sql/workbench/contrib/query/browser/queryEditor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
export interface IResultMessageIntern {
id?: string;
@@ -55,6 +58,7 @@ interface IMessageTemplate {
interface IBatchTemplate extends IMessageTemplate {
timeStamp: HTMLElement;
disposable: DisposableStore;
}
const TemplateIds = {
@@ -64,93 +68,97 @@ const TemplateIds = {
ERROR: 'error'
};
export class AccessibilityProvider implements IListAccessibilityProvider<IResultMessageIntern> {
getWidgetAriaLabel(): string {
return localize('messagePanel', "Message Panel");
}
getAriaLabel(element: IResultMessageIntern): string {
return element.message;
}
}
export class MessagePanel extends Disposable {
private ds = new MessageDataSource();
private renderer = new MessageRenderer();
private model = new Model();
private controller: MessageController;
private container = $('.message-tree');
private styleElement = createStyleSheet(this.container);
private queryRunnerDisposables = this._register(new DisposableStore());
private _state: MessagePanelState | undefined;
private tree: ITree;
private tree: WorkbenchDataTree<Model, IResultMessageIntern, FuzzyScore>;
constructor(
@IInstantiationService instantiationService: IInstantiationService,
@IThemeService private readonly themeService: IThemeService,
@IContextMenuService private readonly contextMenuService: IContextMenuService
@IContextMenuService private readonly contextMenuService: IContextMenuService,
@IClipboardService private readonly clipboardService: IClipboardService,
@ITextResourcePropertiesService private readonly textResourcePropertiesService: ITextResourcePropertiesService
) {
super();
this.controller = instantiationService.createInstance(MessageController, { openMode: OpenMode.SINGLE_CLICK, clickBehavior: ClickBehavior.ON_MOUSE_UP /* do not change, to preserve focus behaviour in input field */ });
this.controller.toFocusOnClick = this.model;
this.tree = this._register(new Tree(this.container, {
dataSource: this.ds,
renderer: this.renderer,
controller: this.controller
}, { keyboardSupport: false, horizontalScrollMode: ScrollbarVisibility.Auto }));
this.tree = <WorkbenchDataTree<Model, IResultMessageIntern, FuzzyScore>>instantiationService.createInstance(
WorkbenchDataTree,
'MessagePanel',
this.container,
new MessagePanelDelegate(),
[
new MessageRenderer(),
new ErrorMessageRenderer(),
instantiationService.createInstance(BatchMessageRenderer)
],
new MessageDataSource(),
{
accessibilityProvider: new AccessibilityProvider(),
mouseSupport: false,
setRowLineHeight: false,
supportDynamicHeights: true
});
this._register(this.tree.onContextMenu(e => this.onContextMenu(e)));
this._register(this.tree.onDidScroll(() => this.state.viewState = this.tree.getViewState()));
this.tree.setInput(this.model);
this.tree.onDidScroll(e => {
// convert to old VS Code tree interface with expandable methods
let expandableTree: IExpandableTree = <IExpandableTree>this.tree;
if (this.state) {
this.state.scrollPosition = expandableTree.getScrollPosition();
}
});
this.container.style.width = '100%';
this.container.style.height = '100%';
this._register(attachListStyler(this.tree, this.themeService));
this._register(this.themeService.onDidColorThemeChange(this.applyStyles, this));
this.applyStyles(this.themeService.getColorTheme());
this.controller.onKeyDown = (tree, event) => {
if (event.ctrlKey && event.code === 'KeyC') {
let context: IMessagesActionContext = {
selection: document.getSelection(),
tree: this.tree,
};
let copyMessageAction = instantiationService.createInstance(CopyMessagesAction);
copyMessageAction.run(context);
event.preventDefault();
event.stopPropagation();
return true;
}
return false;
};
this.controller.onContextMenu = (tree, element, event) => {
if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') {
return false; // allow context menu on input fields
}
}
// Prevent native context menu from showing up
if (event) {
event.preventDefault();
event.stopPropagation();
private onContextMenu(event: ITreeContextMenuEvent<IResultMessageIntern>): void {
// Prevent native context menu from showing up
const actions: IAction[] = [];
actions.push(new Action('messagePanel.copy', localize('copy', "Copy"), undefined, true, async () => {
const nativeSelection = window.getSelection();
if (nativeSelection) {
await this.clipboardService.writeText(nativeSelection.toString());
}
return Promise.resolve();
}));
actions.push(new Action('workbench.queryEditor.messages.action.copyAll', localize('copyAll', "Copy All"), undefined, true, async () => {
await this.clipboardService.writeText(this.getVisibleContent());
return Promise.resolve();
}));
const selection = document.getSelection();
this.contextMenuService.showContextMenu({
getAnchor: () => event.anchor,
getActions: () => actions
});
}
this.contextMenuService.showContextMenu({
getAnchor: () => {
return { x: event.posx, y: event.posy };
},
getActions: () => {
return [
instantiationService.createInstance(CopyMessagesAction),
instantiationService.createInstance(CopyAllMessagesAction, this.tree)
];
},
getActionsContext: () => {
return <IMessagesActionContext>{
selection,
tree
};
getVisibleContent(): string {
let text = '';
const lineDelimiter = this.textResourcePropertiesService.getEOL(URI.parse(`queryEditor:messagePanel`));
const traverseAndAppend = (node: ITreeNode<IResultMessageIntern, FuzzyScore>) => {
node.children.forEach(child => {
text += child.element.message.trimRight() + lineDelimiter;
if (!child.collapsed && child.children.length) {
traverseAndAppend(child);
}
});
return true;
};
traverseAndAppend(this.tree.getNode());
return removeAnsiEscapeCodes(text);
}
public render(container: HTMLElement): void {
@@ -158,22 +166,11 @@ export class MessagePanel extends Disposable {
}
public layout(size: Dimension): void {
// convert to old VS Code tree interface with expandable methods
let expandableTree: IExpandableTree = <IExpandableTree>this.tree;
const previousScrollPosition = expandableTree.getScrollPosition();
this.tree.layout(size.height);
if (this.state && this.state.scrollPosition) {
expandableTree.setScrollPosition(this.state.scrollPosition);
} else {
if (previousScrollPosition === 1) {
expandableTree.setScrollPosition(1);
}
}
this.tree.updateChildren();
}
public focus(): void {
this.tree.refresh();
this.tree.domFocus();
}
@@ -191,23 +188,7 @@ export class MessagePanel extends Disposable {
} else {
this.model.messages.push(message);
}
// convert to old VS Code tree interface with expandable methods
let expandableTree: IExpandableTree = <IExpandableTree>this.tree;
if (this.state && this.state.scrollPosition) {
const previousScroll = this.state.scrollPosition;
this.tree.refresh(this.model, false).then(() => {
// Restore the previous scroll position when switching between tabs
expandableTree.setScrollPosition(previousScroll);
});
} else {
const previousScrollPosition = expandableTree.getScrollPosition();
this.tree.refresh(this.model).then(() => {
// Scroll to the end if the user was already at the end otherwise leave the current scroll position
if (previousScrollPosition === 1) {
expandableTree.setScrollPosition(1);
}
});
}
this.tree.updateChildren();
}
private applyStyles(theme: IColorTheme): void {
@@ -227,16 +208,12 @@ export class MessagePanel extends Disposable {
this.model.messages = [];
this._state = undefined;
this.model.totalExecuteMessage = undefined;
this.tree.refresh(this.model);
this.tree.updateChildren();
}
public set state(val: MessagePanelState) {
this._state = val;
// convert to old VS Code tree interface with expandable methods
let expandableTree: IExpandableTree = <IExpandableTree>this.tree;
if (this.state.scrollPosition) {
expandableTree.setScrollPosition(this.state.scrollPosition);
}
this.tree.setInput(this.model, val.viewState);
}
public get state(): MessagePanelState {
@@ -260,48 +237,28 @@ export class MessagePanel extends Disposable {
}
}
class MessageDataSource implements IDataSource {
getId(tree: ITree, element: Model | IResultMessageIntern): string {
if (element instanceof Model) {
return element.uuid;
} else {
if (!element.id) {
element.id = generateUuid();
}
return element.id;
}
}
hasChildren(tree: ITree, element: any): boolean {
class MessageDataSource implements IDataSource<Model, IMessagePanelMessage | IMessagePanelBatchMessage> {
hasChildren(element: Model | IMessagePanelMessage | IMessagePanelBatchMessage): boolean {
return element instanceof Model;
}
getChildren(tree: ITree, element: any): Promise<(IMessagePanelMessage | IMessagePanelBatchMessage)[]> {
if (element instanceof Model) {
let messages = element.messages;
if (element.totalExecuteMessage) {
messages = messages.concat(element.totalExecuteMessage);
}
return Promise.resolve(messages);
} else {
return Promise.resolve(undefined);
getChildren(element: Model): (IMessagePanelMessage | IMessagePanelBatchMessage)[] {
let messages = element.messages;
if (element.totalExecuteMessage) {
messages = messages.concat(element.totalExecuteMessage);
}
}
getParent(tree: ITree, element: any): Promise<void> {
return Promise.resolve(null);
return messages || [];
}
}
class MessageRenderer implements IRenderer {
getHeight(tree: ITree, element: IResultMessageIntern): number {
class MessagePanelDelegate extends CachedListVirtualDelegate<IResultMessageIntern> {
protected estimateHeight(element: IResultMessageIntern): number {
const lineHeight = 22;
let lines = element.message.split('\n').length;
return lineHeight * lines;
}
getTemplateId(tree: ITree, element: IResultMessageIntern): string {
getTemplateId(element: IResultMessageIntern): string {
if (element instanceof Model) {
return TemplateIds.MODEL;
} else if (element.selection) {
@@ -313,92 +270,86 @@ class MessageRenderer implements IRenderer {
}
}
renderTemplate(tree: ITree, templateId: string, container: HTMLElement): IMessageTemplate | IBatchTemplate {
if (templateId === TemplateIds.MESSAGE) {
container.append($('.time-stamp'));
const message = $('.message');
message.style.whiteSpace = 'pre';
container.append(message);
return { message };
} else if (templateId === TemplateIds.BATCH) {
const timeStamp = $('.time-stamp');
container.append(timeStamp);
const message = $('.batch-start');
message.style.whiteSpace = 'pre';
container.append(message);
return { message, timeStamp };
} else if (templateId === TemplateIds.ERROR) {
container.append($('.time-stamp'));
const message = $('.error-message');
container.append(message);
return { message };
} else {
return undefined;
}
hasDynamicHeight(element: IResultMessageIntern): boolean {
// Empty elements should not have dynamic height since they will be invisible
return element.message.toString().length > 0;
}
renderElement(tree: ITree, element: IResultMessageIntern, templateId: string, templateData: IMessageTemplate | IBatchTemplate): void {
if (templateId === TemplateIds.MESSAGE || templateId === TemplateIds.ERROR) {
let data: IMessageTemplate = templateData;
data.message.innerText = element.message;
} else if (templateId === TemplateIds.BATCH) {
let data = templateData as IBatchTemplate;
if (isString(element.time)) {
element.time = new Date(element.time!);
}
data.timeStamp.innerText = (element.time as Date).toLocaleTimeString();
data.message.innerText = element.message;
}
}
class ErrorMessageRenderer implements ITreeRenderer<IResultMessageIntern, void, IMessageTemplate> {
public readonly templateId = TemplateIds.ERROR;
renderTemplate(container: HTMLElement): IMessageTemplate {
container.append($('.time-stamp'));
const message = $('.error-message');
container.append(message);
return { message };
}
disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
renderElement(node: ITreeNode<IResultMessageIntern, void>, index: number, templateData: IMessageTemplate): void {
let data: IMessageTemplate = templateData;
data.message.innerText = node.element.message;
}
disposeTemplate(templateData: IMessageTemplate | IBatchTemplate): void {
}
}
export class MessageController extends WorkbenchTreeController {
class BatchMessageRenderer implements ITreeRenderer<IResultMessageIntern, void, IBatchTemplate> {
public readonly templateId = TemplateIds.BATCH;
private lastSelectedString: string = null;
public toFocusOnClick: { focus(): void };
constructor(@IEditorService private readonly editorService: IEditorService) { }
constructor(
options: IControllerOptions,
@IConfigurationService configurationService: IConfigurationService,
@IEditorService private workbenchEditorService: IEditorService
) {
super(options, configurationService);
renderTemplate(container: HTMLElement): IBatchTemplate {
const timeStamp = $('.time-stamp');
container.append(timeStamp);
const message = $('.batch-start');
message.style.whiteSpace = 'pre';
container.append(message);
return { message, timeStamp, disposable: new DisposableStore() };
}
protected onLeftClick(tree: ITree, element: any, eventish: ICancelableEvent, origin: string = 'mouse'): boolean {
// input and output are one element in the tree => we only expand if the user clicked on the output.
// if ((element.reference > 0 || (element instanceof RawObjectReplElement && element.hasChildren)) && mouseEvent.target.className.indexOf('input expression') === -1) {
super.onLeftClick(tree, element, eventish, origin);
tree.clearFocus();
tree.deselect(element);
// }
const selection = window.getSelection();
if (selection.type !== 'Range' || this.lastSelectedString === selection.toString()) {
// only focus the input if the user is not currently selecting.
this.toFocusOnClick.focus();
renderElement(node: ITreeNode<IResultMessageIntern, void>, index: number, templateData: IBatchTemplate): void {
if (isString(node.element.time)) {
node.element.time = new Date(node.element.time!);
}
this.lastSelectedString = selection.toString();
if (element.selection) {
let selection: ISelectionData = element.selection;
// this is a batch statement
let editor = this.workbenchEditorService.activeEditorPane as QueryEditor;
const codeEditor = <ICodeEditor>editor.getControl();
codeEditor.focus();
codeEditor.setSelection({ endColumn: selection.endColumn + 1, endLineNumber: selection.endLine + 1, startColumn: selection.startColumn + 1, startLineNumber: selection.startLine + 1 });
codeEditor.revealRangeInCenterIfOutsideViewport({ endColumn: selection.endColumn + 1, endLineNumber: selection.endLine + 1, startColumn: selection.startColumn + 1, startLineNumber: selection.startLine + 1 });
templateData.timeStamp.innerText = (node.element.time as Date).toLocaleTimeString();
templateData.message.innerText = node.element.message;
if (node.element.selection) {
const selection = { endColumn: node.element.selection.endColumn + 1, endLineNumber: node.element.selection.endLine + 1, startColumn: node.element.selection.startColumn + 1, startLineNumber: node.element.selection.startLine + 1 };
templateData.disposable.add(addStandardDisposableGenericMouseDownListner(templateData.message, () => {
let editor = this.editorService.activeEditorPane as QueryEditor;
const codeEditor = <ICodeEditor>editor.getControl();
codeEditor.focus();
codeEditor.setSelection(selection);
codeEditor.revealRangeInCenterIfOutsideViewport(selection);
}));
}
return true;
}
public onContextMenu(tree: ITree, element: any, event: ContextMenuEvent): boolean {
return true;
disposeTemplate(templateData: IBatchTemplate): void {
dispose(templateData.disposable);
}
}
class MessageRenderer implements ITreeRenderer<IResultMessageIntern, void, IMessageTemplate> {
public readonly templateId = TemplateIds.MESSAGE;
renderTemplate(container: HTMLElement): IMessageTemplate {
container.append($('.time-stamp'));
const message = $('.message');
message.style.whiteSpace = 'pre';
container.append(message);
return { message };
}
renderElement(node: ITreeNode<IResultMessageIntern, void>, index: number, templateData: IMessageTemplate): void {
let data: IMessageTemplate = templateData;
data.message.innerText = node.element.message;
}
disposeTemplate(templateData: IMessageTemplate | IBatchTemplate): void {
}
}
@@ -407,8 +358,4 @@ export class Model {
public totalExecuteMessage: IMessagePanelMessage;
public uuid = generateUuid();
public focus() {
}
}