@material-ui/core#Hidden TypeScript Examples

The following examples show how to use @material-ui/core#Hidden. 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: Grid.tsx    From firetable with Apache License 2.0 6 votes vote down vote up
export default function GridPage() {
  const router = useRouter();
  const tableCollection = decodeURIComponent(router.match.params.id);

  let filters: FireTableFilter[] = [];
  const parsed = queryString.parse(router.location.search);
  if (typeof parsed.filters === "string") {
    // decoded
    //[{"key":"cohort","operator":"==","value":"AMS1"}]
    filters = JSON.parse(parsed.filters);
    //TODO: json schema validator
  }

  return (
    <Navigation tableCollection={tableCollection}>
      <Grid
        key={tableCollection}
        collection={tableCollection}
        filters={filters}
      />

      <Hidden smDown>
        <SideDrawer />
      </Hidden>
    </Navigation>
  );
}
Example #2
Source File: index.tsx    From lobis-frontend with MIT License 6 votes vote down vote up
function ViewBase({ children }: IViewBaseProps) {
    const classes = useStyles();

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

    const isSmallerScreen = useMediaQuery("(max-width: 960px)");
    const isSmallScreen = useMediaQuery("(max-width: 600px)");

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

    return (
        <div className="view-base-root">
            <Messages />
            <Header drawe={!isSmallerScreen} handleDrawerToggle={handleDrawerToggle} />
            <div className={classes.drawer}>
                <Hidden mdUp>
                    <MobileDrawer mobileOpen={mobileOpen} handleDrawerToggle={handleDrawerToggle} />
                </Hidden>
                <Hidden smDown>
                    <Drawer />
                </Hidden>
            </div>
            <div className={`${classes.content} ${isSmallerScreen && classes.contentShift}`}>{children}</div>
        </div>
    );
}
Example #3
Source File: Sidebar.tsx    From knboard with MIT License 6 votes vote down vote up
Sidebar = () => {
  const dispatch = useDispatch();
  const mobileOpen = useSelector(mobileDrawerOpen);

  const handleCloseMobileDrawer = () => {
    dispatch(setMobileDrawerOpen(false));
  };

  return (
    <>
      <Hidden smUp implementation="css">
        <Drawer
          variant="temporary"
          anchor="left"
          open={mobileOpen}
          onClose={handleCloseMobileDrawer}
          ModalProps={{ keepMounted: true }}
        >
          <DrawerContent />
        </Drawer>
      </Hidden>
      <Hidden xsDown implementation="css">
        <Drawer anchor="left" variant="permanent">
          <DrawerContent />
        </Drawer>
      </Hidden>
    </>
  );
}
Example #4
Source File: Navbar.tsx    From knboard with MIT License 6 votes vote down vote up
Navbar = () => {
  const dispatch = useDispatch();

  return (
    <Container>
      <Item>
        <Icons>
          <Hidden smUp implementation="css">
            <FontAwesomeIcon
              icon={faBars}
              onClick={() => dispatch(setMobileDrawerOpen(true))}
            />
          </Hidden>
          <Hidden xsDown implementation="css">
            <FontAwesomeIcon icon={faRocket} />
          </Hidden>
        </Icons>
      </Item>
      <Item>Knboard</Item>
      <Item>
        <UserMenu />
      </Item>
    </Container>
  );
}
Example #5
Source File: body.tsx    From aqualink-app with MIT License 6 votes vote down vote up
RowNumberCell = ({
  color,
  unit,
  decimalPlaces,
  value,
  classes,
  isExtended,
}: {
  color?: string;
  unit?: string;
  value: number | null;
  decimalPlaces?: number;
  classes: SiteTableBodyProps["classes"];
  isExtended?: boolean;
}) => {
  return (
    <TableCell
      className={
        isExtended ? classes.cellTextAlignExtended : classes.cellTextAlign
      }
    >
      <Typography
        variant="h6"
        style={{ color }}
        className={classes.numberCellsTitle}
      >
        {formatNumber(value, decimalPlaces)}
        <Hidden smUp>
          &nbsp;
          <Typography variant="h6" component="span">
            {unit}
          </Typography>
        </Hidden>
      </Typography>
    </TableCell>
  );
}
Example #6
Source File: Topbar.tsx    From knests with MIT License 5 votes vote down vote up
Topbar = (props) => {
  const { className, onSidebarOpen, ...rest } = props;

  const classes = useStyles();

  const [notifications] = useState([]);

  const router = useRouter();

  const handleSignOut = () => {
    router.push('/login');
    localStorage.removeItem('token');
  };

  return (
    <AppBar
      {...rest}
      className={clsx(classes.root, className)}
    >
      <Toolbar>
        <Link href="/">
          <a >
            <img
              alt="Logo"
              src="/images/logos/logo--white.svg"
            />
          </a>
        </Link>
        <div className={classes.flexGrow} />
        <Hidden mdDown>
          <IconButton color="inherit">
            <Badge
              badgeContent={notifications.length}
              color="primary"
              variant="dot"
            >
              <NotificationsIcon />
            </Badge>
          </IconButton>
          <IconButton
            className={classes.signOutButton}
            color="inherit"
            onClick={handleSignOut}
          >
            <InputIcon />
          </IconButton>
        </Hidden>
        <Hidden lgUp>
          <IconButton
            color="inherit"
            onClick={onSidebarOpen}
          >
            <MenuIcon />
          </IconButton>
        </Hidden>
      </Toolbar>
    </AppBar>
  );
}
Example #7
Source File: ModeTabs.tsx    From prompts-ai with MIT License 5 votes vote down vote up
export default function ModeTabs() {
    const dispatch = useDispatch();
    const classes = useStyles();
    const tabIndex = useSelector(selectTabIndex);

    const handleTabIndexChange = (event: React.ChangeEvent<{}>, newValue: number) => {
        dispatch(updateTabIndex(newValue));
    };

    return (
        <div className={classes.root}>
            <AppBar position="static">
                <Grid
                    justify="space-between" // Add it here :)
                    alignItems="center"
                    container
                    spacing={1}
                >
                    <Grid item>
                        <Tabs value={tabIndex} onChange={handleTabIndexChange} aria-label="simple tabs example">
                            <Tab label="Simple" {...a11yProps(TabIndex.basic)} />
                            <Tab label="Examples" {...a11yProps(TabIndex.multipleExamples)} />
                            <Tab label="Variations" {...a11yProps(TabIndex.variations)} />
                            <Tab label="Conversations" {...a11yProps(TabIndex.conversations)} />
                        </Tabs>
                    </Grid>
                    <Hidden smDown>
                        <Grid item className={classes.additionalItemsGridItem}>
                            <CodeGeneratorButton/>
                        </Grid>
                    </Hidden>
                </Grid>
            </AppBar>
            <TabPanel value={tabIndex} index={TabIndex.basic}>
                <BasicTab/>
            </TabPanel>
            <TabPanel value={tabIndex} index={TabIndex.multipleExamples}>
                <ExamplesTab/>
            </TabPanel>
            <TabPanel value={tabIndex} index={TabIndex.variations}>
                <VariationsTab/>
            </TabPanel>
            <TabPanel value={tabIndex} index={TabIndex.conversations}>
                <ConversationsTab/>
            </TabPanel>
        </div>
    );
}
Example #8
Source File: LabelDialog.tsx    From knboard with MIT License 5 votes vote down vote up
LabelDialog = () => {
  const theme = useTheme();
  const dispatch = useDispatch();
  const open = useSelector((state: RootState) => state.label.dialogOpen);
  const labels = useSelector(selectAllLabels);
  const [searchValue, setSearchValue] = useState("");
  const [creating, setCreating] = useState(false);
  const xsDown = useMediaQuery(theme.breakpoints.down("xs"));

  const filteredLabels = labels.filter((label) =>
    label.name.toLowerCase().match(searchValue.trim().toLowerCase())
  );

  const handleClose = () => {
    dispatch(setDialogOpen(false));
  };

  return (
    <Dialog
      open={open}
      onClose={handleClose}
      maxWidth="sm"
      fullWidth
      fullScreen={xsDown}
    >
      <Close onClose={handleClose} />
      <DialogTitle id="edit-labels">Edit labels</DialogTitle>
      <Container>
        <Flex
          css={css`
            align-items: flex-end;
            ${creating && "margin-bottom: 1rem;"}
          `}
        >
          <LabelCount>
            {filteredLabels.length} label{filteredLabels.length !== 1 && "s"}
          </LabelCount>
          <div>
            <Hidden xsDown implementation="css">
              <TextField
                value={searchValue}
                onChange={(e) => setSearchValue(e.target.value)}
                placeholder="Search labels"
                InputProps={{
                  startAdornment: (
                    <InputAdornment
                      position="start"
                      css={css`
                        color: #ccc;
                      `}
                    >
                      <FontAwesomeIcon icon={faSearch} />
                    </InputAdornment>
                  ),
                }}
              />
            </Hidden>
          </div>
          <Button
            size="small"
            color="primary"
            variant="contained"
            onClick={() => setCreating(true)}
            css={css`
              margin-right: 1rem;
            `}
          >
            New label
          </Button>
        </Flex>
        {creating && <LabelCreate setCreating={setCreating} />}
        <Table>
          {filteredLabels.map((label) => (
            <LabelRow key={label.id + label.color + label.name} label={label} />
          ))}
        </Table>
      </Container>
    </Dialog>
  );
}
Example #9
Source File: CreatePage.tsx    From clearflask with Apache License 2.0 5 votes vote down vote up
CreateLayout = (props: {
  title: string;
  isOnboarding?: boolean;
  description: React.ReactNode;
  stretchContent?: boolean;
  content?: React.ReactNode;
  actions?: React.ReactNode[];
  img?: Img;
}) => {
  const classes = useStyles();
  return (
    <div className={classes.layout}>
      {props.isOnboarding && (
        <div className={classes.layoutHeader}>
          <div className={classes.layoutHeaderLogo}><Logo /></div>
        </div>
      )}
      <div className={classes.layoutContentAndImage}>
        <div className={classes.layoutContentContainer}>
          <div className={classes.layoutLimitWidth}>
            <Typography variant='h3' component='h1' className={classes.layoutContentTitle}>{props.title}</Typography>
            <Typography variant='h6' component='div' className={classes.layoutContentDescription}>{props.description}</Typography>
          </div>
          {!!props.content && (
            <div className={classNames(
              classes.layoutContent,
              !props.stretchContent && classes.layoutLimitWidth,
            )}>{props.content}</div>
          )}
          {!!props.actions?.length && (
            <div className={classNames(classes.layoutContentActions, classes.layoutLimitWidth)}>
              {props.actions.map(action => (
                <div className={classes.layoutContentAction}>
                  {action}
                </div>
              ))}
            </div>
          )}
        </div>
        {!!props.img && (
          <Hidden mdDown>
            <ImgIso
              alt=''
              className={classes.layoutImage}
              src={props.img.src}
              aspectRatio={props.img.aspectRatio}
              width={props.img.width}
              height={props.img.height}
              maxWidth={props.img.width}
              maxHeight={props.img.height}
            />
          </Hidden>
        )}
      </div>
    </div>
  );
}
Example #10
Source File: index.tsx    From aqualink-app with MIT License 5 votes vote down vote up
Homepage = ({ classes }: HomepageProps) => {
  const dispatch = useDispatch();
  const siteOnMap = useSelector(siteOnMapSelector);

  const { initialZoom, initialSiteId, initialCenter }: MapQueryParams =
    useQuery();

  useEffect(() => {
    dispatch(sitesRequest());
  }, [dispatch]);

  useEffect(() => {
    if (!siteOnMap && initialSiteId) {
      dispatch(siteRequest(initialSiteId));
      dispatch(surveysRequest(initialSiteId));
    } else if (siteOnMap) {
      dispatch(siteRequest(`${siteOnMap.id}`));
      dispatch(surveysRequest(`${siteOnMap.id}`));
    }
  }, [dispatch, initialSiteId, siteOnMap]);

  const [isDrawerOpen, setDrawerOpen] = useState(false);

  const toggleDrawer = () => {
    setDrawerOpen(!isDrawerOpen);
  };

  // scroll drawer to top when its closed.
  // the lib we use doesn't support passing ID or ref, so we rely on class name here.
  // scrollTopAtClose prop doesn't work with manually controlled state
  useEffect(() => {
    if (isDrawerOpen) return;
    const className = "ReactSwipeableBottomSheet";
    const drawer =
      document.getElementsByClassName(`${className}--opened`)[0] ||
      document.getElementsByClassName(`${className}--closed`)[0];
    if (!drawer) return;
    // eslint-disable-next-line fp/no-mutation
    drawer.scrollTop = 0;
  });

  return (
    <>
      <div role="presentation" onClick={isDrawerOpen ? toggleDrawer : () => {}}>
        <HomepageNavBar searchLocation geocodingEnabled />
      </div>
      <div className={classes.root}>
        <Grid container>
          <Grid className={classes.map} item xs={12} md={6}>
            <HomepageMap
              initialZoom={initialZoom}
              initialCenter={initialCenter}
            />
          </Grid>
          <Hidden smDown>
            <Grid className={classes.siteTable} item md={6}>
              <SiteTable />
            </Grid>
          </Hidden>
          <Hidden mdUp>
            <SwipeableBottomSheet
              overflowHeight={60}
              bodyStyle={{
                borderTopLeftRadius: "25px",
                borderTopRightRadius: "25px",
                maxHeight: "80vh",
              }}
              onChange={setDrawerOpen}
              open={isDrawerOpen}
            >
              <div role="presentation" onClick={toggleDrawer}>
                <SiteTable isDrawerOpen={isDrawerOpen} />
              </div>
            </SwipeableBottomSheet>
          </Hidden>
        </Grid>
      </div>
    </>
  );
}
Example #11
Source File: index.tsx    From aqualink-app with MIT License 5 votes vote down vote up
SurveyTimeline = ({
  loading,
  isAdmin,
  siteId,
  addNewButton,
  timeZone,
  observation,
  pointName,
  pointId,
  classes,
}: SurveyTimelineProps) => {
  const surveyList = useSelector(surveyListSelector);
  const displayAddButton =
    isAdmin &&
    addNewButton &&
    !(window && window.location.pathname.includes("new_survey"));
  // If the site is loading, then display two survey card skeletons,
  // else display the actual survey cards.
  const filteredSurveys = loading
    ? [null, null]
    : filterSurveys(surveyList, observation, pointId);
  const timelineProps: TimelineProps = {
    siteId,
    loading,
    isAdmin,
    pointId,
    pointName,
    surveys: filteredSurveys,
    timeZone,
    displayAddButton,
  };

  return (
    <div className={classes.root}>
      <Hidden mdDown>
        <TimelineDesktop {...timelineProps} />
      </Hidden>
      <Hidden lgUp>
        <TimelineTablet {...timelineProps} />
      </Hidden>
    </div>
  );
}
Example #12
Source File: Table.tsx    From firetable with Apache License 2.0 5 votes vote down vote up
export default function TablePage() {
  const router = useRouter();
  const tableCollection = decodeURIComponent(router.match.params.id);

  const { tableState, tableActions, sideDrawerRef } = useFiretableContext();
  const { userDoc } = useAppContext();

  let filters: FireTableFilter[] = [];
  const parsed = queryString.parse(router.location.search);
  if (typeof parsed.filters === "string") {
    filters = JSON.parse(parsed.filters);
    // TODO: json schema validator
  }

  useEffect(() => {
    if (
      tableActions &&
      tableState &&
      tableState.tablePath !== tableCollection
    ) {
      tableActions.table.set(tableCollection, filters);
      if (filters && filters.length !== 0) {
        userDoc.dispatch({
          action: DocActions.update,
          data: {
            tables: { [`${tableState.tablePath}`]: { filters } },
          },
        });
      }
      if (sideDrawerRef?.current) sideDrawerRef.current.setCell!(null);
    }
  }, [tableCollection]);

  if (!tableState) return null;

  return (
    <Navigation tableCollection={tableCollection}>
      <ActionParamsProvider>
        {tableState.loadingColumns && (
          <>
            <TableHeaderSkeleton />
            <HeaderRowSkeleton />
          </>
        )}

        {!tableState.loadingColumns && !_isEmpty(tableState.columns) && (
          <Table key={tableCollection} />
        )}

        {!tableState.loadingColumns && _isEmpty(tableState.columns) && (
          <EmptyTable />
        )}

        <Hidden smDown>
          <SideDrawer />
        </Hidden>
      </ActionParamsProvider>
    </Navigation>
  );
}
Example #13
Source File: index.tsx    From wonderland-frontend with MIT License 5 votes vote down vote up
function ViewBase({ children }: IViewBaseProps) {
    const classes = useStyles();

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

    const isSmallerScreen = useMediaQuery("(max-width: 960px)");
    const isSmallScreen = useMediaQuery("(max-width: 600px)");

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

    return (
        <div className="view-base-root">
            <Messages />
            <Header drawe={!isSmallerScreen} handleDrawerToggle={handleDrawerToggle} />
            <div className={classes.drawer}>
                <Hidden mdUp>
                    <MobileDrawer mobileOpen={mobileOpen} handleDrawerToggle={handleDrawerToggle} />
                </Hidden>
                <Hidden smDown>
                    <Drawer />
                </Hidden>
            </div>
            <div className={`${classes.content} ${isSmallerScreen && classes.contentShift}`}>
                {!isSmallerScreen && (
                    <div className="cubes-top">
                        <p>{cubesImage}</p>
                    </div>
                )}
                {!isSmallScreen && (
                    <div className="cubes-bottom">
                        <p>{cubesImage}</p>
                    </div>
                )}
                {children}
            </div>
        </div>
    );
}
Example #14
Source File: MetaMaskConnector.tsx    From metamask-snap-polkadot with Apache License 2.0 5 votes vote down vote up
MetaMaskConnector = () => {

    const [state, dispatch] = useContext(MetaMaskContext);

    useEffect( () => {
        (async () => {
            if(await isPolkadotSnapInstalled()) {
                dispatch({type: MetamaskActions.SET_INSTALLED_STATUS, payload: {isInstalled: true}});
            }
        })();
    }, [dispatch]);

    const installSnap = useCallback(async () => {
       const isInitiated = await installPolkadotSnap();
       if(!isInitiated) {
           dispatch({type: MetamaskActions.SET_INSTALLED_STATUS, payload: {isInstalled: false, message: "Please accept snap installation prompt"}})
       } else {
           dispatch({type: MetamaskActions.SET_INSTALLED_STATUS, payload: {isInstalled: true}});
       }
    }, [dispatch]);

    const handleClose = (event: React.SyntheticEvent | React.MouseEvent, reason?: string) => {
        if (reason === 'clickaway') {
          return;
        }
        dispatch({type: MetamaskActions.SET_INSTALLED_STATUS, payload: false})
      };
    
    const shouldDisplaySnackbar = (): boolean => {
      if (!state.polkadotSnap.isInstalled && state.polkadotSnap.message) return true;
      else return false;
    }

    return(
        <div>
            <Snackbar
                anchorOrigin={{
                    vertical: 'bottom',
                    horizontal: 'left',
                }}
                open={shouldDisplaySnackbar()}
                autoHideDuration={6000}
                onClose={handleClose}
                message={state.polkadotSnap.message}
                action={
                    <React.Fragment>
                      <IconButton size="small" aria-label="close" color="inherit" onClick={handleClose}>
                        <CloseIcon fontSize="small" />
                      </IconButton>
                    </React.Fragment>
                  }
            />
            <Hidden xsUp={state.hasMetaMask}>
                <Alert severity="warning">Ensure that MetaMask is installed!</Alert>
                <Box mt={"1rem"} />
            </Hidden>
            <Button
                disabled={!state.hasMetaMask}
                onClick={installSnap}
                variant="contained"
                size={"large"}
                color="primary"
            >
                Connect to MetaMask
            </Button>
        </div>
    );

}
Example #15
Source File: index.tsx    From rugenerous-frontend with MIT License 5 votes vote down vote up
function ViewBase({ children }: IViewBaseProps) {
  const classes = useStyles();

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

  const isSmallerScreen = useMediaQuery("(max-width: 960px)");
  const isSmallScreen = useMediaQuery("(max-width: 600px)");

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

  return (
    <div className="view-base-root">
      <Messages />
      <Header drawe={!isSmallerScreen} handleDrawerToggle={handleDrawerToggle} />
      <div className={classes.drawer}>
        <Hidden mdUp>
          <MobileDrawer mobileOpen={mobileOpen} handleDrawerToggle={handleDrawerToggle} />
        </Hidden>
        <Hidden smDown>
          <Drawer />
        </Hidden>
      </div>
      <div className={`${classes.content} ${isSmallerScreen && classes.contentShift}`}>
        {!isSmallerScreen && (
          <div className="cubes-top">
            <p>{cubesImage}</p>
          </div>
        )}
        {!isSmallScreen && (
          <div className="cubes-bottom">
            <p>{cubesImage}</p>
          </div>
        )}
        {children}
      </div>
    </div>
  );
}
Example #16
Source File: index.tsx    From gatsby-theme-pristine with Apache License 2.0 4 votes vote down vote up
Layout: React.FC = ({ children }) => {
  const darkMode = useDarkMode();
  const theme = darkMode.value ? darkTheme : lightTheme;
  const components = {
    h1: (props: any) => <Typography variant={"h1"} {...props} gutterBottom={true} />,
    h2: (props: any) => <Typography variant={"h2"} {...props} gutterBottom={true} />,
    h3: (props: any) => <Typography variant={"h3"} {...props} gutterBottom={true} />,
    h4: (props: any) => <Typography variant={"h4"} {...props} gutterBottom={true} />,
    h5: (props: any) => <Typography variant={"h5"} {...props} gutterBottom={true} />,
    h6: (props: any) => <Typography variant={"h6"} {...props} gutterBottom={true} />,
    Demo: (props: any) => <h1>This is a demo component</h1>,
    code: (props: any) => <CodeBlock darkMode={darkMode.value} {...props} />,
    thematicBreak: (props: any) => <Divider  {...props} />,
    a: (props: any) => <Link {...props} />,
    table: (props: any) => <Table {...props} style={{ marginBottom: "15px", ...props.style }} />,
    thead: (props: any) => <TableHead {...props} />,
    tr: (props: any) => <TableRow {...props} />,
    tableBody: (props: any) => <TableBody {...props} />,
    td: (props: any) => {
      return (
        <TableCell>
          {props.children || ""}
        </TableCell>
      );
    },
  };
  const [open, setOpen] = useState();

  const data = useStaticQuery(graphql`
    query LayoutQuery {
      site {
        siteMetadata {
          title
          description
          logoUrl
          primaryColor
          secondaryColor
          footerLinks {
            name
            link
          }
        }
      }
    }
  `);

  return (
    <MDXProvider components={components}>
      <MuiThemeProvider theme={{
        ...theme,
        palette: {
          ...theme.palette,
          primary: {
            ...theme.palette.primary,
            main: data.site.siteMetadata.primaryColor,
          },
          secondary: {
            ...theme.palette.secondary,
            main: data.site.siteMetadata.secondaryColor,
          }
        }
      }}>
        <Sidebar open={open} onClose={() => setOpen(false)} />
        <AppBar position="fixed" color="default" elevation={0}>
          <Toolbar>
            <IconButton onClick={() => setOpen(true)}>
              <MenuIcon fontSize="small" />
            </IconButton>
            <Grid container alignContent="center" alignItems="center" justify="space-between">
              <Grid item container direction="row" xs={5}>
                <Grid style={{ paddingRight: "5px" }}>
                  <img
                    alt="logo"
                    height="30"
                    style={{
                      marginTop: "6px",
                    }}
                    src={data.site.siteMetadata.logoUrl} />
                </Grid>
                <Grid style={{ marginTop: "7px" }}>
                  <GatsbyLink to="/" style={{ textDecoration: "none" }}>
                    <Typography color="textSecondary" variant="h6">
                      {data.site.siteMetadata.title}
                    </Typography>
                  </GatsbyLink>
                </Grid>
              </Grid>
              <Grid item container direction="row" xs={7} justify="flex-end" alignItems="center">
                <Hidden only="xs">
                  <Search />
                </Hidden>
                <Tooltip title={"Toggle Dark Mode"}>
                  <IconButton onClick={darkMode.toggle}>
                    {darkMode.value ? <Brightness3Icon fontSize="small" /> : <WbSunnyIcon fontSize="small" />}
                  </IconButton>
                </Tooltip>
              </Grid>
            </Grid>
          </Toolbar>
        </AppBar>
        <Container>
          <CssBaseline />
          <div style={{ padding: "30px", paddingTop: "64px" }}>
            {children}
            <Footer footerLinks={data.site.siteMetadata.footerLinks} />
          </div>
        </Container>
      </MuiThemeProvider >
    </MDXProvider >
  );
}
Example #17
Source File: AdrScene.tsx    From log4brains with Apache License 2.0 4 votes vote down vote up
export function AdrScene({ projectName, currentAdr }: AdrSceneProps) {
  const classes = useStyles();

  const mode = React.useContext(Log4brainsModeContext);
  const adrNav = React.useContext(AdrNavContext);

  const [mdContent, setMdContent] = React.useState<
    React.ReactElement | undefined
  >(undefined);

  if (!currentAdr) {
    return null; // Happens during Next.js initial build
  }

  let alert;
  if (currentAdr.status === "superseded") {
    alert = (
      <Alert severity="warning" className={classes.alert}>
        This ADR is <strong>superseded</strong>
        {currentAdr.supersededBy ? (
          <>
            {" by "}
            <Link href={buildAdrUrl(currentAdr.supersededBy)} passHref>
              <MuiLink>{currentAdr.supersededBy.title || "Untitled"}</MuiLink>
            </Link>
          </>
        ) : null}
        .
      </Alert>
    );
  } else if (currentAdr.status === "deprecated") {
    alert = (
      <Alert severity="warning" className={classes.alert}>
        This ADR is <strong>deprecated</strong>.
      </Alert>
    );
  } else if (currentAdr.status === "rejected") {
    alert = (
      <Alert severity="error" className={classes.alert}>
        This ADR was <strong>rejected</strong>.
      </Alert>
    );
  }

  return (
    <>
      <Head>
        <title>
          {currentAdr.title || "Untitled"} ADR - Architecture knowledge base of{" "}
          {projectName}
        </title>
      </Head>
      <TwoColContent
        rightColContent={<MarkdownToc content={mdContent} levelStart={2} />}
      >
        <Typography variant="h3" gutterBottom>
          {currentAdr.title || "Untitled"}
        </Typography>
        <Divider />
        <AdrHeader
          adr={currentAdr}
          className={classes.header}
          locallyEditable={mode === Log4brainsMode.preview}
        />

        {alert}

        <Markdown onCompiled={setMdContent}>
          {currentAdr.body.enhancedMdx}
        </Markdown>

        <Divider className={classes.bottomNavDivider} />

        <nav className={classes.bottomNav}>
          {adrNav.previousAdr ? (
            <Link href={buildAdrUrl(adrNav.previousAdr)} passHref>
              <Tooltip
                title={adrNav.previousAdr.title || ""}
                aria-label="previous"
              >
                <Button startIcon={<ArrowBackIcon />}>
                  <Hidden xsDown implementation="css">
                    Previous
                  </Hidden>
                </Button>
              </Tooltip>
            </Link>
          ) : (
            <div />
          )}

          <div className={classes.bottomInfo}>
            <Typography className={classes.bottomInfoText}>
              Last edited by {currentAdr.lastEditAuthor}
            </Typography>
            <Typography className={classes.bottomInfoText}>
              on {moment(currentAdr.lastEditDate).format("lll")}
            </Typography>
          </div>
          {adrNav.nextAdr ? (
            <Link href={buildAdrUrl(adrNav.nextAdr)} passHref>
              <Tooltip title={adrNav.nextAdr.title || ""} aria-label="next">
                <Button endIcon={<ArrowForwardIcon />}>
                  <Hidden xsDown implementation="css">
                    Next
                  </Hidden>
                </Button>
              </Tooltip>
            </Link>
          ) : (
            <div />
          )}
        </nav>
      </TwoColContent>
    </>
  );
}
Example #18
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>
  );
}
Example #19
Source File: index.tsx    From prism-frontend with MIT License 4 votes vote down vote up
function DateSelector({ availableDates = [], classes }: DateSelectorProps) {
  const { startDate: stateStartDate } = useSelector(dateRangeSelector);
  const { t, i18n } = useSafeTranslation();
  const [dateRange, setDateRange] = useState<DateRangeType[]>([
    {
      value: 0,
      label: '',
      month: '',
      isFirstDay: false,
    },
  ]);

  const [timelinePosition, setTimelinePosition] = useState<Point>({
    x: 0,
    y: 0,
  });
  const [pointerPosition, setPointerPosition] = useState<Point>({ x: 0, y: 0 });

  const dateRef = useRef(availableDates);

  const timeLine = useRef(null);
  const timeLineWidth = get(timeLine.current, 'offsetWidth', 0);

  const { updateHistory } = useUrlHistory();

  // Move the slider automatically so that the pointer always visible
  useEffect(() => {
    let x = 0;
    if (
      pointerPosition.x >=
      dateRange.length * TIMELINE_ITEM_WIDTH - timeLineWidth
    ) {
      // eslint-disable-next-line fp/no-mutation
      x = timeLineWidth - dateRange.length * TIMELINE_ITEM_WIDTH;
    } else if (pointerPosition.x > timeLineWidth) {
      // eslint-disable-next-line fp/no-mutation
      x = -pointerPosition.x + timeLineWidth / 2;
    }
    if (
      -timelinePosition.x > pointerPosition.x ||
      -timelinePosition.x + timeLineWidth < pointerPosition.x
    ) {
      setTimelinePosition({ x, y: 0 });
    }
  }, [dateRange.length, pointerPosition, timeLineWidth, timelinePosition.x]);

  // Create timeline range and set pointer position
  useEffect(() => {
    const locale = t('date_locale') ? t('date_locale') : 'en';
    const range = Array.from(
      moment()
        .range(
          moment(stateStartDate).startOf('year'),
          moment(stateStartDate).endOf('year'),
        )
        .by('days'),
    ).map(date => {
      date.locale(locale);
      return {
        value: date.valueOf(),
        label: date.format(MONTH_FIRST_DATE_FORMAT),
        month: date.format(MONTH_ONLY_DATE_FORMAT),
        isFirstDay: date.date() === date.startOf('month').date(),
      };
    });
    setDateRange(range);
    const dateIndex = findIndex(range, date => {
      return (
        date.label ===
        moment(stateStartDate).locale(locale).format(MONTH_FIRST_DATE_FORMAT)
      );
    });
    setPointerPosition({
      x: dateIndex * TIMELINE_ITEM_WIDTH,
      y: 0,
    });
  }, [stateStartDate, t, i18n]);

  function updateStartDate(date: Date) {
    const time = date.getTime();
    // This updates state because a useEffect in MapView updates the redux state
    // TODO this is convoluted coupling, we should update state here if feasible.
    updateHistory('date', moment(time).format(DEFAULT_DATE_FORMAT));
  }

  function setDatePosition(date: number | undefined, increment: number) {
    const dates = availableDates.map(d => {
      return d + USER_DATE_OFFSET;
    });
    const selectedIndex = findDateIndex(dates, date);
    if (dates[selectedIndex + increment]) {
      updateStartDate(new Date(dates[selectedIndex + increment]));
    }
  }

  // move pointer to closest date when change map layer
  useEffect(() => {
    if (!isEqual(dateRef.current, availableDates)) {
      setDatePosition(stateStartDate, 0);
      dateRef.current = availableDates;
    }
  });

  function incrementDate() {
    setDatePosition(stateStartDate, 1);
  }

  function decrementDate() {
    setDatePosition(stateStartDate, -1);
  }

  // Click on available date to move the pointer
  const clickDate = (index: number) => {
    const dates = availableDates.map(date => {
      return date + USER_DATE_OFFSET;
    });
    const selectedIndex = findDateIndex(dates, dateRange[index].value);
    if (selectedIndex >= 0 && dates[selectedIndex] !== stateStartDate) {
      setPointerPosition({ x: index * TIMELINE_ITEM_WIDTH, y: 0 });
      updateStartDate(new Date(dates[selectedIndex]));
    }
  };

  // Set timeline position after being dragged
  const onTimelineStop = (e: DraggableEvent, position: Point) => {
    setTimelinePosition(position);
  };

  // Set pointer position after being dragged
  const onPointerStop = (e: DraggableEvent, position: Point) => {
    const exactX = Math.round(position.x / TIMELINE_ITEM_WIDTH);
    if (exactX >= dateRange.length) {
      return;
    }
    const dates = availableDates.map(date => {
      return date + USER_DATE_OFFSET;
    });
    const selectedIndex = findDateIndex(dates, dateRange[exactX].value);
    if (selectedIndex >= 0 && dates[selectedIndex] !== stateStartDate) {
      setPointerPosition({ x: exactX * TIMELINE_ITEM_WIDTH, y: position.y });
      updateStartDate(new Date(dates[selectedIndex]));
    }
  };

  return (
    <div className={classes.container}>
      <Grid
        container
        alignItems="center"
        justify="center"
        className={classes.datePickerContainer}
      >
        <Grid item xs={12} sm={1} className={classes.datePickerGrid}>
          <Hidden smUp>
            <Button onClick={decrementDate}>
              <ChevronLeft />
            </Button>
          </Hidden>

          <DatePicker
            locale={t('date_locale')}
            dateFormat="PP"
            className={classes.datePickerInput}
            selected={moment(stateStartDate).toDate()}
            onChange={updateStartDate}
            maxDate={new Date()}
            todayButton={t('Today')}
            peekNextMonth
            showMonthDropdown
            showYearDropdown
            dropdownMode="select"
            customInput={<Input />}
            includeDates={availableDates.map(
              d => new Date(d + USER_DATE_OFFSET),
            )}
          />

          <Hidden smUp>
            <Button onClick={incrementDate}>
              <ChevronRight />
            </Button>
          </Hidden>
        </Grid>

        <Grid item xs={12} sm className={classes.slider}>
          <Hidden xsDown>
            <Button onClick={decrementDate}>
              <ChevronLeft />
            </Button>
          </Hidden>
          <Grid className={classes.dateContainer} ref={timeLine}>
            <Draggable
              axis="x"
              handle={`#${TIMELINE_ID}`}
              bounds={{
                top: 0,
                bottom: 0,
                right: 0,
                left: timeLineWidth - dateRange.length * TIMELINE_ITEM_WIDTH,
              }}
              position={timelinePosition}
              onStop={onTimelineStop}
            >
              <div className={classes.timeline} id={TIMELINE_ID}>
                <Grid
                  container
                  alignItems="stretch"
                  className={classes.dateLabelContainer}
                >
                  <TimelineItems
                    dateRange={dateRange}
                    availableDates={availableDates}
                    clickDate={clickDate}
                  />
                </Grid>
                <Draggable
                  axis="x"
                  handle={`#${POINTER_ID}`}
                  bounds={{
                    top: 0,
                    bottom: 0,
                    left: 0,
                    right: dateRange.length * TIMELINE_ITEM_WIDTH,
                  }}
                  grid={[TIMELINE_ITEM_WIDTH, 1]}
                  position={pointerPosition}
                  onStart={(e: DraggableEvent) => e.stopPropagation()}
                  onStop={onPointerStop}
                >
                  <div className={classes.pointer} id={POINTER_ID}>
                    <FontAwesomeIcon
                      icon={faCaretUp}
                      style={{ fontSize: 40 }}
                      color="white"
                    />
                  </div>
                </Draggable>
              </div>
            </Draggable>
          </Grid>
          <Hidden xsDown>
            <Button onClick={incrementDate}>
              <ChevronRight />
            </Button>
          </Hidden>
        </Grid>
      </Grid>
    </div>
  );
}
Example #20
Source File: index.tsx    From prism-frontend with MIT License 4 votes vote down vote up
function Download({ classes }: DownloadProps) {
  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
  const [open, setOpen] = useState(false);
  const selectedMap = useSelector(mapSelector);
  const previewRef = useRef<HTMLCanvasElement>(null);

  const { t } = useSafeTranslation();

  const handleClick = (event: React.MouseEvent<HTMLElement>) => {
    setAnchorEl(event.currentTarget);
  };

  const handleClose = () => {
    setAnchorEl(null);
  };

  const openModal = () => {
    if (selectedMap) {
      const activeLayers = selectedMap.getCanvas();
      const canvas = previewRef.current;
      if (canvas) {
        canvas.setAttribute('width', activeLayers.width.toString());
        canvas.setAttribute('height', activeLayers.height.toString());
        const context = canvas.getContext('2d');
        if (context) {
          context.drawImage(activeLayers, 0, 0);
        }
      }
      setOpen(true);
    }
    handleClose();
  };

  const download = (format: string) => {
    const ext = format === 'pdf' ? 'png' : format;
    const canvas = previewRef.current;
    if (canvas) {
      const file = canvas.toDataURL(`image/${ext}`);
      if (format === 'pdf') {
        // eslint-disable-next-line new-cap
        const pdf = new jsPDF({
          orientation: 'landscape',
        });
        const imgProps = pdf.getImageProperties(file);
        const pdfWidth = pdf.internal.pageSize.getWidth();
        const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
        pdf.addImage(file, 'PNG', 0, 0, pdfWidth, pdfHeight);
        pdf.save('map.pdf');
      } else {
        const link = document.createElement('a');
        link.setAttribute('href', file);
        link.setAttribute('download', `map.${ext}`);
        link.click();
      }
      setOpen(false);
      handleClose();
    }
  };

  return (
    <Grid item>
      <Button variant="contained" color="primary" onClick={handleClick}>
        <CloudDownload fontSize="small" />
        <Hidden smDown>
          <Typography className={classes.label} variant="body2">
            {t('Export')}
          </Typography>
        </Hidden>
        <ArrowDropDown fontSize="small" />
      </Button>
      <ExportMenu
        id="export-menu"
        anchorEl={anchorEl}
        keepMounted
        open={Boolean(anchorEl)}
        onClose={handleClose}
      >
        <ExportMenuItem onClick={openModal}>
          <ListItemIcon>
            <Image fontSize="small" style={{ color: 'white' }} />
          </ListItemIcon>
          <ListItemText primary={t('IMAGE')} />
        </ExportMenuItem>
      </ExportMenu>
      <Dialog
        maxWidth="xl"
        open={open}
        keepMounted
        onClose={() => setOpen(false)}
        aria-labelledby="dialog-preview"
      >
        <DialogTitle className={classes.title} id="dialog-preview">
          {t('Map Preview')}
        </DialogTitle>
        <DialogContent>
          <canvas ref={previewRef} />
        </DialogContent>
        <DialogActions>
          <Button onClick={() => setOpen(false)} color="primary">
            {t('Cancel')}
          </Button>
          <Button
            variant="contained"
            onClick={() => download('png')}
            color="primary"
          >
            {t('Download PNG')}
          </Button>
          <Button
            variant="contained"
            onClick={() => download('jpeg')}
            color="primary"
          >
            {t('Download JPEG')}
          </Button>
          <Button
            variant="contained"
            onClick={() => download('pdf')}
            color="primary"
          >
            {t('Download PDF')}
          </Button>
        </DialogActions>
      </Dialog>
    </Grid>
  );
}
Example #21
Source File: SideDrawer.tsx    From glific-frontend with GNU Affero General Public License v3.0 4 votes vote down vote up
SideDrawer: React.SFC<SideDrawerProps> = () => {
  const location = useLocation();
  const { drawerOpen, setDrawerOpen } = useContext(SideDrawerContext);

  const classes = useStyles();
  const [mobileOpen, setMobileOpen] = useState(false);
  const { t } = useTranslation();

  const { provider } = useContext(ProviderContext);

  const drawer = (
    <div>
      <Toolbar className={classes.anotherToolBar}>
        {drawerOpen ? (
          <div className={classes.outerBox}>
            <ThemeProvider theme={themeUI}>
              <Typography variant="h6" className={classes.title}>
                <img src={GlificLogo} className={styles.GlificLogo} alt="Glific" />
              </Typography>
            </ThemeProvider>
            <IconButton
              className={classes.iconButton}
              onClick={() => setDrawerOpen(false)}
              data-testid="drawer-button"
            >
              <MenuIcon />
            </IconButton>
          </div>
        ) : (
          <IconButton
            color="inherit"
            aria-label="open drawer"
            data-testid="drawer-button-closed"
            style={{ margin: 'auto' }}
            onClick={() => setDrawerOpen(true)}
          >
            <MenuIcon />
          </IconButton>
        )}
      </Toolbar>
      <SideMenus opened={drawerOpen} />
    </div>
  );

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

  let settingMenu;
  const userRolePermissions = getUserRolePermissions();
  if (userRolePermissions.accessSettings) {
    settingMenu = (
      <div>
        <Tooltip title={t('Settings')} placement="top">
          <Link to="/settings">
            <IconButton data-testid="settingsMenu">
              <img
                src={location.pathname === '/settings' ? ActiveIcon : InactiveIcon}
                className={styles.UserIcon}
                alt="settings"
              />
            </IconButton>
          </Link>
        </Tooltip>
      </div>
    );
  }

  // set the appropriate classes to display bottom menus correctly
  const bottonMenuClasses = [classes.BottomMenus];
  if (provider === GUPSHUP_ENTERPRISE_SHORTCODE) {
    bottonMenuClasses.unshift(classes.BottomMenusWithoutWallet);
  }

  if (!drawerOpen) {
    bottonMenuClasses.unshift(classes.BottomMenusVertical);
  }

  return (
    <nav
      className={clsx({
        [classes.drawer]: drawerOpen,
        [classes.navClose]: !drawerOpen,
      })}
      aria-label="navigation menus"
      data-testid="navbar"
    >
      <Hidden smUp implementation="css">
        <Drawer
          container={container}
          variant="temporary"
          anchor={themeUI.direction === 'rtl' ? 'right' : 'left'}
          open={mobileOpen}
          onClose={() => {
            setMobileOpen(!mobileOpen);
          }}
          classes={{
            paper: classes.drawerPaper,
          }}
          ModalProps={{
            keepMounted: true,
          }}
        >
          {drawer}
        </Drawer>
      </Hidden>
      <Drawer
        className={clsx(classes.drawer, {
          [classes.drawerOpen]: drawerOpen,
          [classes.drawerClose]: !drawerOpen,
        })}
        classes={{
          paper: clsx({
            [classes.drawerOpen]: drawerOpen,
            [classes.drawerClose]: !drawerOpen,
          }),
        }}
        variant="permanent"
      >
        <div className={bottonMenuClasses.join(' ')}>
          {settingMenu}
          <div>
            <Menu
              menus={getStaffManagementMenus()}
              eventType="MouseEnter"
              placement={drawerOpen ? 'top' : 'right-end'}
            >
              <IconButton data-testid="staffManagementMenu">
                <img
                  src={
                    [
                      '/collection',
                      '/staff-management',
                      '/blocked-contacts',
                      '/consulting-hours',
                    ].includes(location.pathname)
                      ? ActiveStaffIcon
                      : InactiveStaffIcon
                  }
                  className={styles.StaffIcon}
                  alt="staff icon"
                />
              </IconButton>
            </Menu>
          </div>
          <div>
            <Menu
              menus={getUserAccountMenus()}
              eventType="MouseEnter"
              placement={drawerOpen ? 'top' : 'right-end'}
            >
              <IconButton data-testid="profileMenu">
                <img
                  src={location.pathname === '/user-profile' ? ActiveUserIcon : InactiveUserIcon}
                  className={styles.UserIcon}
                  alt="user icon"
                />
              </IconButton>
            </Menu>
          </div>
        </div>
        {drawer}
        <WalletBalance fullOpen={drawerOpen} />
      </Drawer>
    </nav>
  );
}
Example #22
Source File: index.tsx    From aqualink-app with MIT License 4 votes vote down vote up
PointSelector = ({
  siteId,
  pointOptions,
  point,
  pointId,
  editSurveyPointNameDraft,
  isSiteAdmin,
  editSurveyPointNameLoading,
  onChangeSurveyPointName,
  handlePointChange,
  enableeditSurveyPointName,
  disableeditSurveyPointName,
  submitSurveyPointNameUpdate,
  onDeleteButtonClick,
}: PointSelectorProps) => {
  const classes = useStyles();
  const [editDialogOpen, setEditDialogOpen] = useState(false);
  const [editSurveyPoint, seteditSurveyPoint] = useState<SurveyPoints>();
  const errored =
    !editSurveyPointNameDraft ||
    editSurveyPointNameDraft.length > maxLengths.SURVEY_POINT_NAME;

  const onEditDialogClose = () => {
    disableeditSurveyPointName();
    seteditSurveyPoint(undefined);
    setEditDialogOpen(false);
  };

  const onEditSurveyPointSubmit = () => {
    if (editSurveyPoint) {
      submitSurveyPointNameUpdate(editSurveyPoint.id);
    }
  };

  const getHelperText = () => {
    switch (true) {
      case !editSurveyPointNameDraft:
        return "Cannot be empty";
      case editSurveyPointNameDraft &&
        editSurveyPointNameDraft.length > maxLengths.SURVEY_POINT_NAME:
        return `Must not exceed ${maxLengths.SURVEY_POINT_NAME} characters`;
      default:
        return "";
    }
  };

  useEffect(() => {
    if (!editSurveyPointNameLoading) {
      seteditSurveyPoint(undefined);
      setEditDialogOpen(false);
    }
  }, [editSurveyPointNameLoading]);

  const editDialogActions: Action[] = [
    {
      size: "small",
      variant: "contained",
      color: "secondary",
      text: "Close",
      action: onEditDialogClose,
    },
    {
      size: "small",
      variant: "contained",
      color: "primary",
      text: editSurveyPointNameLoading ? "Updating..." : "Save",
      action: onEditSurveyPointSubmit,
      disabled: editSurveyPointNameLoading || (editSurveyPoint && errored),
    },
  ];

  return (
    <>
      {editSurveyPoint && (
        <EditDialog
          actions={editDialogActions}
          open={editDialogOpen}
          header={editSurveyPoint.name || ""}
          onClose={onEditDialogClose}
          content={
            <TextField
              variant="outlined"
              autoFocus
              className={classes.editSurveyPointTextField}
              fullWidth
              value={editSurveyPointNameDraft}
              onChange={onChangeSurveyPointName}
              error={errored}
              helperText={getHelperText()}
            />
          }
        />
      )}
      <Grid container alignItems="center" item md={12} lg={5} spacing={1}>
        <Grid item>
          <Typography variant="h6" className={classes.subTitle}>
            Survey Point:
          </Typography>
        </Grid>
        <Grid item className={classes.selectorWrapper}>
          <Grid container alignItems="center">
            <Grid item>
              <Select
                className={classes.selector}
                labelId="survey-point"
                id="survey-point"
                name="survey-point"
                value={
                  pointOptions.some((item) => item.name === point)
                    ? point
                    : "All"
                }
                onChange={handlePointChange}
                onClose={() => disableeditSurveyPointName()}
                renderValue={(selected) => selected as string}
              >
                <MenuItem value="All">
                  <Typography className={classes.menuItem} variant="h6">
                    All
                  </Typography>
                </MenuItem>
                {pointOptions.map(
                  (item) =>
                    item.name !== null && (
                      <MenuItem
                        className={classes.menuItem}
                        value={item.name}
                        key={item.id}
                      >
                        <Grid
                          container
                          alignItems="center"
                          justify="space-between"
                          spacing={1}
                        >
                          <Grid className={classes.itemName} item>
                            {item.name}
                          </Grid>
                          <Grid item>
                            <Grid container item spacing={1}>
                              <Grid item>
                                <CustomLink
                                  to={`/sites/${siteId}/points/${item.id}`}
                                  isIcon
                                  tooltipTitle="View survey point"
                                />
                              </Grid>
                              {isSiteAdmin && (
                                <>
                                  <Grid item>
                                    <Tooltip
                                      title="Edit survey point name"
                                      placement="top"
                                      arrow
                                    >
                                      <IconButton
                                        className={classes.menuButton}
                                        onClick={(event) => {
                                          enableeditSurveyPointName(item.id);
                                          setEditDialogOpen(true);
                                          seteditSurveyPoint(item);
                                          event.stopPropagation();
                                        }}
                                      >
                                        <Create color="primary" />
                                      </IconButton>
                                    </Tooltip>
                                  </Grid>
                                  <Grid item>
                                    <Tooltip
                                      title="Delete survey point"
                                      placement="top"
                                      arrow
                                    >
                                      <IconButton
                                        className={classes.menuButton}
                                        onClick={(event) => {
                                          onDeleteButtonClick(item.id);
                                          event.stopPropagation();
                                        }}
                                      >
                                        <DeleteOutline color="primary" />
                                      </IconButton>
                                    </Tooltip>
                                  </Grid>
                                </>
                              )}
                            </Grid>
                          </Grid>
                        </Grid>
                      </MenuItem>
                    )
                )}
              </Select>
            </Grid>
            {pointId !== -1 && (
              <Grid item>
                <Hidden smUp>
                  <CustomLink
                    to={`/sites/${siteId}/points/${pointId}`}
                    isIcon
                    tooltipTitle="View survey point"
                  />
                </Hidden>
                <Hidden xsDown>
                  <Button
                    variant="outlined"
                    color="primary"
                    size="small"
                    component={Link}
                    to={`/sites/${siteId}/points/${pointId}`}
                  >
                    View Survey Point
                  </Button>
                </Hidden>
              </Grid>
            )}
          </Grid>
        </Grid>
      </Grid>
    </>
  );
}
Example #23
Source File: index.tsx    From prism-frontend with MIT License 4 votes vote down vote up
function Legends({ classes, layers, extent }: LegendsProps) {
  const [open, setOpen] = useState(true);
  const isAnalysisLayerActive = useSelector(isAnalysisLayerActiveSelector);
  const analysisResult = useSelector(analysisResultSelector);
  const features = analysisResult?.featureCollection.features;
  const hasData = features ? features.length > 0 : false;

  const { t } = useSafeTranslation();

  const handleAnalysisDownload = (e: React.ChangeEvent<{}>): void => {
    e.preventDefault();
    downloadToFile(
      {
        content: JSON.stringify(features),
        isUrl: false,
      },
      analysisResult ? analysisResult.getTitle() : 'prism_extract',
      'application/json',
    );
  };

  const legendItems = [
    ...layers.map(layer => {
      if (!layer.legend || !layer.legendText) {
        // this layer doesn't have a legend (likely boundary), so lets ignore.
        return null;
      }

      // If legend array is empty, we fetch from remote server the legend as GetLegendGraphic request.
      const legendUrl =
        layer.type === 'wms' && layer.legend.length === 0
          ? formatWMSLegendUrl(layer.baseUrl, layer.serverLayerName)
          : undefined;

      const exposure = GetExposureFromLayer(layer);

      return (
        <LegendItem
          classes={classes}
          key={layer.id}
          id={layer.id}
          title={layer.title ? t(layer.title) : undefined}
          legend={layer.legend}
          legendUrl={legendUrl}
          type={layer.type}
          opacity={layer.opacity}
          exposure={exposure}
          extent={extent}
        >
          {t(layer.legendText)}
        </LegendItem>
      );
    }),
    // add analysis legend item if layer is active and analysis result exists
    ...(isAnalysisLayerActive && hasData
      ? [
          <LegendItem
            key={analysisResult?.key}
            legend={analysisResult?.legend}
            title={analysisResult?.getTitle(t)}
            classes={classes}
            opacity={0.5} // TODO: initial opacity value
          >
            {analysisResult instanceof BaselineLayerResult && (
              <LegendImpactResult result={analysisResult} />
            )}
            <Divider />
            <Grid item>
              <Button
                variant="contained"
                color="primary"
                size="small"
                onClick={e => handleAnalysisDownload(e)}
                fullWidth
              >
                {t('Download')}
              </Button>
            </Grid>
          </LegendItem>,
        ]
      : []),
  ];

  return (
    <Grid item className={classes.container}>
      <Button
        variant="contained"
        color="primary"
        onClick={() => setOpen(!open)}
      >
        {open ? (
          <VisibilityOff fontSize="small" />
        ) : (
          <Visibility fontSize="small" />
        )}
        <Hidden smDown>
          <Typography className={classes.label} variant="body2">
            {t('Legend')}
          </Typography>
        </Hidden>
      </Button>
      {open && <List className={classes.list}>{legendItems}</List>}
    </Grid>
  );
}
Example #24
Source File: Site.tsx    From clearflask with Apache License 2.0 4 votes vote down vote up
render() {
    const isSingleCustomer = detectEnv() === Environment.PRODUCTION_SELF_HOST;
    if (isSingleCustomer) {
      return (
        <RedirectIso to='/login' />
      );
    }
    const menuItemsLeft: Array<MenuButton | MenuDropdown> = [
      {
        type: 'dropdown', title: this.props.t('product'), items: [
          { type: 'button', link: '/product/demo', title: this.props.t('demo'), icon: DemoIcon },
          { type: 'divider' },
          { type: 'button', link: '/product/ask', title: this.props.t('collect-feedback'), icon: CollectIcon },
          { type: 'button', link: '/product/analyze', title: this.props.t('manage-and-analyze'), icon: AnalyzeIcon },
          { type: 'button', link: '/product/act', title: this.props.t('respond-and-inform'), icon: ActIcon },
          { type: 'divider' },
          { type: 'button', link: '/product/integrations', title: this.props.t('integrations') },
          { type: 'button', link: '/product/scale-with-us', title: this.props.t('scale-with-us') },
          { type: 'divider' },
          { type: 'button', link: '/product/compare', title: this.props.t('competing-products'), icon: CompareIcon },
        ]
      },
      { type: 'button', link: '/pricing', title: this.props.t('pricing') },
      // TODO Needs a complete SEO revamp
      // {
      //   type: 'dropdown', title: 'Solutions', items: [
      //     { type: 'button', link: '/solutions/feature-request-tracking', title: 'Feature Request Tracking', icon: RequestTrackingIcon },
      //     { type: 'button', link: '/solutions/product-roadmap', title: 'Product Roadmap', icon: RoadmapIcon, iconClassName: this.props.classes.roadmapIcon },
      //     // TODO Re-enable once Crowd-funding gets a revamp
      //     // import CrowdfundingIcon from '@material-ui/icons/MonetizationOn';
      //     // { type: 'button', link: '/solutions/feature-crowdfunding', title: 'Feature Crowdfunding', icon: CrowdfundingIcon },
      //     { type: 'divider' },
      //     { type: 'button', link: '/solutions/idea-management', title: 'Idea Management' },
      //     // TODO Re-enable once Crowd-funding gets a revamp
      //     // { type: 'button', link: '/solutions/content-creator-forum', title: 'Content Creator Forum' },
      //     { type: 'button', link: '/solutions/internal-feedback', title: 'Internal Feedback' },
      //   ]
      // },
      {
        type: 'dropdown', title: this.props.t('resources'), items: [
          { type: 'button', link: `/open-source`, title: this.props.t('open-source'), icon: OpenSourceIcon },
          { type: 'button', link: '/roadmap', title: this.props.t('our-roadmap'), icon: RoadmapIcon, iconClassName: this.props.classes.roadmapIcon },
          { type: 'button', link: '/feedback', title: this.props.t('feedback'), icon: FeedbackIcon },
          { type: 'button', link: '/blog', title: this.props.t('blog'), icon: BlogIcon },
          // { type: 'button', link: urlAddCfJwt(`${windowIso.location.protocol}//product.${windowIso.location.host}/blog`, this.props.account), linkIsExternal: true, title: 'Blog', icon: BlogIcon },
          { type: 'divider' },
          { type: 'button', link: urlAddCfJwt(`${windowIso.location.protocol}//product.${windowIso.location.host}/docs`, this.props.account), linkIsExternal: true, title: 'Docs', icon: DocsIcon },
          { type: 'button', link: `${windowIso.location.protocol}//${windowIso.location.host}/api`, linkIsExternal: true, title: 'API', icon: CodeIcon },
          { type: 'button', link: 'https://stats.uptimerobot.com/jM01vFY31w/', linkIsNewWindowNoFollow: true, title: 'Status', icon: StatusIcon },
        ]
      },
    ];
    const menuItemsRight: Array<MenuButton> = [
      (!!this.props.account
        ? { type: 'button', link: '/dashboard', title: this.props.t('dashboard') }
        : { type: 'button', link: '/login', title: this.props.t('log-in') }),
      { type: 'button', link: '/signup', title: this.props.t('get-started'), primary: true, },
    ];
    const bottomNavigation: Array<MenuButton | MenuDropdown> = [
      ...menuItemsLeft,
      {
        type: 'dropdown', title: `© ${this.props.t('smotana')}`, items: [
          { type: 'button', link: 'https://smotana.com', linkIsExternal: true, title: 'Smotana.com' },
          { type: 'button', link: '/contact', title: this.props.t('contact') },
          { type: 'divider' },
          { type: 'button', link: '/signup', title: this.props.t('sign-up') },
          { type: 'button', link: '/dashboard', title: this.props.t('dashboard') },
          { type: 'divider' },
          { type: 'button', link: '/privacy-policy', title: this.props.t('privacy-policy') },
          { type: 'button', link: '/terms-of-service', title: this.props.t('terms-of-service') },
        ]
      }
    ];
    return (
      <div className={this.props.classes.growAndFlex}>
        <ClearFlaskEmbedHoverFeedback path='embed/feedback' Icon={FeedbackIcon} />
        <AppBar position='fixed' color='inherit' elevation={0} variant='elevation' className={this.props.classes.appBar}>
          <Container maxWidth='md' disableGutters>
            <Toolbar className={this.props.classes.toolbar}>
              <Hidden mdUp implementation='css'>
                <IconButton
                  aria-label='Menu'
                  onClick={() => this.setState({ menuOpen: true })}
                >
                  <MenuIcon />
                </IconButton>

                <Drawer
                  variant='temporary'
                  open={this.state.menuOpen}
                  onClose={() => this.setState({ menuOpen: false })}
                  ModalProps={{
                    keepMounted: true,
                  }}
                  classes={{ paper: this.props.classes.menuItemsDrawer }}
                >
                  <MenuItems
                    items={[
                      { type: 'header', title: 'ClearFlask' },
                      ...menuItemsRight,
                      ...menuItemsLeft]}
                    insideDrawer
                    onClick={() => this.setState({ menuOpen: false })}
                  />
                </Drawer>
              </Hidden>
              <Link to='/' className={this.props.classes.logoLink}>
                <Logo />
              </Link>
              <Hidden smDown implementation='css'>
                <div className={this.props.classes.menuItemsContainer}>
                  <MenuItems
                    items={menuItemsLeft}
                  />

                </div>
              </Hidden>
              <div className={this.props.classes.grow} />
              <Hidden smDown implementation='css'>
                <div className={this.props.classes.menuItemsContainer}>
                  <MenuItems
                    items={menuItemsRight}
                  />
                </div>
              </Hidden>
              <LanguageSelect />
            </Toolbar>
          </Container>
        </AppBar>
        <div className={this.props.classes.appBarSpacer} />
        <div className={classNames(this.props.classes.growAndFlex, this.props.classes.page)}>
          <MuiAnimatedSwitch>
            <Route path='/contact'>
              <SetTitle title={this.props.t('contact')} />
              <ContactPage />
            </Route>
            <Route exact path='/sso-demo'>
              <SetTitle title='Single Sign-On' />
              <DemoOnboardingLogin type='sso' />
            </Route>
            <Route path='/oauth-demo'>
              <SetTitle title='OAuth' />
              <DemoOnboardingLogin type='oauth' />
            </Route>
            <Route exact path='/terms-of-service'>
              <SetTitle title={this.props.t('terms-of-service')} />
              <LegalPage type='terms' />
            </Route>
            <Route exact path='/(tos|terms)'>
              <RedirectIso to='/terms-of-service' />
            </Route>
            <Route exact path='/privacy-policy'>
              <SetTitle title={this.props.t('terms-of-service')} />
              <LegalPage type='privacy' />
            </Route>
            <Route exact path='/(privacy|policy)'>
              <RedirectIso to='/privacy-policy' />
            </Route>

            <Route exact path='/'>
              <SetTitle />
              <Landing />
            </Route>

            <Route exact path='/product/ask'>
              <SetTitle title={this.props.t('ask-your-users')} />
              <LandingCollectFeedback />
            </Route>
            <Route exact path='/product/analyze'>
              <SetTitle title={this.props.t('analyze-feedback')} />
              <LandingPrioritization />
            </Route>
            <Route exact path='/product/act'>
              <SetTitle title={this.props.t('take-action')} />
              <LandingEngagement />
            </Route>
            <Route exact path='/product/integrations'>
              <SetTitle title={this.props.t('integrations')} />
              <LandingIntegrations />
            </Route>
            <Route exact path='/product/compare'>
              <SetTitle title='30+ Customer Feedback Tools comparison' />
              <LandingCompare />
            </Route>
            <Route exact path='/product/scale-with-us'>
              <SetTitle title='Scale with us' />
              <LandingGrowWithUs />
            </Route>
            <Route exact path='/product/demo'>
              <SetTitle title={this.props.t('demo')} />
              <LandingDemo />
            </Route>

            <Route exact path='/solutions/feature-request-tracking'>
              <SetTitle title={this.props.t('feature-request-tracking')} />
              <LandingFeatureRequestTracking />
            </Route>
            <Route exact path='/solutions/product-roadmap'>
              <SetTitle title={this.props.t('product-roadmap')} />
              <LandingPublicRoadmap />
            </Route>
            <Route exact path='/solutions/feature-crowdfunding'>
              <SetTitle title='Feature Crowdfunding' />
              <LandingCrowdFunding />
            </Route>
            <Route exact path='/solutions/idea-management'>
              <SetTitle title='Idea Management' />
              <LandingIdeaManagement />
            </Route>
            <Route exact path='/solutions/content-creator-forum'>
              <SetTitle title='Content Creator Forum' />
              <LandingContentCreator />
            </Route>
            <Route exact path='/solutions/internal-feedback'>
              <SetTitle title='Internal Feedback' />
              <LandingInternalFeedback />
            </Route>

            <Route exact path='/open-source'>
              <SetTitle title={this.props.t('open-source')} />
              <LandingOpenSource />
            </Route>
            <Route exact path='/pricing'>
              <SetTitle title={this.props.t('pricing')} />
              <PricingPage />
            </Route>

            {!isProd() && (
              <Route exact path='/promo'>
                <SetTitle title='Promo' />
                <LandingPromo />
              </Route>
            )}

            <Route path='/(post|user|blog|feedback|roadmap|changelog|announcements|docs|account|issue|account|sso|oauth)'>
              <App slug={`product.${windowIso.parentDomain}`} settings={{ forceEmbed: true }} />
            </Route>
            <Route path='/e'>
              <LandingEmbedFeedbackPage browserPathPrefix='/e' embed />
            </Route>

            <RouteWithStatus httpCode={404} >
              <SetTitle title={this.props.t('page-not-found')} />
              <ErrorPage pageNotFound />
            </RouteWithStatus>
          </MuiAnimatedSwitch>
        </div>
        <div className={this.props.classes.bottomBar}>
          <Container maxWidth='md' disableGutters>
            <Grid container justify='center' alignContent='center' spacing={6}>
              {bottomNavigation.map((item, index) => {
                if (item.type === 'dropdown') {
                  return (
                    <Grid key={index} item xs={6} sm={3} md={3} lg={2}>
                      <div key='header' className={this.props.classes.bottomHeader}>{item.title}</div>
                      {item.items.map((subItem, subIndex) => {
                        switch (subItem.type) {
                          case 'header':
                            return (
                              <div key={subIndex} className={this.props.classes.bottomHeader}>{subItem.title}</div>
                            );
                          case 'button':
                            if (subItem['linkIsExternal'] !== undefined) {
                              return (<MuiLink key={subIndex} href={subItem['link']} className={this.props.classes.bottomItem} underline='none'>{subItem.title}</MuiLink>);
                            } else if (subItem['linkIsNewWindowNoFollow'] !== undefined) {
                              return (<MuiLink key={subIndex} href={subItem['link']} target='_blank' rel='noopener nofollow' className={this.props.classes.bottomItem} underline='none'>{subItem.title}</MuiLink>);
                            } else if (subItem['link'] !== undefined) {
                              return (<NavLink key={subIndex} to={subItem['link']} className={this.props.classes.bottomItem}>{subItem.title}</NavLink>);
                            } else {
                              return (<MuiLink key={subIndex} onClick={subItem['onClick']} className={this.props.classes.bottomItem} underline='none'>{subItem.title}</MuiLink>);
                            }
                          case 'divider':
                            return (
                              <div className={this.props.classes.bottomNavigationDivider} />
                            );
                          default:
                            return null;
                        }
                      })}
                    </Grid>
                  );
                }
                return null;
              })}
            </Grid>
          </Container>
        </div>
      </div >
    );
  }
