@material-ui/core#Popover TypeScript Examples

The following examples show how to use @material-ui/core#Popover. 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: AutoCompletedResults.tsx    From crossfeed with Creative Commons Zero v1.0 Universal 6 votes vote down vote up
AutoCompleteResults: React.FC<Props> = (props) => {
  const { values, ...rest } = props;

  return (
    <Popover {...rest}>
      {values.map((value) => (
        <MenuItem key={value.id.raw}>{value.text.raw}</MenuItem>
      ))}
    </Popover>
  );
}
Example #2
Source File: Info.tsx    From swap-ui with Apache License 2.0 6 votes vote down vote up
function InfoButton() {
  const styles = useStyles();
  return (
    <PopupState variant="popover">
      {
        //@ts-ignore
        (popupState) => (
          <div style={{ display: "flex" }}>
            <IconButton
              {...bindTrigger(popupState)}
              className={styles.infoButton}
            >
              <Info fontSize="small" />
            </IconButton>
            <Popover
              {...bindPopover(popupState)}
              anchorOrigin={{
                vertical: "bottom",
                horizontal: "left",
              }}
              transformOrigin={{
                vertical: "top",
                horizontal: "right",
              }}
              PaperProps={{ style: { borderRadius: "10px" } }}
              disableRestoreFocus
            >
              <InfoDetails />
            </Popover>
          </div>
        )
      }
    </PopupState>
  );
}
Example #3
Source File: PopupHover.tsx    From frontegg-react with MIT License 5 votes vote down vote up
PopupHover = forwardRef<HTMLElement, IPopoverProps>((props, ref) => {
  const { trigger, content, anchorOrigin, transformOrigin, mountNode } = props;

  const debounceRef = useRef<any>(0);
  const classes = useStyles();
  const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);

  const open = Boolean(anchorEl);

  const onMouseEnter = useCallback((e: MouseEvent<HTMLElement>) => {
    clearTimeout(debounceRef.current);
    setAnchorEl(e.currentTarget);
  }, []);
  const onMouseLeave = useCallback((e: MouseEvent<HTMLElement>) => {
    clearTimeout(debounceRef.current);
    debounceRef.current = setTimeout(() => {
      setAnchorEl(null);
    }, debounceDelay);
  }, []);

  const onMouseEnterPopup = useCallback((e: MouseEvent<HTMLElement>) => {
    clearTimeout(debounceRef.current);
  }, []);

  useEffect(() => {
    if (open) {
      props.onOpen?.();
    } else {
      props.onClose?.();
    }
  }, [open]);

  return (
    <>
      {React.cloneElement(trigger, { ref, onMouseEnter, onMouseLeave })}
      <Popover
        className={classes.popover}
        container={mountNode}
        open={open}
        anchorEl={anchorEl}
        anchorOrigin={anchorOrigin}
        transformOrigin={transformOrigin}
        onClose={() => setAnchorEl(null)}
        disableRestoreFocus
      >
        <Box
          className={classnames(classes.box, 'fe-material-popup-content')}
          onMouseEnter={onMouseEnterPopup}
          onMouseLeave={onMouseLeave}
        >
          {content}
        </Box>
      </Popover>
    </>
  );
})
Example #4
Source File: PopupFocus.tsx    From frontegg-react with MIT License 5 votes vote down vote up
PopupFocus = forwardRef<HTMLElement, IPopoverProps>((props, ref) => {
  const { trigger, content, anchorOrigin, transformOrigin, mountNode } = props;
  const classes = useStyles();
  const [open, setOpen] = useState<boolean>(false);
  const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
  const [focused, setFocused] = useState<boolean>(false);

  const handleFocus = useCallback(
    (event: React.FocusEvent<HTMLButtonElement>) => {
      if (focused) {
        setFocused(false);
      } else {
        setAnchorEl(event.currentTarget);
        setOpen(true);
        setFocused(true);
      }
    },
    [focused]
  );

  const handleClose = useCallback(() => {
    setAnchorEl(null);
    setOpen(false);
    props.onClose?.();
  }, []);

  useEffect(() => {
    if (open) {
      props.onOpen?.();
    }
  }, [open]);
  return (
    <>
      {React.cloneElement(trigger, { onFocus: handleFocus, ref })}
      <Popover
        open={open}
        anchorEl={anchorEl}
        onClose={handleClose}
        container={mountNode}
        anchorOrigin={anchorOrigin}
        transformOrigin={transformOrigin}
      >
        <Box className={classnames(classes.box, 'fe-m-popup-content')}>{content}</Box>
      </Popover>
    </>
  );
})
Example #5
Source File: PopupClick.tsx    From frontegg-react with MIT License 5 votes vote down vote up
PopupClick = forwardRef<HTMLElement, IPopoverProps>((props, ref) => {
  const { trigger, content, anchorOrigin, transformOrigin, mountNode } = props;
  const classes = useStyles();
  const [open, setOpen] = useState<boolean>(false);
  const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);

  useImperativeHandle<any, any>(ref, () => ({
    closePopup: () => handleClose(),
  }));

  const handleClick = useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
    setAnchorEl(event.currentTarget);
    setOpen(true);
  }, []);

  const handleClose = useCallback(() => {
    setAnchorEl(null);
    setOpen(false);
    props.onClose?.();
  }, []);

  useEffect(() => {
    if (open) {
      props.onOpen?.();
    }
  }, [open]);

  useEffect(() => {
    if (props.open != null) {
      setOpen(props.open);
    }
  }, [props.open]);

  return (
    <>
      {React.cloneElement(trigger, { onClick: handleClick, ref: ref })}
      <Popover
        open={open}
        anchorEl={anchorEl}
        onClose={handleClose}
        container={mountNode}
        anchorOrigin={anchorOrigin}
        transformOrigin={transformOrigin}
      >
        <Box className={classnames(classes.box, 'fe-m-popup-content')}>{content}</Box>
      </Popover>
    </>
  );
})
Example #6
Source File: Toolbar.tsx    From homebase-app with MIT License 5 votes vote down vote up
StyledPopover = styled(Popover)({
  ".MuiPaper-root": {
    borderRadius: 4,
  },
})
Example #7
Source File: ChangeNetworkButton.tsx    From homebase-app with MIT License 5 votes vote down vote up
StyledPopover = styled(Popover)({
  ".MuiPaper-root": {
    borderRadius: 4,
  },
})
Example #8
Source File: ERC721Token.tsx    From safe-airdrop with MIT License 5 votes vote down vote up
ERC721Token = (props: TokenProps) => {
  const [anchorEl, setAnchorEl] = useState<HTMLImageElement | null>(null);

  const [isMetaDataLoading, setIsMetaDataLoading] = useState(false);

  const [tokenMetaData, setTokenMetaData] = useState<CollectibleTokenMetaInfo | undefined>(undefined);

  const collectibleTokenInfoProvider = useCollectibleTokenInfoProvider();

  const { tokenAddress, id, token_type, hasMetaData } = props;

  const imageZoomedIn = Boolean(anchorEl);
  useEffect(() => {
    let isMounted = true;
    if (hasMetaData) {
      setIsMetaDataLoading(true);
      collectibleTokenInfoProvider.fetchMetaInfo(tokenAddress, id, token_type).then((result) => {
        if (isMounted) {
          setTokenMetaData(result);
          setIsMetaDataLoading(false);
        }
      });
    }
    return function callback() {
      isMounted = false;
    };
  }, [hasMetaData, collectibleTokenInfoProvider, tokenAddress, id, token_type]);

  return (
    <Container>
      {isMetaDataLoading ? (
        <Loader size="sm" />
      ) : (
        tokenMetaData?.imageURI && (
          <>
            <img
              alt={""}
              src={tokenMetaData?.imageURI}
              onClick={(event) => {
                setAnchorEl(event.currentTarget);
              }}
              style={{
                maxWidth: 20,
                marginRight: 3,
                verticalAlign: "middle",
                cursor: "pointer",
              }}
            />{" "}
            <Popover
              style={{ padding: 8 }}
              anchorEl={anchorEl}
              open={imageZoomedIn}
              onClose={() => setAnchorEl(null)}
              anchorOrigin={{
                vertical: "bottom",
                horizontal: "center",
              }}
              transformOrigin={{
                vertical: "top",
                horizontal: "center",
              }}
            >
              <img
                alt={""}
                src={tokenMetaData?.imageURI}
                style={{
                  maxWidth: 320,
                  marginRight: 3,
                  verticalAlign: "middle",
                }}
              />{" "}
            </Popover>
          </>
        )
      )}
      <Text size="md">{tokenMetaData?.name || tokenAddress}</Text>
    </Container>
  );
}
Example #9
Source File: MenuAction.tsx    From parity-bridges-ui with GNU General Public License v3.0 5 votes vote down vote up
MenuAction = ({ actions, changeMenu, action }: MenuActionProps) => {
  const classes = useStyles();
  const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null);
  const [id, setId] = React.useState<string | undefined>(undefined);
  const [open, setOpen] = React.useState<boolean>(false);

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

  useEffect(() => {
    setOpen(Boolean(anchorEl));
    setId(anchorEl ? 'simple-popover' : undefined);
  }, [anchorEl]);

  const item = actions.find(({ type }) => type === action);

  return (
    <>
      <ButtonBase className={`${classes.item} current`} onClick={handleClick}>
        {item?.title || '-'}
        <ArrowDropDownIcon />
      </ButtonBase>
      <Popover
        id={id}
        open={open}
        anchorEl={anchorEl}
        onClose={() => setOpen(false)}
        anchorOrigin={{
          vertical: 'bottom',
          horizontal: 'right'
        }}
        transformOrigin={{
          vertical: 'top',
          horizontal: 24
        }}
        PaperProps={{
          className: classes.menu
        }}
      >
        {actions.map((i, n) => (
          <ButtonBase
            className={`${classes.item} ${!i.isEnabled && 'disabled'}`}
            key={n}
            onClick={() => {
              setOpen(!open);
              changeMenu(i.type);
            }}
          >
            {i.title}
          </ButtonBase>
        ))}
      </Popover>
    </>
  );
}
Example #10
Source File: AddShortcut.tsx    From backstage with Apache License 2.0 5 votes vote down vote up
AddShortcut = ({ onClose, anchorEl, api }: Props) => {
  const classes = useStyles();
  const alertApi = useApi(alertApiRef);
  const { pathname } = useLocation();
  const [formValues, setFormValues] = useState<FormValues>();
  const open = Boolean(anchorEl);

  const handleSave: SubmitHandler<FormValues> = async ({ url, title }) => {
    const shortcut: Omit<Shortcut, 'id'> = { url, title };

    try {
      await api.add(shortcut);
      alertApi.post({
        message: `Added shortcut '${title}' to your sidebar`,
        severity: 'success',
      });
    } catch (error) {
      alertApi.post({
        message: `Could not add shortcut: ${error.message}`,
        severity: 'error',
      });
    }

    onClose();
  };

  const handlePaste = () => {
    setFormValues({ url: pathname, title: document.title });
  };

  const handleClose = () => {
    setFormValues(undefined);
    onClose();
  };

  return (
    <Popover
      open={open}
      anchorEl={anchorEl}
      TransitionProps={{ onExit: handleClose }}
      onClose={onClose}
      anchorOrigin={{
        vertical: 'top',
        horizontal: 'right',
      }}
    >
      <Card className={classes.card}>
        <CardHeader
          className={classes.header}
          title="Add Shortcut"
          titleTypographyProps={{ variant: 'subtitle2' }}
          action={
            <Button
              className={classes.button}
              variant="text"
              size="small"
              color="primary"
              onClick={handlePaste}
            >
              Use current page
            </Button>
          }
        />
        <ShortcutForm
          onClose={handleClose}
          onSave={handleSave}
          formValues={formValues}
        />
      </Card>
    </Popover>
  );
}
Example #11
Source File: SenderDropdown.tsx    From parity-bridges-ui with GNU General Public License v3.0 5 votes vote down vote up
export default function SenderDropdown({ anchorEl, removeAnchor }: Props) {
  const classes = useStyles();
  const [showEmpty, setShowEmpty] = useState(true);
  const [showCompanion, setShowCompanion] = useState(true);
  const [filterInput, setFilterInput] = useState<string | null>(null);
  const { initialLoadingAccounts } = useAccountContext();
  const open = Boolean(anchorEl);
  const id = open ? 'test-sender-component' : undefined;

  const handleClose = useCallback(() => {
    removeAnchor();
    setFilterInput(null);
  }, [removeAnchor]);

  return (
    <Popover
      id={id}
      open={open}
      anchorEl={anchorEl}
      onClose={handleClose}
      anchorOrigin={{
        vertical: 'top',
        horizontal: 'center'
      }}
      transformOrigin={{
        vertical: 'top',
        horizontal: 'center'
      }}
      classes={{
        paper: classes.paper
      }}
    >
      {initialLoadingAccounts ? (
        <SenderAccountsLoading />
      ) : (
        <>
          <SenderFilters
            setFilterInput={setFilterInput}
            setShowEmpty={setShowEmpty}
            setShowCompanion={setShowCompanion}
            showEmpty={showEmpty}
            showCompanion={showCompanion}
          />
          <SenderAccountsSection
            showEmpty={showEmpty}
            showCompanion={showCompanion}
            filterInput={filterInput}
            handleClose={handleClose}
          />
        </>
      )}
    </Popover>
  );
}
Example #12
Source File: index.tsx    From prism-frontend with MIT License 5 votes vote down vote up
function MenuItem({ classes, title, icon, layersCategories }: MenuItemProps) {
  const { t } = useSafeTranslation();
  const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);

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

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

  const open = Boolean(anchorEl);
  const id = open ? 'menu-item-popover' : undefined;

  return (
    <>
      <Button
        className={classes.title}
        onClick={handleClick}
        aria-describedby={id}
      >
        <img className={classes.icon} src={`/images/${icon}`} alt={title} />
        <Typography variant="body2">{t(title)}</Typography>
      </Button>

      <Popover
        id={id}
        open={open}
        anchorEl={anchorEl}
        onClose={handleClose}
        className={classes.popover}
        anchorOrigin={{
          vertical: 'bottom',
          horizontal: 'center',
        }}
        transformOrigin={{
          vertical: 'top',
          horizontal: 'center',
        }}
        PaperProps={{
          className: classes.paper,
        }}
      >
        {layersCategories.map(({ title: categoryTitle, layers, tables }) => (
          <MenuSwitch
            key={categoryTitle}
            title={t(categoryTitle)}
            layers={layers}
            tables={tables}
          />
        ))}
      </Popover>
    </>
  );
}
Example #13
Source File: Settings.tsx    From swap-ui with Apache License 2.0 5 votes vote down vote up
export function SettingsButton() {
  const styles = useStyles();

  return (
    <PopupState variant="popover">
      {
        //@ts-ignore
        (popupState) => (
          <div>
            <IconButton
              {...bindTrigger(popupState)}
              className={styles.settingsButton}
            >
              <Settings />
            </IconButton>
            <Popover
              {...bindPopover(popupState)}
              anchorOrigin={{
                vertical: "bottom",
                horizontal: "left",
              }}
              transformOrigin={{
                vertical: "top",
                horizontal: "right",
              }}
              PaperProps={{
                style: {
                  borderRadius: "10px",
                  boxShadow: "0px 0px 30px 5px rgba(0,0,0,0.075)",
                },
              }}
            >
              <SettingsDetails />
            </Popover>
          </div>
        )
      }
    </PopupState>
  );
}
Example #14
Source File: Footer.tsx    From knboard with MIT License 5 votes vote down vote up
Footer = () => {
  const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(
    null
  );

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

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

  const open = Boolean(anchorEl);
  const id = open ? "about-popover" : undefined;

  return (
    <Container>
      <List>
        <Item>
          <Button
            aria-describedby={id}
            onClick={handleClick}
            css={css`
              padding: 0;
              text-transform: initial;
              color: #888;
              font-weight: 400;
              &:hover {
                background-color: initial;
              }
            `}
          >
            About
          </Button>
        </Item>
      </List>
      <Popover
        id={id}
        open={open}
        anchorEl={anchorEl}
        onClose={handleClose}
        anchorOrigin={{
          vertical: "top",
          horizontal: "center",
        }}
        transformOrigin={{
          vertical: "bottom",
          horizontal: "center",
        }}
      >
        <Alert
          severity="info"
          css={css`
            padding: 1rem 1rem 2rem 1rem;
            max-width: 400px;
          `}
        >
          <AlertTitle>About</AlertTitle>
          <p>
            <b>Knboard</b> is an app that helps visualize your work using kanban
            boards, maximizing efficiency.
          </p>
          It is an <b>open-source</b> project built using Django & React,
          available on{" "}
          <Link
            href="https://github.com/rrebase/knboard"
            target="_blank"
            rel="noopener"
          >
            GitHub
          </Link>
          .
        </Alert>
      </Popover>
    </Container>
  );
}
Example #15
Source File: PlayersContainer.tsx    From cards-against-formality-pwa with BSD 2-Clause "Simplified" License 5 votes vote down vote up
MobilePlayer = ({ player, isHost, isCzar, onPlayerKick, isCurrentUserHost }: any) => {
  const anchorEl = useRef<any>();
  const [isOpen, setIsOpen] = useState(false);
  if (!player) {
    return null;
  }

  function renderKick() {
    if (isHost || !isCurrentUserHost) {
      return null;
    }

    return <List>
      <ListItem style={{ padding: 0 }}>
        <Button size="small" color="secondary" onClick={() => { onPlayerKick(player?._id) }}>Kick</Button>
      </ListItem>
    </List>;
  }

  return <>
    <div className={`player ${isCzar ? 'special' : ''}`} ref={anchorEl} onClick={() => setIsOpen(prev => !prev)}>
      <Badge badgeContent={player.score} color="error">
        {player?.username}
      </Badge>
    </div>
    <Popover
      anchorEl={anchorEl.current}
      open={isHost || !isCurrentUserHost ? false : isOpen}
      onClose={() => setIsOpen(false)}
      anchorOrigin={{
        vertical: 'top',
        horizontal: 'right',
      }}
      transformOrigin={{
        vertical: 'bottom',
        horizontal: 'left',
      }}
    >
      {renderKick()}
    </Popover>
  </>
}
Example #16
Source File: index.tsx    From firetable with Apache License 2.0 5 votes vote down vote up
Notification = () => {
  const classes = useStyles();
  const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(
    null
  );

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

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

  const open = Boolean(anchorEl);
  const id = open ? "simple-popover" : undefined;

  const notifications: Notification[] = [
    {
      title: "a",
      subtitle: "a",
      variant: "error",
      link:
        "https://console.cloud.google.com/cloud-build/builds;region=global/f7b8fd9b-eb6e-401f-a889-73c4bf75f232?project=antler-vc",
    },
  ];

  const notificationsCount = notifications.length;
  return (
    <>
      <IconButton onClick={handleClick}>
        <Badge
          color={"primary"}
          variant="standard"
          badgeContent={notificationsCount}
        >
          <BellIcon />
        </Badge>
      </IconButton>
      <Popover
        id={id}
        open={open}
        anchorEl={anchorEl}
        onClose={handleClose}
        anchorOrigin={{
          vertical: "bottom",
          horizontal: "center",
        }}
        transformOrigin={{
          vertical: "top",
          horizontal: "center",
        }}
      >
        <List>
          {notifications.map((notification) => (
            <ListItem>
              <ListItemAvatar>
                <Avatar>
                  <ErrorIcon />
                </Avatar>
              </ListItemAvatar>
              <ListItemText
                primary={notification.title}
                secondary={notification.subtitle}
              />
              <ListItemSecondaryAction>
                <IconButton edge="end" aria-label="delete">
                  <DeleteIcon />
                </IconButton>
              </ListItemSecondaryAction>
            </ListItem>
          ))}
        </List>
      </Popover>
    </>
  );
}
Example #17
Source File: MoreMenu.tsx    From anchor-web-app with Apache License 2.0 5 votes vote down vote up
function MoreMenuBase({ children, className }: MoreMenuProps) {
  const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);

  return (
    <>
      <MoreButton
        onClick={(event: MouseEvent<HTMLButtonElement>) =>
          setAnchorEl(event.currentTarget)
        }
      >
        More {anchorEl ? <ExpandLess /> : <ExpandMore />}
      </MoreButton>

      <Popover
        open={!!anchorEl}
        anchorEl={anchorEl}
        onClose={() => setAnchorEl(null)}
        anchorOrigin={{
          vertical: 'bottom',
          horizontal: 'right',
        }}
        transformOrigin={{
          vertical: 'top',
          horizontal: 'right',
        }}
        className={className}
      >
        <MenuList variant="menu">
          {Children.map(children, (child) => {
            return cloneElement(child, {
              children: (
                <IconSpan>
                  {child.props.children} <ChevronRightRounded />
                </IconSpan>
              ),
            });
          })}
        </MenuList>
      </Popover>
    </>
  );
}
Example #18
Source File: ColumnTitle.tsx    From knboard with MIT License 4 votes vote down vote up
ColumnTitle = ({ id, title, tasksCount, ...props }: Props) => {
  const dispatch = useDispatch();
  const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(
    null
  );
  const [pendingTitle, setPendingTitle] = useState<string>(title);
  const [editing, setEditing] = useState<boolean>(false);
  const titleTextAreaRef = useRef<HTMLTextAreaElement>(null);

  useEffect(() => {
    if (!editing && title === pendingTitle) {
      titleTextAreaRef?.current?.blur();
    }
  }, [pendingTitle, editing]);

  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (e.keyCode === Key.Enter) {
      e.preventDefault();
      if (pendingTitle.length > 0) {
        titleTextAreaRef?.current?.blur();
      }
    }
    if (e.keyCode === Key.Escape) {
      e.preventDefault();
      setPendingTitle(title);
      setEditing(false);
      // blur via useEffect
    }
  };

  const handleSave = () => {
    if (editing && pendingTitle.length > 0) {
      setEditing(false);
      if (pendingTitle !== title) {
        dispatch(patchColumn({ id, fields: { title: pendingTitle } }));
      }
    }
  };

  const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
    setPendingTitle(e.target.value);
  };

  const handleFocus = (e: React.FocusEvent<HTMLTextAreaElement>) => {
    e.target.select();
  };

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

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

  const handleDelete = () => {
    if (
      window.confirm(
        "Are you sure? Deleting the column will also delete related tasks and this cannot be undone."
      )
    ) {
      dispatch(deleteColumn(id));
      handleOptionsClose();
    }
  };

  const open = Boolean(anchorEl);
  const popoverId = open ? `col-${id}options-popover` : undefined;

  return (
    <Container {...props}>
      {editing ? (
        <InputTitle>
          <TextareaAutosize
            ref={titleTextAreaRef}
            value={pendingTitle}
            onChange={handleChange}
            onBlur={handleSave}
            onKeyDown={handleKeyDown}
            data-testid="column-title-textarea"
            onFocus={handleFocus}
            autoFocus
          />
        </InputTitle>
      ) : (
        <RegularTitle onClick={() => setEditing(true)}>
          {pendingTitle}
        </RegularTitle>
      )}
      <Extra>
        <InnerExtra>
          <Count>{tasksCount}</Count>
          <Button
            onClick={handleOptionsClick}
            data-testid="col-options"
            css={css`
              margin-left: 0.25rem;
              min-width: 0;
              padding: 2px 8px;
              height: 22px;
            `}
          >
            <FontAwesomeIcon icon={faEllipsisV} />
          </Button>
        </InnerExtra>
        <Popover
          id={popoverId}
          open={open}
          anchorEl={anchorEl}
          onClose={handleOptionsClose}
          anchorOrigin={{
            vertical: "bottom",
            horizontal: "left",
          }}
          transformOrigin={{
            vertical: "top",
            horizontal: "left",
          }}
        >
          <OptionsContent>
            <Button
              startIcon={<FontAwesomeIcon fixedWidth icon={faTrash} />}
              onClick={handleDelete}
              data-testid="delete-column"
              size="small"
              css={css`
                font-size: 12px;
                font-weight: bold;
                color: ${ACTION_G};
              `}
            >
              Delete column
            </Button>
          </OptionsContent>
        </Popover>
      </Extra>
    </Container>
  );
}
Example #19
Source File: MemberFilter.tsx    From knboard with MIT License 4 votes vote down vote up
MemberFilter = ({ boardId }: Props) => {
  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
  const [filteredAssignee, setFilteredAssignee] = useState<BoardMember[]>([]);
  const dispatch = useDispatch();
  const members = useSelector(selectAllMembers);

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

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

  const postFilterMember = async (users: BoardMember[]) => {
    dispatch(
      fetchBoardById({
        boardId: boardId,
        assigneeIds: users.map((m) => m.id),
      })
    );
    handleClose();
  };

  const handleClickFilter = () => {
    postFilterMember(filteredAssignee);
  };

  const handleClickClearFilter = () => {
    setFilteredAssignee([]);
    postFilterMember([]);
  };

  const ClearFilterButton = () => (
    <FilterButton>
      <Button
        variant="outlined"
        size="small"
        css={css`
          text-transform: none;
        `}
        onClick={handleClickClearFilter}
        data-testid="clear-filter"
      >
        Clear Filters
      </Button>
    </FilterButton>
  );

  return (
    <>
      <FilterButton>
        <Button
          variant="outlined"
          size="small"
          css={css`
            text-transform: none;
          `}
          onClick={handleClick}
          aria-controls="member-filter-menu"
          aria-haspopup="true"
          data-testid="member-filter"
        >
          Filter
        </Button>
      </FilterButton>
      {filteredAssignee.length > 0 ? <ClearFilterButton /> : null}
      <Popover
        id="member-filter-menu"
        anchorEl={anchorEl}
        getContentAnchorEl={null}
        anchorOrigin={{
          vertical: "bottom",
          horizontal: "left",
        }}
        transformOrigin={{
          vertical: "top",
          horizontal: "left",
        }}
        open={Boolean(anchorEl)}
        onClose={handleClose}
        transitionDuration={0}
      >
        <Content>
          <Description>Filter tasks by assignees</Description>
          <Box display="flex" alignItems="center">
            <Box flexGrow={1}>
              <AssigneeAutoComplete
                assignee={filteredAssignee}
                members={members}
                controlId={"assignee-filter"}
                dataTestId={"filter-assignees"}
                setAssignee={setFilteredAssignee}
              />
            </Box>
            <Button
              color="primary"
              variant="contained"
              css={css`
                font-size: 0.625rem;
                margin-left: 0.5rem;
              `}
              onClick={handleClickFilter}
              data-testid="filter-selected"
              disabled={filteredAssignee.length === 0}
            >
              Filter
            </Button>
          </Box>
        </Content>
      </Popover>
    </>
  );
}
Example #20
Source File: WebhookLogsList.tsx    From glific-frontend with GNU Affero General Public License v3.0 4 votes vote down vote up
WebhookLogsList: React.SFC<WebhookLogsListProps> = () => {
  const [anchorEl, setAnchorEl] = useState(null);
  const [open, setOpen] = useState(false);
  const [text, setText] = useState<any>();
  const { t } = useTranslation();

  const handleClick = (event: any) => {
    setAnchorEl(event.currentTarget);
  };

  const getCroppedText = (croppedtext: string, isUrl: boolean = false) => {
    if (!croppedtext) {
      return <div className={styles.TableText}>NULL</div>;
    }

    let newtext = croppedtext;
    if (isUrl) {
      newtext = JSON.stringify(croppedtext);
    }

    const Menus = [
      {
        title: t('Copy text'),
        icon: <img src={CopyIcon} alt="copy" />,
        onClick: () => {
          copyToClipboard(croppedtext);
        },
      },
      {
        title: t('View'),
        icon: <ViewIcon />,
        onClick: () => {
          setText(newtext);
          setOpen(true);
        },
      },
    ];
    return (
      <Menu menus={Menus}>
        <div
          className={styles.CroppedText}
          onClick={handleClick}
          onKeyDown={handleClick}
          aria-hidden="true"
        >
          {newtext.length > 21 ? `${newtext.slice(0, 21)}...` : newtext}
        </div>
      </Menu>
    );
  };

  const getColumns = ({
    url,
    method,
    status,
    requestHeaders,
    requestJson,
    statusCode,
    responseJson,
    error,
    updatedAt,
  }: any) => ({
    updatedAt: getTime(updatedAt),
    url: getCroppedText(url, true),
    status: getStatus(status),
    statusCode: getText(statusCode),
    error: getCroppedText(error, true),
    method: getText(method),
    requestHeaders: getCroppedText(requestHeaders),
    requestJson: getCroppedText(requestJson),
    responseJson: getCroppedText(responseJson),
  });

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

  const columnAttributes = {
    columnNames,
    columns: getColumns,
    columnStyles,
  };

  const popover = (
    <Popover open={open} anchorEl={anchorEl} onClose={handleClose}>
      <div className={styles.PopoverText}>
        <pre>{JSON.stringify(text ? JSON.parse(text) : '', null, 2)}</pre>
      </div>
      <div className={styles.PopoverActions}>
        <span
          onClick={() => copyToClipboard(text)}
          aria-hidden="true"
          data-testid="copyToClipboard"
        >
          <img src={CopyIcon} alt="copy" />
          {t('Copy text')}
        </span>
        <Button variant="contained" color="default" onClick={handleClose}>
          {t('Done')}
        </Button>
      </div>
    </Popover>
  );
  return (
    <div className={styles.Container}>
      <List
        title={t('Webhook Logs')}
        listItem="webhookLogs"
        listItemName="webhookLog"
        pageLink="webhookLog"
        listIcon={webhookLogsIcon}
        searchParameter={['contact_phone', 'url']}
        button={{ show: false, label: '' }}
        {...queries}
        dialogMessage=""
        restrictedAction={restrictedAction}
        {...columnAttributes}
        removeSortBy={['STATUS']}
        listOrder="desc"
      />
      {popover}
    </div>
  );
}
Example #21
Source File: NotificationList.tsx    From glific-frontend with GNU Affero General Public License v3.0 4 votes vote down vote up
NotificationList: React.SFC<NotificationListProps> = () => {
  const client = useApolloClient();
  const [open, setOpen] = useState(false);
  const [text, setText] = useState<any>();
  const { t } = useTranslation();
  const history = useHistory();
  const [filters, setFilters] = useState<any>({
    Critical: true,
    Warning: false,
  });

  const menuRef = useRef(null);
  let filterValue: any = '';

  const [markNotificationAsRead] = useMutation(MARK_NOTIFICATIONS_AS_READ, {
    onCompleted: (data) => {
      if (data.markNotificationAsRead) {
        client.writeQuery({
          query: GET_NOTIFICATIONS_COUNT,
          variables: {
            filter: {
              is_read: false,
              severity: 'critical',
            },
          },
          data: { countNotifications: 0 },
        });
      }
    },
  });

  useEffect(() => {
    setTimeout(() => {
      markNotificationAsRead();
    }, 1000);
  }, []);

  const setDialog = (id: any, item: any) => {
    if (item.category === 'Message') {
      const chatID = JSON.parse(item.entity).id;
      history.push({ pathname: `/chat/${chatID}` });
    } else if (item.category === 'Flow') {
      const uuidFlow = JSON.parse(item.entity).flow_uuid;
      history.push({ pathname: `/flow/configure/${uuidFlow}` });
    } else {
      // this is item.category == Partner
      // need to figure out what should be done
    }
  };

  const additionalAction = [
    {
      icon: <ArrowForwardIcon className={styles.RedirectArrow} />,
      parameter: 'id',
      label: 'Check',
      dialog: setDialog,
    },
  ];
  const getCroppedText = (croppedtext: string) => {
    if (!croppedtext) {
      return <div className={styles.TableText}>NULL</div>;
    }

    const entityObj = JSON.parse(croppedtext);

    const Menus = [
      {
        title: t('Copy text'),
        icon: <img src={CopyIcon} alt="copy" />,
        onClick: () => {
          copyToClipboard(croppedtext);
        },
      },
      {
        title: t('View'),
        icon: <ViewIcon />,
        onClick: () => {
          setText(croppedtext);
          setOpen(true);
        },
      },
    ];

    return (
      <Menu menus={Menus}>
        <div
          className={styles.CroppedText}
          data-testid="NotificationRowMenu"
          ref={menuRef}
          aria-hidden="true"
        >
          {entityObj.name ? (
            <span>
              Contact: {entityObj.name}
              <br />
              {croppedtext.slice(0, 25)}...
            </span>
          ) : (
            `${croppedtext.slice(0, 25)}...`
          )}
        </div>
      </Menu>
    );
  };

  const getColumns = ({ category, entity, message, severity, updatedAt, isRead }: any) => ({
    isRead: getDot(isRead),
    updatedAt: getTime(updatedAt),
    category: getText(category),
    severity: getText(severity.replace(/"/g, '')),
    entity: getCroppedText(entity),
    message: getText(message),
  });

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

  const columnNames = ['', 'TIMESTAMP', 'CATEGORY', 'SEVERITY', 'ENTITY', 'MESSAGE'];

  const columnAttributes = {
    columnNames,
    columns: getColumns,
    columnStyles,
  };

  const popover = (
    <Popover open={open} anchorEl={menuRef.current} onClose={handleClose}>
      <div className={styles.PopoverText}>
        <pre>{JSON.stringify(text ? JSON.parse(text) : '', null, 2)}</pre>
      </div>
      <div className={styles.PopoverActions}>
        <span
          onClick={() => copyToClipboard(text)}
          aria-hidden="true"
          data-testid="copyToClipboard"
        >
          <img src={CopyIcon} alt="copy" />
          {t('Copy text')}
        </span>
        <Button variant="contained" color="default" onClick={handleClose}>
          {t('Done')}
        </Button>
      </div>
    </Popover>
  );

  const severityList = ['Critical', 'Warning'];

  const handleCheckedBox = (event: any) => {
    setFilters({ ...filters, [event.target.name]: event.target.checked });
  };

  const filterName = Object.keys(filters).filter((k) => filters[k] === true);
  if (filterName.length === 1) {
    [filterValue] = filterName;
  }

  const filterOnSeverity = (
    <div className={styles.Filters}>
      {severityList.map((label, index) => {
        const key = index;
        return (
          <FormControlLabel
            key={key}
            control={
              <Checkbox
                checked={filters[label]}
                color="primary"
                onChange={handleCheckedBox}
                name={severityList[index]}
              />
            }
            label={severityList[index]}
            classes={{
              label: styles.FilterLabel,
            }}
          />
        );
      })}
    </div>
  );
  return (
    <div>
      <List
        title="Notifications"
        listItem="notifications"
        listItemName="notification"
        pageLink="notifications"
        listIcon={notificationIcon}
        searchParameter={['message']}
        button={{ show: false }}
        dialogMessage=""
        {...queries}
        restrictedAction={restrictedAction}
        additionalAction={additionalAction}
        {...columnAttributes}
        removeSortBy={[t('Entity'), t('Severity'), t('Category')]}
        filters={{ severity: filterValue }}
        filterList={filterOnSeverity}
        listOrder="desc"
      />
      {popover}
    </div>
  );
}
Example #22
Source File: MemberInvite.tsx    From knboard with MIT License 4 votes vote down vote up
MemberInvite = ({ boardId }: Props) => {
  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
  const [tagsValue, setTagsValue] = useState<UserOption[]>([]);
  const dispatch = useDispatch();

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

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

  const postInviteMember = async (users: number[]) => {
    try {
      const response = await api.post(
        `${API_BOARDS}${boardId}/invite_member/`,
        { users }
      );
      const newMembers = response.data as BoardMember[];
      dispatch(addBoardMembers(newMembers));
      dispatch(
        createSuccessToast(
          `Invited ${newMembers.map((m) => m.username).join(", ")}`
        )
      );
      handleClose();
      setTagsValue([]);
    } catch (err) {
      dispatch(createErrorToast(err.toString()));
    }
  };

  const handleClickInvite = () => {
    postInviteMember(tagsValue.map((v) => v.id));
  };

  return (
    <>
      <InviteMember>
        <Button
          variant="outlined"
          size="small"
          css={css`
            text-transform: none;
          `}
          onClick={handleClick}
          aria-controls="member-invite-menu"
          aria-haspopup="true"
          data-testid="member-invite"
        >
          Invite
        </Button>
      </InviteMember>
      <Popover
        id="member-invite-menu"
        anchorEl={anchorEl}
        getContentAnchorEl={null}
        anchorOrigin={{
          vertical: "bottom",
          horizontal: "left",
        }}
        transformOrigin={{
          vertical: "top",
          horizontal: "left",
        }}
        open={Boolean(anchorEl)}
        onClose={handleClose}
        transitionDuration={0}
      >
        <Content>
          <Description>Invite to this board</Description>
          <Box display="flex" alignItems="center">
            <UserSearch
              boardId={boardId}
              tagsValue={tagsValue}
              setTagsValue={setTagsValue}
            />
            <Button
              color="primary"
              variant="contained"
              css={css`
                font-size: 0.625rem;
                margin-left: 0.5rem;
              `}
              onClick={handleClickInvite}
              data-testid="invite-selected"
              disabled={tagsValue.length === 0}
            >
              Invite
            </Button>
          </Box>
        </Content>
      </Popover>
    </>
  );
}
Example #23
Source File: UserAvatar.tsx    From knboard with MIT License 4 votes vote down vote up
UserAvatar = () => {
  const user = useSelector((state: RootState) => state.profile.userDetail);
  const avatars = useSelector((state: RootState) => state.profile.avatars);
  const loading = useSelector(
    (state: RootState) => state.profile.avatarLoading
  );
  const dispatch = useDispatch();
  const popupState = usePopupState({
    variant: "popover",
    popupId: "avatarPopover",
  });

  const handleChangeAvatar = async (id: number) => {
    dispatch(updateAvatar(id));
  };

  return (
    <Container>
      <Avatar
        src={user?.avatar?.photo}
        alt={user?.avatar?.name}
        css={css`
          height: 6rem;
          width: 6rem;
          font-size: 36px;
        `}
      />
      <Button
        css={css`
          text-transform: initial;
          margin-top: 0.75rem;
          font-size: 0.75rem;
          padding: 4px 12px;
        `}
        variant="outlined"
        {...bindTrigger(popupState)}
        data-testid="change-avatar"
      >
        Change
      </Button>
      <Popover
        {...bindPopover(popupState)}
        keepMounted
        anchorOrigin={{ vertical: "top", horizontal: "center" }}
        transformOrigin={{ vertical: "top", horizontal: "center" }}
      >
        <AvatarListContainer>
          <GridTitle>
            <Text>Pick an Avatar</Text>
            <Button
              size="small"
              onClick={() => popupState.close()}
              css={css`
                min-width: 0;
              `}
              data-testid="close-avatar-picker"
            >
              <FontAwesomeIcon icon={faTimes} />
            </Button>
          </GridTitle>
          <Grid container>
            {avatars.map((avatar) => (
              <Grid item xs={3} key={avatar.id}>
                <img
                  onClick={() => handleChangeAvatar(avatar.id)}
                  css={css`
                    &:hover {
                      ${avatarBorderStyles}
                    }
                    ${avatar.id === loading && pulseStyles}
                  `}
                  src={avatar.photo}
                  alt={avatar.name}
                  width={60}
                  height={60}
                  data-testid={`avatar-${avatar.name}`}
                />
              </Grid>
            ))}
          </Grid>
          <GridFooter>
            <Alert
              severity="info"
              variant="outlined"
              css={css`
                font-size: 12px;
              `}
            >
              <span>
                Icons made by{" "}
                <Link
                  href="https://www.flaticon.com/authors/pixel-perfect"
                  title="Pixel perfect"
                >
                  Pixel perfect
                </Link>{" "}
                from{" "}
                <Link href="https://www.flaticon.com/" title="Flaticon">
                  www.flaticon.com
                </Link>
              </span>
            </Alert>
          </GridFooter>
        </AvatarListContainer>
      </Popover>
    </Container>
  );
}
Example #24
Source File: TaskAssignees.tsx    From knboard with MIT License 4 votes vote down vote up
TaskAssignees = ({ task }: Props) => {
  const dispatch = useDispatch();
  const membersById = useSelector(selectMembersEntities);
  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
  const [pendingAssignees, setPendingAssignees] = useState<BoardMember[]>([]);
  const members = useSelector(selectAllMembers);
  const assignees = task.assignees.map(
    (id) => membersById[id]
  ) as BoardMember[];

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

  const handleClose = () => {
    const currentIds = assignees.map((a) => a.id);
    const pendingIds = pendingAssignees.map((member) => member.id);
    if (
      !(
        pendingIds.length === currentIds.length &&
        pendingIds
          .sort()
          .every((value, index) => value === currentIds.sort()[index])
      )
    ) {
      dispatch(
        patchTask({
          id: task.id,
          fields: { assignees: pendingIds },
        })
      );
    }
    setAnchorEl(null);
  };

  const open = Boolean(anchorEl);
  const popoverId = open ? "task-assignees-popover" : undefined;

  return (
    <Container>
      <Label>Assignees</Label>
      {assignees.map((assignee) => (
        <List key={assignee.id}>
          <div>
            <Avatar
              css={css`
                height: 2rem;
                width: 2rem;
                margin-right: 0.5rem;
              `}
              src={assignee.avatar?.photo}
              alt={assignee.avatar?.name}
            >
              {assignee.username.charAt(0)}
            </Avatar>
          </div>
          <div>{assignee.username}</div>
        </List>
      ))}
      <Button
        size="small"
        onClick={handleClick}
        data-testid="open-edit-assignees"
        css={css`
          color: ${PRIMARY};
          font-size: 0.7rem;
        `}
      >
        Change
      </Button>
      <Popover
        id={popoverId}
        open={open}
        anchorEl={anchorEl}
        transitionDuration={0}
        style={{ zIndex: modalPopperIndex }}
        onClose={handleClose}
        css={css`
          .MuiPaper-rounded {
            border-radius: 0;
          }
        `}
      >
        <Content>
          <Close onClose={handleClose} onPopper />
          <ContentTitle>Assigned board members</ContentTitle>
          <AssigneeContainer>
            <AssigneeAutoComplete
              assignee={pendingAssignees}
              members={members}
              setAssignee={setPendingAssignees}
              controlId="assignee-select"
              dataTestId={"edit-assignees"}
            />
          </AssigneeContainer>
        </Content>
      </Popover>
    </Container>
  );
}
Example #25
Source File: Settings.tsx    From Twenty48 with GNU General Public License v3.0 4 votes vote down vote up
Settings: React.FC = () => {
  const version = packJson['version']
  const [theme, setTheme] = useRecoilState<ThemeType>(ThemeState)
  const [anchorEl, setAnchorEl] = useState<Element | null | undefined>(null)

  const [isCacheClear, setIsCacheClear] = useState(() => {
    if (
      localStorage.getItem(BESTSCORE) ||
      localStorage.getItem(GAMESTATE) ||
      localStorage.getItem(THEME)
    ) {
      return false
    }
    return true
  })
  const cacheClearText = isCacheClear ? 'Cache is Clear' : 'Clear Cache'

  useEffect(() => {
    localStorage.setItem(THEME, theme)
  }, [theme])

  const removeLocalStorage = (): void => {
    localStorage.removeItem(THEME)
    localStorage.removeItem(GAMESTATE)
    localStorage.removeItem(BESTSCORE)
    setIsCacheClear(true)
  }

  const setSelectedTheme = (
    e: React.ChangeEvent<{ name?: string; value: unknown }>,
  ): void => {
    setTheme(e.target.value as ThemeType)
  }

  const getPopoverBody = (): React.ReactElement => {
    return (
      <div className={`popover-body popover-body-${theme}`}>
        <div className="settings-item center">
          <h3 onClick={removeLocalStorage}>{cacheClearText}</h3>
        </div>
        <Divider />
        <div className="settings-item">
          <h3>Theme:</h3>
          <div className="center-item">
            {theme === ThemeType.DARK ? (
              <GiMoonBats size="22px" className="theme-icon" />
            ) : (
              <GiUbisoftSun size="22px" className="theme-icon" />
            )}
            <FormControl className="no-decoration">
              <NativeSelect
                value={theme}
                className={`select-${theme}`}
                onChange={setSelectedTheme}
              >
                {Object.values(ThemeType).map((type, idx) => {
                  return (
                    <option key={idx} value={type}>
                      {type}
                    </option>
                  )
                })}
              </NativeSelect>
            </FormControl>
          </div>
        </div>
        <Divider />
        <div className="settings-item">
          <h3>Version:</h3>
          <h4 className="center-item">{version}</h4>
        </div>
        <Divider />
        <div className="settings-item ">
          <h3>Made with:</h3>
          <img src={logo} className="react-icon" />
        </div>
      </div>
    )
  }

  return (
    <>
      <div>
        <FiSettings
          id="settings-cog"
          className="settings-cog"
          size="30px"
          onClick={(e): void => setAnchorEl(e.currentTarget)}
        />
      </div>

      {/* Popover component: hidden until above button click */}
      <Popover
        open={anchorEl !== null}
        anchorEl={anchorEl}
        onClose={(): void => setAnchorEl(null)}
        anchorOrigin={{
          vertical: 'bottom',
          horizontal: 'center',
        }}
        transformOrigin={{
          vertical: 'top',
          horizontal: 'center',
        }}
      >
        {getPopoverBody()}
      </Popover>
    </>
  )
}
Example #26
Source File: NotesPanel.tsx    From vscode-crossnote with GNU Affero General Public License v3.0 4 votes vote down vote up
export function NotesPanel(props: Props) {
  const classes = useStyles(props);
  const { t } = useTranslation();
  const [selectedSection, setSelectedSection] = useState<SelectedSection>(null);
  const [selectedNote, setSelectedNote] = useState<Note>(null);
  const [notes, setNotes] = useState<Note[]>([]);
  const [searchValue, setSearchValue] = useState<string>("");
  const [searchValueInputTimeout, setSearchValueInputTimeout] = useState<
    NodeJS.Timeout
  >(null);
  const [finalSearchValue, setFinalSearchValue] = useState<string>("");
  const [sortMenuAnchorEl, setSortMenuAnchorEl] = useState<HTMLElement>(null);
  const [orderBy, setOrderBy] = useState<OrderBy>(OrderBy.ModifiedAt);
  const [orderDirection, setOrderDirection] = useState<OrderDirection>(
    OrderDirection.DESC
  );

  const createNewNote = useCallback(() => {
    if (!selectedSection) {
      return;
    }
    const message: Message = {
      action: MessageAction.CreateNewNote,
      data: selectedSection,
    };
    vscode.postMessage(message);
  }, [selectedSection]);

  const onChangeSearchValue = useCallback(
    (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
      const value = event.target.value;
      setSearchValue(value);
      if (searchValueInputTimeout) {
        clearTimeout(searchValueInputTimeout);
      }
      const timeout = setTimeout(() => {
        setFinalSearchValue(value);
      }, 400);
      setSearchValueInputTimeout(timeout);
    },
    [searchValueInputTimeout]
  );

  useEffect(() => {
    const message: Message = {
      action: MessageAction.InitializedNotesPanelWebview,
      data: {},
    };
    vscode.postMessage(message);
  }, []);

  useEffect(() => {
    const onMessage = (event) => {
      const message: Message = event.data;
      let note: Note;
      switch (message.action) {
        case MessageAction.SelectedSection:
          setSelectedSection(message.data);
          if (
            selectedSection &&
            message.data.notebook.dir !== selectedSection.notebook.dir
          ) {
            setSearchValue("");
          }
          break;
        case MessageAction.SendNotes:
          const notes: Note[] = message.data;
          notes.forEach((note) => {
            note.config.createdAt = new Date(note.config.createdAt || 0);
            note.config.modifiedAt = new Date(note.config.modifiedAt || 0);
          });
          setNotes(message.data || []);
          break;
        case MessageAction.CreatedNewNote:
          note = message.data;
          note.config.createdAt = new Date(note.config.createdAt || 0);
          note.config.modifiedAt = new Date(note.config.modifiedAt || 0);

          setNotes((notes) => [note, ...notes]);
          setSelectedNote(note);
          break;
        case MessageAction.SelectedNote:
          note = message.data;
          note.config.createdAt = new Date(note.config.createdAt || 0);
          note.config.modifiedAt = new Date(note.config.modifiedAt || 0);
          setSelectedNote(note);
          break;
        default:
          break;
      }
    };
    window.addEventListener("message", onMessage);
    return () => {
      window.removeEventListener("message", onMessage);
    };
  }, [selectedSection]);

  return (
    <Box className={clsx(classes.notesPanel)}>
      <Card className={clsx(classes.topPanel)}>
        <Box className={clsx(classes.row)}>
          <div className={clsx(classes.search)}>
            <div className={clsx(classes.searchIcon)}>
              <Magnify />
            </div>
            <InputBase
              placeholder={t("search/placeholder")}
              classes={{
                root: classes.inputRoot,
                input: classes.inputInput,
              }}
              value={searchValue}
              inputProps={{ "aria-label": "search" }}
              onChange={onChangeSearchValue}
              autoComplete={"off"}
              autoCorrect={"off"}
            />
          </div>
          <IconButton onClick={createNewNote}>
            <FileEditOutline></FileEditOutline>
          </IconButton>
        </Box>
        <Box
          className={clsx(classes.row)}
          style={{ justifyContent: "space-between" }}
        >
          {selectedSection?.type === CrossnoteSectionType.Notes ||
          selectedSection?.type === CrossnoteSectionType.Notebook ? (
            <Box className={clsx(classes.row)}>
              <span role="img" aria-label="notes">
                ?
              </span>
              <Typography className={clsx(classes.sectionName)}>
                {selectedSection.notebook.name}
              </Typography>
            </Box>
          ) : selectedSection?.type === CrossnoteSectionType.Today ? (
            <Box className={clsx(classes.row)}>
              <span role="img" aria-label="today-notes">
                ?
              </span>
              <Typography className={clsx(classes.sectionName)}>
                {t("general/today")}
              </Typography>
            </Box>
          ) : selectedSection?.type === CrossnoteSectionType.Todo ? (
            <Box className={clsx(classes.row)}>
              <span role="img" aria-label="todo-notes">
                ☑️
              </span>
              <Typography className={clsx(classes.sectionName)}>
                {t("general/todo")}
              </Typography>
            </Box>
          ) : selectedSection?.type === CrossnoteSectionType.Tagged ? (
            <Box className={clsx(classes.row)}>
              <span role="img" aria-label="tagged-notes">
                ?️
              </span>
              <Typography className={clsx(classes.sectionName)}>
                {t("general/tagged")}
              </Typography>
            </Box>
          ) : selectedSection?.type === CrossnoteSectionType.Untagged ? (
            <Box className={clsx(classes.row)}>
              <span role="img" aria-label="untagged-notes">
                ?
              </span>
              <Typography className={clsx(classes.sectionName)}>
                {t("general/untagged")}
              </Typography>
            </Box>
          ) : selectedSection?.type === CrossnoteSectionType.Tag ? (
            <Box className={clsx(classes.row)}>
              <span role="img" aria-label="tag">
                ?️
              </span>
              <Typography className={clsx(classes.sectionName)}>
                {selectedSection.path}
              </Typography>
            </Box>
          ) : selectedSection?.type === CrossnoteSectionType.Encrypted ? (
            <Box className={clsx(classes.row)}>
              <span role="img" aria-label="encrypted-notes">
                ?
              </span>
              <Typography className={clsx(classes.sectionName)}>
                {t("general/encrypted")}
              </Typography>
            </Box>
          ) : selectedSection?.type === CrossnoteSectionType.Conflicted ? (
            <Box className={clsx(classes.row)}>
              <span role="img" aria-label="conflicted-notes">
                ⚠️
              </span>
              <Typography className={clsx(classes.sectionName)}>
                {t("general/conflicted")}
              </Typography>
            </Box>
          ) : (
            selectedSection?.type === CrossnoteSectionType.Directory && (
              <Box className={clsx(classes.row)}>
                <span role="img" aria-label="folder">
                  {"?"}
                </span>
                <Typography className={clsx(classes.sectionName)}>
                  {selectedSection.path}
                </Typography>
              </Box>
            )
          )}

          <Box>
            <IconButton
              onClick={(event) => setSortMenuAnchorEl(event.currentTarget)}
            >
              <SortVariant></SortVariant>
            </IconButton>
            <Popover
              anchorEl={sortMenuAnchorEl}
              keepMounted
              open={Boolean(sortMenuAnchorEl)}
              onClose={() => setSortMenuAnchorEl(null)}
            >
              <List>
                <ListItem
                  button
                  onClick={() => setOrderBy(OrderBy.ModifiedAt)}
                  className={clsx(
                    orderBy === OrderBy.ModifiedAt && classes.sortSelected
                  )}
                >
                  <ListItemText
                    primary={t("general/date-modified")}
                  ></ListItemText>
                </ListItem>
                <ListItem
                  button
                  onClick={() => setOrderBy(OrderBy.CreatedAt)}
                  className={clsx(
                    orderBy === OrderBy.CreatedAt && classes.sortSelected
                  )}
                >
                  <ListItemText
                    primary={t("general/date-created")}
                  ></ListItemText>
                </ListItem>
                <ListItem
                  button
                  onClick={() => setOrderBy(OrderBy.Title)}
                  className={clsx(
                    orderBy === OrderBy.Title && classes.sortSelected
                  )}
                >
                  <ListItemText primary={t("general/title")}></ListItemText>
                </ListItem>
                <Divider></Divider>
                <ListItem
                  button
                  onClick={() => setOrderDirection(OrderDirection.DESC)}
                  className={clsx(
                    orderDirection === OrderDirection.DESC &&
                      classes.sortSelected
                  )}
                >
                  <ListItemText primary={"Desc"}></ListItemText>
                  <ListItemIcon style={{ marginLeft: "8px" }}>
                    <SortDescending></SortDescending>
                  </ListItemIcon>
                </ListItem>
                <ListItem
                  button
                  onClick={() => setOrderDirection(OrderDirection.ASC)}
                  className={clsx(
                    orderDirection === OrderDirection.ASC &&
                      classes.sortSelected
                  )}
                >
                  <ListItemText primary={"Asc"}></ListItemText>
                  <ListItemIcon style={{ marginLeft: "8px" }}>
                    <SortAscending></SortAscending>
                  </ListItemIcon>
                </ListItem>
              </List>
            </Popover>
          </Box>
        </Box>
      </Card>

      <Notes
        searchValue={finalSearchValue}
        notes={notes}
        orderBy={orderBy}
        orderDirection={orderDirection}
        selectedNote={selectedNote}
        setSelectedNote={setSelectedNote}
      ></Notes>
    </Box>
  );
}
Example #27
Source File: Home.tsx    From signer with Apache License 2.0 4 votes vote down vote up
renderCreateNewVault() {
    const showPasswordHelp = (event: React.MouseEvent<HTMLButtonElement>) => {
      this.setState({ helpAnchorEl: event.currentTarget });
    };
    const helpOpen = Boolean(showPasswordHelp);
    const helpId = helpOpen ? 'password-help' : undefined;
    const closePasswordHelp = () => {
      this.setState({ helpAnchorEl: null });
    };
    return (
      <div>
        <Grid
          container
          spacing={4}
          direction={'column'}
          justify={'flex-start'}
          alignItems={'center'}
        >
          <Grid item className={this.props.classes.alignCenter}>
            <img src={logo} alt="logo" width={80} />
            <Typography variant={'h6'} align={'center'}>
              New Vault
            </Typography>
            <Typography>
              Please set a password for your vault. You will need it later to
              unlock it so keep it safe.
            </Typography>
          </Grid>

          <Grid item container>
            <form
              style={{ textAlign: 'center', width: '100%' }}
              onSubmit={e => e.preventDefault()}
            >
              <FormControl style={{ width: '80%', float: 'left' }}>
                <TextFieldWithFormState
                  autoFocus
                  fieldState={
                    this.props.homeContainer.homeForm.$.setPasswordField
                  }
                  required
                  label={'Set Password'}
                  type={'password'}
                />
              </FormControl>
              <IconButton
                onClick={showPasswordHelp}
                style={{
                  float: 'right',
                  transform: 'translateY(.3em)'
                }}
              >
                <HelpIcon />
              </IconButton>
              {this.state.helpAnchorEl && (
                <Popover
                  id={helpId}
                  open={helpOpen}
                  anchorEl={this.state.helpAnchorEl}
                  onClose={closePasswordHelp}
                  anchorOrigin={{
                    vertical: 'top',
                    horizontal: 'right'
                  }}
                  transformOrigin={{
                    vertical: 'bottom',
                    horizontal: 'right'
                  }}
                >
                  <Typography
                    component={'summary'}
                    style={{
                      padding: '1.4em',
                      backgroundColor: 'var(--cspr-dark-blue)',
                      color: 'white'
                    }}
                  >
                    For a password of min. length 10 please include at least one
                    of each of the following:
                    <ul>
                      <li>lowercase letter</li>
                      <li>UPPERCASE letter</li>
                      <li>Number</li>
                      <li>Special character</li>
                    </ul>
                    Or you can enter a &gt;20 char passphrase if you would
                    prefer e.g. 'correct horse battery staple'
                  </Typography>
                </Popover>
              )}
              {this.props.homeContainer.homeForm.$.setPasswordField
                .hasBeenValidated &&
                !this.props.homeContainer.homeForm.$.setPasswordField
                  .hasError && (
                  <FormControl style={{ width: '80%', float: 'left' }}>
                    <TextFieldWithFormState
                      fieldState={
                        this.props.homeContainer.homeForm.$.confirmPasswordField
                      }
                      required
                      label={'Confirm Password'}
                      type={'password'}
                    />
                  </FormControl>
                )}
              <Typography variant="subtitle2" className="text-danger">
                {this.props.homeContainer.homeForm.showFormError &&
                  this.props.homeContainer.homeForm.formError}
              </Typography>
              <FormControl fullWidth className={this.props.classes.margin}>
                <Button
                  type="submit"
                  variant="contained"
                  color="primary"
                  disabled={this.props.homeContainer.createVaultDisabled}
                  onClick={async () => {
                    const password =
                      this.props.homeContainer.homeForm.$.setPasswordField.$;
                    await this.props.accountManager.createNewVault(password);
                    this.props.homeContainer.homeForm.$.setPasswordField.reset();
                    this.props.homeContainer.homeForm.$.confirmPasswordField.reset();
                  }}
                >
                  Create Vault
                </Button>
              </FormControl>
            </form>
          </Grid>
        </Grid>
      </div>
    );
  }
Example #28
Source File: EditShortcut.tsx    From backstage with Apache License 2.0 4 votes vote down vote up
EditShortcut = ({ shortcut, onClose, anchorEl, api }: Props) => {
  const classes = useStyles();
  const alertApi = useApi(alertApiRef);
  const open = Boolean(anchorEl);

  const handleSave: SubmitHandler<FormValues> = async ({ url, title }) => {
    const newShortcut: Shortcut = {
      ...shortcut,
      url,
      title,
    };

    try {
      await api.update(newShortcut);
      alertApi.post({
        message: `Updated shortcut '${title}'`,
        severity: 'success',
      });
    } catch (error) {
      alertApi.post({
        message: `Could not update shortcut: ${error.message}`,
        severity: 'error',
      });
    }

    onClose();
  };

  const handleRemove = async () => {
    try {
      await api.remove(shortcut.id);
      alertApi.post({
        message: `Removed shortcut '${shortcut.title}' from your sidebar`,
        severity: 'success',
      });
    } catch (error) {
      alertApi.post({
        message: `Could not delete shortcut: ${error.message}`,
        severity: 'error',
      });
    }
  };

  const handleClose = () => {
    onClose();
  };

  return (
    <Popover
      open={open}
      anchorEl={anchorEl}
      onClose={onClose}
      anchorOrigin={{
        vertical: 'top',
        horizontal: 'right',
      }}
    >
      <Card className={classes.card}>
        <CardHeader
          className={classes.header}
          title="Edit Shortcut"
          titleTypographyProps={{ variant: 'subtitle2' }}
          action={
            <Button
              className={classes.button}
              variant="text"
              size="small"
              color="secondary"
              startIcon={<DeleteIcon />}
              onClick={handleRemove}
            >
              Remove
            </Button>
          }
        />
        <ShortcutForm
          formValues={{ url: shortcut.url, title: shortcut.title }}
          onClose={handleClose}
          onSave={handleSave}
        />
      </Card>
    </Popover>
  );
}
Example #29
Source File: CalendarEvent.tsx    From backstage with Apache License 2.0 4 votes vote down vote up
CalendarEvent = ({ event }: { event: GCalendarEvent }) => {
  const classes = useStyles({ event });
  const popoverState = usePopupState({
    variant: 'popover',
    popupId: event.id,
    disableAutoFocus: true,
  });
  const [hovered, setHovered] = useState(false);
  const analytics = useAnalytics();
  const zoomLink = getZoomLink(event);

  const { onClick, ...restBindProps } = bindTrigger(popoverState);

  return (
    <>
      <Paper
        onClick={e => {
          onClick(e);
          analytics.captureEvent('click', 'event info');
        }}
        {...restBindProps}
        onMouseEnter={() => setHovered(true)}
        onMouseLeave={() => setHovered(false)}
        elevation={hovered ? 4 : 1}
        className={classnames(classes.event, {
          [classes.passed]: isPassed(event),
        })}
        data-testid="calendar-event"
      >
        <Box className={classes.calendarColor} mr={1} alignSelf="stretch" />
        <Box flex={1} pt={1} pb={1}>
          <Typography
            variant="subtitle2"
            className={classnames({
              [classes.declined]:
                event.responseStatus === ResponseStatus.declined,
            })}
          >
            {event.summary}
          </Typography>
          {!isAllDay(event) && (
            <Typography variant="body2" data-testid="calendar-event-time">
              {getTimePeriod(event)}
            </Typography>
          )}
        </Box>

        {zoomLink && (
          <Tooltip title="Join Zoom Meeting">
            <Link
              data-testid="calendar-event-zoom-link"
              className={classes.link}
              href={zoomLink}
              target="_blank"
              onClick={e => {
                e.stopPropagation();
                analytics.captureEvent('click', 'zoom link');
              }}
            >
              <img src={zoomIcon} alt="Zoom link" />
            </Link>
          </Tooltip>
        )}
      </Paper>

      <Popover
        {...bindPopover(popoverState)}
        anchorOrigin={{
          vertical: 'top',
          horizontal: 'left',
        }}
        transformOrigin={{
          vertical: 'top',
          horizontal: 'center',
        }}
        data-testid="calendar-event-popover"
      >
        <CalendarEventPopoverContent event={event} />
      </Popover>
    </>
  );
}