@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 vote down vote up
/**
 * @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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
/**
 * 비밀번호 찾기 화면입니다.
 */
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 vote down vote up
/**
 * 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&apos;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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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'>&nbsp;{!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 vote down vote up
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 vote down vote up
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>)}&nbsp;
                                  <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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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>
        );
    }
}