Skip to content

Commit

Permalink
feat(ui): Update flux functions insertion from toolbar to add to curr…
Browse files Browse the repository at this point in the history
…ent line or below
  • Loading branch information
ischolten committed Mar 16, 2019
1 parent 0a9ad0c commit e250eb1
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 20 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
12 changes: 11 additions & 1 deletion ui/src/shared/components/FluxEditor.tsx
Original file line number Diff line number Diff line change
@@ -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'

Expand Down Expand Up @@ -34,6 +34,7 @@ interface Props {
onSubmitScript?: () => void
suggestions: Suggestion[]
visibility?: string
onCursorChange?: (position: Position) => void
}

interface Widget extends LineWidget {
Expand Down Expand Up @@ -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
Expand Down
33 changes: 32 additions & 1 deletion ui/src/timeMachine/components/TimeMachineFluxEditor.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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'
Expand Down Expand Up @@ -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,
}
Expand All @@ -60,6 +64,7 @@ class TimeMachineFluxEditor extends PureComponent<Props, State> {
onChangeScript={onSetActiveQueryText}
onSubmitScript={onSubmitQueries}
suggestions={[]}
onCursorChange={this.handleCursorPosition}
/>
),
},
Expand Down Expand Up @@ -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})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@ 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'

// Types
import {AppState} from 'src/types/v2'

interface OwnProps {
onInsertFluxFunction: (functionName: string, text: string) => void
}

interface StateProps {
activeQueryText: string
}
Expand All @@ -32,7 +36,7 @@ interface DispatchProps {
onSetActiveQueryText: (script: string) => void
}

type Props = StateProps & DispatchProps
type Props = OwnProps & StateProps & DispatchProps

interface State {
searchTerm: string
Expand All @@ -59,7 +63,7 @@ class FluxFunctionsToolbar extends PureComponent<Props, State> {
key={category}
category={category}
funcs={funcs}
onClickFunction={this.handleUpdateScript}
onClickFunction={this.handleClickFunction}
/>
))
}
Expand All @@ -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)
}
}

Expand Down
98 changes: 98 additions & 0 deletions ui/src/timeMachine/utils/scriptInsertion.ts
Original file line number Diff line number Diff line change
@@ -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}
}

0 comments on commit e250eb1

Please sign in to comment.