@material-ui/core#Grow TypeScript Examples

The following examples show how to use @material-ui/core#Grow. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Players.tsx    From planning-poker with MIT License 6 votes vote down vote up
Players: React.FC<PlayersProps> = ({ game, players }) => {
  return (
    <Grow in={true} timeout={800}>
      <div className='PlayersContainer'>
        {players.map((player: Player) => (
          <PlayerCard key={player.id} game={game} player={player} />
        ))}
      </div>
    </Grow>
  );
}
Example #2
Source File: Layout.tsx    From fishbowl with MIT License 5 votes vote down vote up
function GrowTransition(props: TransitionProps) {
  return <Grow {...props} />
}
Example #3
Source File: menus.tsx    From clearflask with Apache License 2.0 5 votes vote down vote up
render() {
    const onMouseOverButton = () => {
      this.setState({ hover: true });
      const lastEventId = ++this.lastEventId;
      setTimeout(() => lastEventId === this.lastEventId
        && this.state.hover
        && this.setState({ open: true }), 1);
    };
    const onMouseOverPopper = () => {
      ++this.lastEventId; // Cancel any events including mouse out
    };
    const onMouseOut = () => {
      this.setState({ hover: false });
      const lastEventId = ++this.lastEventId;
      setTimeout(() => lastEventId === this.lastEventId
        && !this.state.hover
        && this.setState({ open: false }), 1);
    };
    return (
      <div className={this.props.classes.dropdownContainer}>
        <Button
          size='large'
          className={classNames(this.props.classes.button, this.props.isOuter && this.props.classes.buttonOuter)}
          color={this.props.color}
          onClick={() => {
            ++this.lastEventId;
            this.setState({ open: true })
          }}
          onMouseOver={onMouseOverButton}
          onMouseOut={onMouseOut}
        >
          {this.props.dropdown.title}
        </Button>
        <ClosablePopper
          anchorType='in-place'
          paperClassName={this.props.classes.menuPopperPaper}
          className={this.props.classes.menuPopper}
          clickAway
          closeButtonPosition='disable'
          open={!!this.state.open}
          onClose={() => {
            ++this.lastEventId;
            if (!this.state.open) this.setState({ open: false });
          }}
          onMouseOver={onMouseOverPopper}
          onMouseOut={onMouseOut}
          transitionCmpt={Grow}
          transitionProps={{
            style: { transformOrigin: '50% 0 0' },
            timeout: this.props.theme.transitions.duration.shortest,
          }}
          placement='bottom'
          modifiers={{
            // preventOverflow: { enabled: false },
            flip: { enabled: false },
          }}
        >
          <MenuItems
            items={this.props.dropdown.items}
            onClick={() => {
              ++this.lastEventId;
              this.setState({ open: false })
            }}
            insideDropdown
          />
        </ClosablePopper>
      </div>
    );
  }
