@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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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>
)
}