@material-ui/core#Popper TypeScript Examples

The following examples show how to use @material-ui/core#Popper. 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: PopperTitle.tsx    From twitch-live-extension with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
PopperTitle = ({ open, anchorEl, title }: PoppertTitleProps) => {
    const classes = useStyles();

    return (
        <Popper className={classes.root} open={open} anchorEl={anchorEl} placement={'bottom-start'}>
            <Paper>
                <Typography className={classes.typography} variant={'subtitle2'} noWrap>
                    {title}
                </Typography>
            </Paper>
        </Popper>
    );
}
Example #2
Source File: index.tsx    From lobis-frontend with MIT License 5 votes vote down vote up
function Header() {
    const [anchorEl, setAnchorEl] = useState(null);

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

    const open = Boolean(anchorEl);

    return (
        <div className="landing-header">
            <div className="logo-section">
                <Link href="https://lobis.finance" target="_blank">
                    <img src={LobisIcon} width={50} />
                </Link>
                <h2>LOBIS</h2>
            </div>
            <div className="landing-header-nav-wrap">
                <Box component="div" onMouseEnter={e => handleClick(e)} onMouseLeave={e => handleClick(e)}>
                    <p className="landing-header-nav-text">Social</p>
                    <Popper className="landing-header-poper" open={open} anchorEl={anchorEl} transition>
                        {({ TransitionProps }) => (
                            <Fade {...TransitionProps} timeout={200}>
                                <div className="tooltip">
                                    <Link className="tooltip-item" href="https://github.com/LobisHQWorkspace" target="_blank">
                                        <SvgIcon color="primary" component={GitHub} />
                                        <p>GitHub</p>
                                    </Link>
                                    <Link className="tooltip-item" href="https://twitter.com/LobisHQ" target="_blank">
                                        <SvgIcon color="primary" component={Twitter} />
                                        <p>Twitter</p>
                                    </Link>
                                    <Link className="tooltip-item" href="https://discord.gg/2X44WhkFHz" target="_blank">
                                        <SvgIcon color="primary" component={Discord} />
                                        <p>Discord</p>
                                    </Link>
                                </div>
                            </Fade>
                        )}
                    </Popper>
                </Box>
            </div>
        </div>
    );
}
Example #3
Source File: Menu.tsx    From glific-frontend with GNU Affero General Public License v3.0 5 votes vote down vote up
Menu: React.SFC<MenuProps> = ({
  menus,
  children,
  eventType = 'Click',
  placement = 'top',
}) => {
  const [open, setOpen] = useState(false);
  const anchorRef = useRef<HTMLDivElement>(null);

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

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

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

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

      <Popper
        open={open}
        anchorEl={anchorRef.current}
        role={undefined}
        transition
        disablePortal={placement === 'top'}
        placement={placement}
      >
        {({ TransitionProps }) => (
          <Grow {...TransitionProps}>
            <Paper>
              <ClickAwayListener onClickAway={handleClose}>
                <div
                  onMouseEnter={eventType === 'MouseEnter' ? handleOpen : undefined}
                  onMouseLeave={eventType === 'MouseEnter' ? handleClose : undefined}
                >
                  <MenuList autoFocusItem={open}>{menuList}</MenuList>
                </div>
              </ClickAwayListener>
            </Paper>
          </Grow>
        )}
      </Popper>
    </div>
  );
}
Example #4
Source File: HelpPopper.tsx    From clearflask with Apache License 2.0 5 votes vote down vote up
render() {
    return (
      <>
        <Button
          color='inherit'
          className={classNames(this.props.className, this.props.classes.iconButton)}
          onClick={e => this.setState({ open: this.state.open ? undefined : e.currentTarget })}
          onMouseOver={e => this.setState({ open: e.currentTarget })}
          onMouseOut={e => this.setState({ open: undefined })}
        >
          {this.props.children || (
            <HelpIcon fontSize='inherit' color='inherit' />
          )}
        </Button>
        <Popper
          className={this.props.classes.popper}
          open={!!this.state.open}
          anchorEl={this.state.open}
          placement='bottom'
          transition
          modifiers={{
            flip: {
              enabled: true,
            },
            preventOverflow: {
              enabled: true,
              boundariesElement: 'scrollParent',
            },
          }}
        >
          {({ TransitionProps }) => (
            <Fade mountOnEnter unmountOnExit {...TransitionProps}>
              <Paper variant='outlined' className={this.props.classes.content}>
                {this.props.title && (<Typography variant='h6'>{this.props.title}</Typography>)}
                {this.props.description && (<Typography variant='body1'>{this.props.description}</Typography>)}
              </Paper>
            </Fade>
          )}
        </Popper>
      </>
    );
  }
