diff --git a/ui/src/flux/components/TimeMachineEditor.tsx b/ui/src/flux/components/TimeMachineEditor.tsx index 812e699c4..06f9b23a6 100644 --- a/ui/src/flux/components/TimeMachineEditor.tsx +++ b/ui/src/flux/components/TimeMachineEditor.tsx @@ -1,6 +1,6 @@ import React, {PureComponent} from 'react' import {Controlled as ReactCodeMirror, IInstance} from 'react-codemirror2' -import {EditorChange} from 'codemirror' +import {EditorChange, LineWidget} from 'codemirror' import {ShowHintOptions} from 'src/types/codemirror' import {ErrorHandling} from 'src/shared/decorators/errors' import {OnChangeScript, OnSubmitScript, Suggestion} from 'src/types/flux' @@ -27,13 +27,22 @@ interface Props { suggestions: Suggestion[] } +interface Widget extends LineWidget { + node: HTMLElement +} + +interface State { + lineWidgets: Widget[] +} + interface EditorInstance extends IInstance { showHint: (options?: ShowHintOptions) => void } @ErrorHandling -class TimeMachineEditor extends PureComponent { +class TimeMachineEditor extends PureComponent { private editor: EditorInstance + private lineWidgets: Widget[] = [] constructor(props) { super(props) @@ -48,6 +57,7 @@ class TimeMachineEditor extends PureComponent { if (status.type !== 'error') { this.editor.clearGutter('error-gutter') + this.clearWidgets() } if (prevProps.visibility === visibility) { @@ -98,23 +108,56 @@ class TimeMachineEditor extends PureComponent { this.editor.clearGutter('error-gutter') const lineNumbers = this.statusLine lineNumbers.forEach(({line, text}) => { + const lineNumber = line - 1 this.editor.setGutterMarker( - line - 1, + lineNumber, 'error-gutter', - this.errorMarker(text) + this.errorMarker(text, lineNumber) ) }) this.editor.refresh() } - private errorMarker(message: string): HTMLElement { + private errorMarker(message: string, line: number): HTMLElement { const span = document.createElement('span') span.className = 'icon stop error-warning' span.title = message + span.addEventListener('click', this.handleClickError(message, line)) return span } + private handleClickError = (text: string, line: number) => () => { + let widget = this.lineWidgets.find(w => w.node.textContent === text) + + if (widget) { + return this.clearWidget(widget) + } + + const errorDiv = document.createElement('div') + errorDiv.id = text + errorDiv.className = 'inline-error-message' + errorDiv.innerText = text + widget = this.editor.addLineWidget(line, errorDiv) as Widget + + this.lineWidgets = [...this.lineWidgets, widget] + } + + private clearWidget = (widget: Widget): void => { + widget.clear() + this.lineWidgets = this.lineWidgets.filter( + w => w.node.textContent !== widget.node.textContent + ) + } + + private clearWidgets = () => { + this.lineWidgets.forEach(w => { + w.clear() + }) + + this.lineWidgets = [] + } + private get statusLine(): Gutter[] { const {status} = this.props const messages = status.text.split('\n') diff --git a/ui/src/style/components/time-machine/flux-editor.scss b/ui/src/style/components/time-machine/flux-editor.scss index 28cdfc680..4543a2624 100644 --- a/ui/src/style/components/time-machine/flux-editor.scss +++ b/ui/src/style/components/time-machine/flux-editor.scss @@ -23,4 +23,9 @@ .error-warning { color: $c-dreamsicle; cursor: pointer; +} + +.inline-error-message { + color: white; + background-color: red; } \ No newline at end of file