aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorShin'ya Ueoka <ueokande@i-beam.org>2021-04-03 18:05:20 +0900
committerShin'ya Ueoka <ueokande@i-beam.org>2021-04-04 17:34:57 +0900
commit4d043107b80e104c3a029acb405dd69a8371a9a8 (patch)
treeb2fed6bcbc2457998e00c3c4efbacd27047bf523 /src
parentc6795c190b582b4d00b13a9051f2c0c33527451c (diff)
Make Completion as a React.FC
Diffstat (limited to 'src')
-rw-r--r--src/console/components/Console.tsx8
-rw-r--r--src/console/components/console/Completion.tsx136
-rw-r--r--src/console/components/console/Input.tsx60
-rw-r--r--src/console/reducers/index.ts2
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,
};