Insert flux function at or below cursor line
parent
a7843d0451
commit
9d7b70a860
ui/src
flux
components
helpers
shared/components/TimeMachine
|
@ -2,6 +2,7 @@
|
|||
### Bug Fixes
|
||||
1. [#5110](https://github.com/influxdata/chronograf/pull/5110): Fix the input for line controls in visualization options.
|
||||
1. [#5111](https://github.com/influxdata/chronograf/pull/5111): Stop scrollbars from covering text in flux editor
|
||||
1. [#5114](https://github.com/influxdata/chronograf/pull/5114): Insert flux function near cursor in flux editor
|
||||
|
||||
## v1.7.8 [2019-02-08]
|
||||
### Bug Fixes
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Libraries
|
||||
import React, {PureComponent} from 'react'
|
||||
import {Position} from 'codemirror'
|
||||
|
||||
// Components
|
||||
import FluxScriptEditor from 'src/flux/components/FluxScriptEditor'
|
||||
|
@ -16,6 +17,7 @@ interface Props {
|
|||
onChangeScript: (draftScript: string) => void
|
||||
onSubmitScript: () => void
|
||||
onShowWizard: () => void
|
||||
onCursorChange?: (position: Position) => void
|
||||
}
|
||||
|
||||
class FluxEditor extends PureComponent<Props> {
|
||||
|
@ -28,6 +30,7 @@ class FluxEditor extends PureComponent<Props> {
|
|||
onChangeScript,
|
||||
onSubmitScript,
|
||||
onShowWizard,
|
||||
onCursorChange,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
|
@ -39,6 +42,7 @@ class FluxEditor extends PureComponent<Props> {
|
|||
suggestions={suggestions}
|
||||
onChangeScript={onChangeScript}
|
||||
onSubmitScript={onSubmitScript}
|
||||
onCursorChange={onCursorChange}
|
||||
>
|
||||
{script.trim() === '' && (
|
||||
<div className="flux-script-wizard--bg-hint">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, {PureComponent, MouseEvent} from 'react'
|
||||
import {Controlled as ReactCodeMirror, IInstance} from 'react-codemirror2'
|
||||
import {EditorChange, LineWidget} from 'codemirror'
|
||||
import {EditorChange, LineWidget, Position} from 'codemirror'
|
||||
import {ShowHintOptions} from 'src/types/codemirror'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {OnChangeScript, Suggestion} from 'src/types/flux'
|
||||
|
@ -24,6 +24,7 @@ interface Props {
|
|||
onChangeScript: OnChangeScript
|
||||
onSubmitScript: () => void
|
||||
suggestions: Suggestion[]
|
||||
onCursorChange?: (position: Position) => void
|
||||
}
|
||||
|
||||
interface Widget extends LineWidget {
|
||||
|
@ -32,6 +33,7 @@ interface Widget extends LineWidget {
|
|||
|
||||
interface State {
|
||||
script: string
|
||||
cursorPosition: Position
|
||||
}
|
||||
|
||||
interface EditorInstance extends IInstance {
|
||||
|
@ -51,6 +53,7 @@ class FluxScriptEditor extends PureComponent<Props, State> {
|
|||
super(props)
|
||||
this.state = {
|
||||
script: props.script,
|
||||
cursorPosition: null,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,12 +111,21 @@ class FluxScriptEditor extends PureComponent<Props, State> {
|
|||
onTouchStart={this.onTouchStart}
|
||||
editorDidMount={this.handleMount}
|
||||
onKeyUp={this.handleKeyUp}
|
||||
onCursor={this.handleCursorChange}
|
||||
/>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private handleCursorChange = (__: IInstance, position: Position) => {
|
||||
const {onCursorChange} = this.props
|
||||
|
||||
if (onCursorChange) {
|
||||
onCursorChange(position)
|
||||
}
|
||||
}
|
||||
|
||||
private handleMouseEnter = (e: MouseEvent<HTMLDivElement>) => {
|
||||
const {width, height} = e.currentTarget.getBoundingClientRect()
|
||||
const {width: prevWidth, height: prevHeight} = this.containerDimensions
|
||||
|
|
|
@ -9,7 +9,7 @@ import SearchBar from 'src/flux/components/flux_functions_toolbar/SearchBar'
|
|||
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||
|
||||
// Constants
|
||||
import {FUNCTIONS, FROM, UNION} from 'src/flux/constants/functions'
|
||||
import {FUNCTIONS} from 'src/flux/constants/functions'
|
||||
|
||||
// Utils
|
||||
import {TimeMachineContainer} from 'src/shared/utils/TimeMachineContainer'
|
||||
|
@ -17,20 +17,28 @@ import {TimeMachineContainer} from 'src/shared/utils/TimeMachineContainer'
|
|||
// Decorators
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
interface PassedProps {
|
||||
onInsertFluxFunction: (functionName: string, text: string) => void
|
||||
}
|
||||
|
||||
interface ConnectedProps {
|
||||
script: string
|
||||
onUpdateScript: (script: string) => void
|
||||
}
|
||||
|
||||
type Props = PassedProps & ConnectedProps
|
||||
|
||||
interface State {
|
||||
searchTerm: string
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
class FluxFunctionsToolbar extends PureComponent<ConnectedProps, State> {
|
||||
public constructor(props) {
|
||||
class FluxFunctionsToolbar extends PureComponent<Props, State> {
|
||||
public constructor(props: Props) {
|
||||
super(props)
|
||||
this.state = {searchTerm: ''}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {searchTerm} = this.state
|
||||
return (
|
||||
|
@ -50,7 +58,7 @@ class FluxFunctionsToolbar extends PureComponent<ConnectedProps, State> {
|
|||
key={category}
|
||||
category={category}
|
||||
funcs={funcs}
|
||||
onClickFunction={this.handleUpdateScript}
|
||||
onClickFunction={this.handleClickFunction}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -63,32 +71,23 @@ class FluxFunctionsToolbar extends PureComponent<ConnectedProps, State> {
|
|||
)
|
||||
}
|
||||
|
||||
private handleClickFunction = (
|
||||
fluxFunction: string,
|
||||
funcExample: string
|
||||
): void => {
|
||||
this.props.onInsertFluxFunction(fluxFunction, funcExample)
|
||||
}
|
||||
|
||||
private handleSearch = (searchTerm: string): void => {
|
||||
this.setState({searchTerm})
|
||||
}
|
||||
|
||||
private handleUpdateScript = (funcName: string, funcExample: string) => {
|
||||
const {script, onUpdateScript} = this.props
|
||||
|
||||
switch (funcName) {
|
||||
case FROM.name: {
|
||||
onUpdateScript(`${script}\n${funcExample}`)
|
||||
return
|
||||
}
|
||||
case UNION.name: {
|
||||
onUpdateScript(`${script.trimRight()}\n\n${funcExample}`)
|
||||
return
|
||||
}
|
||||
default:
|
||||
onUpdateScript(`${script}\n |> ${funcExample}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ConnectedFluxFunctionsToolbar = () => (
|
||||
const ConnectedFluxFunctionsToolbar = (props: PassedProps) => (
|
||||
<Subscribe to={[TimeMachineContainer]}>
|
||||
{(container: TimeMachineContainer) => (
|
||||
<FluxFunctionsToolbar
|
||||
{...props}
|
||||
script={container.state.draftScript}
|
||||
onUpdateScript={container.handleUpdateDraftScript}
|
||||
/>
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
import {Position} from 'codemirror'
|
||||
|
||||
// Constants
|
||||
import {FROM, UNION} from 'src/flux/constants/functions'
|
||||
|
||||
const rejoinScript = (scriptLines: string[]): string => {
|
||||
return scriptLines.join('\n')
|
||||
}
|
||||
|
||||
const insertAtLine = (
|
||||
lineNumber: number,
|
||||
scriptLines: string[],
|
||||
textToInsert: string,
|
||||
insertOnSameLine?: boolean
|
||||
): string => {
|
||||
const front = scriptLines.slice(0, lineNumber)
|
||||
|
||||
const backStartIndex = insertOnSameLine ? lineNumber + 1 : lineNumber
|
||||
const back = scriptLines.slice(backStartIndex)
|
||||
|
||||
const updated = [...front, textToInsert, ...back]
|
||||
|
||||
return rejoinScript(updated)
|
||||
}
|
||||
|
||||
const getInsertLineNumber = (
|
||||
currentLineNumber: number,
|
||||
scriptLines: string[]
|
||||
): number => {
|
||||
const currentLine = scriptLines[currentLineNumber]
|
||||
|
||||
// Insert on the current line if its an empty line
|
||||
if (!currentLine.trim()) {
|
||||
return currentLineNumber
|
||||
}
|
||||
|
||||
return currentLineNumber + 1
|
||||
}
|
||||
|
||||
const formatFunctionForInsert = (funcName: string, fluxFunction: string) => {
|
||||
switch (funcName) {
|
||||
case FROM.name:
|
||||
case UNION.name: {
|
||||
return `\n${fluxFunction}`
|
||||
}
|
||||
default:
|
||||
return ` |> ${fluxFunction}`
|
||||
}
|
||||
}
|
||||
|
||||
const getCursorPosition = (insertLineNumber, formattedFunction): Position => {
|
||||
const endOfLine = formattedFunction.length - 1
|
||||
|
||||
return {line: insertLineNumber, ch: endOfLine}
|
||||
}
|
||||
|
||||
export const insertFluxFunction = (
|
||||
currentLineNumber: number,
|
||||
currentScript: string,
|
||||
functionName: string,
|
||||
fluxFunction: string
|
||||
): {updatedScript: string; cursorPosition: Position} => {
|
||||
const scriptLines = currentScript.split('\n')
|
||||
const insertLineNumber = getInsertLineNumber(currentLineNumber, scriptLines)
|
||||
const insertOnSameLine = currentLineNumber === insertLineNumber
|
||||
|
||||
const formattedFunction = formatFunctionForInsert(functionName, fluxFunction)
|
||||
|
||||
const updatedScript = insertAtLine(
|
||||
insertLineNumber,
|
||||
scriptLines,
|
||||
formattedFunction,
|
||||
insertOnSameLine
|
||||
)
|
||||
|
||||
const updatedCursorPosition = getCursorPosition(
|
||||
insertLineNumber,
|
||||
formattedFunction
|
||||
)
|
||||
|
||||
return {updatedScript, cursorPosition: updatedCursorPosition}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
// Libraries
|
||||
import React, {PureComponent} from 'react'
|
||||
import {Subscribe} from 'unstated'
|
||||
import {Position} from 'codemirror'
|
||||
|
||||
// Components
|
||||
import SchemaExplorer from 'src/flux/components/SchemaExplorer'
|
||||
|
@ -20,6 +21,7 @@ import DefaultDebouncer, {Debouncer} from 'src/shared/utils/debouncer'
|
|||
import {TimeMachineContainer} from 'src/shared/utils/TimeMachineContainer'
|
||||
import {parseError} from 'src/flux/helpers/scriptBuilder'
|
||||
import {getSuggestions} from 'src/flux/helpers/suggestions'
|
||||
import {insertFluxFunction} from 'src/flux/helpers/scriptInsertion'
|
||||
|
||||
// Types
|
||||
import {NotificationAction, Source} from 'src/types'
|
||||
|
@ -56,6 +58,7 @@ interface State {
|
|||
class FluxQueryMaker extends PureComponent<Props, State> {
|
||||
private debouncer: Debouncer = new DefaultDebouncer()
|
||||
private getAST = restartable(getAST)
|
||||
private cursorPosition: Position
|
||||
|
||||
public constructor(props: Props) {
|
||||
super(props)
|
||||
|
@ -122,6 +125,7 @@ class FluxQueryMaker extends PureComponent<Props, State> {
|
|||
onChangeScript={this.handleChangeDraftScript}
|
||||
onSubmitScript={this.handleSubmitScript}
|
||||
onShowWizard={this.handleShowWizard}
|
||||
onCursorChange={this.handleCursorPosition}
|
||||
/>
|
||||
),
|
||||
},
|
||||
|
@ -130,7 +134,11 @@ class FluxQueryMaker extends PureComponent<Props, State> {
|
|||
size: rightSize,
|
||||
headerButtons: [],
|
||||
menuOptions: [],
|
||||
render: () => <FluxFunctionsToolbar />,
|
||||
render: () => (
|
||||
<FluxFunctionsToolbar
|
||||
onInsertFluxFunction={this.handleInsertFluxFunction}
|
||||
/>
|
||||
),
|
||||
headerOrientation: HANDLE_VERTICAL,
|
||||
},
|
||||
]
|
||||
|
@ -152,6 +160,28 @@ class FluxQueryMaker extends PureComponent<Props, State> {
|
|||
)
|
||||
}
|
||||
|
||||
private handleCursorPosition = (position: Position): void => {
|
||||
this.cursorPosition = position
|
||||
}
|
||||
|
||||
private handleInsertFluxFunction = async (
|
||||
functionName: string,
|
||||
fluxFunction: string
|
||||
): Promise<void> => {
|
||||
const {draftScript} = this.props
|
||||
const {line} = this.cursorPosition
|
||||
|
||||
const {updatedScript, cursorPosition} = insertFluxFunction(
|
||||
line,
|
||||
draftScript,
|
||||
functionName,
|
||||
fluxFunction
|
||||
)
|
||||
await this.handleChangeDraftScript(updatedScript)
|
||||
|
||||
this.handleCursorPosition(cursorPosition)
|
||||
}
|
||||
|
||||
private handleSubmitScript = () => {
|
||||
const {
|
||||
onChangeScript,
|
||||
|
|
Loading…
Reference in New Issue