Merge pull request #12663 from influxdata/feat/flux--insert-function

feat(ui): Update flux functions insertion from  toolbar to add to current line or below
pull/12665/head
Iris Scholten 2019-03-15 17:37:56 -07:00 committed by GitHub
commit 009e5130e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 151 additions and 20 deletions

View File

@ -1,6 +1,7 @@
## v2.0.0-alpha.7 [unreleased]
### Features
1. [12663](https://github.com/influxdata/influxdb/pull/12663): Insert flux function near cursor in flux editor
### Bug Fixes

View File

@ -1,7 +1,7 @@
// Libraries
import React, {PureComponent} 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 'src/external/codemirror'
@ -34,6 +34,7 @@ interface Props {
onSubmitScript?: () => void
suggestions: Suggestion[]
visibility?: string
onCursorChange?: (position: Position) => void
}
interface Widget extends LineWidget {
@ -108,11 +109,20 @@ class FluxEditor extends PureComponent<Props, State> {
onTouchStart={this.onTouchStart}
editorDidMount={this.handleMount}
onKeyUp={this.handleKeyUp}
onCursor={this.handleCursorChange}
/>
</div>
)
}
private handleCursorChange = (__: IInstance, position: Position) => {
const {onCursorChange} = this.props
if (onCursorChange) {
onCursorChange(position)
}
}
private makeError(): void {
this.editor.clearGutter('error-gutter')
const lineNumbers = this.statusLine

View File

@ -1,6 +1,7 @@
// Libraries
import React, {PureComponent} from 'react'
import {connect} from 'react-redux'
import {Position} from 'codemirror'
// Components
import FluxEditor from 'src/shared/components/FluxEditor'
@ -15,6 +16,7 @@ import {saveAndExecuteQueries} from 'src/timeMachine/actions/queries'
// Utils
import {getActiveQuery} from 'src/timeMachine/selectors'
import {insertFluxFunction} from 'src/timeMachine/utils/scriptInsertion'
// Constants
import {HANDLE_VERTICAL, HANDLE_NONE} from 'src/shared/constants'
@ -42,6 +44,8 @@ interface State {
type Props = StateProps & DispatchProps
class TimeMachineFluxEditor extends PureComponent<Props, State> {
private cursorPosition: Position = {line: 0, ch: 0}
public state: State = {
displayFluxFunctions: true,
}
@ -60,6 +64,7 @@ class TimeMachineFluxEditor extends PureComponent<Props, State> {
onChangeScript={onSetActiveQueryText}
onSubmitScript={onSubmitQueries}
suggestions={[]}
onCursorChange={this.handleCursorPosition}
/>
),
},
@ -102,12 +107,38 @@ class TimeMachineFluxEditor extends PureComponent<Props, State> {
const {displayFluxFunctions} = this.state
if (displayFluxFunctions) {
return <FluxFunctionsToolbar />
return (
<FluxFunctionsToolbar
onInsertFluxFunction={this.handleInsertFluxFunction}
/>
)
}
return <VariablesToolbar />
}
private handleCursorPosition = (position: Position): void => {
this.cursorPosition = position
}
private handleInsertFluxFunction = async (
functionName: string,
fluxFunction: string
): Promise<void> => {
const {activeQueryText} = this.props
const {line} = this.cursorPosition
const {updatedScript, cursorPosition} = insertFluxFunction(
line,
activeQueryText,
functionName,
fluxFunction
)
await this.props.onSetActiveQueryText(updatedScript)
this.handleCursorPosition(cursorPosition)
}
private showFluxFunctions = () => {
this.setState({displayFluxFunctions: true})
}

View File

@ -16,7 +16,7 @@ import {setActiveQueryText} from 'src/timeMachine/actions'
import {getActiveQuery} from 'src/timeMachine/selectors'
// Constants
import {FLUX_FUNCTIONS, FROM, UNION} from 'src/shared/constants/fluxFunctions'
import {FLUX_FUNCTIONS} from 'src/shared/constants/fluxFunctions'
// Styles
import 'src/timeMachine/components/fluxFunctionsToolbar/FluxFunctionsToolbar.scss'
@ -24,6 +24,10 @@ import 'src/timeMachine/components/fluxFunctionsToolbar/FluxFunctionsToolbar.scs
// Types
import {AppState} from 'src/types/v2'
interface OwnProps {
onInsertFluxFunction: (functionName: string, text: string) => void
}
interface StateProps {
activeQueryText: string
}
@ -32,7 +36,7 @@ interface DispatchProps {
onSetActiveQueryText: (script: string) => void
}
type Props = StateProps & DispatchProps
type Props = OwnProps & StateProps & DispatchProps
interface State {
searchTerm: string
@ -59,7 +63,7 @@ class FluxFunctionsToolbar extends PureComponent<Props, State> {
key={category}
category={category}
funcs={funcs}
onClickFunction={this.handleUpdateScript}
onClickFunction={this.handleClickFunction}
/>
))
}
@ -74,21 +78,8 @@ class FluxFunctionsToolbar extends PureComponent<Props, State> {
this.setState({searchTerm})
}
private handleUpdateScript = (funcName: string, funcExample: string) => {
const {activeQueryText, onSetActiveQueryText} = this.props
switch (funcName) {
case FROM.name: {
onSetActiveQueryText(`${activeQueryText}\n${funcExample}`)
return
}
case UNION.name: {
onSetActiveQueryText(`${activeQueryText.trimRight()}\n\n${funcExample}`)
return
}
default:
onSetActiveQueryText(`${activeQueryText}\n |> ${funcExample}`)
}
private handleClickFunction = (funcName: string, funcExample: string) => {
this.props.onInsertFluxFunction(funcName, funcExample)
}
}

View File

@ -0,0 +1,98 @@
import {Position} from 'codemirror'
// Constants
import {FROM, UNION} from 'src/shared/constants/fluxFunctions'
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 functionRequiresNewLine = (funcName: string): boolean => {
switch (funcName) {
case FROM.name:
case UNION.name: {
return true
}
default:
return false
}
}
const formatFunctionForInsert = (funcName: string, fluxFunction: string) => {
if (functionRequiresNewLine(funcName)) {
return `\n${fluxFunction}`
}
return ` |> ${fluxFunction}`
}
const getCursorPosition = (
insertLineNumber,
formattedFunction,
funcName
): Position => {
const ch = formattedFunction.length - 1
const line = functionRequiresNewLine(funcName)
? insertLineNumber + 1
: insertLineNumber
return {line, ch}
}
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,
functionName
)
return {updatedScript, cursorPosition: updatedCursorPosition}
}