CommentInput.tsx

  1import { useState, useEffect } from 'react';
  2import * as React from 'react';
  3
  4import { Typography } from '@material-ui/core';
  5import Tab from '@material-ui/core/Tab';
  6import Tabs from '@material-ui/core/Tabs';
  7import TextField from '@material-ui/core/TextField';
  8import { makeStyles } from '@material-ui/core/styles';
  9
 10import Content from '../Content';
 11
 12/**
 13 * Styles
 14 */
 15const useStyles = makeStyles((theme) => ({
 16  container: {
 17    margin: theme.spacing(2, 0),
 18    padding: theme.spacing(0, 2, 2, 2),
 19  },
 20  textarea: {
 21    '& textarea.MuiInputBase-input': {
 22      resize: 'vertical',
 23    },
 24  },
 25  tabContent: {
 26    margin: theme.spacing(2, 0),
 27  },
 28  preview: {
 29    overflow: 'auto',
 30    borderBottom: `solid 3px ${theme.palette.grey['200']}`,
 31    minHeight: '5rem',
 32  },
 33  previewPlaceholder: {
 34    color: theme.palette.text.secondary,
 35    fontStyle: 'italic',
 36  },
 37}));
 38
 39type TabPanelProps = {
 40  children: React.ReactNode;
 41  value: number;
 42  index: number;
 43} & React.HTMLProps<HTMLDivElement>;
 44function TabPanel({ children, value, index, ...props }: TabPanelProps) {
 45  return (
 46    <div
 47      role="tabpanel"
 48      hidden={value !== index}
 49      id={`editor-tabpanel-${index}`}
 50      aria-labelledby={`editor-tab-${index}`}
 51      {...props}
 52    >
 53      {value === index && children}
 54    </div>
 55  );
 56}
 57
 58const a11yProps = (index: number) => ({
 59  id: `editor-tab-${index}`,
 60  'aria-controls': `editor-tabpanel-${index}`,
 61});
 62
 63type Props = {
 64  inputProps?: any;
 65  inputText?: string;
 66  loading: boolean;
 67  onChange: (comment: string) => void;
 68};
 69
 70/**
 71 * Component for issue comment input
 72 *
 73 * @param inputProps Reset input value
 74 * @param loading Disable input when component not ready yet
 75 * @param onChange Callback to return input value changes
 76 */
 77function CommentInput({ inputProps, inputText, loading, onChange }: Props) {
 78  const [input, setInput] = useState<string>(inputText ? inputText : '');
 79  const [tab, setTab] = useState(0);
 80  const classes = useStyles();
 81
 82  useEffect(() => {
 83    if (inputProps) setInput(inputProps.value);
 84  }, [inputProps]);
 85
 86  useEffect(() => {
 87    onChange(input);
 88  }, [input, onChange]);
 89
 90  return (
 91    <div>
 92      <Tabs value={tab} onChange={(_, t) => setTab(t)}>
 93        <Tab label="Write" {...a11yProps(0)} />
 94        <Tab label="Preview" {...a11yProps(1)} />
 95      </Tabs>
 96      <div className={classes.tabContent}>
 97        <TabPanel value={tab} index={0}>
 98          <TextField
 99            fullWidth
100            label="Comment"
101            placeholder="Leave a comment"
102            className={classes.textarea}
103            multiline
104            value={input}
105            variant="filled"
106            rows="4" // TODO: rowsMin support
107            onChange={(e: any) => setInput(e.target.value)}
108            disabled={loading}
109          />
110        </TabPanel>
111        <TabPanel value={tab} index={1} className={classes.preview}>
112          {input !== '' ? (
113            <Content markdown={input} />
114          ) : (
115            <Typography className={classes.previewPlaceholder}>
116              Nothing to preview.
117            </Typography>
118          )}
119        </TabPanel>
120      </div>
121    </div>
122  );
123}
124
125export default CommentInput;