@mui/material#Link TypeScript Examples
The following examples show how to use
@mui/material#Link.
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: UrlComponentPreview.tsx From firecms with MIT License | 9 votes |
/**
* @category Preview components
*/
export function UrlComponentPreview({
value,
property,
size
}: PreviewComponentProps<string>): React.ReactElement {
const classes = useStyles();
if (!value) return <div/>;
const url = value;
if (typeof property.config?.url === "boolean" && property.config.url) {
return (
<Link className={classes.link}
href={url}
onMouseDown={(e: React.MouseEvent) => {
e.preventDefault();
}}
target="_blank">
<OpenInNewIcon style={{ marginRight: 8 }} fontSize={"small"}/>
{url}
</Link>
);
}
const mediaType: MediaType = property.config?.url as MediaType ||
property.config?.storageMeta?.mediaType;
if (mediaType === "image") {
return <ImagePreview key={`image_preview_${url}_${size}`}
url={url}
size={size}/>;
} else if (mediaType === "audio") {
return <audio controls
src={url}
key={`audio_preview_${url}_${size}`}>
Your browser does not support the
<code>audio</code> element.
</audio>;
} else if (mediaType === "video") {
return <CardMedia
key={`video_preview_${url}_${size}`}
style={{ maxWidth: size === "small" ? 300 : 500 }}
component="video"
controls
image={url}
/>;
} else {
return <a
key={`link_preview_${url}_${size}`}
href={url}
rel="noopener noreferrer"
target="_blank"
onClick={(e) => e.stopPropagation()}>
<div className={classes.flexCenter}
style={{
width: getThumbnailMeasure(size),
height: getThumbnailMeasure(size)
}}>
<DescriptionOutlinedIcon/>
</div>
</a>;
}
}
Example #2
Source File: notify.tsx From wallet-adapter with Apache License 2.0 | 9 votes |
StyledLink = styled(Link)(() => ({
color: '#ffffff',
display: 'flex',
alignItems: 'center',
marginLeft: 16,
textDecoration: 'underline',
'&:hover': {
color: '#000000',
},
}))
Example #3
Source File: index.tsx From genshin-optimizer with MIT License | 8 votes |
export default function ArtifactInfoDisplay() {
const { t } = useTranslation("artifact")
return <Grid container spacing={1} >
<Grid item xs={12} lg={5} xl={4}>
<ImgFullwidth src={artifactcard} />
</Grid>
<Grid item xs={12} lg={7} xl={8}>
<Trans t={t} i18nKey="info.section1">
<Typography variant="h5">Substat rolls</Typography>
<Typography gutterBottom>The <b>number of rolls</b> a substat has is shown to the left of the substat. As the number gets higher, the substat is more colorful:<Colors />.</Typography>
<Typography variant="h5">Substat Roll Value</Typography>
<Typography gutterBottom>The Roll Value(RV) of an subtat is a percentage of the current value over the highest potential 5<Stars stars={1} /> value. From the Image, the maximum roll value of CRIT DMG is 7.8%. In RV: <b>5.8/7.8 = 69.2%.</b></Typography>
<Typography variant="h5">Current Roll Value vs. Maximum Roll Value</Typography>
<Typography gutterBottom>When a 5<Stars stars={1} /> have 9(4+5) total rolls, with each of the rolls having the highest value, that is defined as a 900% RV artifact. However, most of the artifacts are not this lucky. The <b>Current RV</b> of an artifact is a percentage over that 100% artifact. The <b>Maximum RV</b> is the maximum possible RV an artifact can achieve, if the remaining artifact rolls from upgrades are the hightest possible value.</Typography>
<Typography variant="h5">Locking an artifact</Typography>
<Typography>By locking an artifact <FontAwesomeIcon icon={faBan} />, This artifact will not be picked up by the build generator for optimization. An equipped artifact is locked by default.</Typography>
</Trans>
</Grid>
<Grid item xs={12} lg={6} xl={7} >
<Trans t={t} i18nKey="info.section2">
<Typography variant="h5">Artifact Editor</Typography>
<Typography gutterBottom>A fully featured artifact editor, that can accept any 3<Stars stars={1} /> to 5<Stars stars={1} /> Artifact. When a substat is inputted, it can calculate the exact roll values. It will also make sure that you have the correct number of rolls in the artifact according to the level, along with other metrics of validation.</Typography>
<Typography variant="h5">Scan screenshots</Typography>
<Typography gutterBottom>Manual input is not your cup of tea? You can scan in your artifacts with screenshots! On the Artifact Editor, click the <SqBadge color="info">Show Me How!</SqBadge> button to learn more.</Typography>
<Typography variant="h6">Automatic Artifact Scanner</Typography>
<Typography gutterBottom>If you are playing Genshin on PC, you can download a tool that automatically scans all your artifacts for you, and you can then import that data in <FontAwesomeIcon icon={faCog} /> Database. <Link component={RouterLink} to="/scanner">Click here</Link> for a list of scanners that are compatible with GO.</Typography>
<Typography variant="h5">Duplicate/Upgrade artifact detection</Typography>
<Typography>Did you know GO can detect if you are adding a <b>duplicate</b> artifact that exists in the system? It can also detect if the current artifact in editor is an <b>upgrade</b> of an existing artifact as well. Once a duplicate/upgrade is detected, a preview will allow you to compare the two artifacts in question(See Image).</Typography>
</Trans>
</Grid>
<Grid item xs={12} lg={6} xl={5}>
<ImgFullwidth src={artifacteditor} />
</Grid>
<Grid item xs={12} lg={7} xl={6}>
<ImgFullwidth src={artifactfilter} />
</Grid>
<Grid item xs={12} lg={5} xl={6}>
<Trans t={t} i18nKey="info.section3">
<Typography variant="h5">Artifact Inventory</Typography>
<Typography gutterBottom>All your artifacts that you've added to GO is displayed here. The filters here allow you to further refine your view of your artifacts. </Typography>
<Typography variant="h5">Example: Finding Fodder Artifacts</Typography>
<Typography>By utilizing the artifact filter, and the artifact RV, you can quickly find artifacts to feed as food.</Typography>
<Typography>In this example, the filters are set thusly: </Typography>
<Typography component="div" >
<ul>
<li>Limit level to 0-8.</li>
<li>Unlocked artifacts in Inventory.</li>
<li>Removing the contribution of flat HP, flat DEF and Energy Recharge to RV calculations.</li>
<li>Sorted by Ascending Max Roll Value.</li>
</ul>
</Typography>
<Typography>This will filter the artifact Inventory by the lowest RV artifacts, for desired substats.</Typography>
</Trans>
</Grid>
</Grid>
}
Example #4
Source File: about-page.tsx From example with MIT License | 8 votes |
export function AboutPage() {
return (
<Page header="About this example">
With this example, you can:
<ul>
<li>Connect wallets</li>
<li>Deploy collections</li>
<li>Mint NFTs</li>
<li>Sell NFTs</li>
<li>Buy NFTs</li>
<li>Make and accept Bid</li>
</ul>
This example uses:
<ul>
<li><InlineCode>@rarible/sdk</InlineCode> — <Link href="https://github.com/rarible/sdk" target="_blank">Rarible Protocol SDK</Link></li>
<li><InlineCode>@rarible/connector</InlineCode> — <Link href="https://github.com/rarible/sdk/tree/master/packages/connector" target="_blank">Rarible SDK Wallet Connector</Link></li>
<li><InlineCode>@rixio/react</InlineCode> — <Link href="https://github.com/roborox/rixio" target="_blank">Rixio</Link></li>
</ul>
See more information about SDK usage in <Link href="https://docs.rarible.org/" target="_blank">Protocol documentation</Link>.
</Page>
);
}
Example #5
Source File: HomeScreen.tsx From rewind with MIT License | 6 votes |
export function HomeScreen() {
const { appVersion } = useAppInfo();
return (
<Stack gap={4} sx={{ justifyContent: "center", alignItems: "center", margin: "auto", height: "100%" }}>
<Stack alignItems={"center"}>
<FastRewind sx={{ height: "2em", width: "2em" }} />
<Typography fontSize={"1em"} sx={{ userSelect: "none", marginBottom: 2 }}>
REWIND
</Typography>
<Typography fontSize={"caption.fontSize"} color={"text.secondary"}>
Rewind {appVersion} by{" "}
<Link href={RewindLinks.OsuPpyShAbstrakt} target={"_blank"} color={"text.secondary"}>
abstrakt
</Link>
</Typography>
<Typography fontSize={"caption.fontSize"} color={"text.secondary"}>
osu! University
<IconButton href={discordUrl} target={"_blank"} size={"small"}>
<FaDiscord />
</IconButton>
<IconButton href={twitterUrl} target={"_blank"} size={"small"}>
<FaTwitter />
</IconButton>
<IconButton href={youtubeUrl} target={"_blank"} size={"small"}>
<FaYoutube />
</IconButton>
</Typography>
</Stack>
</Stack>
);
}
Example #6
Source File: download.tsx From website with Apache License 2.0 | 6 votes |
StyledLink = styled(Link)`
color: ${({ theme }) => theme.accent.accentColor};
text-decoration: none;
transition: color 0.2s ease-in-out;
&:hover {
color: ${({ theme }) => theme.accent.accentColor}9d;
}
`
Example #7
Source File: Footer.tsx From website with Apache License 2.0 | 6 votes |
FooterItem = styled(Link)`
font-size: 1.15em;
color: ${({ theme }) => theme.text.textColor};
display: block;
transition: color ease-in-out 0.15s;
text-decoration: none;
&:hover {
color: ${({ theme }) => theme.text.textColorDark};
}
`
Example #8
Source File: ResetPasswordView.tsx From bouncecode-cms with GNU General Public License v3.0 | 6 votes |
/**
* 비밀번호 찾기 화면입니다.
*/
function ResetPasswordView(props: IResetPasswordView) {
const {
values,
handleSubmit,
handleChange,
errors,
touched,
isSubmitting,
} = useResetPasswordFormik(props.onSubmit);
return (
<Container maxWidth="xs">
<UserFormPaper>
{/* <Avatar className={classes.avatar}>
<LockOutlinedIcon color="primary" />
</Avatar> */}
<Typography component="h1" variant="h5">
비밀번호 찾기
</Typography>
<UserForm onSubmit={handleSubmit} noValidate>
<Grid container spacing={2}>
<Grid item xs={12}>
<TextField
variant="outlined"
required
fullWidth
id="email"
label="이메일 주소"
name="email"
autoComplete="email"
autoFocus
onChange={handleChange}
value={values.email}
helperText={touched.email ? errors.email : ''}
error={touched.email && Boolean(errors.email)}
/>
</Grid>
</Grid>
<UserFormSubmit
type="submit"
fullWidth
variant="contained"
color="primary"
size="large"
disabled={isSubmitting}
endIcon={isSubmitting ? <CircularProgress size={16} /> : undefined}>
비밀번호 찾기
</UserFormSubmit>
<Grid container justify="flex-end">
<Grid item>
<Link href="/signin/" variant="body2">
이미 계정이 있으신가요?
</Link>
</Grid>
</Grid>
</UserForm>
</UserFormPaper>
</Container>
);
}
Example #9
Source File: UpdateRequiredDialog.tsx From airmessage-web with Apache License 2.0 | 6 votes |
/**
* A dialog that warns the user to check their server for updates
*/
export default function UpdateRequiredDialog(props: {isOpen: boolean, onDismiss: () => void}) {
return (
<Dialog
open={props.isOpen}
onClose={props.onDismiss}
fullWidth>
<DialogTitle>Your server needs to be updated</DialogTitle>
<DialogContent>
<DialogContentText>
<Typography paragraph>
You're running an unsupported version of AirMessage Server.
</Typography>
<Typography paragraph>
Unsupported versions of AirMessage Server may contain security or stability issues,
and will start refusing connections late January.
</Typography>
<Typography paragraph>
Please install the latest version of AirMessage Server from <Link href="https://airmessage.org" target="_blank">airmessage.org</Link> on your Mac.
</Typography>
</DialogContentText>
</DialogContent>
</Dialog>
);
}
Example #10
Source File: Markdown.tsx From airmessage-web with Apache License 2.0 | 6 votes |
options: MarkdownToJSX.Options = {
overrides: {
h1: {
component: Typography,
props: {
gutterBottom: true,
variant: "h5",
},
},
h2: { component: Typography, props: { gutterBottom: true, variant: "h6" } },
h3: { component: Typography, props: { gutterBottom: true, variant: "subtitle1" } },
h4: {
component: Typography,
props: { gutterBottom: true, variant: "caption", paragraph: true },
},
span: { component: Typography },
p: { component: Typography, props: { paragraph: true } },
a: { component: Link, props: { target: "_blank", rel: "noopener"} },
li: { component: SpacedListItem },
},
}
Example #11
Source File: BaseDialog.tsx From rewind with MIT License | 6 votes |
export function PromotionFooter() {
const { appVersion } = useAppInfo();
return (
<Typography fontSize={"caption.fontSize"} color={"text.secondary"}>
Rewind {appVersion} by{" "}
<Link href={RewindLinks.OsuPpyShAbstrakt} target={"_blank"} color={"text.secondary"}>
abstrakt
</Link>{" "}
| osu! University
<IconButton href={RewindLinks.OsuUniDiscord} target={"_blank"} size={"small"}>
<FaDiscord />
</IconButton>
</Typography>
);
}
Example #12
Source File: FaqIntro.tsx From frontend with MIT License | 6 votes |
FaqIntro = () => {
return (
<Box sx={{ display: 'flex', alignItems: 'center', flexDirection: 'column' }}>
<Typography sx={{ mb: 2 }} variant="subtitle1">
Моделът ни на работа се основава на{' '}
<Link href={routes.about}>Принципите, които ни обединяват</Link>
</Typography>
</Box>
)
}
Example #13
Source File: NativeComment.tsx From GTAV-NativeDB with MIT License | 6 votes |
function NativeComment({ children, sx, ...rest }: NativeCommentProps) {
return (
<Typography sx={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word', ...sx }} {...rest}>
<Linkify componentDecorator={(decoratedHref, decoratedText, key) => (
<Link href={decoratedHref} key={key} target="_blank">
{decoratedText}
</Link>
)}
>
{children || 'No comment available.'}
</Linkify>
</Typography>
)
}
Example #14
Source File: ExternalLink.tsx From frontend with MIT License | 5 votes |
export default function ExternalLink({ children, ...props }: ExternalLinkParams) {
return (
<Link target="_blank" rel="noreferrer noopener" {...props}>
{children}
</Link>
)
}
Example #15
Source File: connector-usage-comment.tsx From example with MIT License | 5 votes |
export function ConnectorUsageComment() {
return <>
<Typography gutterBottom>
To simplify connection to various wallets, we moved this logic to a separate
package <InlineCode>@rarible/connector</InlineCode>.
</Typography>
<Code>
{`
import { Connector, InjectedWeb3ConnectionProvider } from "@rarible/connector"
import { MEWConnectionProvider } from "@rarible/connector-mew"
// 1. Configure providers
const injected = new InjectedWeb3ConnectionProvider()
const mew = new MEWConnectionProvider({
networkId: 4,
rpcUrl: ethereumRpcMap[4]
})
// 2. Create connector
const connector = Connector
.create(injected)
.add(mew)
// 3. Connector ready to use
connector.connection.subscribe((con) => {
if (con.status === "connected") {
// use connection to create sdk here
}
})
// get list of available options
const options = await connector.getOptions()
// connect to first one
await connector.connect(options[0])
`}
</Code>
<Typography gutterBottom>
Check out more <Link href="https://github.com/rarible/sdk/tree/master/packages/connector"
target="_blank">documentation in package repository</Link>.
</Typography>
</>
}
Example #16
Source File: CampaignCard.tsx From frontend with MIT License | 5 votes |
export default function CampaignCard({ campaign }: Props) {
const { t } = useTranslation()
const target = campaign.targetAmount
const summary = campaign.summary.find(() => true)
const pictureUrl = campaignListPictureUrl(campaign)
const reached = summary ? summary.reachedAmount : 0
return (
<StyledCard variant="outlined" className={classes.cardWrapper}>
<CardActionArea>
<Link href={routes.campaigns.viewCampaignBySlug(campaign.slug)}>
<CardMedia
className={classes.media}
image={pictureUrl}
title="campaign image placeholder"
/>
</Link>
<CardContent className={classes.cardContent}>
<Typography textAlign={'left'} gutterBottom variant="h5" component="h2">
{campaign.title}
</Typography>
<Typography textAlign={'left'} variant="body2" color="textSecondary" component="p">
{campaign.essence}
</Typography>
</CardContent>
</CardActionArea>
<CardActions className={classes.cardActions}>
<Grid container justifyContent="space-around">
<Box p={2} width={1}>
<CampaignProgress raised={reached} target={target} />
</Box>
<Typography variant="subtitle1" component="p" className={classes.progressBar}>
{t('campaigns:campaign.reached')} <b>{money(reached)}</b> /{' '}
{t('campaigns:campaign.target')} <b>{money(target)}</b>
</Typography>
<Grid item xs={12}>
<Box mx={2} mb={2}>
<LinkButton
fullWidth
href={routes.campaigns.oneTimeDonation(campaign.slug)}
variant="contained"
color="secondary"
endIcon={<Favorite color="error" />}>
{t('campaigns:cta.support-now')}
</LinkButton>
</Box>
<Box mx={2} mb={2}>
<LinkButton
fullWidth
href={routes.campaigns.viewCampaignBySlug(campaign.slug)}
variant="outlined"
endIcon={<ArrowForwardIosIcon />}>
{t('campaigns:cta.see-more')}
</LinkButton>
</Box>
</Grid>
</Grid>
</CardActions>
</StyledCard>
)
}
Example #17
Source File: Profiler.tsx From NekoMaid with MIT License | 5 votes |
Threads: React.FC = React.memo(() => {
const plugin = usePlugin()
const [id, setId] = useState(-1)
const [threads, setThreads] = useState<GridRowData[]>([])
useEffect(() => {
plugin.emit('profiler:threads', (threads: any, id: number) => {
setThreads(threads)
setId(id)
})
}, [])
const threadsColumns: GridColDef[] = [
{ field: 'id', headerName: 'PID', minWidth: 80 },
{
field: 'name',
headerName: lang.profiler.threadName,
minWidth: 200,
flex: 1,
renderCell: ({ value, row: { id: cid } }) => cid === id ? <Typography color='primary'>{lang.profiler.serverThread}</Typography> : value
},
{
field: 'state',
headerName: lang.profiler.state,
width: 150,
renderCell: ({ value, row: { lock } }) => {
const text = (lang.profiler.threadState as any)[value as string] || value
return lock ? <Link onClick={() => dialog({ content: lock, title: lang.profiler.lock, cancelButton: false })} underline='hover'>{text}</Link> : text
}
},
{
field: 'stack',
headerName: lang.profiler.stack,
width: 100,
renderCell: ({ value: content }) => content && <IconButton
size='small'
onClick={() => dialog({ content, cancelButton: false, title: lang.profiler.stack })}
><ViewList /></IconButton>
}
]
return <Container maxWidth={false} sx={{ py: 3, '& .MuiDataGrid-root': { border: 'none' } }}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Card>
<CardHeader title={lang.profiler.threads} />
<Divider />
<div style={{ height: '70vh', position: 'relative' }}>
<CircularLoading loading={!threads.length} />
<DataGrid
disableColumnMenu
disableSelectionOnClick
rows={threads}
columns={threadsColumns}
/>
</div>
</Card>
</Grid>
</Grid>
</Container>
})
Example #18
Source File: index.tsx From genshin-optimizer with MIT License | 5 votes |
export default function PageScanner(props: any) {
const { t: ui } = useTranslation('ui')
const { t } = useTranslation('page_scanner')
ReactGA.send({ hitType: "pageview", page: '/scanner' })
return <Box display="flex" flexDirection="column" gap={1} my={1}>
<CardDark><CardContent>
<Trans t={t} i18nKey="intro">
<Typography variant="h5">Automatic Scanners</Typography>
<Typography gutterBottom>Automatic Scanners are Genshin tools that can automatically scan in-game data by manipulating your mouse movements, taking screenshots of the game, and then scanning information from those screenshots. These are low-risk tools that can help you automate a lot of manual process with scanning artifacts for GO. As any tools that indirectly interact with the game, althought their usage is virtually undetectable, <Link href="https://genshin.mihoyo.com/en/news/detail/5763" target="_blank" rel="noreferrer">there could still be risk with using them.</Link> Users discretion is advised.
</Typography>
<Typography >The most important aspect of using these Scanners with GO is the output format:</Typography>
<Typography gutterBottom component="div">
<ul>
<li>As of <code>v5.21.0</code>, GO can import artifact data in the <code>mona-uranai</code> format. </li>
<li>As of <code>v6.0.0</code>, GO can import data in the <code>Genshin Open Object Description (GOOD)</code> format.</li>
</ul>
</Typography>
<Typography gutterBottom>Below are several scanners that have been tested with GO.</Typography>
<Typography>To upload the exported file, go to <Link component={RouterLink} to="/setting">Settings</Link> page, and upload your file in the <strong>Database Upload</strong> section.</Typography>
</Trans>
</CardContent></CardDark>
<CardDark><CardContent><Grid container spacing={2} >
<Grid item xs={12} md={4}><ImgFullwidth src={GIScanner} /></Grid>
<Grid item xs={12} md={8}>
<Typography variant="h5"><Trans t={t} i18nKey="ik.title">Inventory Kamera</Trans><SqBadge color="success" sx={{ ml: 1 }}><Trans t={ui} i18nKey="updatedfor" values={{ version: "2.6" }} /></SqBadge></Typography>
<Typography gutterBottom><Trans t={t} i18nKey="ik.p1">This light-weight app will scan all your characters + weapons + artifacts in your inventory. Follow the instrutions in the app to set it up.</Trans></Typography>
<Typography gutterBottom><Trans t={t} i18nKey="ik.p2">This scanner can also scan materials for <Link href="https://seelie.me/" target="_blank" rel="noreferrer">Seelie.me</Link></Trans></Typography>
<Typography gutterBottom><Trans t={t} i18nKey="goodeng">This scanner only scans in english, and exports to GOOD format.</Trans></Typography>
<Button href="https://github.com/Andrewthe13th/Inventory_Kamera" target="_blank" startIcon={<FontAwesomeIcon icon={faDownload} />} ><Trans t={ui} i18nKey="link.download" /></Button>
</Grid>
</Grid></CardContent></CardDark>
<CardDark><CardContent><Grid container spacing={2} >
<Grid item xs={12} md={8}>
<Typography variant="h5"><Trans t={t} i18nKey="as.title">AdeptiScanner</Trans><SqBadge color="success" sx={{ ml: 1 }}><Trans t={ui} i18nKey="updatedfor" values={{ version: "2.6+" }} /></SqBadge></Typography>
<Typography gutterBottom><Trans t={t} i18nKey="as.p1">Scans all your artifacts in inventory. Has a manual scanning mode.</Trans></Typography>
<Typography gutterBottom><Trans t={t} i18nKey="as.p2">This scanner can also be configured for new artifacts in new game versions without needing an update.</Trans></Typography>
<Typography gutterBottom><Trans t={t} i18nKey="goodeng">This scanner only scans in english, and exports to GOOD format.</Trans></Typography>
<Button href="https://github.com/D1firehail/AdeptiScanner-GI" target="_blank" startIcon={<FontAwesomeIcon icon={faDownload} />} ><Trans t={ui} i18nKey="link.download" /></Button>
</Grid>
<Grid item xs={12} md={4}><ImgFullwidth src={AdScanner} /></Grid>
</Grid></CardContent></CardDark>
<CardDark><CardContent><Grid container spacing={2} >
<Grid item xs={12} md={4}><ImgFullwidth src={Amenoma} /></Grid>
<Grid item xs={12} md={8}>
<Typography variant="h5"><Trans t={t} i18nKey="am.title">「天目」-- Amenoma</Trans><SqBadge color="success" sx={{ ml: 1 }}><Trans t={ui} i18nKey="updatedfor" values={{ version: "2.6" }} /></SqBadge></Typography>
<Typography gutterBottom><Trans t={t} i18nKey="am.p1">Scans all you artifacts in your inventory. Follow the instruction to capture the window and scan. Has both Chinese and English version. (Download the <code>_EN.exe</code> version to scan in english). Both the <code>mona-uranai</code> and <code>GOOD</code> format is accepted in GO. the <code>GOOD</code> format is recommended.</Trans></Typography>
<Typography gutterBottom><Trans t={t} i18nKey="am.p2">Beta version of this scanner can also scan materials for <Link href="https://seelie.me/" target="_blank" rel="noreferrer">Seelie.me</Link></Trans></Typography>
<Button sx={{ mb: 2 }} href="https://github.com/daydreaming666/Amenoma" target="_blank" startIcon={<FontAwesomeIcon icon={faDownload} />} ><Trans t={ui} i18nKey="link.download" /></Button>
<Typography gutterBottom><Trans t={t} i18nKey="am.p3">Please feel free to join their discord if you find any bugs. They are in need of more english testers.</Trans></Typography>
<Button href="https://discord.gg/BTrCYgVGFP" target="_blank" startIcon={<FontAwesomeIcon icon={faDiscord} />} ><Trans t={ui} i18nKey="link.discord" /></Button>
</Grid>
</Grid></CardContent></CardDark>
<CardDark><CardContent>
<Button component={RouterLink} to="/" startIcon={<FontAwesomeIcon icon={faHome} />}><Trans t={t} i18nKey="backHome">Go back to home page</Trans></Button>
</CardContent></CardDark>
</Box >
}
Example #19
Source File: Config.tsx From NekoMaid with MIT License | 4 votes |
configs.push({
title: lang.config.serverConfig,
component () {
const plugin = usePlugin()
const globalData = useGlobalData()
const [flag, update] = useState(0)
const [info, setInfo] = useState<Record<string, string>>({ })
const [open, setOpen] = useState(false)
const [canGetData, setCanGetData] = useState(true)
const [loading, setLoading] = useState(false)
const setValue = (field: string, value: any, isGlobal = true) => {
plugin.emit('server:set', field, value)
success()
if (isGlobal) {
(globalData as any)[field] = value
update(flag + 1)
location.reload()
}
}
const createEditButtom = (field: string, isGlobal?: boolean, isInt = true) => <IconButton
onClick={() => dialog(
{
content: lang.inputValue,
input: isInt
? {
error: true,
type: 'number',
helperText: lang.invalidValue,
validator: (it: string) => /^\d+$/.test(it) && +it >= 0
}
: { }
}).then(res => res != null && setValue(field, isInt ? parseInt(res as any) : (res || null), isGlobal))}
><Edit /></IconButton>
const infoElm: JSX.Element[] = []
for (const key in info) {
const name = (lang.config as any)[key]
infoElm.push(<ListItem key={key} sx={{ pl: 4 }}>
<ListItemText
primary={key === 'isAikarFlags' ? <Link href='https://mcflags.emc.gs' target='_blank' rel='noopener'>{name}</Link> : name}
secondary={info[key].toString()}
/>
</ListItem>)
}
return <List>
<CircularLoading loading={loading} />
<ListItem secondaryAction={globalData.canSetMaxPlayers
? createEditButtom('maxPlayers')
: undefined}>
<ListItemText primary={lang.config.maxPlayers + ': ' + globalData.maxPlayers} />
</ListItem>
<ListItem secondaryAction={createEditButtom('spawnRadius')}>
<ListItemText primary={lang.config.spawnRadius + ': ' + globalData.spawnRadius} />
</ListItem>
<ListItem secondaryAction={createEditButtom('motd', false, false)}>
<ListItemText primary={lang.config.motd} />
</ListItem>
<ListItem secondaryAction={<Switch checked={globalData.hasWhitelist} onChange={e => setValue('hasWhitelist', e.target.checked)} />}>
<ListItemText primary={lang.config.whitelist} />
</ListItem>
{canGetData && <>
<ListItemButton onClick={() => {
if (infoElm.length) setOpen(!open)
else {
setLoading(true)
plugin.emit('server:fetchInfo', (data: any) => {
setLoading(false)
if (!data) {
failed(lang.unsupported)
setCanGetData(false)
return
}
setInfo(data)
setOpen(true)
})
}
}}>
<ListItemIcon><Equalizer /></ListItemIcon>
<ListItemText primary={lang.info} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={open} timeout='auto' unmountOnExit>
<List component='div' dense disablePadding>{infoElm}</List>
</Collapse>
</>}
</List>
}
},
{
title: lang.history,
component () {
const [cur, update] = useState(0)
const list: ServerRecord[] = JSON.parse(localStorage.getItem('NekoMaid:servers') || '[]')
return <List>
{list.sort((a, b) => b.time - a.time).map(it => {
const i = it.address.indexOf('?')
return <ListItem
disablePadding
key={it.address}
secondaryAction={<IconButton edge='end' size='small' onClick={() => {
localStorage.setItem('NekoMaid:servers', JSON.stringify(list.filter(s => s.address !== it.address)))
success()
update(cur + 1)
}}><Delete /></IconButton>}
>
<ListItemButton onClick={() => {
location.hash = ''
location.search = it.address
}} dense>
<ListItemAvatar><Avatar src={it.icon} variant='rounded'><HelpOutline /></Avatar></ListItemAvatar>
<ListItemText primary={<Tooltip title={it.address.slice(i + 1)}>
<span>{it.address.slice(0, i)}</span></Tooltip>} secondary={dayjs(it.time).fromNow()} />
</ListItemButton>
</ListItem>
})}
</List>
}
},
{
title: lang.config.theme,
component () {
const color = localStorage.getItem('NekoMaid:color') || 'blue'
return <CardContent sx={{ textAlign: 'center' }}>
<Box>
<ToggleButtonGroup exclusive value={localStorage.getItem('NekoMaid:colorMode') || ''} onChange={(_, it) => {
localStorage.setItem('NekoMaid:colorMode', it)
location.reload()
}}>
<ToggleButton value='light'><Brightness7 /> {lang.config.light}</ToggleButton>
<ToggleButton value=''><SettingsBrightness /> {lang.config.system}</ToggleButton>
<ToggleButton value='dark'><Brightness4 /> {lang.config.dark}</ToggleButton>
</ToggleButtonGroup>
</Box>
<Paper sx={{ marginTop: 2, width: '176px', overflow: 'hidden', display: 'inline-block' }}>
{Object.keys(colors).slice(1, 17).map((key, i) => {
const checked = color === key
const elm = <Box
key={key}
onClick={() => {
localStorage.setItem('NekoMaid:color', key)
location.reload()
}}
sx={{
backgroundColor: (colors as any)[key][600],
width: '44px',
height: '44px',
display: 'inline-block',
cursor: 'pointer'
}}
><Check htmlColor='white' sx={{ top: '10px', position: 'relative', opacity: checked ? 1 : 0 }} /></Box>
return (i + 1) % 4 === 0 ? <React.Fragment key={key}>{elm}<br /></React.Fragment> : elm
})}
</Paper>
</CardContent>
}
})
Example #20
Source File: Dashboard.tsx From NekoMaid with MIT License | 4 votes |
Dashboard: React.FC = () => {
const plugin = usePlugin()
const { version, hasGeoIP } = useGlobalData()
const [status, setStatus] = useState<Status[]>([])
const [current, setCurrent] = useState<CurrentStatus | undefined>()
useEffect(() => {
const offSetStatus = plugin.once('dashboard:info', setStatus)
const offCurrent = plugin.on('dashboard:current', (data: CurrentStatus) => setCurrent(old => {
if (old && isEqual(old.players, data.players)) data.players = old.players
return data
}))
plugin.switchPage('dashboard')
return () => {
offSetStatus()
offCurrent()
}
}, [])
const playerCount = current?.players?.length || 0
const prev = status[status.length - 1]?.players || 0
const percent = (prev ? playerCount / prev - 1 : playerCount) * 100
const tpsColor = !current || current.tps >= 18 ? green : current.tps >= 15 ? yellow : red
return <Box sx={{ minHeight: '100%', py: 3 }}>
<Toolbar />
<Container maxWidth={false}>
<Grid container spacing={3}>
<Grid item lg={3} sm={6} xl={3} xs={12}>
<TopCard
title={lang.dashboard.version}
content={current ? version : <Skeleton animation='wave' width={150} />}
icon={<Handyman />}
color={orange[600]}
>
<Box sx={{ pt: 2, display: 'flex', alignItems: 'flex-end' }}>
{!current || current.behinds < 0
? <Refresh htmlColor={blue[900]} />
: current?.behinds === 0
? <Check htmlColor={green[900]} />
: <Update htmlColor={yellow[900]} />}
<Typography color='textSecondary' variant='caption'> {!current || current.behinds === -3
? lang.dashboard.updateChecking
: current.behinds < 0
? <Link underline='hover' color='inherit' sx={{ cursor: 'pointer' }} onClick={() => {
toast(lang.dashboard.updateChecking)
plugin.emit('dashboard:checkUpdate')
}}>{lang.dashboard.updateFailed}</Link>
: current.behinds === 0 ? lang.dashboard.updated : lang.dashboard.behinds(current.behinds)}</Typography>
</Box>
</TopCard>
</Grid>
<Grid item lg={3} sm={6} xl={3} xs={12}>
<TopCard
title={lang.dashboard.onlinePlayers}
content={current ? playerCount : <Skeleton animation='wave' width={150} />}
icon={<People />}
color={deepPurple[600]}
>
<Box sx={{ pt: 2, display: 'flex', alignItems: 'flex-end' }}>
{percent === 0 ? <Remove color='primary' /> : percent < 0 ? <ArrowDownward color='error' /> : <ArrowUpward color='success' />}
<Typography
sx={{ color: (percent === 0 ? blue : percent < 0 ? red : green)[900], mr: 1 }}
variant='body2'
>{Math.abs(percent).toFixed(0)}%</Typography>
<Typography color='textSecondary' variant='caption'>{lang.dashboard.lastHour}</Typography>
</Box>
</TopCard>
</Grid>
<Grid item lg={3} sm={6} xl={3} xs={12}>
<TopCard
title='TPS'
content={current ? (current.tps === -1 ? '?' : current.tps.toFixed(2)) : <Skeleton animation='wave' width={150} />}
icon={!current || current.tps >= 18 || current.tps === -1
? <SentimentVerySatisfied />
: current.tps >= 15 ? <SentimentSatisfied /> : <SentimentDissatisfied />}
color={tpsColor[600]}
>
<Box sx={{ pt: 2.1, display: 'flex', alignItems: 'flex-end' }}>
<Typography
sx={{ color: tpsColor[900], mr: 1 }}
variant='body2'
>{!current || current.mspt === -1 ? '?' : current.mspt.toFixed(2) + 'ms'}</Typography>
<Typography color='textSecondary' variant='caption'>{lang.dashboard.mspt}</Typography>
</Box>
</TopCard>
</Grid>
<Grid item lg={3} sm={6} xl={3} xs={12}>
<TopCard
title={lang.dashboard.uptime}
content={current ? <Uptime time={current.time} /> : <Skeleton animation='wave' width={150} />}
icon={<AccessTime />}
color={blue[600]}
>
<Box sx={{ pt: 2.7, display: 'flex', alignItems: 'center' }}>
<Typography color='textSecondary' variant='caption' sx={{ marginRight: 1 }}>{lang.dashboard.memory}</Typography>
<Tooltip title={current?.totalMemory ? prettyBytes(current.memory) + ' / ' + prettyBytes(current.totalMemory) : ''}>
<LinearProgress
variant='determinate'
value={current?.totalMemory ? current.memory / current.totalMemory * 100 : 0}
sx={{ flex: '1' }}
/>
</Tooltip>
</Box>
</TopCard>
</Grid>
<Grid item lg={8} md={12} xl={9} xs={12}>{useMemo(() => <Charts data={status} />, [status])}</Grid>
<Grid item lg={4} md={12} xl={3} xs={12}><Players players={current?.players} /></Grid>
{hasGeoIP && current?.players && typeof current.players[0] !== 'string' && <Grid item xs={12}>
<Accordion TransitionProps={{ unmountOnExit: true }} disableGutters>
<AccordionSummary expandIcon={<ExpandMore />}>
<Typography>{lang.dashboard.playersDistribution}</Typography>
</AccordionSummary>
<Divider />
<WorldMap players={current.players as Player[]} />
</Accordion>
</Grid>}
</Grid>
</Container>
</Box>
}
Example #21
Source File: PlayerList.tsx From NekoMaid with MIT License | 4 votes |
PlayerInfo: React.FC<{ name?: string }> = React.memo(({ name }) => {
const plugin = usePlugin()
const globalData = useGlobalData()
const [open, setOpen] = useState(false)
const [info, setInfo] = useState<IPlayerInfo | undefined>()
const refresh = () => plugin.emit('playerList:query', setInfo, name)
useEffect(() => {
setInfo(undefined)
if (name) refresh()
}, [name])
return name && info
? <>
<Divider />
<List
sx={{ width: '100%' }}
component='nav'
subheader={<ListSubheader component='div' sx={{ backgroundColor: 'inherit' }}>{lang.playerList.details}</ListSubheader>}
>
<ListItem>
<ListItemIcon><AssignmentInd /></ListItemIcon>
<ListItemText primary={globalData.onlineMode
? <Link underline='hover' rel='noopener' target='_blank' href={'https://namemc.com/profile/' + info.id}>{info.id}</Link>
: info.id
} />
</ListItem>
{!info.hasPlayedBefore && <ListItem>
<ListItemIcon><ErrorOutline color='error' /></ListItemIcon>
<ListItemText primary={lang.playerList.hasNotPlayed} />
</ListItem>}
{info.ban != null && <ListItem>
<ListItemIcon><Block color='error' /></ListItemIcon>
<ListItemText primary={lang.playerList.banned + (info.ban ? ': ' + info.ban : '')} />
</ListItem>}
{info.whitelisted && <ListItem>
<ListItemIcon><Star color='warning' /></ListItemIcon>
<ListItemText primary={lang.playerList.whitelisted} />
</ListItem>}
{info.isOP && <ListItem>
<ListItemIcon><Security color='primary' /></ListItemIcon>
<ListItemText primary={lang.playerList.op} />
</ListItem>}
{info.hasPlayedBefore && <>
<ListItemButton onClick={() => setOpen(!open)}>
<ListItemIcon><Equalizer /></ListItemIcon>
<ListItemText primary={minecraft['gui.stats']} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={open} timeout="auto" unmountOnExit>
<List component='div' dense disablePadding>
{[
minecraft['stat.minecraft.play_time'] + ': ' + dayjs.duration(info.playTime / 20, 'seconds').humanize(),
lang.playerList.firstPlay + ': ' + dayjs(info.firstPlay).fromNow(),
lang.playerList.lastPlay + ': ' + dayjs(info.lastOnline).fromNow(),
minecraft['stat.minecraft.leave_game'] + ': ' + info.quit,
minecraft['stat.minecraft.deaths'] + ': ' + info.death,
minecraft['stat.minecraft.player_kills'] + ': ' + info.playerKill,
minecraft['stat.minecraft.mob_kills'] + ': ' + info.entityKill,
lang.playerList.tnt + ': ' + info.tnt
].map((it, i) => <ListItem key={i} sx={{ pl: 4 }}>
<ListItemIcon>{icons[i]}</ListItemIcon>
<ListItemText primary={it} />
</ListItem>)}
</List>
</Collapse>
</>}
</List>
<CardActions disableSpacing sx={{ justifyContent: 'flex-end' }}>
<Tooltip title={lang.playerList[info.whitelisted ? 'clickToRemoveWhitelist' : 'clickToAddWhitelist']}>
<IconButton onClick={() => whitelist(name, plugin, refresh, !info.whitelisted)}>
{info.whitelisted ? <Star color='warning' /> : <StarBorder />}
</IconButton>
</Tooltip>
<Tooltip title={lang.playerList[info.ban == null ? 'clickToBan' : 'clickToPardon']}>
<IconButton onClick={() => banPlayer(name, plugin, refresh, info.ban == null)}>
<Block color={info.ban == null ? undefined : 'error'} />
</IconButton>
</Tooltip>
</CardActions>
</>
: <></>
})
Example #22
Source File: Plugins.tsx From NekoMaid with MIT License | 4 votes |
Plugins: React.FC = () => {
const plugin = usePlugin()
const theme = useTheme()
const { canLoadPlugin } = useGlobalData()
const [plugins, setPlugins] = useState<Plugin[]>([])
useEffect(() => {
const offList = plugin.on('plugins:list', (plugins: Plugin[]) => {
const arr: Plugin[] = []
setPlugins(plugins.filter(it => {
const res = canPluginBeDisabled(it.name)
if (res) arr.push(it)
return !res
}).concat(arr))
})
plugin.emit('plugins:fetch')
return () => {
offList()
}
}, [])
const map: Record<string, number> = { }
let id = 0
const data = plugins.map(it => {
map[it.name] = id
return { id: id++, name: it.name, category: 1 - (it.enabled as any) }
})
const links: Array<{ source: number, target: number }> = []
plugins.forEach(it => {
const source = map[it.name]
it.depends.forEach(dep => {
if (!(dep in map)) {
map[dep] = id
data.push({ id: id++, name: dep, category: 3 })
}
links.push({ source, target: map[dep] })
})
it.softDepends.forEach(dep => {
if (!(dep in map)) {
map[dep] = id
data.push({ id: id++, name: dep, category: 2 })
}
links.push({ source, target: map[dep] })
})
})
return <Box sx={{ minHeight: '100%', py: 3 }}>
<Toolbar />
<Container maxWidth={false}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Card>
<CardHeader title={lang.plugins.title} />
<Divider />
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell sx={{ paddingRight: 0 }}>{lang.plugins.enable}</TableCell>
<TableCell>{lang.plugins.name}</TableCell>
<TableCell>{lang.plugins.version}</TableCell>
<TableCell>{lang.plugins.author}</TableCell>
<TableCell>{lang.plugins.description}</TableCell>
<TableCell align='right'>{lang.operations}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{plugins.map(it => {
const canBeDisabled = canPluginBeDisabled(it.name)
const disabledForever = it.file.endsWith('.disabled')
return <TableRow key={it.name}>
<TableCell padding='checkbox'>
<Checkbox
color='primary'
checked={it.enabled}
disabled={disabledForever || canBeDisabled}
onChange={() => plugin.emit('plugins:enable', it.file, it.name, action)
} />
</TableCell>
<TableCell><Tooltip title={it.file}><span>{it.name}</span></Tooltip></TableCell>
<TableCell>{it.website
? <Link underline='hover' rel='noopener' target='_blank' href={it.website}>{it.version}</Link>
: it.version
}</TableCell>
<TableCell>{it.author}</TableCell>
<TableCell>{it.description}</TableCell>
<TableCell align='right' sx={{ whiteSpace: 'nowrap' }}>
<Tooltip title={lang.plugins[disabledForever ? 'enablePlugin' : 'disableForever']}><span>
<IconButton
disabled={it.enabled || (it.loaded && !canLoadPlugin)}
onClick={() => plugin.emit('plugins:disableForever', it.file, action)}
>{disabledForever ? <LockOpen /> : <Lock />}</IconButton>
</span></Tooltip>
{disabledForever && <Tooltip title={lang.plugins.delete}><span>
<IconButton
color='error'
disabled={canBeDisabled}
onClick={() => dialog({
okButton: { color: 'error' },
content: <>{lang.plugins.confirmDelete(<span className='bold'>{it.file.replace(/\.disabled$/, '')}</span>)}
<span className='bold' style={{ color: theme.palette.error.main }}>({lang.unrecoverable})</span></>
}).then(res => res && plugin.emit('plugins:delete', it.file, action))}
><DeleteForever /></IconButton>
</span></Tooltip>}
</TableCell>
</TableRow>
})}
</TableBody>
</Table>
</TableContainer>
</Card>
</Grid>
<Grid item xs={12}>
<Card>
<CardHeader title={lang.plugins.dependency} />
<Divider />
<ReactECharts style={{ marginTop: theme.spacing(1), height: 450 }} theme={theme.palette.mode === 'dark' ? 'dark' : undefined} option={{
backgroundColor: 'rgba(0, 0, 0, 0)',
legend: { data: lang.plugins.categories },
series: [
{
edgeSymbol: ['none', 'arrow'],
symbolSize: 13,
type: 'graph',
layout: 'force',
data,
links,
categories: lang.plugins.categories.map(name => ({ name, base: name })),
roam: true,
label: {
show: true,
position: 'right',
formatter: '{b}'
},
labelLayout: {
hideOverlap: true
}
}
]
}} />
</Card>
</Grid>
</Grid>
</Container>
</Box>
}
Example #23
Source File: index.tsx From genshin-optimizer with MIT License | 4 votes |
export default function PageArtifact() {
const [{ tcMode }] = useDBState("GlobalSettings", initGlobalSettings)
const { t } = useTranslation(["artifact", "ui"]);
const { database } = useContext(DatabaseContext)
const [state, setState] = useDBState("ArtifactDisplay", initialState)
const stateDispatch = useCallback(
action => {
if (action.type === "reset") setState(initialArtifactSortFilter())
else setState(action)
},
[setState],
)
const brPt = useMediaQueryUp()
const maxNumArtifactsToDisplay = numToShowMap[brPt]
const { effFilter, filterOption, ascending, probabilityFilter } = state
let { sortType } = state
const showProbability = tcMode && sortType === "probability"
//force the sortType back to a normal value after exiting TC mode
if (sortType === "probability" && !tcMode) stateDispatch({ sortType: artifactSortKeys[0] })
const [pageIdex, setpageIdex] = useState(0)
const invScrollRef = useRef<HTMLDivElement>(null)
const [dbDirty, forceUpdate] = useForceUpdate()
const effFilterSet = useMemo(() => new Set(effFilter), [effFilter]) as Set<SubstatKey>
const deleteArtifact = useCallback((id: string) => database.removeArt(id), [database])
useEffect(() => {
ReactGA.send({ hitType: "pageview", page: '/artifact' })
return database.followAnyArt(forceUpdate)
}, [database, forceUpdate])
const filterOptionDispatch = useCallback((action) => {
stateDispatch({
filterOption: {
...filterOption,
...action
}
})
}, [stateDispatch, filterOption])
const setProbabilityFilter = useCallback(probabilityFilter => stateDispatch({ probabilityFilter }), [stateDispatch],)
const noArtifact = useMemo(() => !database._getArts().length, [database])
const sortConfigs = useMemo(() => artifactSortConfigs(effFilterSet, probabilityFilter), [effFilterSet, probabilityFilter])
const filterConfigs = useMemo(() => artifactFilterConfigs(), [])
const { artifactIds, totalArtNum } = useMemo(() => {
const { sortType = artifactSortKeys[0], ascending = false, filterOption } = state
let allArtifacts = database._getArts()
const filterFunc = filterFunction(filterOption, filterConfigs)
const sortFunc = sortFunction(sortType, ascending, sortConfigs)
//in probability mode, filter out the artifacts that already reach criteria
if (showProbability) {
allArtifacts.forEach(art => (art as any).probability = probability(art, probabilityFilter))
allArtifacts = allArtifacts.filter(art => (art as any).probability && (art as any).probability !== 1)
}
const artifactIds = allArtifacts.filter(filterFunc).sort(sortFunc).map(art => art.id)
return { artifactIds, totalArtNum: allArtifacts.length, ...dbDirty }//use dbDirty to shoo away warnings!
}, [state, dbDirty, database, sortConfigs, filterConfigs, probabilityFilter, showProbability])
const { artifactIdsToShow, numPages, currentPageIndex } = useMemo(() => {
const numPages = Math.ceil(artifactIds.length / maxNumArtifactsToDisplay)
const currentPageIndex = clamp(pageIdex, 0, numPages - 1)
return { artifactIdsToShow: artifactIds.slice(currentPageIndex * maxNumArtifactsToDisplay, (currentPageIndex + 1) * maxNumArtifactsToDisplay), numPages, currentPageIndex }
}, [artifactIds, pageIdex, maxNumArtifactsToDisplay])
//for pagination
const totalShowing = artifactIds.length !== totalArtNum ? `${artifactIds.length}/${totalArtNum}` : `${totalArtNum}`
const setPage = useCallback(
(e, value) => {
invScrollRef.current?.scrollIntoView({ behavior: "smooth" })
setpageIdex(value - 1);
},
[setpageIdex, invScrollRef],
)
return <Box display="flex" flexDirection="column" gap={1} my={1}>
<InfoComponent
pageKey="artifactPage"
modalTitle={t`info.title`}
text={t("tipsOfTheDay", { returnObjects: true }) as string[]}
>
<InfoDisplay />
</InfoComponent>
{noArtifact && <Alert severity="info" variant="filled">Looks like you haven't added any artifacts yet. If you want, there are <Link color="warning.main" component={RouterLink} to="/scanner">automatic scanners</Link> that can speed up the import process!</Alert>}
<ArtifactFilter filterOption={filterOption} filterOptionDispatch={filterOptionDispatch} filterDispatch={stateDispatch}
numShowing={artifactIds.length} total={totalArtNum} />
{showProbability && <ProbabilityFilter probabilityFilter={probabilityFilter} setProbabilityFilter={setProbabilityFilter} />}
<CardDark ref={invScrollRef}>
<CardContent>
<Grid container sx={{ mb: 1 }}>
<Grid item flexGrow={1}><span><Trans t={t} i18nKey="efficiencyFilter.title">Substats to use in efficiency calculation</Trans></span></Grid>
<Grid item>
<Button size="small" color="error" onClick={() => stateDispatch({ effFilter: [...allSubstatKeys] })} startIcon={<Replay />}><Trans t={t} i18nKey="ui:reset" /></Button>
</Grid>
</Grid>
<EfficiencyFilter selectedKeys={effFilter} onChange={n => stateDispatch({ effFilter: n })} />
</CardContent>
</CardDark>
<CardDark ><CardContent>
<Grid container alignItems="center" sx={{ pb: 2 }}>
<Grid item flexGrow={1}>
<Pagination count={numPages} page={currentPageIndex + 1} onChange={setPage} />
</Grid>
<Grid item flexGrow={1}>
<ShowingArt numShowing={artifactIdsToShow.length} total={totalShowing} t={t} />
</Grid>
<Grid item xs={12} sm={6} md={4} lg={4} xl={3} display="flex">
<Box flexGrow={1} />
<SortByButton sortKeys={[...artifactSortKeys.filter(key => (artifactSortKeysTC as unknown as string[]).includes(key) ? tcMode : true)]}
value={sortType} onChange={sortType => stateDispatch({ sortType })}
ascending={ascending} onChangeAsc={ascending => stateDispatch({ ascending })}
/>
</Grid>
</Grid>
<ArtifactRedButtons artifactIds={artifactIds} filterOption={filterOption} />
</CardContent></CardDark>
<Suspense fallback={<Skeleton variant="rectangular" sx={{ width: "100%", height: "100%", minHeight: 5000 }} />}>
<Grid container spacing={1} columns={columns} >
<Grid item xs={1} >
<NewArtifactCard />
</Grid>
{artifactIdsToShow.map(artId =>
<Grid item key={artId} xs={1} >
<ArtifactCard
artifactId={artId}
effFilter={effFilterSet}
onDelete={deleteArtifact}
probabilityFilter={showProbability ? probabilityFilter : undefined}
editor
canExclude
canEquip
/>
</Grid>
)}
</Grid>
</Suspense>
{numPages > 1 && <CardDark ><CardContent>
<Grid container>
<Grid item flexGrow={1}>
<Pagination count={numPages} page={currentPageIndex + 1} onChange={setPage} />
</Grid>
<Grid item>
<ShowingArt numShowing={artifactIdsToShow.length} total={totalShowing} t={t} />
</Grid>
</Grid>
</CardContent></CardDark>}
</Box >
}
Example #24
Source File: DonationTable.tsx From frontend with MIT License | 4 votes |
function DonationTable({ donations }: DonationTableProps) {
const { t, i18n } = useTranslation()
const [fromDate, setFromDate] = React.useState<Date | null>(null)
const [toDate, setToDate] = React.useState<Date | null>(null)
const [monthly, setMonthly] = React.useState(true)
const [oneTime, setOneTime] = React.useState(true)
const filteredByTypeDonations = useMemo(() => {
if (monthly && oneTime) {
return donations
}
if (!monthly && !oneTime) {
return []
}
if (monthly) {
return donations?.filter((d) => d.type !== 'donation')
}
if (oneTime) {
return donations?.filter((d) => d.type === 'donation')
}
return donations
}, [donations, monthly, oneTime])
const filteredDonations = useMemo(() => {
if (!fromDate && !toDate) {
return filteredByTypeDonations
}
if (fromDate && toDate) {
return filteredByTypeDonations?.filter((d) => {
const createdAtDate = parseISO(d.createdAt)
return isAfter(createdAtDate, fromDate) && isBefore(createdAtDate, toDate)
})
}
if (fromDate) {
return filteredByTypeDonations?.filter((d) => {
const createdAtDate = parseISO(d.createdAt)
return isAfter(createdAtDate, fromDate)
})
}
if (toDate) {
return filteredByTypeDonations?.filter((d) => {
const createdAtDate = parseISO(d.createdAt)
return isBefore(createdAtDate, toDate)
})
}
}, [filteredByTypeDonations, fromDate, toDate])
return (
<Card sx={{ padding: theme.spacing(2) }}>
<Grid container alignItems={'flex-start'} spacing={theme.spacing(2)}>
<Grid item xs={6} sm={3}>
<CheckboxLabel>{t('profile:donations.oneTime')}</CheckboxLabel>
<Checkbox
onChange={(e, checked) => setOneTime(checked)}
checked={oneTime}
name="oneTime"
/>
</Grid>
<Grid item xs={6} sm={3}>
<CheckboxLabel>{t('profile:donations.monthly')}</CheckboxLabel>
<Checkbox
onChange={(e, checked) => setMonthly(checked)}
checked={monthly}
name="monthly"
/>
</Grid>
<LocalizationProvider
locale={i18n.language === 'bg' ? bg : enUS}
dateAdapter={AdapterDateFns}>
<Grid item xs={12} sm={3}>
<DatePicker
label={t('profile:donations.fromDate')}
value={fromDate}
onChange={setFromDate}
renderInput={(params) => <TextField size="small" {...params} />}
/>
</Grid>
<Grid item xs={12} sm={3}>
<DatePicker
label={t('profile:donations.toDate')}
value={toDate}
onChange={setToDate}
renderInput={(params) => <TextField size="small" {...params} />}
/>
</Grid>
</LocalizationProvider>
</Grid>
{filteredDonations?.length ? (
<TableContainer>
<Table sx={{ minWidth: 650, backgroundColor: 'white' }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>№</TableCell>
<TableCell>{t('profile:donations.date')}</TableCell>
<TableCell>{t('profile:donations.type')}</TableCell>
<TableCell>{t('profile:donations.cause')}</TableCell>
<TableCell>{t('profile:donations.amount')}</TableCell>
<TableCell>{t('profile:donations.certificate')}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{filteredDonations.map((donation, index) => (
<TableRow key={index} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}>
<TableCell component="th" scope="row">
{index + 1}
</TableCell>
<TableCell>
{format(parseISO(donation.createdAt), 'd.LL.yyyy', {
locale: i18n.language === 'bg' ? bg : enUS,
})}
</TableCell>
<TableCell>
<Avatar sx={{ background: darken(theme.palette.secondary.main, 0.175) }}>
<StarIcon />
</Avatar>
</TableCell>
<TableCell>{donation.targetVault.campaign.title}</TableCell>
<TableCell>{money(donation.amount)}</TableCell>
<TableCell>
<Button variant="outlined" disabled={donation.status != 'succeeded'}>
<Link target="_blank" href={routes.donation.viewCertificate(donation.id)}>
{t('profile:donations.download')} <ArrowForwardIcon />
</Link>
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
) : (
<Box sx={{ fontSize: 20, mt: 4 }}>Към момента няма направени дарения</Box>
)}
</Card>
)
}
Example #25
Source File: PersonalInfoTab.tsx From frontend with MIT License | 4 votes |
export default function PersonalInfoTab() {
const { data: session } = useSession()
const { data: { user: person } = { user: null }, refetch } = useCurrentPerson()
const [isDeleteAccountModalOpen, setIsDeleteAccountModalOpen] = useState(false)
const [isUpdateNameModalOpen, setIsUpdateNameModalOpen] = useState(false)
const [isUpdateBirthdayModalOpen, setIsUpdateBirthdayModalOpen] = useState(false)
return (
<Root>
<Box className={classes.boxTitle}>
<Typography className={classes.h3}>Лична информация</Typography>
</Box>
<ProfileTab name={ProfileTabs.personalInformation}>
<Box>
<h2 className={classes.heading}>Login информация:</h2>
<Box className={classes.infoFlex}>
<Box className={classes.boxInfo}>
<p className={classes.bold}>Email адрес:</p>
<p>{session?.user?.email}</p>
</Box>
<Box className={classes.boxInfo}>
<p className={classes.bold}>Парола:</p>
<p>***********</p>
<Box className={classes.editBox}>
<EditIcon className={classes.editIcon} />
<span className={classes.editSpan}>Редактирай</span>
</Box>
</Box>
</Box>
<Divider className={classes.divider} />
<h2 className={classes.heading}>Лична информация:</h2>
<Box className={classes.infoFlex}>
<Box className={classes.boxInfo}>
<p className={classes.bold}>Име:</p>
<p>
{person?.firstName} {person?.lastName}
</p>
<Box className={classes.editBox}>
<Link href="#" onClick={() => setIsUpdateNameModalOpen(true)}>
<EditIcon className={classes.editIcon} />
<span className={classes.editSpan}>Редактирай</span>
</Link>
</Box>
</Box>
<Box className={classes.boxInfo}>
<p className={classes.bold}>Рожден ден:</p>
<Typography sx={{ color: person?.birthday ? undefined : '#F22727' }}>
{person?.birthday ? formatDateString(person?.birthday) : 'не e наличен'}
</Typography>
<Box className={classes.editBox}>
<Link href="#" onClick={() => setIsUpdateBirthdayModalOpen(true)}>
<EditIcon className={classes.editIcon} />
<span className={classes.editSpan}>Редактирай</span>
</Link>
</Box>
</Box>
</Box>
<Divider className={classes.divider} />
<Link
href="#"
sx={{ color: '#294E85', float: 'right' }}
onClick={() => setIsDeleteAccountModalOpen(true)}>
изтриване на акаунт/ профил
</Link>
</Box>
</ProfileTab>
<Modal
style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}
open={isDeleteAccountModalOpen}
onClose={() => setIsDeleteAccountModalOpen(false)}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description">
<Box bgcolor="white">
<Box
sx={(theme) => ({
padding: theme.spacing(4),
margin: theme.spacing(2),
backgroundColor: '#EEEEEE',
})}>
<Typography variant="h6" component="h2">
Изтриване на акаунт
</Typography>
<Typography className={classes.graySpan}>Съжаляваме, че ни напускате!</Typography>
<Typography className={classes.heading}>Преди да ни напуснете ...</Typography>
<hr />
<ul style={{ listStyle: 'disc', paddingLeft: '20px' }}>
<li className={classes.h5}>
Ако ви е омръзнало да получавате имейли, деактивирайте ги
<Link href="#"> тук</Link>.
</li>
<li className={classes.h5}>
Ако .........................., моля пишете <Link href="#">тук</Link>.
</li>
<li className={classes.h5}>Изтриването на акаунт е необратимо.</li>
<li className={classes.h5}>Ще бъде невъзможно да възстановите акаунта си.</li>
</ul>
<Box display="flex" alignItems="center" justifyContent="space-between">
<Button
variant="contained"
size="large"
color="primary"
onClick={() => setIsDeleteAccountModalOpen(false)}>
Запази моя акаунт
</Button>
<Button variant="contained" size="large" color="inherit">
Изтрий моя акаунт
</Button>
</Box>
</Box>
</Box>
</Modal>
{person && (
<>
<UpdateNameModal
isOpen={isUpdateNameModalOpen}
person={person}
handleClose={() => {
setIsUpdateNameModalOpen(false)
refetch()
}}
/>
<UpdateBirthdayModal
isOpen={isUpdateBirthdayModalOpen}
person={person}
handleClose={() => {
setIsUpdateBirthdayModalOpen(false)
refetch()
}}
/>
</>
)}
</Root>
)
}
Example #26
Source File: AddBenefactorForm.tsx From frontend with MIT License | 4 votes |
export default function AddBenefactorForm({ initialValues = defaults }: BenefactorFormProps) {
const { t } = useTranslation('benefactor')
const mutation = useMutation<
AxiosResponse<BenefactorResponse>,
AxiosError<ApiErrors>,
BenefactorInput
>({
mutationFn: createBenefactor,
onError: () => AlertStore.show(t('alerts.error'), 'error'),
onSuccess: () => AlertStore.show(t('alerts.create'), 'success'),
})
const router = useRouter()
const onSubmit = async (
values: BenefactorFormData,
{ setFieldError, resetForm }: FormikHelpers<BenefactorFormData>,
) => {
try {
const data = {
// id: values.id,
extCustomerId: values.extCustomerId,
// createdAt: values.createdAt,
// updatedAt: values.updatedAt,
person: values.person,
}
await mutation.mutateAsync(data)
resetForm()
router.push(routes.admin.benefactor.index)
} catch (error) {
console.error(error)
if (isAxiosError(error)) {
const { response } = error as AxiosError<ApiErrors>
response?.data.message.map(({ property, constraints }) => {
setFieldError(property, t(matchValidator(constraints)))
})
}
}
}
return (
<Grid
container
direction="column"
component="section"
sx={{
maxWidth: '700px',
margin: '0 auto',
}}>
<GenericForm
onSubmit={onSubmit}
initialValues={initialValues}
validationSchema={validationSchema}>
<Box sx={{ marginTop: '5%', height: '62.6vh' }}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Typography variant="h6">{t('form-heading')}</Typography>
</Grid>
{/* <Grid item xs={12} sm={4}>
<FormTextField
type="text"
label="auth:fields.id"
name="id"
autoComplete="id"
/>
</Grid> */}
<Grid item xs={12} sm={4}>
<FormTextField
type="text"
label={t('customerId')}
name="extCustomerId"
autoComplete="extCustomerId"
/>
</Grid>
<Grid item xs={12} sm={4}>
<FormTextField
type="text"
label={t('personId')}
name="person"
autoComplete="person"
/>
</Grid>
<Grid item xs={12}>
<SubmitButton fullWidth label={t('cta.submit')} />
</Grid>
<Grid item xs={6}>
<Link href={routes.admin.benefactor.index}>
<Button>{t('cta.cancel')}</Button>
</Link>
</Grid>
</Grid>
</Box>
</GenericForm>
</Grid>
)
}
Example #27
Source File: EditForm.tsx From frontend with MIT License | 4 votes |
export default function EditForm({ campaign }: { campaign: CampaignResponse }) {
const router = useRouter()
const [files, setFiles] = useState<File[]>([])
const [roles, setRoles] = useState<FileRole[]>([])
const { t } = useTranslation()
const initialValues: EditFormData = {
title: campaign?.title || '',
coordinatorId: campaign.coordinatorId,
campaignTypeId: campaign.campaignTypeId,
beneficiaryId: campaign.beneficiaryId,
targetAmount: campaign.targetAmount || 0,
allowDonationOnComplete: campaign.allowDonationOnComplete || false,
startDate: format(new Date(campaign.startDate ?? new Date()), formatString),
endDate: format(new Date(campaign.endDate ?? new Date()), formatString),
description: campaign.description || '',
}
const mutation = useMutation<
AxiosResponse<CampaignResponse>,
AxiosError<ApiErrors>,
CampaignInput
>({
mutationFn: useEditCampaign(campaign.id),
onError: () => AlertStore.show(t('common:alerts.error'), 'error'),
onSuccess: () => AlertStore.show(t('common:alerts.message-sent'), 'success'),
})
const fileUploadMutation = useMutation<
AxiosResponse<CampaignUploadImage[]>,
AxiosError<ApiErrors>,
UploadCampaignFiles
>({
mutationFn: useUploadCampaignFiles(),
})
const onSubmit = async (values: EditFormData, { setFieldError }: FormikHelpers<EditFormData>) => {
try {
await mutation.mutateAsync({
title: values.title,
slug: createSlug(values.title),
description: values.description,
targetAmount: values.targetAmount,
allowDonationOnComplete: campaign.allowDonationOnComplete,
startDate: values.startDate,
endDate: values.endDate,
essence: campaign.essence,
campaignTypeId: values.campaignTypeId,
beneficiaryId: values.beneficiaryId,
coordinatorId: values.coordinatorId,
currency: Currency.BGN,
})
await fileUploadMutation.mutateAsync({
files,
roles,
campaignId: campaign.id,
})
router.push(routes.admin.campaigns.index)
} catch (error) {
console.error(error)
if (isAxiosError(error)) {
const { response } = error as AxiosError<ApiErrors>
response?.data.message.map(({ property, constraints }) => {
setFieldError(property, t(matchValidator(constraints)))
})
}
}
}
return (
<Grid container direction="column" component="section">
<Grid item xs={12}>
<Typography
variant="h5"
component="h2"
sx={(theme) => ({
mb: 5,
color: theme.palette.primary.dark,
textAlign: 'center',
})}>
{t('campaigns:edit-form-heading')}
</Typography>
</Grid>
<GenericForm<EditFormData>
onSubmit={onSubmit}
initialValues={initialValues}
validationSchema={validationSchema}>
<Grid container spacing={3}>
<Grid item xs={12}>
<FormTextField
type="text"
label="campaigns:campaign.title"
name="title"
autoComplete="title"
/>
</Grid>
<Grid item xs={12} sm={6}>
<CampaignTypeSelect />
</Grid>
<Grid item xs={12} sm={6}>
<FormTextField
type="number"
name="targetAmount"
autoComplete="target-amount"
label="campaigns:campaign.amount"
/>
</Grid>
<Grid item xs={12} sm={6}>
<FormTextField
type="date"
name="startDate"
label="campaigns:campaign.start-date"
helperText={null}
InputLabelProps={{
shrink: true,
}}
/>
</Grid>
<Grid item xs={12} sm={6}>
<FormTextField
type="date"
name="endDate"
label="campaigns:campaign.end-date"
helperText={null}
InputLabelProps={{
shrink: true,
}}
/>
</Grid>
<Grid item xs={12}>
<FormTextField
rows={5}
multiline
type="text"
name="description"
label="campaigns:campaign.description"
autoComplete="description"
sx={{ '& textarea': { resize: 'vertical' } }}
/>
</Grid>
<Grid item xs={12} sm={6}>
<CoordinatorSelect />
</Grid>
<Grid item xs={12} sm={6}>
<BeneficiarySelect />
</Grid>
<Grid item xs={12}>
<FileUpload
buttonLabel="Добави документи"
onUpload={(newFiles) => {
setFiles((prevFiles) => [...prevFiles, ...newFiles])
setRoles((prevRoles) => [
...prevRoles,
...newFiles.map((file) => ({
file: file.name,
role: CampaignFileRole.background,
})),
])
}}
/>
<FileList
files={files}
filesRole={roles}
onDelete={(deletedFile) =>
setFiles((prevFiles) => prevFiles.filter((file) => file.name !== deletedFile.name))
}
onSetFileRole={(file, role) => {
setRoles((filesRole) => [
...filesRole.filter((f) => f.file !== file.name),
{ file: file.name, role },
])
}}
/>
</Grid>
<Grid item xs={12}>
<SubmitButton fullWidth label="campaigns:cta.submit" loading={mutation.isLoading} />
<Link href={routes.admin.campaigns.index}>
<Button fullWidth={true}>{t('Отказ')}</Button>
</Link>
</Grid>
</Grid>
</GenericForm>
</Grid>
)
}
Example #28
Source File: DocsDetails.tsx From ui-schema with MIT License | 4 votes |
DocContent: React.FC<{
content: string | undefined
id: string
progress: string
doc?: DocRouteModule
}> = ({content, id, progress, doc}) => {
const {palette} = useTheme()
const [loadingModuleDocs, setLoadingModuleDocs] = React.useState<boolean>(false)
const [fullWidth, setFullWidth] = React.useState(window.localStorage.getItem('docs-details--fullWidth') === 'yes')
const [modules, setModules] = React.useState<any>(undefined)
const {breakpoints} = useTheme()
const isLg = useMediaQuery(breakpoints.up('lg'))
const module = doc?.docModule
React.useEffect(() => {
if (!module || (module && moduleDocsCache.current[module.modulePath])) {
setModules(module ? moduleDocsCache.current[module.modulePath] : undefined)
setLoadingModuleDocs(false)
return
}
setLoadingModuleDocs(true)
fetch('/docs/' + module.package + '/' + module.fromPath + '.json')
.then((res) => res.status !== 200 ? Promise.reject(res) : res.json())
.then((data) => {
moduleDocsCache.current[module.modulePath] = data
setModules(data)
setLoadingModuleDocs(false)
})
.catch(e => {
console.error('error loading module-api docs', module, e)
setLoadingModuleDocs(false)
})
return () => setModules(undefined)
}, [module])
const mdData = React.useMemo(() => {
if (!content) return undefined
const lines: string[] = content.split('\n')
// todo: add correct front-matter extraction, but e.g. `front-matter` is no longer maintained/browser-optimized
if (lines[0] === '---') {
const i = lines.slice(1).findIndex((l: string) => l === '---')
if (i !== -1) {
lines.splice(0, i + 2)
}
}
return lines.join('\n')
}, [content])
return <>
<PageContent maxWidth={isLg && fullWidth ? 'xl' : 'md'} style={{flexGrow: 1}}>
<div style={{display: 'flex', alignItems: 'center', margin: '4px 12px'}}>
{isLg ?
<Button
onClick={() => {
setFullWidth(f => {
const n = !f
window.localStorage.setItem('docs-details--fullWidth', n ? 'yes' : 'no')
return n
})
}}
color={'secondary'} size={'small'}
>
{fullWidth ? <IcShowCompact style={{transform: 'rotate(90deg)'}}/> : <IcShowFull style={{transform: 'rotate(90deg)'}}/>}
</Button> : null}
<Typography variant={'body2'} style={{marginLeft: 'auto'}}>
<Link
target={'_blank'} rel="noreferrer noopener nofollow"
href={'https://github.com/ui-schema/ui-schema/tree/develop/packages/docs/src/content/' + id + '.md'}
>Edit Page</Link>
</Typography>
</div>
<Paper style={{margin: '0 0 12px 0', padding: 24, display: 'flex', flexDirection: 'column', borderRadius: 5}} variant={'outlined'}>
{progress === 'start' || progress === 'progress' || loadingModuleDocs ?
<LoadingCircular title={'Loading Docs'}/> :
progress === 'error' ?
'error' :
progress === 'not-found' ?
<PageNotFound
title={'Not Available'}
error={'This document seems to be vanished - or not yet created.'}
/> :
<Markdown source={mdData}/>}
</Paper>
{progress === 'success' && !loadingModuleDocs ?
<>
{doc?.demos?.schema ?
<div style={{display: 'block', textAlign: 'right', margin: '0 12px 4px 12px'}}>
<Typography variant={'body2'} style={{marginLeft: 'auto'}}>
<Link
target={'_blank'} rel="noreferrer noopener nofollow"
href={'https://github.com/ui-schema/ui-schema/tree/develop/packages/docs/src/content/' + id + 'Demo.js'}
>Edit Demos</Link>
</Typography>
</div> : null}
{doc?.demos?.schema ?
<Paper style={{marginBottom: 12, padding: 24, display: 'flex', flexDirection: 'column', borderRadius: 5}} variant={'outlined'}>
<Markdown
source={`
## Demo UI Generator
Examples of this widget, using \`ds-material\`. Type in/change the input and check the data or change the schema (e.g. add specific keywords from above), the demo generators are showing invalid directly.
`}/>
{doc?.demos?.schema.map(([demoText, demoSchema], i) =>
<React.Fragment key={i}>
<Markdown source={demoText}/>
<DemoUIGenerator activeSchema={demoSchema} id={'i-' + i}/>
</React.Fragment>)}
</Paper>
: null}
{doc?.docModule ?
<Paper style={{margin: '12px 0', padding: 24, display: 'flex', flexDirection: 'column', borderRadius: 5}} variant={'outlined'}>
<DocsDetailsModules modules={modules}/>
</Paper> : null}
</> : null}
</PageContent>
<Paper
style={{
margin: '0 12px',
// padding: '0 12px',
display: 'flex',
flexDirection: 'column',
overflowX: 'auto',
opacity: progress === 'success' ? 1 : 0,
transition: '0.32s opacity ease-out',
flexShrink: 0,
position: 'sticky',
bottom: 12,
left: 0,
right: 0,
zIndex: 10,
maxHeight: '85vh',
maxWidth: 375,
borderRadius: 5,
//borderTop: '1px solid ' + palette.primary.main,
//background: palette.primary.main,
boxShadow: '0px 2px 4px -1px rgba(0,0,0,0.2),0px 4px 5px 0px rgba(0,0,0,0.14),0px 1px 10px 0px rgba(0,0,0,0.12)',
}}
// elevation={4}
variant={'outlined'}
>
<LinkableHeadlineMenu
disableNavLink
//style={{margin: 0, padding: 0}}
startIcon={<IcToc/>}
titleStyle={{color: palette.background.paper, fontWeight: 'bold', background: palette.primary.main}}
btnVariant={'contained'}
disablePadding
bindKey={'m'}
linkListStyle={{display: 'flex', flexDirection: 'column', overflow: 'auto'}}
// collapseStyle={{overflow: 'auto'}}
//linkItemStyle={{color: palette.background.paper}}
/>
</Paper>
</>
}
Example #29
Source File: index.tsx From yearn-watch-legacy with GNU Affero General Public License v3.0 | 4 votes |
EtherScanLink = (props: EtherScanLinkProps) => {
const { address, transactionHash, internalHref, network } = props;
const [copied, setCopied] = useState(false);
const [value, setValue] = useState('');
const [extractedValue, setExtractedValue] = useState('');
const [hashValue, setHashValue] = useState('');
const [resolved, setResolved] = useState(false);
const networkConfig = getNetworkConfig(network);
useEffect(() => {
const timeId = setTimeout(() => {
setCopied(false);
}, 1000);
return () => clearTimeout(timeId);
}, [copied]);
useEffect(() => {
if (address) {
// check if ENS
if (address.includes('.')) {
setValue(address);
setExtractedValue(address);
const provider = getEthersDefaultProvider(network);
provider
.resolveName(address)
.then((res) => {
setHashValue(res || '');
setResolved(true);
})
.catch(() => setResolved(false));
} else {
// try to get the checksum address
try {
const checksumAddress = toChecksumAddress(address);
setValue(checksumAddress);
setExtractedValue(extractAddress(address));
setHashValue(checksumAddress);
setResolved(true);
} catch {
setValue(address);
setExtractedValue(address);
setResolved(false);
}
}
}
if (transactionHash) {
setValue(transactionHash);
setExtractedValue(extractAddress(transactionHash));
setHashValue(transactionHash);
setResolved(true);
}
}, []);
const maskedValue = (
<Tooltip title={value} aria-label="Etherscan">
<span>{extractedValue}</span>
</Tooltip>
);
const onCopyToClipboard = (e: MouseEvent<HTMLElement>) => {
e.stopPropagation();
navigator.clipboard.writeText(value);
setCopied(true);
};
const refLink = transactionHash
? networkConfig.toTxExplorerUrl(hashValue)
: networkConfig.toAddressExplorerUrl(hashValue);
if (!resolved) {
return <>{value}</>;
} else {
return (
<Grid container spacing={2} alignItems="center">
<Grid item>
<StyledAddress>
{internalHref ? (
<Link
component={RouterLink}
color="inherit"
to={internalHref}
>
<Hidden smUp>{maskedValue}</Hidden>
<Hidden smDown>{value}</Hidden>
</Link>
) : (
<>
<Hidden smUp>{maskedValue}</Hidden>
<Hidden smDown>{value}</Hidden>
</>
)}
</StyledAddress>
<Tooltip title="Copy to clipboard" aria-label="Clipboard">
<StyledLink onClick={(e) => onCopyToClipboard(e)}>
<StyledCopiedText>
<StyledFileCopy fontSize="inherit" />
{copied ? ' Copied' : ''}
</StyledCopiedText>
</StyledLink>
</Tooltip>
<Tooltip title="View on Explorer" aria-label="Explorer">
<StyledLink href={refLink} target="_blank">
<StyledCallMadeIcon fontSize="inherit" />
</StyledLink>
</Tooltip>
</Grid>
</Grid>
);
}
}