Example #4
Source File: Menu.tsx    From glific-frontend with GNU Affero General Public License v3.0 5 votes vote down vote up
Menu: React.SFC<MenuProps> = ({
  menus,
  children,
  eventType = 'Click',
  placement = 'top',
}) => {
  const [open, setOpen] = useState(false);
  const anchorRef = useRef<HTMLDivElement>(null);

  const handleOpen = () => {
    setOpen(true);
  };

  const handleClose = () => {
    setOpen(false);
  };

  const menuList = menus.map((menu: any) => (
    <div key={menu.title}>
      <MenuItem
        onClickHandler={() => {
          if (menu.onClick) {
            menu.onClick();
          } else {
            handleClose();
          }
        }}
        {...menu}
      />
    </div>
  ));

  return (
    <div data-testid="Menu">
      <div
        onClick={eventType === 'Click' ? handleOpen : undefined}
        onKeyDown={eventType === 'Click' ? handleOpen : undefined}
        onMouseEnter={eventType === 'MouseEnter' ? handleOpen : undefined}
        onMouseLeave={eventType === 'MouseEnter' ? handleClose : undefined}
        aria-hidden="true"
        ref={anchorRef}
        aria-controls={open ? 'menu-list-grow' : undefined}
        aria-haspopup="true"
      >
        {children}
      </div>

      <Popper
        open={open}
        anchorEl={anchorRef.current}
        role={undefined}
        transition
        disablePortal={placement === 'top'}
        placement={placement}
      >
        {({ TransitionProps }) => (
          <Grow {...TransitionProps}>
            <Paper>
              <ClickAwayListener onClickAway={handleClose}>
                <div
                  onMouseEnter={eventType === 'MouseEnter' ? handleOpen : undefined}
                  onMouseLeave={eventType === 'MouseEnter' ? handleClose : undefined}
                >
                  <MenuList autoFocusItem={open}>{menuList}</MenuList>
                </div>
              </ClickAwayListener>
            </Paper>
          </Grow>
        )}
      </Popper>
    </div>
  );
}
Example #5
Source File: CardPicker.tsx    From planning-poker with MIT License 5 votes vote down vote up
CardPicker: React.FC<CardPickerProps> = ({ game, players, currentPlayerId }) => {
  const [randomEmoji, setRandomEmoji] = useState(getRandomEmoji);
  const playPlayer = (gameId: string, playerId: string, card: CardConfig) => {
    if (game.gameStatus !== Status.Finished) {
      updatePlayerValue(gameId, playerId, card.value, randomEmoji);
    }
  };
  const cards = getCards(game.gameType);

  useEffect(() => {
    if (game.gameStatus === Status.Started) {
      setRandomEmoji(getRandomEmoji);
    }
  }, [game.gameStatus]);
  return (
    <Grow in={true} timeout={1000}>
      <div>
        <div className='CardPickerContainer'>
          <Grid container spacing={4} justify='center'>
            {cards.map((card: CardConfig, index) => (
              <Grid key={card.value} item xs>
                <Slide in={true} direction={'right'} timeout={(1000 * index) / 2}>
                  <Card
                    id={`card-${card.displayValue}`}
                    className='CardPicker'
                    variant='outlined'
                    onClick={() => playPlayer(game.id, currentPlayerId, card)}
                    style={{
                      ...getCardStyle(players, currentPlayerId, card),
                      pointerEvents: getPointerEvent(game),
                    }}
                  >
                    <CardContent className='CardContent'>
                      {card.value >= 0 && (
                        <>
                          <Typography className='CardContentTop' variant='caption'>
                            {card.displayValue}
                          </Typography>
                          <Typography className='CardContentMiddle' variant='h4'>
                            {card.displayValue}
                          </Typography>
                          <Typography className='CardContentBottom' variant='caption'>
                            {card.displayValue}
                          </Typography>
                        </>
                      )}
                      {card.value === -1 && (
                        <Typography className='CardContentMiddle' variant='h3'>
                          {randomEmoji}
                        </Typography>
                      )}
                      {card.value === -2 && (
                        <Typography className='CardContentMiddle' variant='h3'>
                          ❓
                        </Typography>
                      )}
                    </CardContent>
                  </Card>
                </Slide>
              </Grid>
            ))}
          </Grid>
        </div>
        <Typography variant='h6'>
          {game.gameStatus !== Status.Finished
            ? 'Click on the card to vote'
            : 'Session not ready for Voting! Wait for moderator to start'}
        </Typography>
      </div>
    </Grow>
  );
}
Example #6
Source File: index.tsx    From firetable with Apache License 2.0 4 votes vote down vote up
export default function BulkActions({ selectedRows, columns, clearSelection }) {
  const classes = useStyles();
  const [loading, setLoading] = useState<Boolean>();
  const { tableActions, tableState } = useFiretableContext();

  const { requestConfirmation } = useConfirmation();
  const snack = useSnackContext();

  const actionColumns: { name: string; key: string; config: any }[] = columns
    .filter((column) => column.type === "ACTION")
    .map((column) => ({
      name: column.name,
      key: column.key,
      config: column.config,
    }));

  const handleDuplicate = () => {
    selectedRows.forEach((row) => {
      const clonedRow = { ...row };
      // remove metadata
      delete clonedRow.ref;
      delete clonedRow.rowHeight;
      delete clonedRow._ft_updatedAt;
      delete clonedRow._ft_updatedBy;
      delete clonedRow._ft_createdAt;
      Object.keys(clonedRow).forEach((key) => {
        if (clonedRow[key] === undefined) {
          delete clonedRow[key];
        }
      });
      if (tableActions) tableActions?.row.add(clonedRow);
    });
    clearSelection();
  };
  const handleDelete = () => {
    selectedRows.forEach((row) => row.ref.delete());
    clearSelection();
  };

  const executeAction = async (key: string, actionType: string) => {
    const actionColumn = _find(actionColumns, { key });
    if (!actionColumn) return;

    console.log({ actionColumn, selectedRows, actionType });
    const callableName = actionColumn.config.callableName ?? "actionScript";

    const calls = selectedRows.map((row) => {
      const { ref } = row;
      const data = {
        ref: {
          path: ref.path,
          id: ref.id,
          tablePath: window.location.pathname,
        },
        column: actionColumn,
        action: actionType,
        schemaDocPath: formatPath(tableState?.tablePath ?? ""),
        actionParams: {},
      };
      return cloudFunction(
        callableName,
        data,
        async (response) => {
          const { message, cellValue, success } = response.data;
          // setIsRunning(false);
          snack.open({
            message: JSON.stringify(message),
            variant: success ? "success" : "error",
          });
          if (cellValue && cellValue.status) {
            return ref.update({ [actionColumn.key]: cellValue });
          }
        },
        (error) => {
          console.error("ERROR", callableName, error);
          //setIsRunning(false);
          snack.open({ message: JSON.stringify(error), variant: "error" });
        }
      );
    });
    setLoading(true);
    const result = await Promise.all(calls);
    await Promise.all(result);
    console.log(result);
    setLoading(false);
    clearSelection();
  };

  const numSelected = selectedRows.length;

  return (
    <div className={classes.root}>
      <Grow in={numSelected > 0}>
        <Paper elevation={8} className={classes.paper}>
          <Grid
            container
            alignItems="center"
            wrap="nowrap"
            className={classes.grid}
          >
            <Grid item className={classes.selectedContainer}>
              <Tooltip title="Clear selection">
                <IconButton
                  color="secondary"
                  onClick={clearSelection}
                  aria-label="Clear selection"
                >
                  <ClearSelectionIcon />
                </IconButton>
              </Tooltip>

              <Typography variant="overline" className={classes.selected}>
                {numSelected} row{numSelected !== 1 && "s"} selected
              </Typography>
            </Grid>

            <Grid item className={classes.spacer} />

            <Grid item>
              <TextField
                select
                variant="filled"
                className={classes.dropdown}
                value=""
                onChange={(event) => executeAction(event.target.value, "run")}
                margin="dense"
                InputProps={{
                  disableUnderline: true,
                  classes: { root: classes.inputBaseRoot },
                }}
                InputLabelProps={{
                  classes: {
                    root: classes.dropdownLabel,
                    focused: classes.dropdownLabelFocused,
                  },
                }}
                SelectProps={{
                  classes: { root: classes.select },
                  displayEmpty: true,
                  MenuProps: {
                    getContentAnchorEl: null,
                    anchorOrigin: { vertical: "top", horizontal: "left" },
                    transformOrigin: { vertical: "bottom", horizontal: "left" },
                    classes: { paper: classes.dropdownMenu },
                  },
                  IconComponent: ArrowDropUpIcon,
                }}
                label={`${actionColumns.length} action${
                  actionColumns.length !== 1 ? "s" : ""
                }`}
              >
                {actionColumns.map((action) => (
                  <MenuItem value={action.key} key={action.key}>
                    {action.name}
                  </MenuItem>
                ))}
              </TextField>
            </Grid>

            <Grid item className={classes.spacer} />

            <Grid item>
              <Tooltip title="Duplicate rows">
                <IconButton
                  color="secondary"
                  onClick={() => {
                    requestConfirmation({
                      title: "Duplicate Rows?",
                      body: `Are you sure you want to duplicate the ${numSelected} selected row${
                        numSelected !== 1 ? "s" : ""
                      }?`,
                      confirm: "Duplicate Rows",
                      handleConfirm: handleDuplicate,
                    });
                  }}
                  aria-label="Duplicate selected rows"
                >
                  <CopyCellsIcon />
                </IconButton>
              </Tooltip>
            </Grid>

            <Grid item>
              <Tooltip title="Delete rows">
                <IconButton
                  color="secondary"
                  onClick={() => {
                    requestConfirmation({
                      title: "Delete Rows?",
                      body: `Are you sure you want to delete the ${numSelected} select row${
                        numSelected !== 1 ? "s" : ""
                      }?`,
                      confirm: "Delete Rows",
                      handleConfirm: handleDelete,
                    });
                  }}
                  aria-label="Delete selected rows"
                >
                  <DeleteIcon />
                </IconButton>
              </Tooltip>
            </Grid>
          </Grid>
        </Paper>
      </Grow>
    </div>
  );
}
Example #7
Source File: CreateGame.tsx    From planning-poker with MIT License 4 votes vote down vote up
CreateGame = () => {
  const history = useHistory();
  const [gameName, setGameName] = useState('Avengers');
  const [createdBy, setCreatedBy] = useState('SuperHero');
  const [gameType, setGameType] = useState(GameType.Fibonacci);

  const handleSubmit = async (event: FormEvent) => {
    event.preventDefault();
    const game: NewGame = {
      name: gameName,
      createdBy: createdBy,
      gameType: gameType,
      createdAt: new Date(),
    };
    const newGameId = await addNewGame(game);
    history.push(`/game/${newGameId}`);
  };

  return (
    <Grow in={true} timeout={1000}>
      <form onSubmit={handleSubmit}>
        <Card variant='outlined' className='CreateGameCard'>
          <CardHeader
            className='CreateGameCardHeader'
            title='Create New Session'
            titleTypographyProps={{ variant: 'h4' }}
          />
          <CardContent className='CreateGameCardContent'>
            <TextField
              className='CreateGameTextField'
              required
              id='filled-required'
              label='Session Name'
              placeholder='Enter a session name'
              defaultValue={gameName}
              variant='outlined'
              onChange={(event: ChangeEvent<HTMLInputElement>) => setGameName(event.target.value)}
            />
            <TextField
              className='CreateGameTextField'
              required
              id='filled-required'
              label='Your Name'
              placeholder='Enter your name'
              defaultValue={createdBy}
              variant='outlined'
              onChange={(event: ChangeEvent<HTMLInputElement>) => setCreatedBy(event.target.value)}
            />
            <RadioGroup
              aria-label='gender'
              name='gender1'
              value={gameType}
              onChange={(
                event: ChangeEvent<{
                  name?: string | undefined;
                  value: any;
                }>
              ) => setGameType(event.target.value)}
            >
              <FormControlLabel
                value={GameType.Fibonacci}
                control={<Radio color='primary' size='small' />}
                label='Fibonacci (0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89)'
              />
              <FormControlLabel
                value={GameType.ShortFibonacci}
                control={<Radio color='primary' size='small' />}
                label='Short Fibonacci (0, ½, 1, 2, 3, 5, 8, 13, 20, 40, 100)'
              />
              <FormControlLabel
                value={GameType.TShirt}
                control={<Radio color='primary' size='small' />}
                label='T-Shirt (XXS, XS, S, M, L, XL, XXL)'
              />
            </RadioGroup>
          </CardContent>
          <CardActions className='CreateGameCardAction'>
            <Button type='submit' variant='contained' color='primary' className='CreateGameButton'>
              Create
            </Button>
          </CardActions>
        </Card>
      </form>
    </Grow>
  );
}
Example #8
Source File: GameController.tsx    From planning-poker with MIT License 4 votes vote down vote up
GameController: React.FC<GameControllerProps> = ({ game, currentPlayerId }) => {
  const history = useHistory();
  const [showCopiedMessage, setShowCopiedMessage] = useState(false);
  const copyInviteLink = () => {
    const dummy = document.createElement('input');
    const url = `${window.location.origin}/join/${game.id}`;
    document.body.appendChild(dummy);
    dummy.value = url;
    dummy.select();
    document.execCommand('copy');
    document.body.removeChild(dummy);
    setShowCopiedMessage(true);
  };

  const leaveGame = () => {
    history.push(`/`);
  };

  const isModerator = (moderatorId: string, currentPlayerId: string) => {
    return moderatorId === currentPlayerId;
  };
  return (
    <Grow in={true} timeout={2000}>
      <div className='GameController'>
        <Card variant='outlined' className='GameControllerCard'>
          <CardHeader
            title={game.name}
            titleTypographyProps={{ variant: 'h6' }}
            action={
              <div className='GameControllerCardHeaderAverageContainer'>
                <Typography variant='subtitle1'>{game.gameStatus}</Typography>
                {game.gameType !== GameType.TShirt && (
                  <>
                    <Divider className='GameControllerDivider' orientation='vertical' flexItem />
                    <Typography variant='subtitle1'>Average:</Typography>
                    <Typography variant='subtitle1' className='GameControllerCardHeaderAverageValue'>
                      {game.average || 0}
                    </Typography>
                  </>
                )}
              </div>
            }
            className='GameControllerCardTitle'
          ></CardHeader>
          <CardContent className='GameControllerCardContentArea'>
            {isModerator(game.createdById, currentPlayerId) && (
              <>
                <div className='GameControllerButtonContainer'>
                  <div className='GameControllerButton'>
                    <IconButton onClick={() => finishGame(game.id)} data-testid='reveal-button' color='primary'>
                      <VisibilityIcon fontSize='large' style={{ color: green[500] }} />
                    </IconButton>
                  </div>
                  <Typography variant='caption'>Reveal</Typography>
                </div>

                <div className='GameControllerButtonContainer'>
                  <div className='GameControllerButton'>
                    <IconButton data-testid={'restart-button'} onClick={() => resetGame(game.id)}>
                      <RefreshIcon fontSize='large' color='error' />
                    </IconButton>
                  </div>
                  <Typography variant='caption'>Restart</Typography>
                </div>
              </>
            )}
            <div className='GameControllerButtonContainer'>
              <div className='GameControllerButton'>
                <IconButton data-testid='exit-button' onClick={() => leaveGame()}>
                  <ExitToApp fontSize='large' style={{ color: orange[500] }} />
                </IconButton>
              </div>
              <Typography variant='caption'>Exit</Typography>
            </div>
            <div title='Copy invite link' className='GameControllerButtonContainer'>
              <div className='GameControllerButton'>
                <IconButton data-testid='invite-button' onClick={() => copyInviteLink()}>
                  <LinkIcon fontSize='large' style={{ color: blue[500] }} />
                </IconButton>
              </div>
              <Typography variant='caption'>Invite</Typography>
            </div>
          </CardContent>
        </Card>
        <Snackbar
          anchorOrigin={{ horizontal: 'right', vertical: 'top' }}
          open={showCopiedMessage}
          autoHideDuration={5000}
          onClose={() => setShowCopiedMessage(false)}
        >
          <Alert severity='success'>Invite Link copied to clipboard!</Alert>
        </Snackbar>
      </div>
    </Grow>
  );
}
Example #9
Source File: JoinGame.tsx    From planning-poker with MIT License 4 votes vote down vote up
JoinGame = () => {
  const history = useHistory();
  let { id } = useParams<{ id: string }>();

  const [joinGameId, setJoinGameId] = useState(id);
  const [playerName, setPlayerName] = useState('');
  const [gameFound, setIsGameFound] = useState(true);

  useEffect(() => {
    async function fetchData() {
      if (joinGameId) {
        if (await getGame(joinGameId)) {
          setIsGameFound(true);
          if (isCurrentPlayerInGame(joinGameId)) {
            history.push(`/game/${joinGameId}`);
          }
        }
      }
    }
    fetchData();
  }, [joinGameId, history]);

  const handleSubmit = async (event: FormEvent) => {
    event.preventDefault();
    if (joinGameId) {
      const res = await addPlayerToGame(joinGameId, playerName);

      setIsGameFound(res);
      if (res) {
        history.push(`/game/${joinGameId}`);
      }
    }
  };

  return (
    <Grow in={true} timeout={500}>
      <div>
        <form onSubmit={handleSubmit}>
          <Card variant='outlined' className='JoinGameCard'>
            <CardHeader
              className='JoinGameCardHeader'
              title='Join a Session'
              titleTypographyProps={{ variant: 'h4' }}
            />
            <CardContent className='JoinGameCardContent'>
              <TextField
                error={!gameFound}
                helperText={!gameFound && 'Session not found, check the ID'}
                className='JoinGameTextField'
                required
                id='filled-required'
                label='Session ID'
                placeholder='xyz...'
                defaultValue={joinGameId}
                variant='outlined'
                onChange={(event: ChangeEvent<HTMLInputElement>) =>
                  setJoinGameId(event.target.value)
                }
              />
              <TextField
                className='JoinGameTextField'
                required
                id='filled-required'
                label='Your Name'
                placeholder='Enter your name'
                defaultValue={playerName}
                variant='outlined'
                onChange={(event: ChangeEvent<HTMLInputElement>) =>
                  setPlayerName(event.target.value)
                }
              />
            </CardContent>
            <CardActions className='JoinGameCardAction'>
              <Button
                type='submit'
                variant='contained'
                color='primary'
                className='JoinGameButton'
              >
                Join
              </Button>
            </CardActions>
          </Card>
        </form>
      </div>
    </Grow>
  );
}
Example #10
Source File: LoginDialog.tsx    From knboard with MIT License 4 votes vote down vote up
LoginDialog = () => {
  const dispatch = useDispatch();
  const [open, setOpen] = useState(false);
  const apiErrors = useSelector((state: RootState) => state.auth.loginErrors);
  const loading = useSelector((state: RootState) => state.auth.loginLoading);
  const { register, handleSubmit, errors } = useForm<FormData>();

  const handleOpen = () => {
    setOpen(true);
  };

  const handleClose = () => {
    setOpen(false);
    dispatch(clearErrors());
  };

  const onSubmit = handleSubmit(({ username, password }) => {
    dispatch(login({ username, password }));
  });

  return (
    <>
      <Button
        variant="contained"
        color="primary"
        onClick={handleOpen}
        data-testid="open-login-btn"
      >
        Login
      </Button>
      <Dialog
        open={open}
        onClose={handleClose}
        keepMounted={false}
        aria-labelledby="login-dialog-title"
        css={css`
          & .MuiDialog-paper {
            padding: 2rem 1.5rem;
          }
        `}
        maxWidth="xs"
        fullWidth
      >
        <Close onClose={handleClose} />
        <DialogTitle id="login-dialog-title">Login</DialogTitle>
        <form onSubmit={onSubmit}>
          <DialogContent>
            {apiErrors?.non_field_errors && (
              <Grow in timeout={100}>
                <Alert
                  severity="error"
                  css={css`
                    margin-bottom: 0.75rem;
                  `}
                >
                  {apiErrors.non_field_errors?.map((errorMsg) => (
                    <div key={errorMsg}>{errorMsg}</div>
                  ))}
                </Alert>
              </Grow>
            )}
            <TextField
              autoFocus
              name="username"
              margin="dense"
              id="username"
              label="Username"
              variant="outlined"
              inputRef={register({ required: "This field is required" })}
              helperText={errors.username?.message}
              error={Boolean(errors.username)}
              fullWidth
            />
            <TextField
              name="password"
              margin="dense"
              id="password"
              label="Password"
              variant="outlined"
              type="password"
              inputRef={register({ required: "This field is required" })}
              helperText={errors.password?.message}
              error={Boolean(errors.password)}
              fullWidth
            />
            <FormActions>
              <Button
                variant="contained"
                color="primary"
                type="submit"
                disabled={loading}
                data-testid="submit-login-btn"
              >
                Login
              </Button>
            </FormActions>
          </DialogContent>
        </form>
      </Dialog>
    </>
  );
}
Example #11
Source File: AdrBrowserLayout.tsx    From log4brains with Apache License 2.0 4 votes vote down vote up
export function AdrBrowserLayout({
  projectName,
  adrs,
  adrsReloading = false,
  currentAdr,
  children,
  routing = false,
  l4bVersion
}: AdrBrowserLayoutProps) {
  const classes = useStyles();
  const router = useRouter();

  const [mobileDrawerOpen, setMobileDrawerOpen] = React.useState(false);

  const handleMobileDrawerToggle = () => {
    setMobileDrawerOpen(!mobileDrawerOpen);
  };

  React.useEffect(() => {
    const closeMobileDrawer = () => setMobileDrawerOpen(false);
    router?.events.on("routeChangeStart", closeMobileDrawer);
    return () => {
      router?.events.off("routeChangeStart", closeMobileDrawer);
    };
  }, [router]);

  const [searchOpen, setSearchOpenState] = React.useState(false);
  const [searchReallyOpen, setSearchReallyOpenState] = React.useState(false);

  const drawer = (
    <div className={classes.drawerContainer}>
      <Toolbar className={classes.drawerToolbar}>
        <div />
        <Link href="/" passHref>
          <IconButton
            size="small"
            color="inherit"
            aria-label="go to homepage"
            title={`Architecture knowledge base of ${projectName}`}
          >
            <img
              src={`${router?.basePath}/l4b-static/Log4brains-logo.png`}
              alt="Log4brains logo"
              width={40}
              height={40}
            />
          </IconButton>
        </Link>
        <IconButton
          size="small"
          color="inherit"
          aria-label="close drawer"
          title="Close"
          onClick={handleMobileDrawerToggle}
        >
          <CloseIcon fontSize="small" />
        </IconButton>
      </Toolbar>

      <div className={classes.adlTitleAndSpinner}>
        <Typography variant="subtitle2" className={classes.adlTitle}>
          Decision log
        </Typography>

        <Fade in={adrsReloading}>
          <CircularProgress size={13} />
        </Fade>
      </div>

      <Grow in={adrs !== undefined} style={{ transformOrigin: "center left" }}>
        <AdrMenu
          adrs={adrs}
          currentAdrSlug={currentAdr?.slug}
          className={classes.adrMenu}
        />
      </Grow>

      {adrs === undefined && (
        <CircularProgress size={30} className={classes.adrMenuSpinner} />
      )}

      <List className={classes.bottomMenuList}>
        {/* <Divider />
      <ListItem button>
        <ListItemIcon>
          <ChevronRightIcon />
        </ListItemIcon>
        <ListItemText>
          <Badge badgeContent={0} color="primary">
            <Typography>Filters</Typography>
          </Badge>
        </ListItemText>
      </ListItem> */}
        {/* <Divider />
      <Link href="/decision-backlog" passHref>
        <ListItem button selected={backlog} component="a">
          <ListItemIcon>
            <PlaylistAddCheckIcon />
          </ListItemIcon>
          <ListItemText primary="Decision backlog" />
        </ListItem>
      </Link> */}
      </List>
    </div>
  );

  return (
    <div className={classes.root}>
      <AppBar position="fixed" className={classes.appBar}>
        {routing && <RoutingProgress />}
        <Toolbar>
          <IconButton
            color="inherit"
            aria-label="open drawer"
            edge="start"
            onClick={handleMobileDrawerToggle}
            className={classes.appBarMenuButton}
          >
            <MenuIcon />
          </IconButton>
          <Link href="/">
            <div className={classes.appBarTitle}>
              <div>
                <img
                  src={`${router?.basePath}/l4b-static/Log4brains-logo-dark.png`}
                  alt="Log4brains logo"
                  width={50}
                  height={50}
                />
              </div>
              <div>
                <Link href="/" passHref>
                  <MuiLink
                    variant="h6"
                    noWrap
                    className={classes.appBarTitleLink}
                  >
                    {projectName}
                  </MuiLink>
                </Link>
                <Link href="/" passHref>
                  <MuiLink
                    variant="body2"
                    noWrap
                    className={classes.appBarTitleLink}
                  >
                    Architecture knowledge base
                  </MuiLink>
                </Link>
              </div>
            </div>
          </Link>
          <div className={classes.layoutLeftCol} />
          <div className={clsx(classes.layoutCenterCol)}>
            <Backdrop open={searchOpen} className={classes.searchBackdrop} />
            <NoSsr>
              <ConnectedSearchBox
                onOpen={() => {
                  setSearchOpenState(true);
                  // Delayed real opening because otherwise the dropdown width is bugged
                  setTimeout(
                    () => setSearchReallyOpenState(true),
                    searchTransitionDuration + 100
                  );
                }}
                onClose={() => {
                  setSearchOpenState(false);
                  setSearchReallyOpenState(false);
                }}
                open={searchReallyOpen}
                className={clsx(classes.searchBox, {
                  [classes.searchBoxOpen]: searchOpen
                })}
              />
            </NoSsr>
          </div>
          <div className={classes.layoutRightCol} />
        </Toolbar>
      </AppBar>

      <nav
        className={classes.drawer}
        aria-label="architecture decision records list"
      >
        <Hidden smUp implementation="css">
          <Drawer
            variant="temporary"
            anchor="left"
            open={mobileDrawerOpen}
            onClose={handleMobileDrawerToggle}
            classes={{
              paper: classes.drawerPaper
            }}
            ModalProps={{
              keepMounted: true // Better open performance on mobile.
            }}
          >
            {drawer}
          </Drawer>
        </Hidden>
        <Hidden xsDown implementation="css">
          <Drawer
            variant="permanent"
            open
            classes={{
              paper: classes.drawerPaper
            }}
          >
            {drawer}
          </Drawer>
        </Hidden>
      </nav>

      <div className={classes.container}>
        <Toolbar />
        <main className={classes.content}>
          <AdrNavContext.Provider
            value={currentAdr && adrs ? buildAdrNav(currentAdr, adrs) : {}}
          >
            {children}
          </AdrNavContext.Provider>
        </main>
        <footer className={classes.footer}>
          <div className={classes.layoutLeftCol} />
          <div className={clsx(classes.layoutCenterCol, classes.footerContent)}>
            <Typography className={classes.footerText}>
              Powered by{" "}
              <MuiLink
                href="https://github.com/thomvaill/log4brains"
                className={classes.footerLink}
                target="_blank"
                rel="noopener"
              >
                Log4brains
              </MuiLink>{" "}
              <span style={{ fontSize: "0.8em" }}>
                {l4bVersion ? `(v${l4bVersion})` : null}
              </span>
            </Typography>
          </div>
          <div className={classes.layoutRightCol} />
        </footer>
      </div>
    </div>
  );
}