@mui/material#Menu TypeScript Examples

The following examples show how to use @mui/material#Menu. 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: WalletMultiButton.tsx    From wallet-adapter with Apache License 2.0 6 votes vote down vote up
StyledMenu = styled(Menu)(({ theme }: { theme: Theme }) => ({
    '& .MuiList-root': {
        padding: 0,
    },
    '& .MuiListItemIcon-root': {
        marginRight: theme.spacing(),
        minWidth: 'unset',
        '& .MuiSvgIcon-root': {
            width: 20,
            height: 20,
        },
    },
}))
Example #2
Source File: AppNavbar.tsx    From sapio-studio with Mozilla Public License 2.0 6 votes vote down vote up
function Simulator() {
    const dispatch = useDispatch();
    const simulateRef = React.useRef<HTMLLIElement>(null);
    const [sim_open, setSimOpen] = React.useState(false);
    return (
        <div>
            <ListItem
                disableGutters
                button={false}
                key={'Simulate'}
                onClick={() => setSimOpen(true)}
                ref={simulateRef}
            >
                <ListItemIcon></ListItemIcon>
                <ListItemText primary={'Simulate'} />
            </ListItem>
            <Menu
                anchorEl={simulateRef.current}
                anchorOrigin={{
                    vertical: 'center',
                    horizontal: 'right',
                }}
                keepMounted
                open={sim_open}
                onClose={() => setSimOpen(false)}
            >
                <MenuItem
                    onClick={() => {
                        setSimOpen(false);
                        dispatch(toggle_showing());
                    }}
                >
                    Timing
                </MenuItem>
            </Menu>
        </div>
    );
}
Example #3
Source File: PublicMenu.tsx    From frontend with MIT License 6 votes vote down vote up
export default function PublicMenu() {
  const { t } = useTranslation()
  const { data: session } = useSession()
  const [anchorEl, setAnchorEl] = useState<Element | null>(null)

  const handleMenu = (event: React.MouseEvent) => setAnchorEl(event.currentTarget)
  const handleClose = () => setAnchorEl(null)

  if (session) {
    return null
  }

  return (
    <StyledGrid item>
      <IconButton onClick={handleMenu} size="large">
        <AccountCircle sx={{ fill: theme.palette.info.light }} />
      </IconButton>
      <Menu
        open={Boolean(anchorEl)}
        keepMounted
        id="menu-appbar"
        anchorEl={anchorEl}
        onClose={handleClose}
        anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
        transformOrigin={{ vertical: 'top', horizontal: 'right' }}>
        <LinkMenuItem href={routes.login} className={classes.dropdownLinkButton}>
          <Typography variant="button" className={classes.dropdownLinkText}>
            {t('nav.login')}
          </Typography>
        </LinkMenuItem>
        <LinkMenuItem href={routes.register} className={classes.dropdownLinkButton}>
          <Typography variant="button" className={classes.dropdownLinkText}>
            {t('nav.register')}
          </Typography>
        </LinkMenuItem>
      </Menu>
    </StyledGrid>
  )
}
Example #4
Source File: GenericMenu.tsx    From frontend with MIT License 6 votes vote down vote up
export default function GenericMenu({ label, children }: Props) {
  const [anchorEl, setAnchorEl] = useState<Element | null>(null)
  const open = Boolean(anchorEl)

  const handleMenu = (event: React.MouseEvent) => setAnchorEl(event.currentTarget)
  const handleClose = () => setAnchorEl(null)

  return (
    <>
      <Button
        variant="text"
        color="primary"
        onClick={handleMenu}
        sx={{ whiteSpace: 'nowrap', px: 2 }}
        endIcon={open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}>
        {label}
      </Button>
      <Menu
        keepMounted
        id="menu-donation"
        anchorEl={anchorEl}
        elevation={6}
        onClose={handleClose}
        open={Boolean(anchorEl)}
        anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
        transformOrigin={{ vertical: 'top', horizontal: 'right' }}>
        {children}
      </Menu>
    </>
  )
}
Example #5
Source File: AppNavbar.tsx    From sapio-studio with Mozilla Public License 2.0 5 votes vote down vote up
function NodeMenu(props: { bitcoin_node_manager: BitcoinNodeManager }) {
    const dispatch = useDispatch();
    const nodeRef = React.useRef<HTMLLIElement>(null);
    const [node_open, setNodeOpen] = React.useState(false);
    const close = () => setNodeOpen(false);

    return (
        <>
            <ListItem
                disableGutters
                button={false}
                key={'Bitcoin Node'}
                onClick={() => setNodeOpen(true)}
                ref={nodeRef}
            >
                <ListItemIcon></ListItemIcon>
                <ListItemText primary={'Bitcoin Node'} />
            </ListItem>
            <Menu
                anchorEl={nodeRef.current}
                anchorOrigin={{
                    vertical: 'center',
                    horizontal: 'right',
                }}
                keepMounted
                open={node_open}
                onClose={close}
            >
                <MenuItem
                    onClick={async () => {
                        close();
                        const addr =
                            await props.bitcoin_node_manager.get_new_address();
                        window.electron.write_clipboard(addr);
                    }}
                >
                    Get New Address to Clipboard
                </MenuItem>
                <MenuItem
                    onClick={() => {
                        close();
                        props.bitcoin_node_manager
                            .generate_blocks(10)
                            .catch((err) => console.error(err));
                    }}
                >
                    Generate 10 Blocks
                </MenuItem>
                <Divider />
                <MenuItem
                    onClick={() => {
                        close();
                        dispatch(toggle_status_bar());
                    }}
                >
                    Toggle Status
                </MenuItem>
            </Menu>
        </>
    );
}
Example #6
Source File: PlayBar.tsx    From rewind with MIT License 5 votes vote down vote up
function MoreMenu() {
  const [anchorEl, setAnchorEl] = useState(null);
  const open = Boolean(anchorEl);
  const handleClick = (event: any) => {
    setAnchorEl(event.currentTarget);
  };
  const handleClose = () => {
    setAnchorEl(null);
  };

  const analyzer = useAnalysisApp();
  const handleTakeScreenshot = () => {
    analyzer.screenshotTaker.takeScreenshot();
    handleClose();
  };

  const [helpOpen, setHelpOpen] = useState(false);

  const handleOpenHelp = () => {
    setHelpOpen(true);
    handleClose();
  };

  return (
    <>
      <HelpModalDialog isOpen={helpOpen} onClose={() => setHelpOpen(false)} />
      <IconButton
        aria-label="more"
        id="long-button"
        aria-controls="long-menu"
        // aria-expanded={open ? "true" : undefined}
        aria-haspopup="true"
        onClick={handleClick}
        onFocus={ignoreFocus}
      >
        <MoreVert />
      </IconButton>
      <Menu
        open={open}
        onClose={handleClose}
        anchorEl={anchorEl}
        anchorOrigin={{
          vertical: "top",
          horizontal: "center",
        }}
        transformOrigin={{
          vertical: "bottom",
          horizontal: "center",
        }}
      >
        <MenuItem onClick={handleTakeScreenshot}>
          <ListItemIcon>
            <PhotoCamera />
          </ListItemIcon>
          <ListItemText>Take Screenshot</ListItemText>
        </MenuItem>
        <MenuItem onClick={handleOpenHelp}>
          <ListItemIcon>
            <Help />
          </ListItemIcon>
          <ListItemText>Help</ListItemText>
        </MenuItem>
      </Menu>
    </>
  );
}
Example #7
Source File: PlayBar.tsx    From rewind with MIT License 5 votes vote down vote up
function BaseSpeedButton(props: BaseSpeedButtonProps) {
  const { value, onChange } = props;

  const [anchorEl, setAnchorEl] = useState(null);
  const open = Boolean(anchorEl);
  const handleClick = (event: any) => {
    setAnchorEl(event.currentTarget);
  };
  const handleClose = () => {
    setAnchorEl(null);
  };
  const formatSpeed = (s: number) => `${s}x`;

  // Floating point issues?

  return (
    <>
      <Button
        sx={{
          color: "text.primary",
          textTransform: "none",
          fontSize: "1em",
          // minWidth: "0",
          // px: 2,
        }}
        size={"small"}
        onClick={handleClick}
        onFocus={ignoreFocus}
      >
        {formatSpeed(value)}
        {/*<Typography>{formatSpeed(value)}</Typography>*/}
      </Button>
      <Menu
        open={open}
        onClose={handleClose}
        anchorEl={anchorEl}
        anchorOrigin={{
          vertical: "top",
          horizontal: "center",
        }}
        transformOrigin={{
          vertical: "bottom",
          horizontal: "center",
        }}
      >
        <MenuList>
          {ALLOWED_SPEEDS.map((s) => (
            <MenuItem
              key={s}
              onClick={() => {
                onChange(s);
                handleClose();
              }}
              sx={{ width: "120px", maxWidth: "100%" }}
            >
              <ListItemText>{formatSpeed(s)}</ListItemText>
              <Typography variant="body2" color="text.secondary">
                {speedLabels[s] ?? ""}
              </Typography>
            </MenuItem>
          ))}
        </MenuList>
      </Menu>
    </>
  );
}
Example #8
Source File: PrivateMenu.tsx    From frontend with MIT License 5 votes vote down vote up
export default function PrivateMenu() {
  const { t } = useTranslation()
  const { data: session, status } = useSession()
  const [anchorEl, setAnchorEl] = useState<Element | null>(null)

  const handleMenu = (event: React.MouseEvent) => setAnchorEl(event.currentTarget)
  const handleClose = () => setAnchorEl(null)

  if (!session) {
    return null
  }

  const title = `${session.name}\n(${session.email})`
  return (
    <StyledGrid item>
      <IconButton onClick={handleMenu} size="large">
        {session?.user?.picture ? (
          <Avatar title={title} alt={title} src={session?.user?.picture} />
        ) : (
          <AccountCircle sx={{ fill: theme.palette.info.light }} />
        )}
      </IconButton>
      <Menu
        keepMounted
        id="menu-appbar"
        anchorEl={anchorEl}
        onClose={handleClose}
        open={Boolean(anchorEl)}
        anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
        transformOrigin={{ vertical: 'top', horizontal: 'right' }}>
        <LinkMenuItem href={routes.profile.index} className={classes.dropdownLinkText}>
          <Typography variant="button">{t('nav.profile')}</Typography>
        </LinkMenuItem>
        {status === 'authenticated' && isAdmin(session) && (
          <LinkMenuItem href={routes.admin.index} className={classes.dropdownLinkText}>
            <Typography variant="button">{t('nav.admin.index')}</Typography>
          </LinkMenuItem>
        )}
        <LinkMenuItem href={routes.logout} className={classes.dropdownLinkText}>
          <Typography variant="button">{t('nav.logout')}</Typography>
        </LinkMenuItem>
      </Menu>
    </StyledGrid>
  )
}
Example #9
Source File: DropdownButton.tsx    From genshin-optimizer with MIT License 5 votes vote down vote up
export default function DropdownButton({ title, children, id = "dropdownbtn", ...props }: DropdownButtonProps) {
  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
  const open = Boolean(anchorEl);
  const handleClick = useCallback(
    (event: React.MouseEvent<HTMLButtonElement>) => setAnchorEl(event.currentTarget),
    [setAnchorEl],
  )
  const handleClose = useCallback(
    () => setAnchorEl(null),
    [setAnchorEl],
  )

  return <Suspense fallback={<Button endIcon={<KeyboardArrowDown />}{...props}><Skeleton width={50} /></Button>} >
    <Button
      {...props}
      id={id}
      aria-controls="basic-menu"
      aria-haspopup="true"
      aria-expanded={open ? 'true' : undefined}
      onClick={handleClick}
      endIcon={<KeyboardArrowDown />}
    >
      {title}
    </Button>
    <Menu
      id="basic-menu"
      anchorEl={anchorEl}
      open={open}
      onClose={handleClose}
      MenuListProps={{
        'aria-labelledby': id,
      }}
      onClick={handleClose}
    >
      {/* set Skeleton to be really high so the taller dropdowns can still be placed properly... */}
      <Suspense fallback={<Skeleton width="100%" height="1000" />}>
        {children}
      </Suspense>
    </Menu>
  </Suspense>
}
Example #10
Source File: InputUnitMenu.tsx    From console with GNU Affero General Public License v3.0 5 votes vote down vote up
InputUnitMenu = ({
  classes,
  id,
  unitSelected,
  unitsList,
  disabled = false,
  onUnitChange,
}: IInputUnitBox) => {
  const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
  const open = Boolean(anchorEl);
  const handleClick = (event: React.MouseEvent<HTMLElement>) => {
    setAnchorEl(event.currentTarget);
  };
  const handleClose = (newUnit: string) => {
    setAnchorEl(null);
    if (newUnit !== "" && onUnitChange) {
      onUnitChange(newUnit);
    }
  };

  return (
    <Fragment>
      <button
        id={`${id}-button`}
        aria-controls={`${id}-menu`}
        aria-haspopup="true"
        aria-expanded={open ? "true" : undefined}
        onClick={handleClick}
        className={classes.buttonTrigger}
        disabled={disabled}
        type={"button"}
      >
        {unitSelected}
      </button>
      <Menu
        id={`${id}-menu`}
        aria-labelledby={`${id}-button`}
        anchorEl={anchorEl}
        open={open}
        onClose={() => {
          handleClose("");
        }}
        anchorOrigin={{
          vertical: "bottom",
          horizontal: "center",
        }}
        transformOrigin={{
          vertical: "top",
          horizontal: "center",
        }}
      >
        {unitsList.map((unit) => (
          <MenuItem
            onClick={() => handleClose(unit.value)}
            key={`itemUnit-${unit.value}-${unit.label}`}
          >
            {unit.label}
          </MenuItem>
        ))}
      </Menu>
    </Fragment>
  );
}
Example #11
Source File: LocaleMenu.tsx    From frontend with MIT License 5 votes vote down vote up
export default function LocaleMenu() {
  const router = useRouter()
  const { t } = useTranslation()
  const [anchorEl, setAnchorEl] = useState<Element | null>(null)

  const handleMenu = (event: React.MouseEvent) => setAnchorEl(event.currentTarget)
  const handleClose = () => setAnchorEl(null)
  const changeLang = useCallback(
    (locale: string) => (event: React.MouseEvent) => {
      event.preventDefault()
      // Same route different language
      router.push(router.route, router.asPath, { locale })
      setAnchorEl(null)
    },
    [],
  )

  if (!router.locale) {
    return null
  }

  return (
    <>
      <Button variant="text" size="small" onClick={handleMenu}>
        {router.locale.toUpperCase()}
      </Button>
      <Menu
        open={Boolean(anchorEl)}
        keepMounted
        id="menu-appbar"
        anchorEl={anchorEl}
        onClose={handleClose}
        anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
        transformOrigin={{ vertical: 'top', horizontal: 'right' }}>
        <MenuItem href={router.route} component="a" onClick={changeLang('bg')}>
          {t('BG')}
        </MenuItem>
        <MenuItem href={router.route} component="a" onClick={changeLang('en')}>
          {t('EN')}
        </MenuItem>
      </Menu>
    </>
  )
}
Example #12
Source File: HoverMenu.tsx    From frontend with MIT License 5 votes vote down vote up
export default function HoverMenu({ menu, items, icon: Icon, isOpen }: Props) {
  const router = useRouter()

  const [anchorMenu, setAnchorMenu] = useState<null | HTMLElement>(null)

  const isSelected = useMemo(
    () => items.filter((item) => item.href !== '#' && router.asPath.includes(item.href)).length > 0,
    [items],
  )
  const handleOpenMenu = (event: React.MouseEvent<HTMLElement>) => {
    setAnchorMenu(event.currentTarget)
  }
  const handleCloseMenu = () => setAnchorMenu(null)

  return (
    <Root>
      <ListItemButton
        selected={isSelected}
        onClick={handleOpenMenu}
        sx={{ borderRadius: '0 25px 25px 0' }}>
        <ListItemIcon
          title={menu}
          sx={(theme) => ({
            minWidth: theme.spacing(4),
            color: isSelected ? theme.palette.primary.main : theme.palette.action.active,
          })}>
          {<Icon />}
        </ListItemIcon>
        {isOpen && (
          <ListItemText
            primary={menu}
            primaryTypographyProps={{ color: isSelected ? 'primary' : undefined }}
          />
        )}
        {isOpen && (
          <ChevronRightIcon
            color={
              anchorMenu ? (isSelected ? 'primary' : 'action') : isSelected ? 'primary' : 'disabled'
            }
          />
        )}
      </ListItemButton>
      <Menu
        keepMounted
        id="menu-appbar"
        anchorEl={anchorMenu}
        onClose={handleCloseMenu}
        open={Boolean(anchorMenu)}
        className={isOpen ? classes.open : classes.close}
        anchorOrigin={{ vertical: 'top', horizontal: 'left' }}
        transformOrigin={{ vertical: 'center', horizontal: 'left' }}>
        {items.map(({ label, icon: Icon, href }) => (
          <CustomListItem
            key={label}
            sx={{ p: 0 }}
            selected={href !== '#' && router.asPath.includes(href)}
            icon={<Icon />}
            label={label}
            onClick={() => router.push(href)}
          />
        ))}
      </Menu>
    </Root>
  )
}
Example #13
Source File: GridColumns.tsx    From mui-toolpad with MIT License 4 votes vote down vote up
function GridColumnsPropEditor({
  label,
  nodeId,
  value = [],
  onChange,
  disabled,
}: EditorProps<GridColumns>) {
  const { bindings } = usePageEditorState();
  const [editColumnsDialogOpen, setEditColumnsDialogOpen] = React.useState(false);
  const [editedIndex, setEditedIndex] = React.useState<number | null>(null);

  const editedColumn = typeof editedIndex === 'number' ? value[editedIndex] : null;
  React.useEffect(() => {
    if (editColumnsDialogOpen) {
      setEditedIndex(null);
    }
  }, [editColumnsDialogOpen]);

  const [menuAnchorEl, setMenuAnchorEl] = React.useState<null | HTMLElement>(null);
  const menuOpen = Boolean(menuAnchorEl);
  const handleMenuClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    setMenuAnchorEl(event.currentTarget);
  };
  const handleClose = () => {
    setMenuAnchorEl(null);
  };

  const rowsValue = nodeId && bindings[`${nodeId}.props.rows`];
  const definedRows: unknown = rowsValue?.value;

  const columnSuggestions = React.useMemo(() => {
    const inferred = inferColumns(Array.isArray(definedRows) ? definedRows : []);
    const existingFields = new Set(value.map(({ field }) => field));
    return inferred.filter((column) => !existingFields.has(column.field));
  }, [definedRows, value]);

  const handleCreateColumn = React.useCallback(
    (suggestion: GridColDef) => () => {
      const existingFields = new Set(value.map(({ field }) => field));
      const newFieldName = generateUniqueString(suggestion.field, existingFields);
      const newValue = [...value, { ...suggestion, field: newFieldName }];
      onChange(newValue);
      setEditedIndex(newValue.length - 1);
      handleClose();
    },
    [value, onChange],
  );

  const handleColumnItemClick = React.useCallback(
    (index: number) => () => {
      setEditedIndex(index);
    },
    [],
  );

  const handleColumnChange = React.useCallback(
    (newValue: GridColDef) => {
      onChange(value.map((column, i) => (i === editedIndex ? newValue : column)));
    },
    [editedIndex, onChange, value],
  );

  const handleColumnDelete = React.useCallback(
    (deletedIndex: number) => (event: React.MouseEvent) => {
      event.stopPropagation();
      onChange(value.filter((column, i) => i !== deletedIndex));
    },
    [onChange, value],
  );

  return (
    <React.Fragment>
      <Button onClick={() => setEditColumnsDialogOpen(true)}>{label}</Button>
      <Dialog
        fullWidth
        open={editColumnsDialogOpen}
        onClose={() => setEditColumnsDialogOpen(false)}
      >
        {editedColumn ? (
          <React.Fragment>
            <DialogTitle>
              <IconButton aria-label="Back" onClick={() => setEditedIndex(null)}>
                <ArrowBackIcon />
              </IconButton>
              Edit column {editedColumn.field}
            </DialogTitle>
            <DialogContent>
              <Stack gap={1} py={1}>
                <TextField
                  label="field"
                  value={editedColumn.field}
                  disabled={disabled}
                  onChange={(event) =>
                    handleColumnChange({ ...editedColumn, field: event.target.value })
                  }
                />
                <TextField
                  select
                  fullWidth
                  label="type"
                  value={editedColumn.type ?? ''}
                  disabled={disabled}
                  onChange={(event) =>
                    handleColumnChange({ ...editedColumn, type: event.target.value })
                  }
                >
                  {COLUMN_TYPES.map((type) => (
                    <MenuItem key={type} value={type}>
                      {type}
                    </MenuItem>
                  ))}
                </TextField>
                <TextField
                  select
                  fullWidth
                  label="align"
                  value={editedColumn.align ?? ''}
                  disabled={disabled}
                  onChange={(event) =>
                    handleColumnChange({
                      ...editedColumn,
                      align: (event.target.value as GridAlignment) || undefined,
                    })
                  }
                >
                  {ALIGNMENTS.map((alignment) => (
                    <MenuItem key={alignment} value={alignment}>
                      {alignment}
                    </MenuItem>
                  ))}
                </TextField>
                <TextField
                  label="width"
                  type="number"
                  value={editedColumn.width}
                  disabled={disabled}
                  onChange={(event) =>
                    handleColumnChange({ ...editedColumn, width: Number(event.target.value) })
                  }
                />
              </Stack>
            </DialogContent>
          </React.Fragment>
        ) : (
          <React.Fragment>
            <DialogTitle>Edit columns</DialogTitle>
            <DialogContent>
              <IconButton aria-label="Add column" onClick={handleMenuClick} disabled={disabled}>
                <AddIcon />
              </IconButton>
              <Menu
                id="new-column-menu"
                anchorEl={menuAnchorEl}
                open={menuOpen}
                onClose={handleClose}
                MenuListProps={{
                  'aria-labelledby': 'basic-button',
                }}
              >
                {columnSuggestions.map((suggestion) => (
                  <MenuItem key={suggestion.field} onClick={handleCreateColumn(suggestion)}>
                    {suggestion.field}
                  </MenuItem>
                ))}
                <MenuItem onClick={handleCreateColumn({ field: 'new' })}>new column</MenuItem>
              </Menu>
              <List>
                {value.map((colDef, i) => {
                  return (
                    <ListItem
                      key={colDef.field}
                      disableGutters
                      onClick={handleColumnItemClick(i)}
                      secondaryAction={
                        <IconButton
                          aria-label="Remove column"
                          edge="end"
                          onClick={handleColumnDelete(i)}
                        >
                          <DeleteIcon />
                        </IconButton>
                      }
                    >
                      <ListItemButton>
                        <ListItemText primary={colDef.field} />
                      </ListItemButton>
                    </ListItem>
                  );
                })}
              </List>
            </DialogContent>
          </React.Fragment>
        )}
        <DialogActions>
          <Button color="inherit" variant="text" onClick={() => setEditColumnsDialogOpen(false)}>
            Close
          </Button>
        </DialogActions>
      </Dialog>
    </React.Fragment>
  );
}
Example #14
Source File: AppNavbar.tsx    From sapio-studio with Mozilla Public License 2.0 4 votes vote down vote up
function ContractMenu(props: { relayout: () => void }) {
    const dispatch = useDispatch();
    const workspace = useSelector(selectWorkspace);
    const contractRef = React.useRef<HTMLLIElement>(null);
    const [contracts_open, setContractsOpen] = React.useState(false);

    return (
        <div>
            <ListItem
                disableGutters
                button={false}
                key={'Contract'}
                onClick={() => setContractsOpen(true)}
                ref={contractRef}
            >
                <ListItemIcon></ListItemIcon>
                <ListItemText primary={'Contract'} />
            </ListItem>
            <Menu
                anchorEl={contractRef.current}
                anchorOrigin={{
                    vertical: 'center',
                    horizontal: 'right',
                }}
                keepMounted
                open={contracts_open}
                onClose={() => setContractsOpen(false)}
            >
                <MenuItem
                    onClick={() => {
                        setContractsOpen(false);
                        dispatch(open_modal('LoadHex'));
                    }}
                >
                    Open Contract from Clipboard
                </MenuItem>
                <MenuItem
                    onClick={() => {
                        setContractsOpen(false);
                        dispatch(create_contract_from_file());
                    }}
                >
                    Open Contract from File
                </MenuItem>
                <MenuItem
                    onClick={() => {
                        setContractsOpen(false);
                        dispatch(open_modal('SaveHex'));
                    }}
                >
                    Save Contract
                </MenuItem>
                <MenuItem
                    onClick={() => {
                        setContractsOpen(false);
                        window.electron.sapio.load_wasm_plugin(workspace);
                    }}
                >
                    Load WASM Plugin
                </MenuItem>
                <MenuItem
                    onClick={async () => {
                        setContractsOpen(false);
                        const apis =
                            await window.electron.sapio.load_contract_list(
                                workspace
                            );
                        if ('err' in apis) {
                            alert(apis.err);
                            return;
                        }
                        dispatch(set_apis(apis.ok));
                        dispatch(switch_showing('ContractCreator'));
                    }}
                >
                    Create New Contract
                </MenuItem>
                <MenuItem
                    onClick={() => {
                        setContractsOpen(false);
                        dispatch(recreate_contract());
                    }}
                >
                    Recreate Last Contract
                </MenuItem>
                <Divider />
                <MenuItem
                    onClick={() => {
                        props.relayout();
                    }}
                >
                    Repair Layout
                </MenuItem>
            </Menu>
        </div>
    );
}
Example #15
Source File: PlayerList.tsx    From NekoMaid with MIT License 4 votes vote down vote up
Players: React.FC = () => {
  const his = useHistory()
  const plugin = usePlugin()
  const [page, setPage] = useState(0)
  const [loading, setLoading] = useState(true)
  const [state, setState] = useState<number | null>(null)
  const [activedPlayer, setActivedPlayer] = useState<PlayerData | null>(null)
  const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null)
  const [data, setData] = useState<{ count: number, players: PlayerData[] }>(() => ({ count: 0, players: [] }))
  const globalData = useGlobalData()
  const { hasWhitelist } = globalData
  const refresh = () => {
    setLoading(true)
    plugin.emit('playerList:fetchPage', (it: any) => {
      if (it.players == null) it.players = []
      setData(it)
      setLoading(false)
    }, page, state === 1 || state === 2 ? state : 0, null)
  }
  useMemo(refresh, [page, state])
  const close = () => {
    setAnchorEl(null)
    setActivedPlayer(null)
  }

  return <Card>
    <CardHeader
      title={lang.playerList.title}
      action={
        <ToggleButtonGroup
          size='small'
          color={(state === 1 ? 'warning' : state === 2 ? 'error' : undefined) as any}
          value={state}
          exclusive
          onChange={(_, it) => {
            if (it === 3) return
            setState(it)
            if (state === 3) refresh()
          }}
        >
          <ToggleButton disabled={loading} value={1}><Star /></ToggleButton>
          <ToggleButton disabled={loading} value={2}><Block /></ToggleButton>
          <ToggleButton disabled={loading} value={3} onClick={() => state !== 3 && dialog(lang.playerList.nameToSearch, lang.username)
            .then(filter => {
              if (filter == null) return
              his.push('/NekoMaid/playerList/' + filter)
              setState(3)
              setLoading(true)
              plugin.emit('playerList:fetchPage', (it: any) => {
                if (it.players == null) it.players = []
                setPage(0)
                setData(it)
                setLoading(false)
              }, page, 0, filter.toLowerCase())
            })}><Search /></ToggleButton>
        </ToggleButtonGroup>
      }
    />
    <Divider />
    <Box sx={{ position: 'relative' }}>
      <CircularLoading loading={loading} />
      <TableContainer>
        <Table>
          <TableHead>
            <TableRow>
              <TableCell padding='checkbox' />
              <TableCell>{lang.username}</TableCell>
              <TableCell align='right'>{minecraft['stat.minecraft.play_time']}</TableCell>
              <TableCell align='right'>{lang.playerList.lastPlay}</TableCell>
              <TableCell align='right'>{lang.operations}</TableCell>
            </TableRow>
          </TableHead>
          <TableBody>
            {data.players.map(it => <TableRow key={it.name}>
              <TableCell sx={{ cursor: 'pointer', padding: theme => theme.spacing(1, 1, 1, 2) }} onClick={() => his.push('/NekoMaid/playerList/' + it.name)}>
                <Avatar src={getSkin(globalData, it.name, true)} imgProps={{ crossOrigin: 'anonymous', style: { width: 40, height: 40 } }} variant='rounded' />
              </TableCell>
              <TableCell>{it.name}</TableCell>
              <TableCell align='right'>{dayjs.duration(it.playTime / 20, 'seconds').humanize()}</TableCell>
              <TableCell align='right'>{dayjs(it.lastOnline).fromNow()}</TableCell>
              <TableCell align='right'>
                {(state === 1 || hasWhitelist) && <Tooltip title={lang.playerList[it.whitelisted ? 'clickToRemoveWhitelist' : 'clickToAddWhitelist']}>
                  <IconButton onClick={() => whitelist(it.name, plugin, refresh, !it.whitelisted)}>
                    {it.whitelisted ? <Star color='warning' /> : <StarBorder />}
                  </IconButton>
                </Tooltip>}
                <Tooltip title={it.ban == null ? lang.playerList.clickToBan : lang.playerList.banned + ': ' + it.ban}>
                  <IconButton onClick={() => banPlayer(it.name, plugin, refresh, it.ban == null)}>
                    <Block color={it.ban == null ? undefined : 'error'} />
                  </IconButton>
                </Tooltip>
                {actions.length
                  ? <IconButton onClick={e => {
                    setActivedPlayer(anchorEl ? null : it)
                    setAnchorEl(anchorEl ? null : e.currentTarget)
                  }}><MoreHoriz /></IconButton>
                  : null}
              </TableCell>
            </TableRow>)}
          </TableBody>
        </Table>
      </TableContainer>
      <TablePagination
        rowsPerPageOptions={[]}
        component='div'
        count={data.count}
        rowsPerPage={10}
        page={page}
        onPageChange={(_, it) => !loading && setPage(it)}
      />
    </Box>
    <Menu
      anchorEl={anchorEl}
      open={Boolean(anchorEl)}
      onClose={() => setAnchorEl(null)}
    >{actions.map((It, i) => <It key={i} onClose={close} player={activedPlayer} />)}</Menu>
  </Card>
}
Example #16
Source File: Files.tsx    From NekoMaid with MIT License 4 votes vote down vote up
Files: React.FC = () => {
  const plugin = usePlugin()
  const theme = useTheme()
  const his = useHistory()
  const loc = useLocation()
  const drawerWidth = useDrawerWidth()
  const tree = useRef<HTMLHRElement | null>(null)
  const editor = useRef<UnControlled | null>(null)
  const prevExpanded = useRef<string[]>([])
  const dirs = useRef<Record<string, boolean>>({ })
  // eslint-disable-next-line func-call-spacing
  const loading = useRef<Record<string, () => Promise<void>> & { '!#LOADING'?: boolean }>({ })
  const [id, setId] = useState(0)
  const [curPath, setCurPath] = useState('')
  const [progress, setProgress] = useState(-1)
  const [copyPath, setCopyPath] = useState('')
  const [expanded, setExpanded] = useState<string[]>([])
  const [compressFile, setCompressFile] = useState<string | null>(null)
  const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null)

  const isDir = !!dirs.current[curPath]
  const dirPath = isDir ? curPath : curPath.substring(0, curPath.lastIndexOf('/'))

  const spacing = theme.spacing(3)
  const refresh = () => {
    loading.current = { }
    dirs.current = { }
    prevExpanded.current = []
    setCurPath('')
    setExpanded([])
    setId(id + 1)
  }

  useEffect(() => {
    if (!tree.current) return
    const resize = () => {
      if (!tree.current) return
      const height = tree.current.style.maxHeight = (window.innerHeight - tree.current.offsetTop - parseInt(spacing)) + 'px'
      const style = (editor as any).current?.editor?.display?.wrapper?.style
      if (style) style.height = height
    }
    resize()
    window.addEventListener('resize', resize)
    return window.removeEventListener('resize', resize)
  }, [tree.current, spacing])

  return <Box sx={{ height: '100vh', py: 3 }}>
    <Toolbar />
    <Container maxWidth={false}>
      <Grid container spacing={3} sx={{ width: { sm: `calc(100vw - ${drawerWidth}px - ${theme.spacing(3)})` } }}>
        <Grid item lg={4} md={12} xl={3} xs={12}>
          <Card sx={{ minHeight: 400 }}>
            <CardHeader
              title={lang.files.filesList}
              sx={{ position: 'relative' }}
              action={<Box sx={{ position: 'absolute', right: theme.spacing(1), top: '50%', transform: 'translateY(-50%)' }}
            >
              <Tooltip title={lang.files.delete}><span>
                <IconButton
                  disabled={!curPath}
                  size='small'
                  onClick={() => dialog({
                    okButton: { color: 'error' },
                    content: <>{lang.files.confirmDelete(<span className='bold'>{curPath}</span>)}&nbsp;
                      <span className='bold' style={{ color: theme.palette.error.main }}>({lang.unrecoverable})</span></>
                  }).then(it => it && plugin.emit('files:update', (res: boolean) => {
                    action(res)
                    if (!res) return
                    refresh()
                    if (loc.pathname.replace(/^\/NekoMaid\/files\/?/, '') === curPath) his.push('/NekoMaid/files')
                  }, curPath))}
                ><DeleteForever /></IconButton>
              </span></Tooltip>
              <Tooltip title={lang.files.createFile}>
                <IconButton size='small' onClick={() => fileNameDialog(lang.files.createFile, curPath)
                  .then(it => it != null && his.push(`/NekoMaid/files/${dirPath ? dirPath + '/' : ''}${it}`))}>
              <Description /></IconButton></Tooltip>
              <Tooltip title={lang.files.createFolder}>
                <IconButton size='small' onClick={() => fileNameDialog(lang.files.createFolder, curPath)
                  .then(it => it != null && plugin.emit('files:createDirectory', (res: boolean) => {
                    action(res)
                    if (res) refresh()
                  }, dirPath + '/' + it))}><CreateNewFolder /></IconButton></Tooltip>
              <Tooltip title={lang.more}>
                <IconButton size='small' onClick={e => setAnchorEl(anchorEl ? null : e.currentTarget)}><MoreHoriz /></IconButton>
              </Tooltip>
            </Box>} />
            <Divider />
            <TreeView
              ref={tree}
              defaultCollapseIcon={<ArrowDropDown />}
              defaultExpandIcon={<ArrowRight />}
              sx={{ flexGrow: 1, width: '100%', overflowY: 'auto' }}
              expanded={expanded}
              onNodeToggle={(_: any, it: string[]) => {
                const l = loading.current
                if (it.length < prevExpanded.current.length || !l[it[0]]) {
                  setExpanded(it)
                  prevExpanded.current = it
                  return
                }
                l[it[0]]().then(() => {
                  prevExpanded.current.unshift(it[0])
                  setExpanded([...prevExpanded.current])
                  delete l[it[0]]
                })
                delete l[it[0]]
              }}
              onNodeSelect={(_: any, it: string) => {
                setCurPath(it[0] === '/' ? it.slice(1) : it)
                if (dirs.current[it] || loading.current['!#LOADING']) return
                if (it.startsWith('/')) it = it.slice(1)
                his.push('/NekoMaid/files/' + it)
              }}
            >
              <Item plugin={plugin} path='' loading={loading.current} dirs={dirs.current} key={id} />
            </TreeView>
          </Card>
        </Grid>
        <Grid item lg={8} md={12} xl={9} xs={12} sx={{ maxWidth: `calc(100vw - ${theme.spacing(1)})`, paddingBottom: 3 }}>
          <Editor plugin={plugin} editorRef={editor} loading={loading.current} dirs={dirs.current} refresh={refresh} />
        </Grid>
      </Grid>
    </Container>
    <Menu
      anchorEl={anchorEl}
      open={Boolean(anchorEl)}
      onClose={() => setAnchorEl(null)}
      anchorOrigin={anchorOrigin}
      transformOrigin={anchorOrigin}
    >
      <MenuItem onClick={() => {
        refresh()
        setAnchorEl(null)
      }}><ListItemIcon><Refresh /></ListItemIcon>{lang.refresh}</MenuItem>
      <MenuItem disabled={!curPath} onClick={() => {
        setAnchorEl(null)
        fileNameDialog(lang.files.rename, curPath).then(it => it != null && plugin.emit('files:rename', (res: boolean) => {
          action(res)
          if (res) refresh()
        }, curPath, dirPath + '/' + it))
      }}><ListItemIcon><DriveFileRenameOutline /></ListItemIcon>{lang.files.rename}</MenuItem>
      <MenuItem disabled={!curPath} onClick={() => {
        setAnchorEl(null)
        setCopyPath(curPath)
      }}>
        <ListItemIcon><FileCopy /></ListItemIcon>{lang.files.copy}
      </MenuItem>
      <MenuItem disabled={!copyPath} onClick={() => {
        setAnchorEl(null)
        toast(lang.files.pasting)
        plugin.emit('files:copy', (res: boolean) => {
          action(res)
          refresh()
        }, copyPath, dirPath)
      }}>
        <ListItemIcon><ContentPaste /></ListItemIcon>{lang.files.paste}
      </MenuItem>
      <MenuItem disabled={progress !== -1} component='label' htmlFor='NekoMaid-files-upload-input' onClick={() => setAnchorEl(null)}>
        <ListItemIcon><Upload /></ListItemIcon>{progress === -1 ? lang.files.upload : `${lang.files.uploading} (${progress.toFixed(2)}%)`}
      </MenuItem>
      <MenuItem disabled={isDir} onClick={() => {
        setAnchorEl(null)
        toast(lang.files.downloading)
        plugin.emit('files:download', (res: ArrayBuffer | null) => {
          if (res) window.open(address! + 'Download/' + res, '_blank')
          else failed()
        }, curPath)
      }}><ListItemIcon><Download /></ListItemIcon>{lang.files.download}</MenuItem>
      <MenuItem onClick={() => {
        setAnchorEl(null)
        setCompressFile(curPath)
      }}><ListItemIcon><Inbox /></ListItemIcon>{lang.files.compress}</MenuItem>
      <MenuItem onClick={() => {
        setAnchorEl(null)
        toast(lang.files.uncompressing)
        plugin.emit('files:compress', (res: boolean) => {
          action(res)
          refresh()
        }, curPath)
      }}><ListItemIcon><Outbox /></ListItemIcon>{lang.files.decompress}</MenuItem>
    </Menu>
    <Input id='NekoMaid-files-upload-input' type='file' sx={{ display: 'none' }} onChange={e => {
      const elm = e.target as HTMLInputElement
      const file = elm.files?.[0]
      elm.value = ''
      if (!file) return
      const size = file.size
      if (size > 128 * 1024 * 1024) return failed(lang.files.uploadTooBig)
      toast(lang.files.uploading)
      const name = dirPath + '/' + file.name
      if (dirs.current[name] != null) return failed(lang.files.exists)
      plugin.emit('files:upload', (res: string | null) => {
        if (!res) return failed(lang.files.exists)
        const formdata = new FormData()
        formdata.append('file', file)
        const xhr = new XMLHttpRequest()
        setProgress(0)
        xhr.open('put', address! + 'Upload/' + res)
        xhr.onreadystatechange = () => {
          if (xhr.readyState !== 4) return
          setProgress(-1)
          action(xhr.status === 200)
          refresh()
        }
        xhr.upload.onprogress = e => e.lengthComputable && setProgress(e.loaded / e.total * 100)
        xhr.send(formdata)
      }, name[0] === '/' ? name.slice(1) : name)
    }} />
    <CompressDialog file={compressFile} path={dirPath} dirs={dirs.current} onClose={() => setCompressFile(null)} refresh={refresh} plugin={plugin} />
  </Box>
}
Example #17
Source File: CollectionRowActions.tsx    From firecms with MIT License 4 votes vote down vote up
/**
 *
 * @param entity
 * @param isSelected
 * @param selectionEnabled
 * @param size
 * @param toggleEntitySelection
 * @param onCopyClicked
 * @param onEditClicked
 * @param onDeleteClicked
 * @constructor
 *
 * @category Collection components
 */
