diff options
author | Shin'ya Ueoka <ueokande@i-beam.org> | 2021-04-03 18:05:20 +0900 |
---|---|---|
committer | Shin'ya Ueoka <ueokande@i-beam.org> | 2021-04-04 17:34:57 +0900 |
commit | 4d043107b80e104c3a029acb405dd69a8371a9a8 (patch) | |
tree | b2fed6bcbc2457998e00c3c4efbacd27047bf523 /src/console | |
parent | c6795c190b582b4d00b13a9051f2c0c33527451c (diff) |
Make Completion as a React.FC
Diffstat (limited to 'src/console')
-rw-r--r-- | src/console/components/Console.tsx | 8 | ||||
-rw-r--r-- | src/console/components/console/Completion.tsx | 136 | ||||
-rw-r--r-- | src/console/components/console/Input.tsx | 60 | ||||
-rw-r--r-- | src/console/reducers/index.ts | 2 |
4 files changed, 87 insertions, 119 deletions
diff --git a/src/console/components/Console.tsx b/src/console/components/Console.tsx index 18a6632..bb9aee7 100644 --- a/src/console/components/Console.tsx +++ b/src/console/components/Console.tsx @@ -28,15 +28,11 @@ interface DispatchProps { type Props = StateProps & DispatchProps; class Console extends React.Component<Props> { - private input: React.RefObject<Input>; - private commandLineParser: CommandLineParser = new CommandLineParser(); private consoleFrameClient = new ConsoleFrameClient(); constructor(props: Props) { super(props); - - this.input = React.createRef(); } onBlur() { @@ -167,7 +163,6 @@ class Console extends React.Component<Props> { select={this.props.select} /> <Input - ref={this.input} mode={this.props.mode} onBlur={this.onBlur.bind(this)} onKeyDown={this.onKeyDown.bind(this)} @@ -195,9 +190,6 @@ class Console extends React.Component<Props> { this.props.dispatch(consoleActions.setColorScheme()); window.focus(); - if (this.input.current) { - this.input.current.focus(); - } } private updateCompletions(text: string) { diff --git a/src/console/components/console/Completion.tsx b/src/console/components/console/Completion.tsx index 09ae278..ed271aa 100644 --- a/src/console/components/console/Completion.tsx +++ b/src/console/components/console/Completion.tsx @@ -19,97 +19,85 @@ interface Props { completions: Group[]; } -interface State { - viewOffset: number; - select: number; -} - -class Completion extends React.Component<Props, State> { - constructor(props: Props) { - super(props); - this.state = { viewOffset: 0, select: -1 }; - } +const Completion: React.FC<Props> = ({ select, size, completions }) => { + const [viewOffset, setViewOffset] = React.useState(0); + const [prevSelect, setPrevSelect] = React.useState(-1); - static getDerivedStateFromProps(nextProps: Props, prevState: State) { - if (prevState.select === nextProps.select) { - return null; + React.useEffect(() => { + if (select === prevSelect) { + return; } const viewSelect = (() => { let index = 0; - for (let i = 0; i < nextProps.completions.length; ++i) { + for (let i = 0; i < completions.length; ++i) { ++index; - const g = nextProps.completions[i]; - if (nextProps.select + i + 1 < index + g.items.length) { - return nextProps.select + i + 1; + const g = completions[i]; + if (select + i + 1 < index + g.items.length) { + return select + i + 1; } index += g.items.length; } return -1; })(); - let viewOffset = 0; - if (nextProps.select < 0) { - viewOffset = 0; - } else if (prevState.select < nextProps.select) { - viewOffset = Math.max( - prevState.viewOffset, - viewSelect - nextProps.size + 1 - ); - } else if (prevState.select > nextProps.select) { - viewOffset = Math.min(prevState.viewOffset, viewSelect); - } - return { viewOffset, select: nextProps.select }; - } + const nextViewOffset = (() => { + if (prevSelect < select) { + return Math.max(viewOffset, viewSelect - size + 1); + } else if (prevSelect > select) { + return Math.min(viewOffset, viewSelect); + } + return 0; + })(); + + setPrevSelect(select); + setViewOffset(nextViewOffset); + }, [select]); - render() { - let itemIndex = 0; - let viewIndex = 0; - const groups: Array<JSX.Element> = []; - const viewOffset = this.state.viewOffset; - const viewSize = this.props.size; + let itemIndex = 0; + let viewIndex = 0; + const groups: Array<JSX.Element> = []; - this.props.completions.forEach((group, groupIndex) => { - const items = []; - const title = ( - <CompletionTitle - id={`title-${groupIndex}`} - key={`group-${groupIndex}`} - shown={viewOffset <= viewIndex && viewIndex < viewOffset + viewSize} - title={group.name} + completions.forEach((group, groupIndex) => { + const items = []; + const title = ( + <CompletionTitle + id={`title-${groupIndex}`} + key={`group-${groupIndex}`} + shown={viewOffset <= viewIndex && viewIndex < viewOffset + size} + title={group.name} + /> + ); + ++viewIndex; + for (const item of group.items) { + items.push( + <CompletionItem + shown={viewOffset <= viewIndex && viewIndex < viewOffset + size} + key={`item-${itemIndex}`} + icon={item.icon} + caption={item.caption} + url={item.url} + highlight={itemIndex === select} + aria-selected={itemIndex === select} + role="menuitem" /> ); ++viewIndex; - for (const item of group.items) { - items.push( - <CompletionItem - shown={viewOffset <= viewIndex && viewIndex < viewOffset + viewSize} - key={`item-${itemIndex}`} - icon={item.icon} - caption={item.caption} - url={item.url} - highlight={itemIndex === this.props.select} - aria-selected={itemIndex === this.props.select} - role="menuitem" - /> - ); - ++viewIndex; - ++itemIndex; - } - groups.push( - <div - key={`group-${groupIndex}`} - role="group" - aria-describedby={`title-${groupIndex}`} - > - {title} - <ul>{items}</ul> - </div> - ); - }); + ++itemIndex; + } + groups.push( + <div + key={`group-${groupIndex}`} + role="group" + aria-describedby={`title-${groupIndex}`} + > + {title} + <ul>{items}</ul> + </div> + ); + }); - return <div role="menu">{groups}</div>; - } -} + return <div role="menu">{groups}</div>; +}; export default Completion; diff --git a/src/console/components/console/Input.tsx b/src/console/components/console/Input.tsx index 448b096..bf48d75 100644 --- a/src/console/components/console/Input.tsx +++ b/src/console/components/console/Input.tsx @@ -26,42 +26,32 @@ interface Props { onChange: (e: React.ChangeEvent<HTMLInputElement>) => void; } -class Input extends React.Component<Props> { - private input: React.RefObject<HTMLInputElement>; - - constructor(props: Props) { - super(props); - - this.input = React.createRef(); - } - - focus() { - if (this.input.current) { - this.input.current.focus(); - } +const Input: React.FC<Props> = (props) => { + const input = React.useRef<HTMLInputElement>(null); + + React.useEffect(() => { + input?.current?.focus(); + }, []); + + let prompt = ""; + if (props.mode === "command") { + prompt = ":"; + } else if (props.mode === "find") { + prompt = "/"; } - render() { - let prompt = ""; - if (this.props.mode === "command") { - prompt = ":"; - } else if (this.props.mode === "find") { - prompt = "/"; - } - - return ( - <Container> - <Prompt>{prompt}</Prompt> - <InputInner - ref={this.input} - onBlur={this.props.onBlur} - onKeyDown={this.props.onKeyDown} - onChange={this.props.onChange} - value={this.props.value} - /> - </Container> - ); - } -} + return ( + <Container> + <Prompt>{prompt}</Prompt> + <InputInner + ref={input} + onBlur={props.onBlur} + onKeyDown={props.onKeyDown} + onChange={props.onChange} + value={props.value} + /> + </Container> + ); +}; export default Input; diff --git a/src/console/reducers/index.ts b/src/console/reducers/index.ts index 752dfd9..dbaf97d 100644 --- a/src/console/reducers/index.ts +++ b/src/console/reducers/index.ts @@ -11,7 +11,6 @@ export interface State { completionSource: string; completions: Completions; select: number; - viewIndex: number; colorscheme: ColorScheme; } @@ -23,7 +22,6 @@ const defaultState = { completionSource: "", completions: [], select: -1, - viewIndex: 0, colorscheme: ColorScheme.System, }; |