aboutsummaryrefslogtreecommitdiff
path: root/src/console/components/CommandPrompt.tsx
blob: 0e2506c45a5ce4adcc78b5442514d64004decb6d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import React from "react";
import Completion from "./console/Completion";
import Input from "./console//Input";
import styled from "styled-components";
import { useCompletions, useSelectCompletion } from "../completion/hooks";
import useAutoResize from "../hooks/useAutoResize";
import { CompletionProvider } from "../completion/provider";
import { useExecCommand, useHide } from "../app/hooks";

const COMPLETION_MAX_ITEMS = 33;

const ConsoleWrapper = styled.div`
  border-top: 1px solid gray;
`;

interface Props {
  initialInputValue: string;
}

const CommandPromptInner: React.FC<Props> = ({ initialInputValue }) => {
  const hide = useHide();
  const [inputValue, setInputValue] = React.useState(initialInputValue);
  const { completions, updateCompletions } = useCompletions();
  const { select, currentValue, selectNext, selectPrev } =
    useSelectCompletion();
  const execCommand = useExecCommand();

  useAutoResize();

  const onBlur = () => {
    hide();
  };

  const isCancelKey = React.useCallback(
    (e: React.KeyboardEvent<HTMLInputElement>) =>
      e.key === "Escape" ||
      (e.ctrlKey && e.key === "[") ||
      (e.ctrlKey && e.key === "c"),
    []
  );

  const isNextKey = React.useCallback(
    (e: React.KeyboardEvent<HTMLInputElement>) =>
      (!e.shiftKey && e.key === "Tab") || (e.ctrlKey && e.key === "n"),
    []
  );

  const isPrevKey = React.useCallback(
    (e: React.KeyboardEvent<HTMLInputElement>) =>
      (e.shiftKey && e.key === "Tab") || (e.ctrlKey && e.key === "p"),
    []
  );

  const isEnterKey = React.useCallback(
    (e: React.KeyboardEvent<HTMLInputElement>) =>
      e.key === "Enter" || (e.ctrlKey && e.key === "m"),
    []
  );

  const onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
    if (isCancelKey(e)) {
      hide();
    } else if (isEnterKey(e)) {
      const value = (e.target as HTMLInputElement).value;
      execCommand(value);
      hide();
    } else if (isNextKey(e)) {
      selectNext();
    } else if (isPrevKey(e)) {
      selectPrev();
    } else {
      return;
    }

    e.stopPropagation();
    e.preventDefault();
  };

  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const text = e.target.value;
    setInputValue(text);
  };

  React.useEffect(() => {
    updateCompletions(inputValue);
  }, [inputValue]);

  return (
    <ConsoleWrapper>
      <Completion
        size={COMPLETION_MAX_ITEMS}
        completions={completions}
        select={select}
      />
      <Input
        prompt={":"}
        onBlur={onBlur}
        onKeyDown={onKeyDown}
        onChange={onChange}
        value={select == -1 ? inputValue : currentValue}
      />
    </ConsoleWrapper>
  );
};

const CommandPrompt: React.FC<Props> = ({ initialInputValue }) => (
  <CompletionProvider initialInputValue={initialInputValue}>
    <CommandPromptInner initialInputValue={initialInputValue} />
  </CompletionProvider>
);

export default CommandPrompt;