Example #5
Source File: SelectionPicker.tsx    From clearflask with Apache License 2.0 5 votes vote down vote up
getSelectionPopper = (propsOverride?: Partial<React.ComponentProps<typeof Popper>>) => (props: React.ComponentProps<typeof Popper>) => (
  <Popper
    {...props}
    disablePortal
    placement='bottom-start'
    {...propsOverride}
  />
)
Example #6
Source File: index.tsx    From wonderland-frontend with MIT License 5 votes vote down vote up
function Header() {
    const [anchorEl, setAnchorEl] = useState(null);

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

    const open = Boolean(anchorEl);

    return (
        <div className="landing-header">
            <SvgIcon color="primary" component={WonderlandIcon} viewBox="0 0 174 40" style={{ minWidth: 174, minHeight: 40 }} />
            <div className="landing-header-nav-wrap">
                <Box component="div" onMouseEnter={e => handleClick(e)} onMouseLeave={e => handleClick(e)}>
                    <p className="landing-header-nav-text">Social</p>
                    <Popper className="landing-header-poper" open={open} anchorEl={anchorEl} transition>
                        {({ TransitionProps }) => (
                            <Fade {...TransitionProps} timeout={200}>
                                <div className="tooltip">
                                    <Link className="tooltip-item" href="https://github.com/Wonderland-Money/wonderland-frontend" target="_blank">
                                        <SvgIcon color="primary" component={GitHub} />
                                        <p>GitHub</p>
                                    </Link>
                                    <Link className="tooltip-item" href="https://twitter.com/wonderland_fi?s=21" target="_blank">
                                        <SvgIcon color="primary" component={Twitter} />
                                        <p>Twitter</p>
                                    </Link>
                                    <Link className="tooltip-item" href="https://t.me/joinchat/6UybL5rJMEhjN2Y5" target="_blank">
                                        <SvgIcon viewBox="0 0 32 32" color="primary" component={Telegram} />
                                        <p>Telegram</p>
                                    </Link>
                                    <Link className="tooltip-item" href="https://discord.gg/thDHseaHUt" target="_blank">
                                        <SvgIcon color="primary" component={Discord} />
                                        <p>Discord</p>
                                    </Link>
                                </div>
                            </Fade>
                        )}
                    </Popper>
                </Box>
            </div>
        </div>
    );
}
Example #7
Source File: index.tsx    From wonderland-frontend with MIT License 5 votes vote down vote up
function TimeMenu() {
    const [anchorEl, setAnchorEl] = useState(null);
    const isEthereumAPIAvailable = window.ethereum;

    const networkID = useSelector<IReduxState, number>(state => {
        return (state.app && state.app.networkID) || DEFAULD_NETWORK;
    });

    const addresses = getAddresses(networkID);

    const MEMO_ADDRESS = addresses.MEMO_ADDRESS;
    const TIME_ADDRESS = addresses.TIME_ADDRESS;

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

    const open = Boolean(anchorEl);

    return (
        <div className="time-menu-root" onMouseEnter={e => handleClick(e)} onMouseLeave={e => handleClick(e)}>
            <div className="time-menu-btn">
                <p>TIME</p>
            </div>

            <Popper className="time-menu-popper" open={open} anchorEl={anchorEl} transition>
                {({ TransitionProps }) => (
                    <Fade {...TransitionProps} timeout={200}>
                        <div className="tooltip">
                            <Link className="tooltip-item" href={`https://www.traderjoexyz.com/#/trade?inputCurrency=&outputCurrency=${TIME_ADDRESS}`} target="_blank">
                                <p>Buy on Trader Joe</p>
                            </Link>

                            {isEthereumAPIAvailable && (
                                <div className="add-tokens">
                                    <div className="divider" />
                                    <p className="add-tokens-title">ADD TOKEN TO WALLET</p>
                                    <div className="divider" />
                                    <div className="tooltip-item" onClick={addTokenToWallet("TIME", TIME_ADDRESS)}>
                                        <p>TIME</p>
                                    </div>
                                    <div className="tooltip-item" onClick={addTokenToWallet("MEMO", MEMO_ADDRESS)}>
                                        <p>MEMO</p>
                                    </div>
                                </div>
                            )}
                        </div>
                    </Fade>
                )}
            </Popper>
        </div>
    );
}
Example #8
Source File: CommentsPopper.tsx    From frontend with Apache License 2.0 5 votes vote down vote up
CommentsPopper: React.FunctionComponent<IProps> = ({
  text,
  onSave,
}) => {
  const classes = useStyles();
  const popupState = usePopupState({
    variant: "popper",
    popupId: "commentPopper",
  });
  const [comment, setComment] = React.useState("");

  React.useEffect(() => setComment(text ? text : ""), [text]);

  return (
    <React.Fragment>
      <Badge
        color="secondary"
        variant="dot"
        badgeContent=" "
        anchorOrigin={{
          vertical: "bottom",
          horizontal: "right",
        }}
        invisible={!comment || comment === ""}
      >
        <Button {...bindToggle(popupState)} color="primary">
          Comments
        </Button>
      </Badge>
      <Popper
        {...bindPopper(popupState)}
        transition
        disablePortal
        className={classes.popperContainer}
      >
        {({ TransitionProps }) => (
          <Fade {...TransitionProps} timeout={350}>
            <Paper className={classes.contentContainer}>
              <React.Fragment>
                <TextField
                  id="comment"
                  name="comment"
                  variant="outlined"
                  value={comment}
                  placeholder={"Add any additional data here"}
                  multiline
                  rows={4}
                  rowsMax={10}
                  fullWidth
                  onChange={(event) =>
                    setComment((event.target as HTMLInputElement).value)
                  }
                  inputProps={{
                    "data-testid": "comment",
                  }}
                />
                <Button
                  onClick={() => {
                    onSave(comment).then(() => popupState.close());
                  }}
                >
                  Save
                </Button>
              </React.Fragment>
            </Paper>
          </Fade>
        )}
      </Popper>
    </React.Fragment>
  );
}
Example #9
Source File: index.tsx    From rugenerous-frontend with MIT License 5 votes vote down vote up
function Header() {
  const [anchorEl, setAnchorEl] = useState(null);

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

  const open = Boolean(anchorEl);

  return (
    <div className="landing-header">
      <SvgIcon
        color="primary"
        component={RugenerousIcon}
        viewBox="0 0 400 40"
        style={{ minWidth: 174, minHeight: 40 }}
      />
      <div className="landing-header-nav-wrap">
        <Box component="div" onMouseEnter={e => handleClick(e)} onMouseLeave={e => handleClick(e)}>
          <p className="landing-header-nav-text">Social</p>
          <Popper className="landing-header-poper" open={open} anchorEl={anchorEl} transition>
            {({ TransitionProps }) => (
              <Fade {...TransitionProps} timeout={200}>
                <div className="tooltip">
                  <Link className="tooltip-item" href="https://github.com/Rugenerous" target="_blank">
                    <SvgIcon color="primary" component={GitHub} />
                    <p>GitHub</p>
                  </Link>
                  <Link className="tooltip-item" href="https://twitter.com/RUGenerous" target="_blank">
                    <SvgIcon color="primary" component={Twitter} />
                    <p>Twitter</p>
                  </Link>
                  <Link className="tooltip-item" href="https://discord.gg/JHeKjn5F2q" target="_blank">
                    <SvgIcon color="primary" component={Discord} />
                    <p>Discord</p>
                  </Link>
                </div>
              </Fade>
            )}
          </Popper>
        </Box>
      </div>
    </div>
  );
}
Example #10
Source File: index.tsx    From rugenerous-frontend with MIT License 5 votes vote down vote up
function TimeMenu() {
  const [anchorEl, setAnchorEl] = useState(null);
  const isEthereumAPIAvailable = window.ethereum;

  const networkID = useSelector<IReduxState, number>(state => {
    return (state.app && state.app.networkID) || DEFAULD_NETWORK;
  });

  const addresses = getAddresses(networkID);

  const SRUG_ADDRESS = addresses.SRUG_ADDRESS;
  const RUG_ADDRESS = addresses.RUG_ADDRESS;

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

  const open = Boolean(anchorEl);

  return (
    <div className="rug-menu-root" onMouseEnter={e => handleClick(e)} onMouseLeave={e => handleClick(e)}>
      <div className="rug-menu-btn">
        <p>BUY RUG</p>
      </div>

      <Popper className="rug-menu-popper" open={open} anchorEl={anchorEl} transition>
        {({ TransitionProps }) => (
          <Fade {...TransitionProps} timeout={200}>
            <div className="tooltip">
              <Link component={NavLink} to="/buy/tjDex" className="tooltip-item">
                <p>Buy on TradeJoe</p>
              </Link>
              {isEthereumAPIAvailable && (
                <div className="add-tokens">
                  <div className="divider" />
                  <p className="add-tokens-title">ADD TOKEN TO WALLET</p>
                  <div className="divider" />
                  <div className="tooltip-item" onClick={addTokenToWallet("RUG", RUG_ADDRESS)}>
                    <p>RUG</p>
                  </div>
                  <div className="tooltip-item" onClick={addTokenToWallet("SRUG", SRUG_ADDRESS)}>
                    <p>SRUG</p>
                  </div>
                </div>
              )}
            </div>
          </Fade>
        )}
      </Popper>
    </div>
  );
}
Example #11
Source File: index.tsx    From lobis-frontend with MIT License 5 votes vote down vote up
function LobiMenu() {
    const [anchorEl, setAnchorEl] = useState(null);
    const isEthereumAPIAvailable = window.ethereum;

    const LOBI_ADDRESS = addresses.lobi;
    const SLOBI_ADDRESS = addresses.sLobi;

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

    const open = Boolean(anchorEl);

    return (
        <div className="lobi-menu-root" onMouseEnter={e => handleClick(e)} onMouseLeave={e => handleClick(e)}>
            <div className="lobi-menu-btn">
                <p>{TOKEN_NAME}</p>
            </div>

            <Popper className="lobi-menu-popper" open={open} anchorEl={anchorEl} transition>
                {({ TransitionProps }) => (
                    <Fade {...TransitionProps} timeout={200}>
                        <div className="tooltip">
                            <Link className="tooltip-item" href={`https://app.sushi.com/swap?outputCurrency=0xDEc41Db0c33F3F6f3cb615449C311ba22D418A8d`} target="_blank">
                                <p>Buy on Sushiswap</p>
                            </Link>

                            {isEthereumAPIAvailable && (
                                <div className="add-tokens">
                                    <div className="tooltip-item" onClick={addTokenToWallet(`${TOKEN_NAME}`, LOBI_ADDRESS)}>
                                        <p>{TOKEN_NAME}</p>
                                    </div>
                                    <div className="tooltip-item" onClick={addTokenToWallet(`${STAKING_TOKEN_NAME}`, SLOBI_ADDRESS)}>
                                        <p>{STAKING_TOKEN_NAME}</p>
                                    </div>
                                </div>
                            )}
                        </div>
                    </Fade>
                )}
            </Popper>
        </div>
    );
}
Example #12
Source File: index.tsx    From rugenerous-frontend with MIT License 4 votes vote down vote up
function Stake() {
  const [anchorEl, setAnchorEl] = useState(null);

  const dispatch = useDispatch();
  const { provider, address, connect, chainID, checkWrongNetwork } = useWeb3Context();

  const [view, setView] = useState(0);
  const [quantity, setQuantity] = useState<string>("");

  const isAppLoading = useSelector<IReduxState, boolean>(state => state.app.loading);
  const app = useSelector<IReduxState, IAppSlice>(state => state.app);
  const currentIndex = useSelector<IReduxState, string>(state => {
    return state.app.currentIndex;
  });
  const fiveDayRate = useSelector<IReduxState, number>(state => {
    return state.app.fiveDayRate;
  });
  const timeBalance = useSelector<IReduxState, string>(state => {
    return state.account.balances && state.account.balances.rug;
  });
  const warmupBalance = useSelector<IReduxState, string>(state => {
    return state.account.warmupInfo && state.account.warmupInfo.deposit;
  });
  const gonsBalance = useSelector<IReduxState, string>(state => {
    return state.account.warmupInfo && state.account.warmupInfo.gonsBalance;
  });
  const warmupExpiry = useSelector<IReduxState, string>(state => {
    return state.account.warmupInfo && state.account.warmupInfo.expiry;
  });
  const currentEpoch = useSelector<IReduxState, string>(state => {
    return state.account.warmupInfo && state.account.warmupInfo.epoch;
  });
  const memoBalance = useSelector<IReduxState, string>(state => {
    return state.account.balances && state.account.balances.srug;
  });
  //Adding Durag - KM
  const duragBalance = useSelector<IReduxState, string>(state => {
    return state.account.balances && state.account.balances.durag;
  });
  const stakeAllowance = useSelector<IReduxState, number>(state => {
    return state.account.staking && state.account.staking.rug;
  });
  const unstakeAllowance = useSelector<IReduxState, number>(state => {
    return state.account.staking && state.account.staking.srug;
  });
  const stakingRebase = useSelector<IReduxState, number>(state => {
    return state.app.stakingRebase;
  });
  const stakingAPY = useSelector<IReduxState, number>(state => {
    return state.app.stakingAPY;
  });
  const stakingTVL = useSelector<IReduxState, number>(state => {
    return state.app.stakingTVL;
  });

  const pendingTransactions = useSelector<IReduxState, IPendingTxn[]>(state => {
    return state.pendingTransactions;
  });

  const setMax = () => {
    if (view === 0) {
      const fullBalance = Number(timeBalance);
      setQuantity(trim(fullBalance, 4));
      console.log(quantity);
    } else {
      setQuantity(memoBalance);
    }
  };

  const onSeekApproval = async (token: string) => {
    if (await checkWrongNetwork()) return;

    await dispatch(changeApproval({ address, token, provider, networkID: chainID }));
  };

  const onChangeStake = async (action: string) => {
    if (await checkWrongNetwork()) return;
    if (quantity === "" || parseFloat(quantity) === 0) {
      dispatch(warning({ text: action === "stake" ? messages.before_stake : messages.before_unstake }));
    } else {
      await dispatch(
        changeStake({
          address,
          action,
          value: String(quantity),
          provider,
          networkID: chainID,
          warmUpBalance: Number(warmupBalance),
        }),
      );
      setQuantity("");
    }
  };

  const onChangeWarmup = async (action: string) => {
    if (await checkWrongNetwork()) return;
    await dispatch(
      forfeitOrClaim({
        address,
        action,
        provider,
        networkID: chainID,
      }),
    );
  };

  const hasAllowance = useCallback(
    token => {
      if (token === "rug") return stakeAllowance > 0;
      if (token === "srug") return unstakeAllowance > 0;
      return 0;
    },
    [stakeAllowance],
  );

  const changeView = (newView: number) => () => {
    setView(newView);
    setQuantity("");
  };

  const trimmedMemoBalance = trim(Number(memoBalance), 6);
  const trimmedMemoBalanceInUSD = trim(Number(memoBalance) * app.marketPrice, 2);
  const trimmedStakingAPY = trim(stakingAPY * 100, 1);
  const stakingRebasePercentage = trim(stakingRebase * 100, 4);
  const nextRewardValue = trim((Number(stakingRebasePercentage) / 100) * Number(trimmedMemoBalance), 6);
  const nextRewardInUSD = Number(nextRewardValue) * app.marketPrice;
  const trimmedEarningsPerDay = trim(nextRewardInUSD * 3, 2);

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

  const open = Boolean(anchorEl);

  return (
    <div className="stake-view">
      <Zoom in={true}>
        <div className="stake-card">
          <Grid className="stake-card-grid" container direction="column" spacing={2}>
            <Grid item>
              <div className="stake-card-header">
                <p className="stake-card-header-title">RUG Staking (?, ?)</p>
                <RebaseTimer />
              </div>
            </Grid>

            <Grid item>
              <div className="stake-card-metrics">
                <Grid container spacing={2}>
                  <Grid item xs={12} sm={4} md={4} lg={4}>
                    <div className="stake-card-apy">
                      <p className="stake-card-metrics-title">APY</p>
                      <>
                        {stakingAPY ? (
                          <div>
                            <p
                              className="stake-card-metrics-value"
                              onMouseEnter={e => handleClick(e)}
                              onMouseLeave={e => handleClick(e)}
                            >
                              `'Big' - trust me bro...`
                            </p>

                            <Popper className="rug-menu-popper tooltip" open={open} anchorEl={anchorEl} transition>
                              {({ TransitionProps }) => (
                                <Fade {...TransitionProps} timeout={200}>
                                  <p className="tooltip-item">
                                    {new Intl.NumberFormat("en-US").format(Number(trimmedStakingAPY))}%
                                  </p>
                                </Fade>
                              )}
                            </Popper>
                          </div>
                        ) : (
                          <p className="stake-card-metrics-value">
                            <Skeleton width="150px" />
                          </p>
                        )}
                      </>
                    </div>
                  </Grid>

                  <Grid item xs={6} sm={4} md={4} lg={4}>
                    <div className="stake-card-index">
                      <p className="stake-card-metrics-title">Current Index</p>
                      <p className="stake-card-metrics-value">
                        {currentIndex ? <>{trim(Number(currentIndex), 2)} RUG</> : <Skeleton width="150px" />}
                      </p>
                    </div>
                  </Grid>

                  <Grid item xs={6} sm={4} md={4} lg={4}>
                    <div className="stake-card-index">
                      <p className="stake-card-metrics-title">Earnings per Day</p>
                      <p className="stake-card-metrics-value">
                        {currentIndex ? <>${trimmedEarningsPerDay}</> : <Skeleton width="150px" />}
                      </p>
                    </div>
                  </Grid>
                </Grid>
              </div>
            </Grid>

            <div className="stake-card-area">
              {!address && (
                <div className="stake-card-wallet-notification">
                  <div className="stake-card-wallet-connect-btn" onClick={connect}>
                    <p>Connect Wallet</p>
                  </div>
                  <p className="stake-card-wallet-desc-text">Connect your wallet to stake RUG tokens!</p>
                </div>
              )}
              {address && (
                <div>
                  <div className="stake-card-action-area">
                    <div className="stake-card-action-stage-btns-wrap">
                      <div
                        onClick={changeView(0)}
                        className={classnames("stake-card-action-stage-btn", { active: !view })}
                      >
                        <p>Stake</p>
                      </div>
                      <div
                        onClick={changeView(1)}
                        className={classnames("stake-card-action-stage-btn", { active: view })}
                      >
                        <p>Unstake</p>
                      </div>
                    </div>

                    <div className="stake-card-action-row">
                      <OutlinedInput
                        type="number"
                        placeholder="Amount"
                        className="stake-card-action-input"
                        value={quantity}
                        onChange={e => setQuantity(e.target.value)}
                        labelWidth={0}
                        endAdornment={
                          <InputAdornment position="end">
                            <div onClick={setMax} className="stake-card-action-input-btn">
                              <p>Max</p>
                            </div>
                          </InputAdornment>
                        }
                      />

                      {view === 0 && (
                        <div className="stake-card-tab-panel">
                          {address && hasAllowance("rug") ? (
                            <div
                              className="stake-card-tab-panel-btn"
                              onClick={() => {
                                if (isPendingTxn(pendingTransactions, "staking")) return;
                                onChangeStake("stake");
                              }}
                            >
                              <p>{txnButtonText(pendingTransactions, "staking", "Stake RUG")}</p>
                            </div>
                          ) : (
                            <div
                              className="stake-card-tab-panel-btn"
                              onClick={() => {
                                if (isPendingTxn(pendingTransactions, "approve_staking")) return;
                                onSeekApproval("rug");
                              }}
                            >
                              <p>{txnButtonText(pendingTransactions, "approve_staking", "Approve")}</p>
                            </div>
                          )}
                        </div>
                      )}

                      {view === 1 && (
                        <div className="stake-card-tab-panel">
                          {address && hasAllowance("srug") ? (
                            <div
                              className="stake-card-tab-panel-btn"
                              onClick={() => {
                                if (isPendingTxn(pendingTransactions, "unstaking")) return;
                                onChangeStake("unstake");
                              }}
                            >
                              <p>{txnButtonText(pendingTransactions, "unstaking", "Unstake RUG")}</p>
                            </div>
                          ) : (
                            <div
                              className="stake-card-tab-panel-btn"
                              onClick={() => {
                                if (isPendingTxn(pendingTransactions, "approve_unstaking")) return;
                                onSeekApproval("srug");
                              }}
                            >
                              <p>{txnButtonText(pendingTransactions, "approve_unstaking", "Approve")}</p>
                            </div>
                          )}
                        </div>
                      )}
                    </div>

                    <div className="stake-card-action-help-text">
                      {address && ((!hasAllowance("rug") && view === 0) || (!hasAllowance("srug") && view === 1)) && (
                        <p>
                          Note: The "Approve" transaction is only needed when staking/unstaking for the first rug;
                          subsequent staking/unstaking only requires you to perform the "Stake" or "Unstake"
                          transaction.
                        </p>
                      )}
                    </div>
                  </div>

                  <div className="stake-user-data">
                    <div className="data-row">
                      <p className="data-row-name">Your Balance</p>
                      <p className="data-row-value">
                        {isAppLoading ? <Skeleton width="80px" /> : <>{trim(Number(timeBalance), 4)} RUG</>}
                      </p>
                    </div>

                    <div className="data-row">
                      <p className="data-row-name">Your Durag Balance</p>
                      <p className="data-row-value">
                        {isAppLoading ? <Skeleton width="80px" /> : <>{trim(Number(duragBalance), 4)} DURAG</>}
                      </p>
                    </div>

                    {Number(warmupBalance) > 0 && (
                      <>
                        <br />
                        <div className="data-row">
                          <p className="data-row-name">Your Warm Up Balance</p>
                          <p className="data-row-value">
                            {isAppLoading ? <Skeleton width="80px" /> : <>{trim(Number(warmupBalance), 4)} RUG</>}
                          </p>
                        </div>

                        {Number(warmupBalance) < Number(gonsBalance) && (
                          <>
                            <div className="data-row">
                              <p className="data-row-name">Warm Up Balance with Rebase Rewards</p>
                              <p className="data-row-value">
                                {isAppLoading ? <Skeleton width="80px" /> : <>{trim(Number(gonsBalance), 4)} RUG</>}
                              </p>
                            </div>
                          </>
                        )}

                        <div className="data-row">
                          <p className="data-row-name">Pending Warm Up Till Release</p>
                          <p className="data-row-value">
                            {isAppLoading ? (
                              <Skeleton width="80px" />
                            ) : Number(warmupExpiry) <= Number(currentEpoch) ? (
                              <>
                                <div
                                  className="claim-btn"
                                  onClick={() => {
                                    if (isPendingTxn(pendingTransactions, "claim")) return;
                                    onChangeWarmup("claim");
                                  }}
                                >
                                  <p>{txnButtonText(pendingTransactions, "claim", "Claim SRUG")}</p>
                                </div>
                                <br />
                              </>
                            ) : (
                              <div className="warmup-text">
                                {" "}
                                {Number(warmupExpiry) - Number(currentEpoch)} Rebase(s) left till claimable
                                <div className="forfeit-btn">{BasicModal(onChangeWarmup)}</div>
                              </div>
                            )}
                          </p>
                        </div>
                      </>
                    )}

                    <div className="data-row">
                      <p className="data-row-name">Your Staked Balance</p>
                      <p className="data-row-value">
                        {isAppLoading ? (
                          <Skeleton width="80px" />
                        ) : (
                          <>
                            {trimmedMemoBalance} sRUG (${trimmedMemoBalanceInUSD})
                          </>
                        )}
                      </p>
                    </div>

                    <div className="data-row">
                      <p className="data-row-name">Next Reward Amount</p>
                      <p className="data-row-value">
                        {isAppLoading ? (
                          <Skeleton width="80px" />
                        ) : (
                          <>
                            {nextRewardValue} sRUG (${trim(nextRewardInUSD, 2)})
                          </>
                        )}
                      </p>
                    </div>

                    <div className="data-row">
                      <p className="data-row-name">Next Reward Yield</p>
                      <p className="data-row-value">
                        {isAppLoading ? <Skeleton width="80px" /> : <>{stakingRebasePercentage}%</>}
                      </p>
                    </div>

                    <div className="data-row">
                      <p className="data-row-name">ROI (5-Day Rate)</p>
                      <p className="data-row-value">
                        {isAppLoading ? <Skeleton width="80px" /> : <>{trim(Number(fiveDayRate) * 100, 4)}%</>}
                      </p>
                    </div>

                    <div className="stake-card-action-help-text">
                      <br />
                      <p>
                        Please Note: there is a two-epoch warm-up period when staking. One epoch is eight hours (one
                        rebase window). During warm-up, your staked tokens are held by the warm-up contract. Exiting the
                        warm-up early will return your original deposit to your wallet.
                        <br />
                        <br />
                        Your staked tokens and their accrued rebase rewards will be available to claim at the start of
                        the third epoch after you originally staked. Once claimed, the tokens move to your staked token
                        balance, where they will continue to earn rebase rewards and can be unstaked at any time without
                        penalty.
                      </p>
                    </div>
                  </div>
                </div>
              )}
            </div>
          </Grid>
        </div>
      </Zoom>
    </div>
  );
}
Example #13
Source File: ClosablePopper.tsx    From clearflask with Apache License 2.0 4 votes vote down vote up
render() {
    const {
      children,
      classes,
      theme,
      paperClassName,
      zIndex,
      onClose,
      anchorType,
      closeButtonPosition,
      clickAway,
      clickAwayProps,
      arrow,
      transitionCmpt,
      transitionProps,
      placement,
      useBackdrop,
      ...popperProps
    } = this.props;

    const TransitionCmpt = transitionCmpt || Fade;

    var anchorEl: PopperProps['anchorEl'];
    if (this.props.anchorType === 'native') {
      anchorEl = this.props.anchor;
    } else {
      // Overly complicated way to ensure popper.js
      // always gets some kind of coordinates
      anchorEl = () => {
        var el: ReferenceObject | undefined | null;
        if (!el && this.props.anchorType === 'ref') {
          el = this.props.anchor.current;
        }
        if (!el && this.props.anchorType === 'element') {
          el = (typeof this.props.anchor === 'function')
            ? this.props.anchor()
            : this.props.anchor;
        }
        if (!el && this.props.anchorType === 'virtual') {
          const virtualAnchor = this.props.anchor;
          const bounds = virtualAnchor?.() || this.boundsLast;
          if (!!bounds) {
            this.boundsLast = bounds;
          }
          if (bounds) {
            el = {
              clientHeight: bounds.height,
              clientWidth: bounds.width,
              getBoundingClientRect: () => {
                const boundsInner = virtualAnchor?.() || this.boundsLast || bounds;
                this.boundsLast = boundsInner;
                const domRect: MyDomRect = {
                  height: boundsInner.height,
                  width: boundsInner.width,
                  top: boundsInner.top,
                  bottom: boundsInner.top + boundsInner.height,
                  left: boundsInner.left,
                  right: boundsInner.left + boundsInner.width,
                  x: boundsInner.width >= 0 ? boundsInner.left : (boundsInner.left - boundsInner.width),
                  y: boundsInner.height >= 0 ? boundsInner.top : (boundsInner.top - boundsInner.height),
                  toJSON: () => domRect,
                };
                return domRect;
              }
            }
          }
        }
        if (!el) {
          el = this.anchorRef.current;
        }
        if (!el) {
          const domRect: MyDomRect = {
            height: 0,
            width: 0,
            top: 0,
            bottom: 0,
            left: 0,
            right: 0,
            x: 0,
            y: 0,
            toJSON: () => domRect,
          };
          el = {
            clientHeight: 0,
            clientWidth: 0,
            getBoundingClientRect: () => domRect,
          };
        }
        return el;
      };
    }

    return (
      <>
        <div ref={this.anchorRef} />
        <Popper
          placement={placement || 'right-start'}
          transition
          {...popperProps}
          anchorEl={anchorEl}
          className={classNames(
            classes.popper,
            popperProps.className,
            !popperProps.open && classes.hidden,
          )}
          modifiers={{
            ...(arrow ? {
              arrow: {
                enabled: true,
                element: this.arrowRef.current || '[x-arrow]',
              },
            } : {}),
            ...(popperProps.modifiers || {}),
          }}
        >
          {props => (
            <ClickAwayListener
              mouseEvent='onMouseDown'
              onClickAway={() => clickAway && onClose()}
              {...clickAwayProps}
            >
              <TransitionCmpt
                {...props.TransitionProps}
                {...transitionProps}
              >
                <Paper className={classNames(paperClassName, classes.paper)}>
                  {arrow && (
                    <span x-arrow='true' className={classes.arrow} ref={this.arrowRef} />
                  )}
                  {closeButtonPosition !== 'disable' && (
                    <IconButton
                      classes={{
                        label: classes.closeButtonLabel,
                        root: classNames(
                          classes.closeButton,
                          (closeButtonPosition === 'top-right' || !closeButtonPosition) && classes.closeButtonTopRight,
                          closeButtonPosition === 'top-left' && classes.closeButtonTopLeft,
                        ),
                      }}
                      aria-label="Close"
                      onClick={() => onClose()}
                    >
                      <CloseIcon className={classes.closeIcon} fontSize='inherit' />
                    </IconButton>
                  )}
                  {children}
                </Paper>
              </TransitionCmpt>
            </ClickAwayListener>
          )}
        </Popper>
      </>
    );
  }