export function CollectionRowActions<M extends { [Key: string]: any }>({
                                                                           entity,
                                                                           isSelected,
                                                                           selectionEnabled,
                                                                           size,
                                                                           toggleEntitySelection,
                                                                           onCopyClicked,
                                                                           onEditClicked,
                                                                           onDeleteClicked
                                                                       }:
                                                                           {
                                                                               entity: Entity<M>,
                                                                               size: CollectionSize,
                                                                               isSelected?: boolean,
                                                                               selectionEnabled?: boolean,
                                                                               toggleEntitySelection?: (selectedEntity: Entity<M>) => void
                                                                               onEditClicked?: (selectedEntity: Entity<M>) => void,
                                                                               onCopyClicked?: (selectedEntity: Entity<M>) => void,
                                                                               onDeleteClicked?: (selectedEntity: Entity<M>) => void,
                                                                           }) {

    const editEnabled = Boolean(onEditClicked);
    const copyEnabled = Boolean(onCopyClicked);
    const deleteEnabled = Boolean(onDeleteClicked);

    const classes = useTableStyles();

    const [anchorEl, setAnchorEl] = React.useState<any | null>(null);

    const openMenu = useCallback((event: React.MouseEvent) => {
        setAnchorEl(event.currentTarget);
        event.stopPropagation();
    }, [setAnchorEl]);

    const closeMenu = useCallback(() => {
        setAnchorEl(null);
    }, [setAnchorEl]);

    const onCheckboxChange = (event: React.ChangeEvent) => {
        if (toggleEntitySelection)
            toggleEntitySelection(entity);
        event.stopPropagation();
    };

    const onDeleteClick = useCallback((event: MouseEvent) => {
        event.stopPropagation();
        if (onDeleteClicked)
            onDeleteClicked(entity);
        setAnchorEl(null);
    }, [entity, onDeleteClicked, setAnchorEl]);

    const onCopyClick = useCallback((event: MouseEvent) => {
        event.stopPropagation();
        if (onCopyClicked)
            onCopyClicked(entity);
        setAnchorEl(null);
    }, [entity, onCopyClicked, setAnchorEl]);

    return (
        <div className={classes.cellButtonsWrap}>

            {(editEnabled || deleteEnabled || selectionEnabled) &&
            <div className={classes.cellButtons}
            >
                {editEnabled &&
                <Tooltip title={`Edit ${entity.id}`}>
                    <IconButton
                        onClick={(event: MouseEvent) => {
                            event.stopPropagation();
                            if (onEditClicked)
                                onEditClicked(entity);
                        }}
                        size="large">
                        <KeyboardTab/>
                    </IconButton>
                </Tooltip>
                }

                {selectionEnabled &&
                <Tooltip title={`Select ${entity.id}`}>
                    <Checkbox
                        checked={isSelected}
                        onChange={onCheckboxChange}
                    />
                </Tooltip>}

                {(copyEnabled || deleteEnabled) &&
                <IconButton onClick={openMenu} size="large">
                    <MoreVert/>
                </IconButton>
                }

                {(copyEnabled || deleteEnabled) && <Menu
                    anchorEl={anchorEl}
                    open={Boolean(anchorEl)}
                    onClose={closeMenu}
                    elevation={2}
                >
                    {deleteEnabled && <MenuItem onClick={onDeleteClick}>
                        <ListItemIcon>
                            <Delete/>
                        </ListItemIcon>
                        <ListItemText primary={"Delete"}/>
                    </MenuItem>}

                    {copyEnabled && <MenuItem onClick={onCopyClick}>
                        <ListItemIcon>
                            <FileCopy/>
                        </ListItemIcon>
                        <ListItemText primary="Copy"/>
                    </MenuItem>}

                </Menu>}


            </div>}

            {size !== "xs" && (
                <div className={classes.cellButtonsId}>

                    {entity
                        ? <Typography
                            className={"mono"}
                            variant={"caption"}
                            color={"textSecondary"}> {entity.id} </Typography>
                        : <Skeleton variant="text"/>
                    }
                </div>
            )}

        </div>
    );

}
Example #18
Source File: UploadFilesButton.tsx    From console with GNU Affero General Public License v3.0 4 votes vote down vote up
UploadFilesButton = ({
  uploadPath,
  bucketName,
  forceDisable = false,
  uploadFileFunction,
  uploadFolderFunction,
  classes,
}: IUploadFilesButton) => {
  const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
  const openUploadMenu = Boolean(anchorEl);
  const handleClick = (event: React.MouseEvent<HTMLElement>) => {
    setAnchorEl(event.currentTarget);
  };
  const handleCloseUpload = () => {
    setAnchorEl(null);
  };

  const uploadObjectAllowed = hasPermission(uploadPath, [
    IAM_SCOPES.S3_PUT_OBJECT,
  ]);
  const uploadFolderAllowed = hasPermission(
    bucketName,
    [IAM_SCOPES.S3_PUT_OBJECT],
    false,
    true
  );

  const uploadEnabled: boolean = uploadObjectAllowed || uploadFolderAllowed;

  return (
    <Fragment>
      <RBIconButton
        id={"upload-main"}
        tooltip={"Upload Files"}
        aria-controls={`upload-main-menu`}
        aria-haspopup="true"
        aria-expanded={openUploadMenu ? "true" : undefined}
        onClick={handleClick}
        text={"Upload"}
        icon={<UploadIcon />}
        color="primary"
        variant={"contained"}
        disabled={forceDisable || !uploadEnabled}
      />
      <Menu
        id={`upload-main-menu`}
        aria-labelledby={`upload-main`}
        anchorEl={anchorEl}
        open={openUploadMenu}
        onClose={() => {
          handleCloseUpload();
        }}
        anchorOrigin={{
          vertical: "bottom",
          horizontal: "center",
        }}
        transformOrigin={{
          vertical: "top",
          horizontal: "center",
        }}
      >
        <MenuItem
          onClick={() => {
            uploadFileFunction(handleCloseUpload);
          }}
          disabled={!uploadObjectAllowed || forceDisable}
        >
          <ListItemIcon className={classes.listUploadIcons}>
            <UploadIcon />
          </ListItemIcon>
          <ListItemText>Upload File</ListItemText>
        </MenuItem>
        <MenuItem
          onClick={() => {
            uploadFolderFunction(handleCloseUpload);
          }}
          disabled={!uploadFolderAllowed || forceDisable}
        >
          <ListItemIcon className={classes.listUploadIcons}>
            <UploadFolderIcon />
          </ListItemIcon>
          <ListItemText>Upload Folder</ListItemText>
        </MenuItem>
      </Menu>
    </Fragment>
  );
}
Example #19
Source File: Navbar.tsx    From website with Apache License 2.0 4 votes vote down vote up
Navbar = ({
  rootPageHasAnimation,
}: {
  rootPageHasAnimation?: boolean;
}) => {
  const [drawerState, setDrawerState] = useState(false);
  const [toggleMoreIcon, setToggleMoreIcon] = useState(false);
  const [initialPageWidth, setInitialPageWidth] = useState(0);
  const theme = useTheme();
  const ref = useRef<HTMLButtonElement | null>(null);

  /* In the future we should come up with a different solution that doesn't
   rerender this component, its not much of a problem right now but later it
   *may* become a problem 

   - Cody
   */
  const scrollPos = useScrollPosition(15);
  const toggleDrawer = useCallback(
    (open: boolean = false) => setDrawerState(open ?? !drawerState),
    [drawerState],
  );

  useEffect(() => {
    // This is used for checking if its mobile or not
    setInitialPageWidth(window.innerWidth);
  }, []);

  return (
    <Wrapper>
      <SkipNavLink
        contentId="skipNav"
        style={{
          background: theme.background.backgroundColor,
          color:
            theme.type === "dark"
              ? theme.text.textColorExtremelyLight
              : theme.text.textColorDark,
          zIndex: 9999999,
        }}
      />
      <Drawer
        anchor="left"
        open={drawerState}
        onClose={() => toggleDrawer(false)}
      >
        <DrawerLogoContainer>
          <DrawerLogo
            alt="dahliaOS logo"
            src={
              theme.type === "dark"
                ? "/images/logos/logo-white.png"
                : "/images/logos/logo-color.png"
            }
          />
        </DrawerLogoContainer>
        <Container>
          <Link href="/#features">Features</Link>
          <Link href="/#download">Download</Link>
          <Link href="mailto:[email protected]">Contact</Link>
          <Link href="https://github.com/orgs/dahliaos/people" target="_blank">
            Developers
          </Link>
          <Link href="https://docs.dahliaOS.io">Documentation</Link>
          <Divider />
          <Category>Find us on</Category>
          <Link href="/discord" target="_blank">
            Discord
          </Link>
          <Link href="/github" target="_blank">
            GitHub
          </Link>
          <Link href="/reddit" target="_blank">
            Reddit
          </Link>
          <Link href="/telegram" target="_blank">
            Telegram
          </Link>
          <Link href="/facebook" target="_blank">
            Facebook
          </Link>
          <Link href="/instagram" target="_blank">
            Instagram
          </Link>
          <Link href="/twitter" target="_blank">
            Twitter
          </Link>
          <Divider />
          <Category>For developers</Category>
          <Link href="/github">Source Code</Link>
          <Link href="/discord">Join Our Team</Link>
        </Container>
      </Drawer>
      <StyledAppBar
        rootPageHasAnimation={
          initialPageWidth < 1075 ? false : rootPageHasAnimation
        }
        position="fixed"
        scrollPos={scrollPos}
      >
        <StyledToolbar scrollPos={scrollPos}>
          <IconButton
            edge="start"
            color="inherit"
            aria-label="menu"
            onClick={() => toggleDrawer(true)}
          >
            <MenuIcon
              style={
                theme.type === "dark"
                  ? undefined
                  : { color: theme.text.textColorDark }
              }
            />
          </IconButton>
          <AppBarLogoLinkContainer href="/">
            <AppBarLogo
              alt="dahliaOS logo"
              src={
                theme.type === "dark"
                  ? "/images/logos/logo-white.png"
                  : "/images/logos/logo-color.png"
              }
              draggable={false}
            />
          </AppBarLogoLinkContainer>
          <DesktopNav>
            <AppBarLink href="/#features">Features</AppBarLink>
            <AppBarLink href="/#download">Download</AppBarLink>
            <AppBarLink href="https://web.dahliaOS.io" target="_blank">
              Demo
            </AppBarLink>
            <AppBarLink
              href="https://github.com/orgs/dahliaos/people"
              target="_blank"
            >
              Developers
            </AppBarLink>
            <AppBarLink href="https://docs.dahliaOS.io">
              Documentation
            </AppBarLink>
            <IconButton
              ref={ref}
              aria-label="nav-more"
              aria-haspopup="true"
              onClick={() => setToggleMoreIcon(true)}
            >
              <MoreVert style={{ color: theme.text.textColorLight }} />
            </IconButton>
            <Menu
              open={toggleMoreIcon}
              onClose={() => setToggleMoreIcon(false)}
              anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
              anchorEl={ref.current}
              keepMounted
            >
              <MenuItem>
                <MenuLink href="/github" target="_blank">
                  Source Code
                </MenuLink>
              </MenuItem>
              <MenuItem disabled>Screenshots</MenuItem>
            </Menu>
          </DesktopNav>
        </StyledToolbar>
      </StyledAppBar>
      <SkipNavContent id="skipNav" />
    </Wrapper>
  );
}
Example #20
Source File: CommentItemModule.tsx    From bouncecode-cms with GNU General Public License v3.0 4 votes vote down vote up
function CommentItemModule({post, comment}: ICommentItemModule) {
  const [moreVert, setMoreVert] = React.useState(null);
  const openMoreVert = Boolean(moreVert);
  const toggleMoreVert = open => {
    return event => {
      if (open) {
        setMoreVert(event.currentTarget);
      } else {
        setMoreVert(null);
      }
    };
  };

  const [commentDrawer, setCommentDrawer] = React.useState(null);
  const toggleCommentDrawer = open => {
    return event => {
      if (
        event.type === 'keydown' &&
        (event.key === 'Tab' || event.key === 'Shift')
      ) {
        return;
      }

      setCommentDrawer(open);
    };
  };

  const [
    emotionMutation,
    {loading: emotionLoading},
  ] = useCommentEmotionMutation();
  const [
    undoEmotionMutation,
    {loading: undoEmotionLoading},
  ] = useCommentEmotionUndoMutation();
  const updateEmotion = emotion => {
    return () => {
      console.log(myEmotion);
      console.log(emotion);
      if (myEmotion === emotion.toLowerCase()) {
        undoEmotionMutation({
          variables: {
            where: {
              id: comment.id,
            },
          },
          refetchQueries: [
            {
              query: CommentMyEmotionDocument,
              variables: {where: {id: comment.id}},
            },
          ],
        });
      } else {
        emotionMutation({
          variables: {
            where: {
              id: comment.id,
            },
            data: {emotion},
          },
          refetchQueries: [
            {
              query: CommentMyEmotionDocument,
              variables: {where: {id: comment.id}},
            },
          ],
        });
      }
    };
  };

  const {
    data: myEmotionData,
    loading: myEmotionLoading,
  } = useCommentMyEmotionQuery({
    variables: {where: {id: comment.id}},
  });
  const myEmotion = myEmotionData?.commentMyEmotion?.emotion;

  return (
    <Grid container direction="column" spacing={1}>
      <Grid item>
        <Grid container spacing={2}>
          <Grid item>
            <Avatar></Avatar>
          </Grid>
          <Grid item xs>
            <Grid container direction="column" spacing={1}>
              <Grid item>
                <Grid container spacing={2} alignItems="center">
                  <Grid item>
                    <Typography variant="body2">
                      {comment.user.email}
                    </Typography>
                  </Grid>
                  <Grid item>
                    <Typography variant="caption">
                      {formatDistance(
                        new Date(comment.createdDate),
                        new Date(),
                        {addSuffix: true},
                      )}
                    </Typography>
                  </Grid>
                </Grid>
              </Grid>
              <Grid item>
                <Typography variant="body1">{comment.text}</Typography>
              </Grid>
              <Grid
                container
                justifyContent="space-between"
                alignItems="center">
                <Grid item>
                  <Grid container alignItems="center">
                    <Grid item>
                      <Button
                        variant="text"
                        size="small"
                        startIcon={<ThumbUpOutlined />}
                        onClick={updateEmotion('LIKE')}
                        disabled={
                          myEmotionLoading ||
                          emotionLoading ||
                          undoEmotionLoading
                        }>
                        {comment.like || ''}
                      </Button>
                    </Grid>
                    <Grid item>
                      <Button
                        variant="text"
                        size="small"
                        startIcon={<ThumbDownOutlined />}
                        onClick={updateEmotion('UNLIKE')}
                        disabled={
                          myEmotionLoading ||
                          emotionLoading ||
                          undoEmotionLoading
                        }>
                        {comment.unlike || ''}
                      </Button>
                    </Grid>
                    {post ? (
                      <Grid item>
                        <Button
                          variant="text"
                          size="small"
                          onClick={toggleCommentDrawer(true)}
                          disabled>
                          답글
                        </Button>
                        <Drawer
                          anchor="bottom"
                          open={commentDrawer}
                          onClose={toggleCommentDrawer(false)}>
                          <Container maxWidth="md">
                            <Box pt={2} pb={2}>
                              <CommentCreateFormModule />
                            </Box>
                          </Container>
                        </Drawer>
                      </Grid>
                    ) : (
                      undefined
                    )}
                  </Grid>
                </Grid>
                <Grid item>
                  <IconButton onClick={toggleMoreVert(true)} disabled>
                    <MoreVert fontSize="small" />
                  </IconButton>
                  <Menu
                    anchorEl={moreVert}
                    open={openMoreVert}
                    onClose={toggleMoreVert(false)}>
                    <MenuItem
                      // selected={option === 'Pyxis'}
                      onClick={toggleMoreVert(false)}>
                      <ListItemIcon>
                        <AssistantPhotoOutlined />
                      </ListItemIcon>
                      <Typography variant="inherit">신고</Typography>
                    </MenuItem>
                  </Menu>
                </Grid>
              </Grid>
              {/* {post ? (
                <Grid item>
                  <Grid container direction="column" spacing={2}>
                    <Grid item>
                      <Button
                        variant="text"
                        size="small"
                        startIcon={<KeyboardArrowDown />}>
                        답글 1개 보기
                      </Button>
                    </Grid>
                    <Grid item>
                      <CommentItemModule comment />
                    </Grid>
                  </Grid>
                </Grid>
              ) : (
                undefined
              )} */}
            </Grid>
          </Grid>
        </Grid>
      </Grid>
    </Grid>
  );
}
Example #21
Source File: Sidebar.tsx    From airmessage-web with Apache License 2.0 4 votes vote down vote up
export default function Sidebar(props: {
	conversations: Conversation[] | undefined;
	selectedConversation?: number;
	onConversationSelected: (id: number) => void;
	onCreateSelected: () => void;
	errorBanner?: ConnectionErrorCode;
	needsServerUpdate?: boolean;
}) {
	const displaySnackbar = useContext(SnackbarContext);
	
	const [overflowMenu, setOverflowMenu] = useState<HTMLElement | null>(null);
	const openOverflowMenu = useCallback((event: React.MouseEvent<HTMLElement>) => {
		setOverflowMenu(event.currentTarget);
	}, [setOverflowMenu]);
	const closeOverflowMenu = useCallback(() => {
		setOverflowMenu(null);
	}, [setOverflowMenu]);
	
	const [isChangelogDialog, showChangelogDialog, hideChangelogDialog] = useSidebarDialog(closeOverflowMenu);
	const [isFeedbackDialog, showFeedbackDialog, hideFeedbackDialog] = useSidebarDialog(closeOverflowMenu);
	const [isSignOutDialog, showSignOutDialog, hideSignOutDialog] = useSidebarDialog(closeOverflowMenu);
	const [isRemoteUpdateDialog, showRemoteUpdateDialog, hideRemoteUpdateDialog] = useSidebarDialog();
	const [isUpdateRequiredDialog, showUpdateRequiredDialog, hideUpdateRequiredDialog] = useSidebarDialog();
	const [faceTimeLinkDialog, setFaceTimeLinkDialog] = useState<string | undefined>(undefined);
	
	//Keep track of remote updates
	const [remoteUpdate, setRemoteUpdate] = useState<ServerUpdateData | undefined>(undefined);
	useEffect(() => {
		const listener: RemoteUpdateListener = {onUpdate: setRemoteUpdate};
		ConnectionManager.addRemoteUpdateListener(listener);
		return () => ConnectionManager.removeRemoteUpdateListener(listener);
	}, [setRemoteUpdate]);
	const [remoteUpdateCache, setRemoteUpdateCache] = useState<ServerUpdateData>({
		id: 0, notes: "", protocolRequirement: [], remoteInstallable: false, version: ""
	});
	useEffect(() => {
		if(remoteUpdate !== undefined) {
			setRemoteUpdateCache(remoteUpdate);
		}
	}, [remoteUpdate, setRemoteUpdateCache]);
	
	//Keep track of whether FaceTime is supported
	const isFaceTimeSupported = useIsFaceTimeSupported();
	
	const [isFaceTimeLinkLoading, setFaceTimeLinkLoading] = useState(false);
	const createFaceTimeLink = useCallback(async () => {
		setFaceTimeLinkLoading(true);
		
		try {
			const link = await ConnectionManager.requestFaceTimeLink();
			
			//Prefer web share, fall back to displaying a dialog
			if(navigator.share) {
				await navigator.share({text: link});
			} else {
				setFaceTimeLinkDialog(link);
			}
		} catch(error) {
			if(error === FaceTimeLinkErrorCode.Network) {
				displaySnackbar({message: "Failed to get FaceTime link: no connection to server"});
			} else if(error === FaceTimeLinkErrorCode.External) {
				displaySnackbar({message: "Failed to get FaceTime link: an external error occurred"});
			}
		} finally {
			setFaceTimeLinkLoading(false);
		}
	}, [setFaceTimeLinkLoading, displaySnackbar]);
	
	return (
		<div className={styles.sidebar}>
			<ChangelogDialog isOpen={isChangelogDialog} onDismiss={hideChangelogDialog} />
			<FeedbackDialog isOpen={isFeedbackDialog} onDismiss={hideFeedbackDialog} />
			<SignOutDialog isOpen={isSignOutDialog} onDismiss={hideSignOutDialog} />
			<RemoteUpdateDialog isOpen={isRemoteUpdateDialog} onDismiss={hideRemoteUpdateDialog} update={remoteUpdateCache} />
			<UpdateRequiredDialog isOpen={isUpdateRequiredDialog} onDismiss={hideUpdateRequiredDialog} />
			<FaceTimeLinkDialog isOpen={faceTimeLinkDialog !== undefined} onDismiss={() => setFaceTimeLinkDialog(undefined)} link={faceTimeLinkDialog ?? ""} />
			
			<Toolbar className={styles.sidebarToolbar}>
				<AirMessageLogo />
				<div style={{flexGrow: 1}} />
				{isFaceTimeSupported && (
					<IconButton
						size="large"
						onClick={createFaceTimeLink}
						disabled={isFaceTimeLinkLoading}>
						<VideoCallOutlined />
					</IconButton>
				)}
				
				<IconButton
					size="large"
					onClick={props.onCreateSelected}
					disabled={props.conversations === undefined}>
					<AddRounded />
				</IconButton>
				
				<IconButton
					aria-haspopup="true"
					size="large"
					onClick={openOverflowMenu}
					disabled={props.conversations === undefined}>
					<MoreVertRounded />
				</IconButton>
				
				<Menu
					anchorEl={overflowMenu}
					anchorOrigin={{
						vertical: "top",
						horizontal: "right",
					}}
					keepMounted
					transformOrigin={{
						vertical: "top",
						horizontal: "right",
					}}
					open={!!overflowMenu}
					onClose={closeOverflowMenu}>
					<MenuItem onClick={showChangelogDialog}>What&apos;s new</MenuItem>
					<MenuItem onClick={showFeedbackDialog}>Help and feedback</MenuItem>
					<MenuItem onClick={showSignOutDialog}>Sign out</MenuItem>
				</Menu>
			</Toolbar>
			
			{props.errorBanner !== undefined && <ConnectionBanner error={props.errorBanner} /> }
			
			{remoteUpdate !== undefined && (
				<SidebarBanner
					icon={<Update />}
					message="A server update is available"
					button="Details"
					onClickButton={showRemoteUpdateDialog} />
			)}
			
			{props.needsServerUpdate && (
				<SidebarBanner
					icon={<SyncProblem />}
					message="Your server needs to be updated"
					button="Details"
					onClickButton={showUpdateRequiredDialog} />
			)}
			
			{props.conversations !== undefined ? (
				<Flipper
					className={styles.sidebarList}
					flipKey={props.conversations.map(conversation => conversation.localID).join(" ")}>
					<List>
						{props.conversations.map((conversation) =>
							<Flipped key={conversation.localID} flipId={conversation.localID}>
								{flippedProps => (
									<ListConversation
										conversation={conversation}
										selected={conversation.localID === props.selectedConversation}
										highlighted={conversation.unreadMessages}
										onSelected={() => props.onConversationSelected(conversation.localID)}
										flippedProps={flippedProps as Record<string, unknown>} />
								)}
							</Flipped>
						)}
					</List>
				</Flipper>
			) : (
				<div className={styles.sidebarListLoading}>
					{[...Array(16)].map((element, index) => <ConversationSkeleton key={`skeleton-${index}`} />)}
				</div>
			)}
		</div>
	);
}
Example #22
Source File: GridLayouts.tsx    From Tachidesk-WebUI with Mozilla Public License 2.0 4 votes vote down vote up
export default function SourceGridLayout() {
    const { options, setOptions } = useLibraryOptionsContext();

    const [anchorEl, setAnchorEl] = React.useState(null);
    const open = Boolean(anchorEl);
    const handleClick = (event: any) => {
        setAnchorEl(event.currentTarget);
    };
    const handleClose = () => {
        setAnchorEl(null);
    };

    function setGridContextOptions(
        e: React.ChangeEvent<HTMLInputElement>,
        checked: boolean,
    ) {
        if (checked) {
            setOptions((prev: any) => ({ ...prev, SourcegridLayout: parseInt(e.target.name, 10) }));
        }
    }

    return (
        <>
            <IconButton
                onClick={handleClick}
                size="small"
                sx={{ ml: 2 }}
                aria-controls={open ? 'account-menu' : undefined}
                aria-haspopup="true"
                aria-expanded={open ? 'true' : undefined}
            >
                <ViewModuleIcon />
            </IconButton>
            <Menu
                id="basic-menu"
                anchorEl={anchorEl}
                open={open}
                onClose={handleClose}
                MenuListProps={{ 'aria-labelledby': 'basic-button' }}
            >
                <MenuItem onClick={handleClose}>
                    <FormControlLabel
                        label="Compact grid"
                        control={(
                            <Radio
                                name="0"
                                // eslint-disable-next-line max-len
                                checked={options.SourcegridLayout === 0 || options.SourcegridLayout === undefined}
                                onChange={setGridContextOptions}
                            />
                        )}
                    />
                </MenuItem>
                <MenuItem onClick={handleClose}>
                    <FormControlLabel
                        label="Comfortable grid"
                        control={(
                            <Radio
                                name="1"
                                checked={options.SourcegridLayout === 1}
                                onChange={setGridContextOptions}
                            />
                        )}
                    />
                </MenuItem>
                <MenuItem onClick={handleClose}>
                    <FormControlLabel
                        label="List"
                        control={(
                            <Radio
                                name="2"
                                checked={options.SourcegridLayout === 2}
                                onChange={setGridContextOptions}
                            />
                        )}
                    />
                </MenuItem>
            </Menu>

        </>
    );
}
Example #23
Source File: Layout.tsx    From abrechnung with GNU Affero General Public License v3.0 4 votes vote down vote up
export default function Layout({ group = null, children, ...props }) {
    const authenticated = useRecoilValue(isAuthenticated);
    const [anchorEl, setAnchorEl] = useState(null);
    const theme: Theme = useTheme();
    const dotsMenuOpen = Boolean(anchorEl);
    const cfg = useRecoilValue(config);
    const location = useLocation();

    const [mobileOpen, setMobileOpen] = useState(true);

    const { window } = props;

    const handleProfileMenuOpen = (event) => {
        setAnchorEl(event.currentTarget);
    };

    const handleDotsMenuClose = (event) => {
        setAnchorEl(null);
    };

    const handleDrawerToggle = () => {
        setMobileOpen(!mobileOpen);
    };

    const drawer = (
        <div style={{ height: "100%" }}>
            <Toolbar />
            <Divider />
            {group != null && (
                <List sx={{ pb: 0 }}>
                    <ListItemLink
                        to={`/groups/${group.id}/`}
                        selected={
                            location.pathname === `/groups/${group.id}/` || location.pathname === `/groups/${group.id}`
                        }
                    >
                        <ListItemIcon>
                            <Paid />
                        </ListItemIcon>
                        <ListItemText primary="Transactions" />
                    </ListItemLink>
                    <ListItemLink
                        to={`/groups/${group.id}/balances`}
                        selected={location.pathname.startsWith(`/groups/${group.id}/balances`)}
                    >
                        <ListItemIcon>
                            <BarChart />
                        </ListItemIcon>
                        <ListItemText primary="Balances" />
                    </ListItemLink>
                    <ListItemLink
                        to={`/groups/${group.id}/accounts`}
                        selected={location.pathname.startsWith(`/groups/${group.id}/accounts`)}
                    >
                        <ListItemIcon>
                            <AccountBalance />
                        </ListItemIcon>
                        <ListItemText primary="Accounts" />
                    </ListItemLink>
                    <ListItemLink
                        to={`/groups/${group.id}/detail`}
                        selected={location.pathname.startsWith(`/groups/${group.id}/detail`)}
                    >
                        <ListItemIcon>
                            <AdminPanelSettings />
                        </ListItemIcon>
                        <ListItemText primary="Group Settings" />
                    </ListItemLink>
                    <ListItemLink
                        to={`/groups/${group.id}/members`}
                        selected={location.pathname.startsWith(`/groups/${group.id}/members`)}
                    >
                        <ListItemIcon>
                            <People />
                        </ListItemIcon>
                        <ListItemText primary="Group Members" />
                    </ListItemLink>
                    <ListItemLink
                        to={`/groups/${group.id}/invites`}
                        selected={location.pathname.startsWith(`/groups/${group.id}/invites`)}
                    >
                        <ListItemIcon>
                            <Mail />
                        </ListItemIcon>
                        <ListItemText primary="Group Invites" />
                    </ListItemLink>
                    <ListItemLink
                        to={`/groups/${group.id}/log`}
                        selected={location.pathname.startsWith(`/groups/${group.id}/log`)}
                    >
                        <ListItemIcon>
                            <Message />
                        </ListItemIcon>
                        <ListItemText primary="Group Log" />
                    </ListItemLink>
                    <Divider />
                </List>
            )}
            <SidebarGroupList group={group} />

            <Box
                sx={{
                    display: "flex",
                    position: "absolute",
                    width: "100%",
                    justifyContent: "center",
                    bottom: 0,
                    padding: 1,
                    borderTop: 1,
                    borderColor: theme.palette.divider,
                }}
            >
                {cfg.imprintURL && (
                    <Link href={cfg.imprintURL} target="_blank" sx={{ mr: 2 }}>
                        imprint
                    </Link>
                )}
                <Tooltip title="Source Code">
                    <Link sx={{ ml: 1 }} target="_blank" href={cfg.sourceCodeURL}>
                        <GitHub />
                    </Link>
                </Tooltip>
                {cfg.issueTrackerURL && (
                    <Tooltip title="Bug reports">
                        <Link sx={{ ml: 1 }} target="_blank" href={cfg.issueTrackerURL}>
                            <BugReport />
                        </Link>
                    </Tooltip>
                )}
            </Box>
        </div>
    );

    const container = window !== undefined ? () => window().document.body : undefined;

    return (
        <Box sx={{ display: "flex" }}>
            <CssBaseline />
            <AppBar
                position="fixed"
                sx={{
                    // width: {sm: `calc(100% - ${drawerWidth}px)`},
                    // ml: {sm: `${drawerWidth}px`},
                    zIndex: (theme) => theme.zIndex.drawer + 1,
                }}
            >
                <Toolbar>
                    <IconButton
                        color="inherit"
                        aria-label="open drawer"
                        onClick={handleDrawerToggle}
                        edge="start"
                        sx={{ mr: 2, display: { sm: "none" } }}
                    >
                        <MenuIcon />
                    </IconButton>
                    <Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
                        Abrechnung
                    </Typography>
                    {authenticated ? (
                        <div>
                            <IconButton
                                aria-label="account of current user"
                                aria-controls="menu-appbar"
                                aria-haspopup="true"
                                onClick={handleProfileMenuOpen}
                                color="inherit"
                            >
                                <AccountCircleIcon />
                            </IconButton>
                            <Menu
                                id="menu-appbar"
                                open={dotsMenuOpen}
                                anchorOrigin={{
                                    vertical: "top",
                                    horizontal: "right",
                                }}
                                keepMounted
                                anchorEl={anchorEl}
                                transformOrigin={{
                                    vertical: "top",
                                    horizontal: "right",
                                }}
                                onClose={handleDotsMenuClose}
                            >
                                <MenuItem component={RouterLink} to="/profile">
                                    Profile
                                </MenuItem>
                                <MenuItem component={RouterLink} to="/profile/settings">
                                    Settings
                                </MenuItem>
                                <MenuItem component={RouterLink} to="/profile/sessions">
                                    Sessions
                                </MenuItem>
                                <MenuItem component={RouterLink} to="/profile/change-email">
                                    Change E-Mail
                                </MenuItem>
                                <MenuItem component={RouterLink} to="/profile/change-password">
                                    Change Password
                                </MenuItem>
                                <Divider />
                                <MenuItem component={RouterLink} to="/logout">
                                    <ListItemIcon>
                                        <Logout fontSize="small" />
                                    </ListItemIcon>
                                    <ListItemText>Sign out</ListItemText>
                                </MenuItem>
                            </Menu>
                        </div>
                    ) : (
                        <Button component={RouterLink} color="inherit" to="/login">
                            Login
                        </Button>
                    )}
                </Toolbar>
            </AppBar>

            {authenticated ? (
                <Box component="nav" sx={{ width: { sm: drawerWidth }, flexShrink: { sm: 0 } }}>
                    <Drawer
                        container={container}
                        variant="temporary"
                        open={mobileOpen}
                        onClose={handleDrawerToggle}
                        ModalProps={{
                            keepMounted: true, // Better open performance on mobile.
                        }}
                        sx={{
                            display: { xs: "block", sm: "none" },
                            "& .MuiDrawer-paper": {
                                boxSizing: "border-box",
                                width: drawerWidth,
                            },
                        }}
                    >
                        {drawer}
                    </Drawer>
                    <Drawer
                        variant="permanent"
                        sx={{
                            flexShrink: 0,
                            display: { xs: "none", sm: "block" },
                            "& .MuiDrawer-paper": {
                                boxSizing: "border-box",
                                width: drawerWidth,
                            },
                        }}
                        open
                    >
                        {drawer}
                    </Drawer>
                </Box>
            ) : null}
            <Box
                component="main"
                sx={{
                    flexGrow: 1,
                    width: { sm: `calc(100% - ${drawerWidth}px)` },
                }}
            >
                <Toolbar />
                <Banner />
                <Container maxWidth="lg" sx={{ padding: { xs: 0, md: 1, lg: 3 } }}>
                    {children}
                </Container>
            </Box>
        </Box>
    );
}
Example #24
Source File: Mobile.tsx    From GTAV-NativeDB with MIT License 4 votes vote down vote up
function Mobile({ ...rest }: AppBarProps) {
  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
  const [settingsOpen, setSettingsOpen] = useState(false)
  const [searchOpen, setSearchOpen] = useState(false)
  const [statsDialog, setStatsDialog] = useState(false)
  const settings = useAppBarSettings()

  const handleSettingsOpen = useCallback(() => {
    setSettingsOpen(true)
  }, [setSettingsOpen])

  const handleSettingsClose = useCallback(() => {
    setSettingsOpen(false)
  }, [setSettingsOpen])

  const handleMenuOpen = useCallback((event: MouseEvent<HTMLElement>) => {
    setAnchorEl(event.currentTarget)
  }, [setAnchorEl])

  const handleMenuClose = useCallback(() => {
    setAnchorEl(null)
  }, [setAnchorEl])

  const handleSearchOpen = useCallback(() => {
    setSearchOpen(true)
  }, [setSearchOpen])

  const handleSearchClose = useCallback(() => {
    setSearchOpen(false)
  }, [setSearchOpen])

  const handleStatsDialogOpen = useCallback(() => {
    setStatsDialog(true)
  }, [setStatsDialog])

  const handleStatsDialogClose = useCallback(() => {
    setStatsDialog(false)
  }, [setStatsDialog])

  const actions: AppBarActionProps[] = useMemo(() => [
    ...(settings.actions ?? []),
    {
      text: 'Settings',
      mobileIcon: SettingsIcon,
      buttonProps: {
        onClick: handleSettingsOpen
      }
    },
    {
      text: 'Stats',
      mobileIcon: StatsIcon,
      buttonProps: {
        onClick: handleStatsDialogOpen
      }
    },
    {
      text: 'Generate Code',
      mobileIcon: CodeIcon,
      buttonProps: {
        href: '/generate-code'
      }
    },
    {
      text: 'View on Github',
      mobileIcon: GithubIcon,
      buttonProps: {
        href: 'https://github.com/DottieDot/GTAV-NativeDB',
        target: '_blank'
      }
    }
  ], [settings, handleSettingsOpen, handleStatsDialogOpen])

  return (
    <Box {...rest}>
      <StatsDialog open={statsDialog} onClose={handleStatsDialogClose} />
      <SettingsDrawer open={settingsOpen} onClose={handleSettingsClose} />
      <MaterialAppBar position="sticky">
        {settings.search && (
          <Box sx={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}>
            <Zoom in={searchOpen}>
              <Box sx={{ height: '100%', position: 'relative', zIndex: 1 }}>
                <MobileSearch 
                  search={settings.search}
                  onClose={handleSearchClose}
                />
              </Box>
            </Zoom>
          </Box>
        )}
        <Toolbar>
          <Typography variant="h6" component="div">
            <Link
              to="/natives"
              color="inherit"
              underline="none"
              component={RouterLink}
            >
              {settings?.title ?? 'GTA V Native Reference'}
            </Link>
          </Typography>
          <Box
            sx={{ display: 'flex', flex: 1 }}
          />
          <StatusButton />
          {settings?.search && (
            <IconButton onClick={handleSearchOpen} aria-label="search">
              <SearchIcon />
            </IconButton>
          )}
          
          <IconButton onClick={handleMenuOpen} aria-label="more">
            <MoreIcon />
          </IconButton>
          <Menu
            anchorEl={anchorEl}
            open={!!anchorEl}
            onClose={handleMenuClose}
            onClick={handleMenuClose}
          >
            {actions.map(action => (
              <AppBarAction
                key={action.text}
                {...action} 
              />
            ))}
          </Menu>
        </Toolbar>
      </MaterialAppBar>
    </Box>
  )
}