Skip to content

Commit

Permalink
wip new init flow
Browse files Browse the repository at this point in the history
  • Loading branch information
timsuchanek committed Aug 19, 2019
1 parent f0e6b7c commit 807914e
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 86 deletions.
2 changes: 1 addition & 1 deletion cli/introspection/src/prompt/components/BoxPrompt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export const Prompt: React.FC<Props> = props => {
: props.elements

useStdin(
(actionKey, text) => {
({ actionKey, text }) => {
setCursor(prevCursor => {
if (actionKey === 'up') {
return up(prevCursor, elementsWithBack)
Expand Down
76 changes: 35 additions & 41 deletions cli/introspection/src/prompt/components/SelectItem.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Box, Color, ColorProps } from 'ink'
import { Box, Color, Text } from 'ink'
import * as React from 'react'
import { COLORS } from '../colors'
import { BACK_SYMBOL } from './helpers'
import { Spinner } from './Spinner'
import { SpinnerState } from '../types'
Expand All @@ -9,78 +8,73 @@ import figures = require('figures')

interface Props {
label: string
spinnerState: SpinnerState | undefined
spinnerState?: SpinnerState | undefined
value?: any
focus: boolean
description?: string
isBackButton?: boolean
onSelect: (value?: any) => void
padding?: number
}

function renderSelectIndicator(spinnerState: SpinnerState | undefined, isBackButton: boolean) {
export const SelectIndicator: React.FC<{
spinnerState?: SpinnerState | undefined
isBackButton?: boolean
color?: any
}> = ({ isBackButton, spinnerState }) => {
if (isBackButton) {
return BACK_SYMBOL
return <>{BACK_SYMBOL}</>
}

if (spinnerState && spinnerState.state === 'running') {
return <Spinner />
}

return figures.pointer
return <>{figures.pointer}</>
}

export const SelectIndicator: React.FC<{
spinnerState?: SpinnerState | undefined
isBackButton?: boolean
color?: ColorProps
}> = props => (
<Box marginRight={1}>
<Color {...props.color}>{renderSelectIndicator(props.spinnerState, props.isBackButton!)}</Color>
</Box>
)
SelectIndicator.defaultProps = {
isBackButton: false,
spinnerState: undefined,
}

function renderDescription(props: Props) {
if (props.spinnerState && props.spinnerState.message) {
if (props.spinnerState.state === 'running' || props.spinnerState.state === 'succeeded') {
return <Color green>{props.spinnerState.message}</Color>
} else if (props.spinnerState.state === 'failed') {
return <Color red>{props.spinnerState.message}</Color>
export const Description: React.FC<Props> = ({ spinnerState, description }) => {
if (spinnerState && spinnerState.message) {
if (spinnerState.state === 'running' || spinnerState.state === 'succeeded') {
return <Color green>{spinnerState.message}</Color>
} else if (spinnerState.state === 'failed') {
return <Color red>{spinnerState.message}</Color>
}
} else {
return <Color dim>{props.description || ''}</Color>
return <Color dim>{description || ''}</Color>
}
}

SelectIndicator.defaultProps = {
isBackButton: false,
spinnerState: undefined,
return null
}

export const SelectItem: React.FC<Props> = props => {
const indicator = (
<SelectIndicator
color={props.focus ? { [COLORS.selection]: true } : {}}
spinnerState={props.spinnerState}
isBackButton={props.isBackButton!}
/>
)

useStdin(
async actionKey => {
async ({ actionKey }) => {
if (props.focus && actionKey === 'submit') {
await props.onSelect(props.value)
}
},
[props.focus, props.value],
)

const padding = props.padding || 20

const textColor = props.focus ? 'cyan' : 'visible'
return (
<Box>
{props.focus ? <Color keyword={COLORS.selection}>{indicator}</Color> : indicator}
<Box marginLeft={1}>
{props.focus ? <Color keyword={COLORS.selection}>{props.label.padEnd(20)}</Color> : props.label.padEnd(20)}
</Box>
{renderDescription(props)}
<Color {...{ [textColor]: true }}>
<Box width={14} marginRight={2}>
{props.focus ? <SelectIndicator spinnerState={props.spinnerState} isBackButton={props.isBackButton!} /> : ' '}{' '}
<Text {...{ bold: props.focus }}>{props.label.padEnd(padding)}</Text>
</Box>
<Color dim>
<Description {...props} />
</Color>
</Color>
</Box>
)
}
Expand Down
2 changes: 1 addition & 1 deletion cli/introspection/src/prompt/inkTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ class Compy extends React.Component<any, { step: number; progress: number }> {

export function renderInk() {
return new Promise(resolve => {
render(<Step0StarterVsBlank />)
render(<Step0StarterVsBlank onSubmit={resolve} />)
})
}
108 changes: 68 additions & 40 deletions cli/introspection/src/prompt/steps/Step0StarterVsBlank.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,74 @@ import { Box, Color, Text } from 'ink'
import BorderBox from '../components/BorderBox'
import chalk from 'chalk'
import figures from 'figures'
import { useStdin } from '../useStdin'
import { SelectItem } from '../components/SelectItem'

export default class Step0StarterVsBlank extends React.Component {
render() {
return (
<Box flexDirection="column">
<Box flexDirection="column" marginLeft={2}>
<Text bold>Select the language for a starter kit or start with a blank project.</Text>
<Color dim>Starter kits provide ready-made setups for various use cases.</Color>
</Box>
<BorderBox flexDirection="column" title={chalk.bold('Languages for starter kits')} marginTop={1}>
<Box>
<Color cyan>
<Box width={14} marginRight={2}>
{figures.pointer} <Text bold>JavaScript</Text>
</Box>
<Color dim>GraphQL, REST, gRPC, ...</Color>
</Color>
</Box>
<Box>
<Box width={14} marginLeft={2}>
TypeScript
</Box>
<Color dim>GraphQL, REST, gRPC, ...</Color>
</Box>
<Box marginLeft={2}>
<Color dim>Go (Coming soon)</Color>
</Box>
<Box marginLeft={2} marginTop={1}>
<Color dim>Note: Starter kits only work with empty databases</Color>
</Box>
</BorderBox>
<BorderBox flexDirection="column" title={chalk.bold('Get started from scratch')} marginBottom={1} marginTop={1}>
<Box>
<Box width={15} marginLeft={2}>
Blank project
</Box>
<Color dim>Supports introspecting your existing DB</Color>
</Box>
</BorderBox>
export type StarterSelection = 'js' | 'ts' | 'blank'

export interface Props {
onSubmit: (selection: StarterSelection) => void
}

const selectionMapping: StarterSelection[] = ['js', 'ts', 'blank']

export default function Step0StarterVsBlank({ onSubmit }: Props) {
const [selectedIndex, selectIndex] = React.useState(0)

useStdin(
async ({ actionKey, text, key }) => {
if (key.name === 'space' || key.name === 'return') {
onSubmit(selectionMapping[selectedIndex])
}

if (key.name === 'down') {
selectIndex((selectedIndex + 1) % 3)
}

if (key.name === 'up') {
selectIndex((selectedIndex - 1 + 3) % 3)
}
},
[selectedIndex],
)

return (
<Box flexDirection="column">
<Box flexDirection="column" marginLeft={2}>
<Text bold>Select the language for a starter kit or start with a blank project.</Text>
<Color dim>Starter kits provide ready-made setups for various use cases.</Color>
</Box>
)
}
<BorderBox flexDirection="column" title={chalk.bold('Languages for starter kits')} marginTop={1}>
<SelectItem
focus={selectedIndex === 0}
label="JavaScript"
description="GraphQL, REST, gRPC, ..."
onSelect={() => onSubmit('js')}
padding={10}
/>
<SelectItem
focus={selectedIndex === 1}
label="TypeScript"
description="GraphQL, REST, gRPC, ..."
onSelect={() => onSubmit('ts')}
padding={10}
/>
<Box marginLeft={2}>
<Color dim>Go (Coming soon)</Color>
</Box>
<Box marginLeft={2} marginTop={1}>
<Color dim>Note: Starter kits only work with empty databases</Color>
</Box>
</BorderBox>
<BorderBox flexDirection="column" title={chalk.bold('Get started from scratch')} marginBottom={1} marginTop={1}>
<SelectItem
focus={selectedIndex === 2}
label="Blank project"
description="Supports introspecting your existing DB"
onSelect={() => onSubmit('blank')}
padding={10}
/>
</BorderBox>
</Box>
)
}
6 changes: 3 additions & 3 deletions cli/introspection/src/prompt/useStdin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import * as React from 'react'
import { Key } from 'readline'
import { action, ActionKey } from './components/helpers'

export function useStdin(keyHandler: (key: ActionKey, text: string) => void, deps: any[] = []) {
export function useStdin(keyHandler: ({ actionKey: ActionKey, text: string, key: Key }) => void, deps: any[] = []) {
const { stdin, setRawMode } = React.useContext(StdinContext)

let didCancel = false

React.useEffect(() => {
function handler(str: string, key: Key) {
function handler(text: string, key: Key) {
if (!didCancel) {
keyHandler(action(key), str)
keyHandler({ actionKey: action(key), text, key })
}
}

Expand Down

0 comments on commit 807914e

Please sign in to comment.