Example #14
Source File: Search.tsx    From gatsby-theme-pristine with Apache License 2.0 4 votes vote down vote up
Search: React.FC = () => {
  const data = useStaticQuery(graphql`
    query SearchIndexQuery {
      localSearchPages {
        index
        store
      }
    }
  `);
  const [query, setQuery] = useState("");
  const [selectedItem, setSelectedItem] = useState();
  const [anchorEl, setAnchorEl] = React.useState(null);

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

  const results = useFlexSearch(
    query,
    data.localSearchPages.index,
    JSON.parse(data.localSearchPages.store),
  );
  const open = Boolean(anchorEl);
  const selected = selectedItem || results[0];

  return (
    <div style={{ marginRight: "5px" }}>
      <InputBase
        onKeyDown={(ev: any) => {
          // Enter
          if (ev.keyCode === 13) {
            if (selected) {
              setQuery("");
              if (anchorEl !== null) {
                (anchorEl as any).blur();
                setAnchorEl(null);
              }
              navigate(selected.slug);
            }
          } else if (ev.keyCode === 40) {
            // Down
            const currIndex = _.findIndex(results, (result: any) => result.id === selected.id);
            const newSelected = results[currIndex + 1];
            if (newSelected) {
              setSelectedItem(newSelected);
            }
          } else if (ev.keyCode === 38) {
            // Up
            const currIndex = _.findIndex(results, (result: any) => result.id === selected.id);
            const newSelected = results[currIndex - 1];
            if (newSelected) {
              setSelectedItem(newSelected);
            }
          }
        }}
        onChange={(ev: any) => {
          setQuery(ev.target.value);
        }}
        onFocus={handleFocus}
        placeholder={"? Search"}
        value={query}
        style={{
          fontSize: "16px",
          background: "rgba(0,0,0,0.1)",
          borderRadius: "4px",
          paddingRight: "10px",
          paddingLeft: "10px",
          paddingTop: "4px",
          marginBottom: "4px",
        }}

      />
      {results.length !== 0 &&
        <Popper
          open={open}
          style={{ marginTop: "5px" }}
          anchorEl={anchorEl as any}
          popperOptions={{
            placement: "bottom",
          }}
        >
          <Paper style={{ maxWidth: "450px", marginTop: "12px" }}>
            <List>
              {results.map((result: any) => (
                <ListItem key={result.id} selected={selected.id === result.id}>
                  <GatsbyLink to={result.slug} style={{ textDecoration: "none" }} onClick={() => {
                    setQuery("");
                    setAnchorEl(null);
                  }}>
                    <ListItemText primary={result.slug} secondary={result.title} />
                  </GatsbyLink>
                </ListItem>
              ))}
            </List>
          </Paper>
        </Popper>
      }
    </div>
  );

}
Example #15
Source File: ContactBar.tsx    From glific-frontend with GNU Affero General Public License v3.0 4 votes vote down vote up
ContactBar: React.SFC<ContactBarProps> = (props) => {
  const {
    contactId,
    collectionId,
    contactBspStatus,
    lastMessageTime,
    contactStatus,
    displayName,
    handleAction,
    isSimulator,
  } = props;

  const [anchorEl, setAnchorEl] = useState(null);
  const open = Boolean(anchorEl);
  const history = useHistory();
  const [showCollectionDialog, setShowCollectionDialog] = useState(false);
  const [showFlowDialog, setShowFlowDialog] = useState(false);
  const [showBlockDialog, setShowBlockDialog] = useState(false);
  const [showClearChatDialog, setClearChatDialog] = useState(false);
  const [addContactsDialogShow, setAddContactsDialogShow] = useState(false);
  const [showTerminateDialog, setShowTerminateDialog] = useState(false);
  const { t } = useTranslation();

  // get collection list
  const [getCollections, { data: collectionsData }] = useLazyQuery(GET_COLLECTIONS, {
    variables: setVariables(),
  });

  // get the published flow list
  const [getFlows, { data: flowsData }] = useLazyQuery(GET_FLOWS, {
    variables: setVariables({
      status: FLOW_STATUS_PUBLISHED,
    }),
    fetchPolicy: 'network-only', // set for now, need to check cache issue
  });

  // get contact collections
  const [getContactCollections, { data }] = useLazyQuery(GET_CONTACT_COLLECTIONS, {
    variables: { id: contactId },
    fetchPolicy: 'cache-and-network',
  });

  useEffect(() => {
    if (contactId) {
      getContactCollections();
    }
  }, [contactId]);

  // mutation to update the contact collections
  const [updateContactCollections] = useMutation(UPDATE_CONTACT_COLLECTIONS, {
    onCompleted: (result: any) => {
      const { numberDeleted, contactGroups } = result.updateContactGroups;
      const numberAdded = contactGroups.length;
      let notification = `Added to ${numberAdded} collection${numberAdded === 1 ? '' : 's'}`;
      if (numberDeleted > 0 && numberAdded > 0) {
        notification = `Added to ${numberDeleted} collection${
          numberDeleted === 1 ? '' : 's  and'
        } removed from ${numberAdded} collection${numberAdded === 1 ? '' : 's '}`;
      } else if (numberDeleted > 0) {
        notification = `Removed from ${numberDeleted} collection${numberDeleted === 1 ? '' : 's'}`;
      }
      setNotification(notification);
    },
    refetchQueries: [{ query: GET_CONTACT_COLLECTIONS, variables: { id: contactId } }],
  });

  const [blockContact] = useMutation(UPDATE_CONTACT, {
    onCompleted: () => {
      setNotification(t('Contact blocked successfully.'));
    },
    refetchQueries: [{ query: SEARCH_QUERY, variables: SEARCH_QUERY_VARIABLES }],
  });

  const [addFlow] = useMutation(ADD_FLOW_TO_CONTACT, {
    onCompleted: () => {
      setNotification(t('Flow started successfully.'));
    },
    onError: (error) => {
      setErrorMessage(error);
    },
  });

  const [addFlowToCollection] = useMutation(ADD_FLOW_TO_COLLECTION, {
    onCompleted: () => {
      setNotification(t('Your flow will start in a couple of minutes.'));
    },
  });

  // mutation to clear the chat messages of the contact
  const [clearMessages] = useMutation(CLEAR_MESSAGES, {
    variables: { contactId },
    onCompleted: () => {
      setClearChatDialog(false);
      setNotification(t('Conversation cleared for this contact.'), 'warning');
    },
  });

  let collectionOptions = [];
  let flowOptions = [];
  let initialSelectedCollectionIds: Array<any> = [];
  let selectedCollectionsName;
  let selectedCollections: any = [];
  let assignedToCollection: any = [];

  if (data) {
    const { groups } = data.contact.contact;
    initialSelectedCollectionIds = groups.map((group: any) => group.id);

    selectedCollections = groups.map((group: any) => group.label);
    selectedCollectionsName = shortenMultipleItems(selectedCollections);

    assignedToCollection = groups.map((group: any) => group.users.map((user: any) => user.name));
    assignedToCollection = Array.from(new Set([].concat(...assignedToCollection)));
    assignedToCollection = shortenMultipleItems(assignedToCollection);
  }

  if (collectionsData) {
    collectionOptions = collectionsData.groups;
  }

  if (flowsData) {
    flowOptions = flowsData.flows;
  }

  let dialogBox = null;

  const handleCollectionDialogOk = (selectedCollectionIds: any) => {
    const finalSelectedCollections = selectedCollectionIds.filter(
      (selectedCollectionId: any) => !initialSelectedCollectionIds.includes(selectedCollectionId)
    );
    const finalRemovedCollections = initialSelectedCollectionIds.filter(
      (gId: any) => !selectedCollectionIds.includes(gId)
    );

    if (finalSelectedCollections.length > 0 || finalRemovedCollections.length > 0) {
      updateContactCollections({
        variables: {
          input: {
            contactId,
            addGroupIds: finalSelectedCollections,
            deleteGroupIds: finalRemovedCollections,
          },
        },
      });
    }

    setShowCollectionDialog(false);
  };

  const handleCollectionDialogCancel = () => {
    setShowCollectionDialog(false);
  };

  if (showCollectionDialog) {
    dialogBox = (
      <SearchDialogBox
        selectedOptions={initialSelectedCollectionIds}
        title={t('Add contact to collection')}
        handleOk={handleCollectionDialogOk}
        handleCancel={handleCollectionDialogCancel}
        options={collectionOptions}
      />
    );
  }

  const handleFlowSubmit = (flowId: any) => {
    const flowVariables: any = {
      flowId,
    };

    if (contactId) {
      flowVariables.contactId = contactId;
      addFlow({
        variables: flowVariables,
      });
    }

    if (collectionId) {
      flowVariables.groupId = collectionId;
      addFlowToCollection({
        variables: flowVariables,
      });
    }

    setShowFlowDialog(false);
  };

  const closeFlowDialogBox = () => {
    setShowFlowDialog(false);
  };

  if (showFlowDialog) {
    dialogBox = (
      <DropdownDialog
        title={t('Select flow')}
        handleOk={handleFlowSubmit}
        handleCancel={closeFlowDialogBox}
        options={flowOptions}
        placeholder={t('Select flow')}
        description={t('The contact will be responded as per the messages planned in the flow.')}
      />
    );
  }

  const handleClearChatSubmit = () => {
    clearMessages();
    setClearChatDialog(false);
    handleAction();
  };

  if (showClearChatDialog) {
    const bodyContext =
      'All the conversation data for this contact will be deleted permanently from Glific. This action cannot be undone. However, you should be able to access it in reports if you have backup configuration enabled.';
    dialogBox = (
      <DialogBox
        title="Are you sure you want to clear all conversation for this contact?"
        handleOk={handleClearChatSubmit}
        handleCancel={() => setClearChatDialog(false)}
        alignButtons="center"
        buttonOk="YES, CLEAR"
        colorOk="secondary"
        buttonCancel="MAYBE LATER"
      >
        <p className={styles.DialogText}>{bodyContext}</p>
      </DialogBox>
    );
  }

  const handleBlock = () => {
    blockContact({
      variables: {
        id: contactId,
        input: {
          status: 'BLOCKED',
        },
      },
    });
  };

  if (showBlockDialog) {
    dialogBox = (
      <DialogBox
        title="Do you want to block this contact"
        handleOk={handleBlock}
        handleCancel={() => setShowBlockDialog(false)}
        alignButtons="center"
        colorOk="secondary"
      >
        <p className={styles.DialogText}>
          You will not be able to view their chats and interact with them again
        </p>
      </DialogBox>
    );
  }

  if (showTerminateDialog) {
    dialogBox = <TerminateFlow contactId={contactId} setDialog={setShowTerminateDialog} />;
  }

  let flowButton: any;

  const blockContactButton = contactId ? (
    <Button
      data-testid="blockButton"
      className={styles.ListButtonDanger}
      color="secondary"
      disabled={isSimulator}
      onClick={() => setShowBlockDialog(true)}
    >
      {isSimulator ? (
        <BlockDisabledIcon className={styles.Icon} />
      ) : (
        <BlockIcon className={styles.Icon} />
      )}
      Block Contact
    </Button>
  ) : null;

  if (collectionId) {
    flowButton = (
      <Button
        data-testid="flowButton"
        className={styles.ListButtonPrimary}
        onClick={() => {
          getFlows();
          setShowFlowDialog(true);
        }}
      >
        <FlowIcon className={styles.Icon} />
        Start a flow
      </Button>
    );
  } else if (
    contactBspStatus &&
    status.includes(contactBspStatus) &&
    !is24HourWindowOver(lastMessageTime)
  ) {
    flowButton = (
      <Button
        data-testid="flowButton"
        className={styles.ListButtonPrimary}
        onClick={() => {
          getFlows();
          setShowFlowDialog(true);
        }}
      >
        <FlowIcon className={styles.Icon} />
        Start a flow
      </Button>
    );
  } else {
    let toolTip = 'Option disabled because the 24hr window expired';
    let disabled = true;
    // if 24hr window expired & contact type HSM. we can start flow with template msg .
    if (contactBspStatus === 'HSM') {
      toolTip =
        'Since the 24-hour window has passed, the contact will only receive a template message.';
      disabled = false;
    }
    flowButton = (
      <Tooltip title={toolTip} placement="right">
        <span>
          <Button
            data-testid="disabledFlowButton"
            className={styles.ListButtonPrimary}
            disabled={disabled}
            onClick={() => {
              getFlows();
              setShowFlowDialog(true);
            }}
          >
            {disabled ? (
              <FlowUnselectedIcon className={styles.Icon} />
            ) : (
              <FlowIcon className={styles.Icon} />
            )}
            Start a flow
          </Button>
        </span>
      </Tooltip>
    );
  }

  const terminateFLows = contactId ? (
    <Button
      data-testid="terminateButton"
      className={styles.ListButtonPrimary}
      onClick={() => {
        setShowTerminateDialog(!showTerminateDialog);
      }}
    >
      <TerminateFlowIcon className={styles.Icon} />
      Terminate flows
    </Button>
  ) : null;

  const viewDetails = contactId ? (
    <Button
      className={styles.ListButtonPrimary}
      disabled={isSimulator}
      data-testid="viewProfile"
      onClick={() => {
        history.push(`/contact-profile/${contactId}`);
      }}
    >
      {isSimulator ? (
        <ProfileDisabledIcon className={styles.Icon} />
      ) : (
        <ProfileIcon className={styles.Icon} />
      )}
      View contact profile
    </Button>
  ) : (
    <Button
      className={styles.ListButtonPrimary}
      data-testid="viewContacts"
      onClick={() => {
        history.push(`/collection/${collectionId}/contacts`);
      }}
    >
      <ProfileIcon className={styles.Icon} />
      View details
    </Button>
  );

  const addMember = contactId ? (
    <>
      <Button
        data-testid="collectionButton"
        className={styles.ListButtonPrimary}
        onClick={() => {
          getCollections();
          setShowCollectionDialog(true);
        }}
      >
        <AddContactIcon className={styles.Icon} />
        Add to collection
      </Button>
      <Button
        className={styles.ListButtonPrimary}
        data-testid="clearChatButton"
        onClick={() => setClearChatDialog(true)}
      >
        <ClearConversation className={styles.Icon} />
        Clear conversation
      </Button>
    </>
  ) : (
    <Button
      data-testid="collectionButton"
      className={styles.ListButtonPrimary}
      onClick={() => {
        setAddContactsDialogShow(true);
      }}
    >
      <AddContactIcon className={styles.Icon} />
      Add contact
    </Button>
  );

  if (addContactsDialogShow) {
    dialogBox = (
      <AddContactsToCollection collectionId={collectionId} setDialog={setAddContactsDialogShow} />
    );
  }

  const popper = (
    <Popper
      open={open}
      anchorEl={anchorEl}
      placement="bottom-start"
      transition
      className={styles.Popper}
    >
      {({ TransitionProps }) => (
        <Fade {...TransitionProps} timeout={350}>
          <Paper elevation={3} className={styles.Container}>
            {viewDetails}
            {flowButton}

            {addMember}
            {terminateFLows}
            {blockContactButton}
          </Paper>
        </Fade>
      )}
    </Popper>
  );

  const handleConfigureIconClick = (event: any) => {
    setAnchorEl(anchorEl ? null : event.currentTarget);
  };

  let contactCollections: any;
  if (selectedCollections.length > 0) {
    contactCollections = (
      <div className={styles.ContactCollections}>
        <span className={styles.CollectionHeading}>Collections</span>
        <span className={styles.CollectionsName} data-testid="collectionNames">
          {selectedCollectionsName}
        </span>
      </div>
    );
  }

  const getTitleAndIconForSmallScreen = (() => {
    const { location } = history;

    if (location.pathname.includes('collection')) {
      return CollectionIcon;
    }

    if (location.pathname.includes('saved-searches')) {
      return SavedSearchIcon;
    }

    return ChatIcon;
  })();

  // CONTACT: display session timer & Assigned to
  const IconComponent = getTitleAndIconForSmallScreen;
  const sessionAndCollectionAssignedTo = (
    <>
      {contactId ? (
        <div className={styles.SessionTimerContainer}>
          <div className={styles.SessionTimer} data-testid="sessionTimer">
            <span>Session Timer</span>
            <Timer
              time={lastMessageTime}
              contactStatus={contactStatus}
              contactBspStatus={contactBspStatus}
            />
          </div>
          <div>
            {assignedToCollection ? (
              <>
                <span className={styles.CollectionHeading}>Assigned to</span>
                <span className={styles.CollectionsName}>{assignedToCollection}</span>
              </>
            ) : null}
          </div>
        </div>
      ) : null}
      <div className={styles.Chat} onClick={() => showChats()} aria-hidden="true">
        <IconButton className={styles.MobileIcon}>
          <IconComponent />
        </IconButton>
      </div>
    </>
  );

  // COLLECTION: display contact info & Assigned to
  let collectionStatus: any;
  if (collectionId) {
    collectionStatus = <CollectionInformation collectionId={collectionId} />;
  }

  return (
    <Toolbar className={styles.ContactBar} color="primary">
      <div className={styles.ContactBarWrapper}>
        <div className={styles.ContactInfoContainer}>
          <div className={styles.ContactInfoWrapper}>
            <div className={styles.InfoWrapperRight}>
              <div className={styles.ContactDetails}>
                <ClickAwayListener onClickAway={() => setAnchorEl(null)}>
                  <div
                    className={styles.Configure}
                    data-testid="dropdownIcon"
                    onClick={handleConfigureIconClick}
                    onKeyPress={handleConfigureIconClick}
                    aria-hidden
                  >
                    <DropdownIcon />
                  </div>
                </ClickAwayListener>
                <Typography
                  className={styles.Title}
                  variant="h6"
                  noWrap
                  data-testid="beneficiaryName"
                >
                  {displayName}
                </Typography>
              </div>
              {contactCollections}
            </div>
            {collectionStatus}
            {sessionAndCollectionAssignedTo}
          </div>
        </div>
      </div>
      {popper}
      {dialogBox}
    </Toolbar>
  );
}
Example #16
Source File: SavedSearchToolbar.tsx    From glific-frontend with GNU Affero General Public License v3.0 4 votes vote down vote up
SavedSearchToolbar: React.SFC<SavedSearchToolbarProps> = (props) => {
  const { searchMode, refetchData, savedSearchCriteriaCallback, onSelect } = props;
  const [selectedSavedSearch, setSelectedSavedSearch] = useState<number | null>(null);
  const [optionsSelected, setOptionsSelected] = useState(false);
  const [fixedSearches, setFixedSearches] = useState<any>([]);
  const [searchesCount, setSearchesCount] = useState<any>({});
  const [anchorEl, setAnchorEl] = useState(null);
  const Ref = useRef(null);
  const open = Boolean(anchorEl);
  const variables = { organizationId: getUserSession('organizationId') };

  const { data: collectionCount } = useSubscription(COLLECTION_COUNT_SUBSCRIPTION, { variables });

  const { data: countData } = useQuery<any>(SEARCHES_COUNT, {
    variables,
  });

  useEffect(() => {
    if (countData) {
      const collectionStats = JSON.parse(countData.collectionStats);
      if (collectionStats[variables.organizationId]) {
        setSearchesCount(collectionStats[variables.organizationId]);
      }
    }
  }, [countData]);

  useEffect(() => {
    if (collectionCount) {
      const countDataSubscription = JSON.parse(collectionCount.collectionCount);
      setSearchesCount(countDataSubscription.collection);
    }
  }, [collectionCount]);

  // default query variables
  const queryVariables = {
    filter: { isReserved: true },
    opts: {},
  };

  // remove selected searches on search
  if (searchMode && selectedSavedSearch) {
    setSelectedSavedSearch(null);
  }

  const { loading, error, refetch } = useQuery<any>(SAVED_SEARCH_QUERY, {
    variables: queryVariables,
    onCompleted: (data) => {
      setFixedSearches(data.savedSearches);
    },
  });

  const handlerSavedSearchCriteria = (
    savedSearchCriteria: string | null,
    savedSearchId: number | null
  ) => {
    savedSearchCriteriaCallback(savedSearchCriteria, savedSearchId);
    setSelectedSavedSearch(savedSearchId);
  };

  const handleAdditionalSavedSearch = (search: any) => {
    const replaceSearchIndex = fixedSearches
      .map((savedSearch: any) => savedSearch.id)
      .indexOf(search.id);
    const fixedSearchesCopy = JSON.parse(JSON.stringify(fixedSearches));
    if (replaceSearchIndex !== -1) {
      [fixedSearchesCopy[replaceSearchIndex], fixedSearchesCopy[2]] = [
        fixedSearches[2],
        fixedSearches[replaceSearchIndex],
      ];
      setFixedSearches(fixedSearchesCopy);
    }
    handlerSavedSearchCriteria(search.args, search.id);
  };

  useEffect(() => {
    // display created searches
    if (refetchData.savedSearches) {
      refetch();
      handleAdditionalSavedSearch(refetchData.savedSearches);
    }
  }, [refetchData.savedSearches]);

  if (loading) return <Loading />;
  if (error) {
    setErrorMessage(error);
    return <div>error</div>;
  }

  const savedSearchList = fixedSearches.slice(0, 3).map((savedSearch: any) => {
    // set the selected class if the button is clicked
    const labelClass = [styles.SavedSearchItemLabel];
    const countClass = [styles.SavedSearchCount];
    if (savedSearch.id === selectedSavedSearch) {
      labelClass.push(styles.SavedSearchItemSelected);
      countClass.push(styles.SavedSearchSelectedCount);
    }

    const count = searchesCount[savedSearch.shortcode] ? searchesCount[savedSearch.shortcode] : 0;
    return (
      <div
        data-testid="savedSearchDiv"
        className={styles.SavedSearchItem}
        key={savedSearch.id}
        onClick={() => {
          handlerSavedSearchCriteria(savedSearch.args, savedSearch.id);
          onSelect();
        }}
        onKeyDown={() => {
          handlerSavedSearchCriteria(savedSearch.args, savedSearch.id);
          onSelect();
        }}
        aria-hidden="true"
      >
        <div className={labelClass.join(' ')}>{savedSearch.shortcode}</div>
        <Tooltip title={count} placement="right">
          <div className={countClass.join(' ')}>{numberToAbbreviation(count)}</div>
        </Tooltip>
      </div>
    );
  });

  const handleClickAway = () => {
    setAnchorEl(null);
    setOptionsSelected(false);
  };

  const additionalOptions = (
    <Popper
      open={open}
      anchorEl={anchorEl}
      placement="bottom"
      transition
      className={styles.PopperContainer}
    >
      {({ TransitionProps }) => (
        <Fade {...TransitionProps} timeout={350}>
          <Paper elevation={3} className={styles.Popper}>
            {fixedSearches.slice(3, 6).map((search: any) => {
              const count = searchesCount[search.shortcode] ? searchesCount[search.shortcode] : 0;
              return (
                <div
                  key={search.id}
                  className={styles.LabelContainer}
                  onClick={() => handleAdditionalSavedSearch(search)}
                  aria-hidden="true"
                >
                  <span className={styles.Label}>{search.shortcode}</span>
                  <span className={styles.Count}>{numberToAbbreviation(count)}</span>
                </div>
              );
            })}
          </Paper>
        </Fade>
      )}
    </Popper>
  );

  return (
    <div className={styles.SavedSearchToolbar}>
      <div className={styles.SaveSearchContainer}>{savedSearchList}</div>
      <div className={styles.MoreLink}>
        <ClickAwayListener onClickAway={handleClickAway}>
          <IconButton
            onClick={() => {
              setAnchorEl(Ref.current);
              setOptionsSelected(true);
            }}
            aria-label="more"
            aria-controls="long-menu"
            aria-haspopup="true"
            size="small"
            ref={Ref}
          >
            {optionsSelected ? (
              <OptionsIconSelected className={styles.OptionsIcon} />
            ) : (
              <OptionsIcon className={styles.OptionsIcon} />
            )}
          </IconButton>
        </ClickAwayListener>
        {additionalOptions}
      </div>
    </div>
  );
}
Example #17
Source File: global-search-field.tsx    From mtcute with GNU Lesser General Public License v3.0 4 votes vote down vote up
export function GlobalSearchField({ isMobile }: { isMobile: boolean }): React.ReactElement {
    const classes = useStyles()
    const allObjects: {
        allTlObject: GraphqlAllResponse<ExtendedTlObject>
    } = useStaticQuery(graphql`
        query {
            allTlObject {
                edges {
                    node {
                        id
                        prefix
                        type
                        name
                    }
                }
            }
        }
    `)

    const [includeMtproto, setIncludeMtproto] = useLocalState('mtproto', false)
    const { hits, query, onSearch } = useFuse(
        allObjects.allTlObject.edges,
        {
            keys: ['node.name'],
            includeMatches: true,
            threshold: 0.3,
        },
        { limit: 25 },
        includeMtproto ? undefined : (it) => it.node.prefix !== 'mtproto/'
    )

    const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null)
    const [open, setOpen] = useState(false)

    const notFound = () => (
        <>
            <ErrorOutlineIcon className={classes.popupEmptyIcon} />
            Nothing found
            {!includeMtproto && (
                <Button
                    variant="text"
                    size="small"
                    style={{
                        margin: '4px auto',
                    }}
                    onClick={() => {
                        setIncludeMtproto(true)
                    }}
                >
                    Retry including MTProto objects
                </Button>
            )}
        </>
    )

    const emptyField = () => (
        <>
            <SearchIcon className={classes.popupEmptyIcon} />
            Start typing...
        </>
    )

    const renderSearchItem = (
        node: ExtendedTlObject,
        matches: ReadonlyArray<Fuse.FuseResultMatch>
    ) => (
        <ListItem
            button
            divider
            component={Link}
            to={`/${node.prefix}${node.type}/${node.name}`}
            className={classes.popupListItem}
            onClick={() => setOpen(false)}
            key={node.id}
        >
            <ListItemAvatar>
                <Avatar
                    style={{
                        backgroundColor:
                            node.type === 'class'
                                ? blue[600]
                                : node.type === 'method'
                                ? red[600]
                                : yellow[700],
                    }}
                >
                    {node.type === 'class' ? (
                        <ClassIcon />
                    ) : node.type === 'method' ? (
                        <FunctionsIcon />
                    ) : (
                        <UnionIcon />
                    )}
                </Avatar>
            </ListItemAvatar>
            <ListItemText
                primary={
                    <>
                        {node.prefix}
                        <FuseHighlight
                            matches={matches}
                            value={node.name}
                            className={classes.searchItemMatch}
                        />
                    </>
                }
                secondary={node.type}
            />
        </ListItem>
    )

    const popupContent = (
        <Paper className={classes.popup}>
            {query.length <= 1 || !hits.length ? (
                <div className={classes.popupEmpty}>
                    {query.length <= 1 ? emptyField() : notFound()}
                </div>
            ) : (
                <List disablePadding dense className={classes.popupList}>
                    {hits.map(({ item: { node }, matches }) =>
                        renderSearchItem(node, matches!)
                    )}
                    <div style={{ textAlign: 'center' }}>
                        <Button
                            variant="text"
                            size="small"
                            style={{
                                margin: '4px auto',
                            }}
                            onClick={() => {
                                setIncludeMtproto(!includeMtproto)
                            }}
                        >
                            {includeMtproto ? 'Hide' : 'Include'} MTProto
                            objects
                        </Button>
                    </div>
                </List>
            )}
        </Paper>
    )

    return (
        <ClickAwayListener onClickAway={() => setOpen(false)}>
            <>
                <ActionBarSearchField
                    inputRef={setAnchorEl}
                    autoComplete="off"
                    onFocus={() => setOpen(true)}
                    onBlur={() => setOpen(false)}
                    onChange={onSearch}
                />
                <Popper
                    open={open}
                    anchorEl={anchorEl}
                    placement="bottom"
                    transition
                    style={{
                        width: isMobile ? '100%' : anchorEl?.clientWidth,
                        zIndex: 9999,
                    }}
                >
                    {({ TransitionProps }) => (
                        <Fade {...TransitionProps} timeout={350}>
                            {popupContent}
                        </Fade>
                    )}
                </Popper>
            </>
        </ClickAwayListener>
    )
}