commit
34916b7f99
|
@ -42,6 +42,8 @@ interface State {
|
|||
}
|
||||
|
||||
export default class TagListItem extends PureComponent<Props, State> {
|
||||
private debouncedOnSearch: () => void
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
|
@ -167,8 +169,6 @@ export default class TagListItem extends PureComponent<Props, State> {
|
|||
)
|
||||
}
|
||||
|
||||
private debouncedOnSearch() {} // See constructor
|
||||
|
||||
private handleInputClick = (e: MouseEvent<HTMLDivElement>): void => {
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
|
|
@ -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<Props> {
|
||||
class TimeMachineEditor extends PureComponent<Props, State> {
|
||||
private editor: EditorInstance
|
||||
private lineWidgets: Widget[] = []
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
@ -48,6 +57,7 @@ class TimeMachineEditor extends PureComponent<Props> {
|
|||
|
||||
if (status.type !== 'error') {
|
||||
this.editor.clearGutter('error-gutter')
|
||||
this.clearWidgets()
|
||||
}
|
||||
|
||||
if (prevProps.visibility === visibility) {
|
||||
|
@ -63,13 +73,13 @@ class TimeMachineEditor extends PureComponent<Props> {
|
|||
const {script} = this.props
|
||||
|
||||
const options = {
|
||||
lineNumbers: true,
|
||||
theme: 'time-machine',
|
||||
tabIndex: 1,
|
||||
readonly: false,
|
||||
completeSingle: false,
|
||||
autoRefresh: true,
|
||||
mode: 'flux',
|
||||
readonly: false,
|
||||
lineNumbers: true,
|
||||
autoRefresh: true,
|
||||
theme: 'time-machine',
|
||||
completeSingle: false,
|
||||
gutters: ['error-gutter'],
|
||||
}
|
||||
|
||||
|
@ -98,23 +108,55 @@ class TimeMachineEditor extends PureComponent<Props> {
|
|||
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.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')
|
||||
|
|
|
@ -54,10 +54,14 @@ interface State {
|
|||
suggestions: Suggestion[]
|
||||
}
|
||||
|
||||
type ScriptFunc = (script: string) => void
|
||||
|
||||
export const FluxContext = React.createContext()
|
||||
|
||||
@ErrorHandling
|
||||
export class FluxPage extends PureComponent<Props, State> {
|
||||
private debouncedASTResponse: ScriptFunc
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
|
@ -70,6 +74,10 @@ export class FluxPage extends PureComponent<Props, State> {
|
|||
text: '',
|
||||
},
|
||||
}
|
||||
|
||||
this.debouncedASTResponse = _.debounce(script => {
|
||||
this.getASTResponse(script, false)
|
||||
}, 250)
|
||||
}
|
||||
|
||||
public async componentDidMount() {
|
||||
|
@ -288,6 +296,7 @@ export class FluxPage extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
private handleChangeScript = (script: string): void => {
|
||||
this.debouncedASTResponse(script)
|
||||
this.props.updateScript(script)
|
||||
}
|
||||
|
||||
|
@ -405,7 +414,7 @@ export class FluxPage extends PureComponent<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
private getASTResponse = async (script: string) => {
|
||||
private getASTResponse = async (script: string, update: boolean = true) => {
|
||||
const {links} = this.props
|
||||
|
||||
if (!script) {
|
||||
|
@ -414,10 +423,14 @@ export class FluxPage extends PureComponent<Props, State> {
|
|||
|
||||
try {
|
||||
const ast = await getAST({url: links.ast, body: script})
|
||||
|
||||
if (update) {
|
||||
this.props.updateScript(script)
|
||||
}
|
||||
|
||||
const body = bodyNodes(ast, this.state.suggestions)
|
||||
const status = {type: 'success', text: ''}
|
||||
this.setState({ast, body, status})
|
||||
this.props.updateScript(script)
|
||||
} catch (error) {
|
||||
this.setState({status: this.parseError(error)})
|
||||
return console.error('Could not parse AST', error)
|
||||
|
|
|
@ -23,4 +23,9 @@
|
|||
.error-warning {
|
||||
color: $c-dreamsicle;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.inline-error-message {
|
||||
color: white;
|
||||
background-color: red;
|
||||
}
|
Loading…
Reference in New Issue