Example #25
Source File: index.tsx    From prism-frontend with MIT License 4 votes vote down vote up
function NavBar({ classes }: NavBarProps) {
  const { t } = useSafeTranslation();

  const rightSideLinks = [
    {
      title: t('about'),
      icon: faInfoCircle,
      href: 'https://innovation.wfp.org/project/prism',
    },
    {
      title: 'GitHub',
      icon: faGithub,
      href: 'https://github.com/oviohub/prism-frontend',
    },
  ];

  const [openMobileMenu, setOpenMobileMenu] = useState(false);
  const menu = menuList.map(({ title, ...category }) => (
    <MenuItem key={title} title={title} {...category} />
  ));

  // menu for mobile, 1 active accordion at a time so I put the state in here
  const [expanded, setExpanded] = useState('');
  const selectAccordion = (title: string) => {
    setExpanded(title);
  };
  const menuMobile = menuList.map(({ title, ...category }) => (
    <MenuItemMobile
      expanded={expanded}
      selectAccordion={selectAccordion}
      key={title}
      title={title}
      {...category}
    />
  ));

  const buttons = rightSideLinks.map(({ title, icon, href }) => (
    <Grid item key={title}>
      <Typography
        variant="body2"
        component="a"
        target="_blank"
        href={href}
        onClick={() => setOpenMobileMenu(false)}
      >
        <FontAwesomeIcon icon={icon} /> {title}
      </Typography>
    </Grid>
  ));

  return (
    <AppBar position="static" className={classes.appBar}>
      <Toolbar variant="dense">
        <Grid container>
          <Grid item xs={3} className={classes.logoContainer}>
            <Typography
              variant="h6"
              className={classes.logo}
              component={Link}
              to="/"
            >
              {t('Prism')}
            </Typography>
          </Grid>

          <Hidden smDown>
            <Grid className={classes.menuContainer} item xs={6}>
              {menu}
            </Grid>

            <Grid
              spacing={3}
              container
              justify="flex-end"
              alignItems="center"
              item
              xs={3}
            >
              {buttons}
              <LanguageSelector />
            </Grid>
          </Hidden>

          <Hidden mdUp>
            <Grid item xs={9} className={classes.mobileMenuContainer}>
              <Button
                onClick={() => setOpenMobileMenu(prevOpen => !prevOpen)}
                aria-controls={openMobileMenu ? 'mobile-menu-list' : undefined}
                aria-haspopup="true"
                className={classes.menuBars}
              >
                <FontAwesomeIcon icon={faBars} />
              </Button>

              <Drawer
                anchor="right"
                open={openMobileMenu}
                onClose={() => setOpenMobileMenu(false)}
              >
                <div className={classes.mobileDrawerContent}>
                  <Grid container spacing={3}>
                    <Grid container justify="space-around" item>
                      {buttons}
                    </Grid>
                    <Grid container direction="column" item>
                      {menuMobile}
                    </Grid>
                  </Grid>
                </div>
              </Drawer>
            </Grid>
          </Hidden>
        </Grid>
      </Toolbar>
    </AppBar>
  );
}
Example #26
Source File: index.tsx    From aqualink-app with MIT License 4 votes vote down vote up
SiteTable = ({
  isDrawerOpen,
  showCard,
  showSpottersOnlySwitch,
  isExtended,
  collection,
  scrollTableOnSelection,
  scrollPageOnSelection,
  classes,
}: SiteTableProps) => {
  const loading = useSelector(sitesListLoadingSelector);
  const siteOnMap = useSelector(siteOnMapSelector);
  const withSpotterOnly = useSelector(withSpotterOnlySelector);
  const dispatch = useDispatch();
  const { height } = useWindowSize() || {};

  const [order, setOrder] = useState<Order>("desc");
  const [orderBy, setOrderBy] = useState<OrderKeys>(OrderKeys.ALERT);

  const handleRequestSort = (event: unknown, property: OrderKeys) => {
    const isAsc = orderBy === property && order === "asc";
    setOrder(isAsc ? "desc" : "asc");
    setOrderBy(property);
  };

  const toggleSwitch = (event: ChangeEvent<HTMLInputElement>) => {
    const {
      target: { checked },
    } = event;
    dispatch(filterSitesWithSpotter(checked));
    dispatch(setWithSpotterOnly(checked));
  };

  // This function is used to prevent the drawer onClick close effect on mobile
  const onInteractiveClick = (
    event: React.MouseEvent<HTMLButtonElement, MouseEvent>
  ) => {
    event.stopPropagation();
  };

  const onMobileSelectChange: SelectProps["onChange"] = (newValue) => {
    const value = newValue.target.value as string;
    const [newOrderBy, newOrder] = value.split("-") as [OrderKeys, Order];
    setOrder(newOrder);
    setOrderBy(newOrderBy);
  };
  return (
    <>
      {/* Holds drawer handle and site name text on mobile */}
      {showCard && (
        <Hidden mdUp>
          <Box
            width="100vw"
            display="flex"
            justifyContent="center"
            marginTop={2}
            marginBottom={3}
          >
            <Box
              className={classNames(classes.topHandle, {
                [classes.bounce]: !!siteOnMap && !isDrawerOpen,
              })}
            />
            {!isDrawerOpen && (
              <Typography
                className={classes.allSitesText}
                variant="h5"
                color="textSecondary"
              >
                {siteOnMap ? getSiteNameAndRegion(siteOnMap).name : "All Sites"}
              </Typography>
            )}
          </Box>
        </Hidden>
      )}
      {showCard && <SelectedSiteCard />}
      {showSpottersOnlySwitch && (
        <Box className={classes.switchWrapper}>
          <Switch
            checked={withSpotterOnly}
            onClick={onInteractiveClick}
            onChange={toggleSwitch}
            color="primary"
          />
          <Typography color="textSecondary" variant="h6">
            deployed buoys only
          </Typography>
        </Box>
      )}
      {/* Holds sort selector on mobile. Sorting on desktop uses table headers. */}
      {!isExtended && (
        <Hidden mdUp>
          <Box
            paddingX={2}
            paddingY={3}
            display="flex"
            alignItems="center"
            onClick={onInteractiveClick}
          >
            <Typography variant="h5">Sort By: </Typography>
            <Select
              value={`${orderBy}-${order}`}
              className={classes.mobileSortSelect}
              onChange={onMobileSelectChange}
            >
              {MOBILE_SELECT_MENU_ITEMS}
            </Select>
          </Box>
        </Hidden>
      )}
      <Box
        className={
          height && height > SMALL_HEIGHT
            ? `${classes.tableHolder} ${classes.scrollable}`
            : `${classes.tableHolder}`
        }
        display="flex"
        flexDirection="column"
        flex={1}
      >
        <TableContainer>
          <Table
            stickyHeader
            className={isExtended ? classes.extendedTable : classes.table}
          >
            <Hidden smDown={!isExtended}>
              <EnhancedTableHead
                order={order}
                orderBy={orderBy}
                onRequestSort={handleRequestSort}
                isExtended={isExtended}
              />
            </Hidden>
            <SiteTableBody
              order={order}
              orderBy={orderBy}
              isExtended={isExtended}
              collection={collection}
              scrollTableOnSelection={scrollTableOnSelection}
              scrollPageOnSelection={scrollPageOnSelection}
            />
          </Table>
        </TableContainer>
        {loading && (
          <Box
            display="flex"
            flex={1}
            alignItems="center"
            justifyContent="center"
          >
            <CircularProgress size="4rem" thickness={1} />
          </Box>
        )}
      </Box>
    </>
  );
}
Example #27
Source File: CardContent.tsx    From aqualink-app with MIT License 4 votes vote down vote up
SelectedSiteCardContent = ({
  site,
  loading,
  error,
  imageUrl = null,
}: SelectedSiteCardContentProps) => {
  const classes = useStyles({ imageUrl, loading });
  const theme = useTheme();
  const isTablet = useMediaQuery(theme.breakpoints.down("sm"));
  const sortedDailyData = sortByDate(site?.dailyData || [], "date");
  const dailyDataLen = sortedDailyData.length;
  const { maxBottomTemperature, satelliteTemperature, degreeHeatingDays } =
    (dailyDataLen && sortedDailyData[dailyDataLen - 1]) || {};
  const useCardWithImageLayout = Boolean(loading || imageUrl);

  const metrics = [
    {
      label: "SURFACE TEMP",
      value: formatNumber(satelliteTemperature, 1),
      unit: " °C",
      display: isNumber(satelliteTemperature),
    },
    {
      label: "HEAT STRESS",
      value: formatNumber(degreeHeatingWeeksCalculator(degreeHeatingDays), 1),
      unit: " DHW",
      display: isNumber(degreeHeatingDays),
    },
    {
      label: `TEMP AT ${site?.depth}m`,
      value: formatNumber(maxBottomTemperature, 1),
      unit: " °C",
      display: isNumber(maxBottomTemperature) && isNumber(site?.depth),
    },
  ];

  const { name, region: regionName } = site
    ? getSiteNameAndRegion(site)
    : { name: null, region: null };

  const chartDataset = site
    ? standardDailyDataDataset(
        site.dailyData,
        site.maxMonthlyMean,
        false,
        site.timezone
      )
    : undefined;

  const ChartComponent =
    site && chartDataset ? (
      <Chart
        siteId={site.id}
        datasets={[chartDataset]}
        surveys={[]}
        temperatureThreshold={
          site.maxMonthlyMean ? site.maxMonthlyMean + 1 : null
        }
        maxMonthlyMean={site.maxMonthlyMean || null}
        background
      />
    ) : null;

  const onExploreButtonClick = () => {
    trackButtonClick(
      GaCategory.BUTTON_CLICK,
      GaAction.MAP_PAGE_BUTTON_CLICK,
      "Explore"
    );
  };

  if (error) {
    return <Alert severity="error">{error}</Alert>;
  }

  if (!loading && !site) {
    return <Alert severity="error">Featured site is not available</Alert>;
  }

  return (
    <Grid
      className={classes.cardWrapper}
      container
      justify="space-between"
      spacing={1}
    >
      {useCardWithImageLayout && (
        <Grid item xs={12} md={6} lg={4}>
          <Box position="relative" height="100%" minHeight={300}>
            <LoadingSkeleton
              className={classes.imageBorderRadius}
              image={featuredImageLoading}
              loading={loading}
              variant="rect"
              height="100%"
            >
              {site && imageUrl && (
                <Link to={`/sites/${site.id}`}>
                  <CardMedia
                    className={classNames(
                      classes.cardImage,
                      classes.imageBorderRadius
                    )}
                    image={imageUrl}
                  />
                </Link>
              )}

              <Hidden mdUp>
                <Box
                  bgcolor="rgba(3, 48, 66, 0.75)"
                  height="55%"
                  width="100%"
                  position="absolute"
                  top={0}
                  left={0}
                >
                  <Box className={classes.cardImageTextWrapper}>
                    <Typography variant="h5">{name}</Typography>

                    {regionName && (
                      <Typography variant="h6">{regionName}</Typography>
                    )}
                  </Box>
                </Box>
              </Hidden>

              {site && (
                <Box position="absolute" bottom={16} px={2} width="100%">
                  <Grid
                    container
                    alignItems="center"
                    justify={
                      site.videoStream && isTablet
                        ? "space-between"
                        : "flex-end"
                    }
                  >
                    {site.videoStream && isTablet && (
                      <Chip
                        live
                        liveText="LIVE VIDEO"
                        to={`/sites/${site.id}`}
                        width={80}
                      />
                    )}
                    <Grid item>
                      <Button
                        className={classes.exporeButton}
                        component={Link}
                        to={`/sites/${site.id}`}
                        onClick={onExploreButtonClick}
                        size="small"
                        variant="contained"
                        color="primary"
                      >
                        EXPLORE
                      </Button>
                    </Grid>
                  </Grid>
                </Box>
              )}
            </LoadingSkeleton>
          </Box>
        </Grid>
      )}

      <Grid
        item
        xs={12}
        md={useCardWithImageLayout ? 6 : 12}
        lg={useCardWithImageLayout ? 6 : 10}
        className={classes.cardAnalyticsWrapper}
      >
        <Box pb="0.5rem" pl="0.5rem" pt="1.5rem" fontWeight={400}>
          <Hidden smDown={useCardWithImageLayout}>
            <LoadingSkeleton
              loading={loading}
              variant="text"
              lines={1}
              textHeight={28}
              longText
            >
              <Grid container alignItems="center">
                <Grid item className={classes.cardTitleWrapper}>
                  <Typography
                    className={classes.cardTitle}
                    color="textSecondary"
                    variant="h5"
                  >
                    <span title={name || ""}>{name}</span>
                  </Typography>
                </Grid>
                {site?.videoStream && (
                  <Chip
                    live
                    liveText="LIVE VIDEO"
                    to={`/sites/${site.id}`}
                    width={80}
                  />
                )}
              </Grid>
            </LoadingSkeleton>
            <LoadingSkeleton
              loading={loading}
              variant="text"
              lines={1}
              textHeight={19}
              className={classes.siteRegionName}
            >
              <Typography
                color="textSecondary"
                variant="h6"
                className={classNames(
                  classes.cardTitle,
                  classes.siteRegionName
                )}
              >
                <span title={regionName || ""}>{regionName}</span>
              </Typography>
            </LoadingSkeleton>
          </Hidden>
          <LoadingSkeleton
            loading={loading}
            variant="text"
            lines={1}
            textHeight={12}
          >
            <Typography color="textSecondary" variant="caption">
              DAILY SURFACE TEMP. (°C)
            </Typography>
          </LoadingSkeleton>
        </Box>
        <Hidden smDown>
          <LoadingSkeleton
            image={chartLoading}
            loading={loading}
            variant="rect"
            width="98%"
            height={180}
          >
            <div>{ChartComponent}</div>
          </LoadingSkeleton>
        </Hidden>
        <Hidden mdUp>
          <LoadingSkeleton
            className={classes.mobileChartLoading}
            image={chartLoading}
            loading={loading}
            variant="rect"
            width="98%"
            height={200}
          >
            {ChartComponent}
          </LoadingSkeleton>
        </Hidden>
      </Grid>

      <Grid item xs={12} lg={2} container>
        <div className={classes.metricsContainer}>
          {metrics.map(({ label, value, unit, display }) => (
            <div key={label} className={classes.metric}>
              <LoadingSkeleton
                longText
                loading={loading}
                variant="text"
                lines={1}
                textHeight={12}
              >
                {display && (
                  <Typography variant="caption" color="textSecondary">
                    {label}
                  </Typography>
                )}
              </LoadingSkeleton>
              <LoadingSkeleton
                loading={loading}
                variant="rect"
                width={80}
                height={32}
              >
                {display && (
                  <Typography variant="h4" color="primary">
                    {value}
                    &nbsp;
                    <Typography variant="h6" component="span">
                      {unit}
                    </Typography>
                  </Typography>
                )}
              </LoadingSkeleton>
            </div>
          ))}
        </div>
      </Grid>
    </Grid>
  );
}
Example #28
Source File: index.tsx    From aqualink-app with MIT License 4 votes vote down vote up
NavBar = ({
  searchLocation,
  geocodingEnabled,
  routeButtons,
  loading,
  classes,
}: NavBarProps) => {
  const user = useSelector(userInfoSelector);
  const storedCollection = useSelector(collectionDetailsSelector);
  const dispatch = useDispatch();
  const theme = useTheme();
  const isTablet = useMediaQuery(theme.breakpoints.up("md"));
  const [registerDialogOpen, setRegisterDialogOpen] = useState<boolean>(false);
  const [signInDialogOpen, setSignInDialogOpen] = useState<boolean>(false);
  const [menuDrawerOpen, setMenuDrawerOpen] = useState<boolean>(false);
  const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null);

  const handleRegisterDialog = (open: boolean) => setRegisterDialogOpen(open);
  const handleSignInDialog = (open: boolean) => setSignInDialogOpen(open);

  const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    setAnchorEl(event.currentTarget);
  };

  const handleMenuClose = () => {
    setAnchorEl(null);
  };

  const onUserSignOut = () => {
    // Clear collection if it belongs to the signed in user
    if (storedCollection?.id === user?.collection?.id) {
      dispatch(clearCollection());
    }
    dispatch(signOutUser());
    handleMenuClose();
  };

  const onSiteChange = () => {
    dispatch(unsetSelectedSite());
    dispatch(unsetLiveData());
    dispatch(unsetLatestData());
  };

  return (
    <>
      <AppBar
        className={classNames(classes.appBar, {
          [classes.appBarXs]: searchLocation,
        })}
        position="static"
        color="primary"
      >
        <Toolbar className={classes.toolbar}>
          <MenuDrawer
            open={menuDrawerOpen}
            onClose={() => setMenuDrawerOpen(false)}
          />
          <Grid
            container
            justify="space-between"
            alignItems="center"
            spacing={1}
          >
            <Grid
              item
              xs={5}
              sm={2}
              // eslint-disable-next-line no-nested-ternary
              md={routeButtons ? 2 : searchLocation ? 6 : 4}
            >
              <Box display="flex" flexWrap="nowrap" alignItems="center">
                <IconButton
                  edge="start"
                  color="inherit"
                  onClick={() => setMenuDrawerOpen(true)}
                >
                  <MenuIcon />
                </IconButton>

                <MuiLink className={classes.navBarLink} href="/map">
                  <Typography color="textPrimary" variant="h4">
                    Aqua
                  </Typography>
                  <Typography style={{ color: "#8AC6DE" }} variant="h4">
                    link
                  </Typography>
                </MuiLink>
              </Box>
            </Grid>

            {searchLocation && (
              <Hidden xsDown>
                <Grid item sm={4} md={3}>
                  <Search geocodingEnabled={geocodingEnabled} />
                </Grid>
              </Hidden>
            )}

            {routeButtons && isTablet && <RouteButtons />}

            <Grid
              container
              justify="flex-end"
              item
              xs={7}
              sm={routeButtons && isTablet ? 3 : 4}
              md={searchLocation || (routeButtons && isTablet) ? 3 : 8}
            >
              {user ? (
                <>
                  <Box display="flex" flexWrap="nowrap" alignItems="center">
                    {user.fullName ? user.fullName : "My Profile"}
                    <IconButton
                      className={classes.button}
                      onClick={handleClick}
                    >
                      <ExpandMoreIcon className={classes.expandIcon} />
                    </IconButton>
                    <Menu
                      key="user-menu"
                      className={classes.userMenu}
                      anchorEl={anchorEl}
                      keepMounted
                      open={Boolean(anchorEl)}
                      onClose={handleMenuClose}
                      MenuListProps={{ className: classes.userMenu }}
                      PopoverClasses={{ paper: classes.userMenuWrapper }}
                    >
                      {sortBy(user.administeredSites, "id").map(
                        ({ id, name, region }, index) => {
                          const siteIdentifier = name || region;
                          return (
                            <Link
                              to={`/sites/${id}`}
                              key={`site-link-${id}`}
                              className={classes.menuItemLink}
                            >
                              <MenuItem
                                onClick={() => onSiteChange()}
                                className={classes.menuItem}
                              >
                                {siteIdentifier || `Site ${index + 1}`}
                              </MenuItem>
                            </Link>
                          );
                        }
                      )}
                      <Divider className={classes.userMenuDivider} />
                      <Link to="/dashboard" className={classes.menuItemLink}>
                        <MenuItem
                          key="user-menu-dashboard"
                          className={classes.menuItem}
                        >
                          <Grid container spacing={1}>
                            <Grid item>
                              <DashboardTwoToneIcon fontSize="small" />
                            </Grid>
                            <Grid item>Dashboard</Grid>
                          </Grid>
                        </MenuItem>
                      </Link>
                      <Divider className={classes.userMenuDivider} />
                      <MenuItem
                        key="user-menu-logout"
                        className={classes.menuItem}
                        onClick={onUserSignOut}
                      >
                        <Grid container spacing={1}>
                          <Grid item>
                            <PowerSettingsNewIcon fontSize="small" />
                          </Grid>
                          <Grid item>Logout</Grid>
                        </Grid>
                      </MenuItem>
                    </Menu>
                  </Box>
                </>
              ) : (
                <>
                  <Grid item>
                    <Button onClick={() => handleSignInDialog(true)}>
                      SIGN IN
                    </Button>
                  </Grid>
                  <Grid item>
                    <Button onClick={() => handleRegisterDialog(true)}>
                      SIGN UP
                    </Button>
                  </Grid>
                </>
              )}
            </Grid>

            {searchLocation && (
              <Hidden smUp>
                <Grid item xs={12} style={{ margin: 0, paddingTop: 0 }}>
                  <Search geocodingEnabled={geocodingEnabled} />
                </Grid>
              </Hidden>
            )}
          </Grid>
        </Toolbar>
      </AppBar>
      {loading && <LinearProgress />}
      <RegisterDialog
        open={registerDialogOpen}
        handleRegisterOpen={handleRegisterDialog}
        handleSignInOpen={handleSignInDialog}
      />
      <SignInDialog
        open={signInDialogOpen}
        handleRegisterOpen={handleRegisterDialog}
        handleSignInOpen={handleSignInDialog}
      />
    </>
  );
}