@mui/icons-material#Download TypeScript Examples

The following examples show how to use @mui/icons-material#Download. 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: DownloadCard.tsx    From genshin-optimizer with MIT License 5 votes vote down vote up
export default function DownloadCard() {
  const { database, setDatabase } = useContext(DatabaseContext)
  const { t } = useTranslation(["settings"]);
  const numChar = database._getCharKeys().length
  const numArt = database._getArts().length
  const numWeapon = database._getWeapons().length
  const downloadValid = Boolean(numChar || numArt)
  const deleteDB = useCallback(() => {
    database.storage.clear()
    setDatabase(new ArtCharDatabase(database.storage))
  }, [database, setDatabase])
  const copyToClipboard = useCallback(
    () => navigator.clipboard.writeText(JSON.stringify(exportGOOD(database.storage)))
      .then(() => alert("Copied database to clipboard."))
      .catch(console.error),
    [database],
  )
  const download = useCallback(
    () => {
      const date = new Date()
      const dateStr = date.toISOString().split(".")[0].replace("T", "_").replaceAll(":", "-")
      const JSONStr = JSON.stringify(exportGOOD(database.storage))
      const filename = `go-data_${dateStr}.json`
      const contentType = "application/json;charset=utf-8"
      const a = document.createElement('a');
      a.download = filename
      a.href = `data:${contentType},${encodeURIComponent(JSONStr)}`
      a.target = "_blank"
      document.body.appendChild(a)
      a.click()
      document.body.removeChild(a)
    },
    [database],
  )


  return <CardLight>
    <CardContent sx={{ py: 1 }}>
      <Typography variant="subtitle1">
        <Trans t={t} i18nKey="downloadCard.databaseDownload" />
      </Typography>
    </CardContent>
    <Divider />
    <CardContent>
      <Grid container mb={2} spacing={2}>
        <Grid item xs={6} md={4}><Typography><Trans t={t} i18nKey="count.chars" /> {numChar}</Typography></Grid>
        <Grid item xs={6} md={4}><Typography><Trans t={t} i18nKey="count.arts" /> {numArt}</Typography></Grid>
        <Grid item xs={6} md={4}><Typography><Trans t={t} i18nKey="count.weapons" /> {numWeapon}</Typography></Grid>
      </Grid>
    </CardContent>
    <Divider />
    <CardContent sx={{ py: 1 }}>
      <Grid container spacing={2}>
        <Grid item><Button disabled={!downloadValid} onClick={download} startIcon={<Download />}><Trans t={t} i18nKey="downloadCard.button.download" /></Button></Grid>
        <Grid item flexGrow={1} ><Button disabled={!downloadValid} color="info" onClick={copyToClipboard} startIcon={<FontAwesomeIcon icon={faClipboard} />}><Trans t={t} i18nKey="downloadCard.button.copy" /></Button></Grid>
        <Grid item><Button disabled={!downloadValid} color="error" onClick={deleteDB} startIcon={<FontAwesomeIcon icon={faTrashAlt} />}><Trans t={t} i18nKey="downloadCard.button.delete" /></Button></Grid>
      </Grid>
    </CardContent>
  </CardLight>
}
Example #2
Source File: ChartCard.tsx    From genshin-optimizer with MIT License 4 votes vote down vote up
export default function ChartCard({ chartData, plotBase, setPlotBase, disabled = false }: ChartCardProps) {
  const [showDownload, setshowDownload] = useState(false)
  const [showMin, setshowMin] = useState(true)
  const { data } = useContext(DataContext)
  const statKeys = ["atk", "hp", "def", "eleMas", "critRate_", "critDMG_", "heal_", "enerRech_"]
  if (data.get(input.weaponType).value !== "catalyst") statKeys.push("physical_dmg_")
  statKeys.push(`${data.get(input.charEle).value}_dmg_`)

  const { displayData, downloadData } = useMemo(() => {
    if (!chartData) return { displayData: null, downloadData: null }
    const points = chartData.data.map(({ value: y, plot: x }) => ({ x, y })) as Point[]
    const increasingX: Point[] = points.sort((a, b) => a.x - b.x)
    const minimumData: Point[] = []
    for (const point of increasingX) {
      let last: Point | undefined
      while ((last = minimumData.pop())) {
        if (last.y > point.y) {
          minimumData.push(last)
          break
        }
      }
      minimumData.push(point)
    }

    // Note:
    // We can also just use `minimumData` if the plotter supports multiple data sources.
    // It could be faster too since there're no empty entries in `minimumData`.
    if (minimumData[0]?.x !== increasingX[0]?.x)
      increasingX[0].min = minimumData[0].y
    minimumData.forEach(x => { x.min = x.y })

    const downloadData = {
      minimum: minimumData.map(({ x, y }) => [x, y]),
      allData: increasingX.map(({ x, y }) => [x, y]),
    }
    return { displayData: increasingX, downloadData }
  }, [chartData])

  return <CardLight>
    <CardContent>
      <Grid container spacing={1} alignItems="center">
        <Grid item>
          <Typography variant="h6" >Optimization Target vs</Typography>
        </Grid>
        <Grid item>
          <DropdownButton title={plotBase ? KeyMap.get(plotBase) : "Not Selected"}
            color={plotBase ? "success" : "primary"}
            disabled={disabled}
          >
            <MenuItem onClick={() => { setPlotBase("") }}>Unselect</MenuItem>
            <Divider />
            {statKeys.map(sKey => <MenuItem key={sKey} onClick={() => { setPlotBase(sKey as any) }}>{KeyMap.get(sKey)}</MenuItem>)}
          </DropdownButton>
        </Grid>
        <Grid item flexGrow={1}>
          <Tooltip placement="top" title="Using data from the builder, this will generate a graph to visualize Optimization Target vs. a selected stat. The graph will show the maximum Optimization Target value per 0.01 of the selected stat.">
            <Info />
          </Tooltip>
        </Grid>
        {!!downloadData && <Grid item>
          <Button startIcon={showMin ? <CheckBox /> : <CheckBoxOutlineBlank />}
            color={showMin ? "success" : "secondary"}
            onClick={() => setshowMin(!showMin)}>Show Min Stat Threshold</Button>
        </Grid>}
        {!!downloadData && <Grid item>
          <Button color="info" startIcon={<Download />} onClick={() => setshowDownload(!showDownload)}>Download Data</Button>
        </Grid>}
      </Grid>
    </CardContent>
    {!!displayData && <Divider />}
    {chartData && !!displayData && <CardContent>
      <Collapse in={!!downloadData && showDownload}>
        <CardDark sx={{ mb: 2 }}>
          <CardContent>
            <Typography>Min Data</Typography>
            <DataDisplay data={downloadData?.minimum} />
            <Typography>All Data</Typography>
            <DataDisplay data={downloadData?.allData} />
          </CardContent>
        </CardDark>
      </Collapse>
      <Chart displayData={displayData} plotNode={chartData.plotNode} valueNode={chartData.valueNode} showMin={showMin} />
    </CardContent>}
  </CardLight >
}
Example #3
Source File: Files.tsx    From NekoMaid with MIT License 4 votes vote down vote up
Files: React.FC = () => {
  const plugin = usePlugin()
  const theme = useTheme()
  const his = useHistory()
  const loc = useLocation()
  const drawerWidth = useDrawerWidth()
  const tree = useRef<HTMLHRElement | null>(null)
  const editor = useRef<UnControlled | null>(null)
  const prevExpanded = useRef<string[]>([])
  const dirs = useRef<Record<string, boolean>>({ })
  // eslint-disable-next-line func-call-spacing
  const loading = useRef<Record<string, () => Promise<void>> & { '!#LOADING'?: boolean }>({ })
  const [id, setId] = useState(0)
  const [curPath, setCurPath] = useState('')
  const [progress, setProgress] = useState(-1)
  const [copyPath, setCopyPath] = useState('')
  const [expanded, setExpanded] = useState<string[]>([])
  const [compressFile, setCompressFile] = useState<string | null>(null)
  const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null)

  const isDir = !!dirs.current[curPath]
  const dirPath = isDir ? curPath : curPath.substring(0, curPath.lastIndexOf('/'))

  const spacing = theme.spacing(3)
  const refresh = () => {
    loading.current = { }
    dirs.current = { }
    prevExpanded.current = []
    setCurPath('')
    setExpanded([])
    setId(id + 1)
  }

  useEffect(() => {
    if (!tree.current) return
    const resize = () => {
      if (!tree.current) return
      const height = tree.current.style.maxHeight = (window.innerHeight - tree.current.offsetTop - parseInt(spacing)) + 'px'
      const style = (editor as any).current?.editor?.display?.wrapper?.style
      if (style) style.height = height
    }
    resize()
    window.addEventListener('resize', resize)
    return window.removeEventListener('resize', resize)
  }, [tree.current, spacing])

  return <Box sx={{ height: '100vh', py: 3 }}>
    <Toolbar />
    <Container maxWidth={false}>
      <Grid container spacing={3} sx={{ width: { sm: `calc(100vw - ${drawerWidth}px - ${theme.spacing(3)})` } }}>
        <Grid item lg={4} md={12} xl={3} xs={12}>
          <Card sx={{ minHeight: 400 }}>
            <CardHeader
              title={lang.files.filesList}
              sx={{ position: 'relative' }}
              action={<Box sx={{ position: 'absolute', right: theme.spacing(1), top: '50%', transform: 'translateY(-50%)' }}
            >
              <Tooltip title={lang.files.delete}><span>
                <IconButton
                  disabled={!curPath}
                  size='small'
                  onClick={() => dialog({
                    okButton: { color: 'error' },
                    content: <>{lang.files.confirmDelete(<span className='bold'>{curPath}</span>)}&nbsp;
                      <span className='bold' style={{ color: theme.palette.error.main }}>({lang.unrecoverable})</span></>
                  }).then(it => it && plugin.emit('files:update', (res: boolean) => {
                    action(res)
                    if (!res) return
                    refresh()
                    if (loc.pathname.replace(/^\/NekoMaid\/files\/?/, '') === curPath) his.push('/NekoMaid/files')
                  }, curPath))}
                ><DeleteForever /></IconButton>
              </span></Tooltip>
              <Tooltip title={lang.files.createFile}>
                <IconButton size='small' onClick={() => fileNameDialog(lang.files.createFile, curPath)
                  .then(it => it != null && his.push(`/NekoMaid/files/${dirPath ? dirPath + '/' : ''}${it}`))}>
              <Description /></IconButton></Tooltip>
              <Tooltip title={lang.files.createFolder}>
                <IconButton size='small' onClick={() => fileNameDialog(lang.files.createFolder, curPath)
                  .then(it => it != null && plugin.emit('files:createDirectory', (res: boolean) => {
                    action(res)
                    if (res) refresh()
                  }, dirPath + '/' + it))}><CreateNewFolder /></IconButton></Tooltip>
              <Tooltip title={lang.more}>
                <IconButton size='small' onClick={e => setAnchorEl(anchorEl ? null : e.currentTarget)}><MoreHoriz /></IconButton>
              </Tooltip>
            </Box>} />
            <Divider />
            <TreeView
              ref={tree}
              defaultCollapseIcon={<ArrowDropDown />}
              defaultExpandIcon={<ArrowRight />}
              sx={{ flexGrow: 1, width: '100%', overflowY: 'auto' }}
              expanded={expanded}
              onNodeToggle={(_: any, it: string[]) => {
                const l = loading.current
                if (it.length < prevExpanded.current.length || !l[it[0]]) {
                  setExpanded(it)
                  prevExpanded.current = it
                  return
                }
                l[it[0]]().then(() => {
                  prevExpanded.current.unshift(it[0])
                  setExpanded([...prevExpanded.current])
                  delete l[it[0]]
                })
                delete l[it[0]]
              }}
              onNodeSelect={(_: any, it: string) => {
                setCurPath(it[0] === '/' ? it.slice(1) : it)
                if (dirs.current[it] || loading.current['!#LOADING']) return
                if (it.startsWith('/')) it = it.slice(1)
                his.push('/NekoMaid/files/' + it)
              }}
            >
              <Item plugin={plugin} path='' loading={loading.current} dirs={dirs.current} key={id} />
            </TreeView>
          </Card>
        </Grid>
        <Grid item lg={8} md={12} xl={9} xs={12} sx={{ maxWidth: `calc(100vw - ${theme.spacing(1)})`, paddingBottom: 3 }}>
          <Editor plugin={plugin} editorRef={editor} loading={loading.current} dirs={dirs.current} refresh={refresh} />
        </Grid>
      </Grid>
    </Container>
    <Menu
      anchorEl={anchorEl}
      open={Boolean(anchorEl)}
      onClose={() => setAnchorEl(null)}
      anchorOrigin={anchorOrigin}
      transformOrigin={anchorOrigin}
    >
      <MenuItem onClick={() => {
        refresh()
        setAnchorEl(null)
      }}><ListItemIcon><Refresh /></ListItemIcon>{lang.refresh}</MenuItem>
      <MenuItem disabled={!curPath} onClick={() => {
        setAnchorEl(null)
        fileNameDialog(lang.files.rename, curPath).then(it => it != null && plugin.emit('files:rename', (res: boolean) => {
          action(res)
          if (res) refresh()
        }, curPath, dirPath + '/' + it))
      }}><ListItemIcon><DriveFileRenameOutline /></ListItemIcon>{lang.files.rename}</MenuItem>
      <MenuItem disabled={!curPath} onClick={() => {
        setAnchorEl(null)
        setCopyPath(curPath)
      }}>
        <ListItemIcon><FileCopy /></ListItemIcon>{lang.files.copy}
      </MenuItem>
      <MenuItem disabled={!copyPath} onClick={() => {
        setAnchorEl(null)
        toast(lang.files.pasting)
        plugin.emit('files:copy', (res: boolean) => {
          action(res)
          refresh()
        }, copyPath, dirPath)
      }}>
        <ListItemIcon><ContentPaste /></ListItemIcon>{lang.files.paste}
      </MenuItem>
      <MenuItem disabled={progress !== -1} component='label' htmlFor='NekoMaid-files-upload-input' onClick={() => setAnchorEl(null)}>
        <ListItemIcon><Upload /></ListItemIcon>{progress === -1 ? lang.files.upload : `${lang.files.uploading} (${progress.toFixed(2)}%)`}
      </MenuItem>
      <MenuItem disabled={isDir} onClick={() => {
        setAnchorEl(null)
        toast(lang.files.downloading)
        plugin.emit('files:download', (res: ArrayBuffer | null) => {
          if (res) window.open(address! + 'Download/' + res, '_blank')
          else failed()
        }, curPath)
      }}><ListItemIcon><Download /></ListItemIcon>{lang.files.download}</MenuItem>
      <MenuItem onClick={() => {
        setAnchorEl(null)
        setCompressFile(curPath)
      }}><ListItemIcon><Inbox /></ListItemIcon>{lang.files.compress}</MenuItem>
      <MenuItem onClick={() => {
        setAnchorEl(null)
        toast(lang.files.uncompressing)
        plugin.emit('files:compress', (res: boolean) => {
          action(res)
          refresh()
        }, curPath)
      }}><ListItemIcon><Outbox /></ListItemIcon>{lang.files.decompress}</MenuItem>
    </Menu>
    <Input id='NekoMaid-files-upload-input' type='file' sx={{ display: 'none' }} onChange={e => {
      const elm = e.target as HTMLInputElement
      const file = elm.files?.[0]
      elm.value = ''
      if (!file) return
      const size = file.size
      if (size > 128 * 1024 * 1024) return failed(lang.files.uploadTooBig)
      toast(lang.files.uploading)
      const name = dirPath + '/' + file.name
      if (dirs.current[name] != null) return failed(lang.files.exists)
      plugin.emit('files:upload', (res: string | null) => {
        if (!res) return failed(lang.files.exists)
        const formdata = new FormData()
        formdata.append('file', file)
        const xhr = new XMLHttpRequest()
        setProgress(0)
        xhr.open('put', address! + 'Upload/' + res)
        xhr.onreadystatechange = () => {
          if (xhr.readyState !== 4) return
          setProgress(-1)
          action(xhr.status === 200)
          refresh()
        }
        xhr.upload.onprogress = e => e.lengthComputable && setProgress(e.loaded / e.total * 100)
        xhr.send(formdata)
      }, name[0] === '/' ? name.slice(1) : name)
    }} />
    <CompressDialog file={compressFile} path={dirPath} dirs={dirs.current} onClose={() => setCompressFile(null)} refresh={refresh} plugin={plugin} />
  </Box>
}
Example #4
Source File: index.tsx    From Search-Next with GNU General Public License v3.0 4 votes vote down vote up
Backup: React.FC = () => {
  const [downloadCount, setDownloadCount] = React.useState<number>(0);

  const handleBackup = () => {
    if (downloadCount > 5) {
      toast.warning('当前下载次数过多,暂停下载');
      return;
    }
    let backupData = {} as { [x: string]: string | null };
    const length = localStorage.length;
    for (let i = 0; i < length; i++) {
      const key = localStorage.key(i);
      if (key) backupData[key] = localStorage.getItem(key);
    }
    exportFile(
      JSON.stringify(backupData),
      `search.virs.xyz_backupData_${dayjs().format(
        'YYYY:MM:DD HH:mm:ss',
      )}.json`,
    );
    toast.success('导出成功');
    setDownloadCount(downloadCount + 1);
  };

  const handleFileChange = (e: any) => {
    fileReader(e.target)
      .then((res) => {
        if (res) {
          confirm({
            title: '导入数据',
            content: '导入数据将覆盖现有数据,是否继续?',
            onOk: () => {
              const data = JSON.parse(res);
              Object.keys(data).forEach((i) => {
                localStorage.setItem(i, data[i]);
              });
              toast.success('数据恢复成功');
              e.target.value = '';
            },
            onCancel: () => (e.target.value = ''),
          });
        }
      })
      .catch((err) => {
        toast.error(err);
      });
  };

  return (
    <ContentList>
      <ItemCard
        icon={<SettingsBackupRestore />}
        title="备份"
        action={
          <Button
            variant="outlined"
            size="small"
            onClick={() => handleBackup()}
            startIcon={<Download />}
          >
            下载备份文件
          </Button>
        }
      />
      <ItemCard
        icon={<FileDownload />}
        title="恢复"
        action={
          <label htmlFor="icon-button-file">
            <Input
              accept="application/json"
              id="icon-button-file"
              type="file"
              onChange={handleFileChange}
            />
            <Button
              component="span"
              variant="outlined"
              size="small"
              startIcon={<Upload />}
            >
              上传备份文件
            </Button>
          </label>
        }
      />
    </ContentList>
  );
}