@mui/icons-material#Upload TypeScript Examples
The following examples show how to use
@mui/icons-material#Upload.
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: UploadCard.tsx From genshin-optimizer with MIT License | 5 votes |
export default function UploadCard() {
const { database } = useContext(DatabaseContext)
const { t } = useTranslation("settings");
const [data, setdata] = useState("")
const [filename, setfilename] = useState("")
const [errorMsg, setErrorMsg] = useState("") // TODO localize error msg
const dataObj: UploadData | undefined = useMemo(() => {
if (!data) return
let parsed: any
try {
parsed = JSON.parse(data)
if (typeof parsed !== "object") {
setErrorMsg("uploadCard.error.jsonParse")
return
}
} catch (e) {
setErrorMsg("uploadCard.error.jsonParse")
return
}
// Figure out the file format
if (parsed.version === "1" && ["flower", "feather", "sand", "cup", "head"].some(k => Object.keys(parsed).includes(k))) {
// Parse as mona format
const imported = importMona(parsed, database)
if (!imported) {
setErrorMsg("uploadCard.error.monaInvalid")
return
}
return imported
} else if (parsed.format === "GOOD") {
// Parse as GOOD format
const imported = importGOOD(parsed, database)
if (!imported) {
setErrorMsg("uploadCard.error.goInvalid")
return
}
return imported
}
setErrorMsg("uploadCard.error.unknown")
return
}, [data, database])
const reset = () => {
setdata("")
setfilename("")
}
const onUpload = async e => {
const file = e.target.files[0]
e.target.value = null // reset the value so the same file can be uploaded again...
if (file) setfilename(file.name)
const reader = new FileReader()
reader.onload = () => setdata(reader.result as string)
reader.readAsText(file)
}
return <CardLight>
<CardContent sx={{ py: 1 }}><Trans t={t} i18nKey="settings:uploadCard.title" /></CardContent>
<CardContent>
<Grid container spacing={2} sx={{ mb: 1 }}>
<Grid item>
<label htmlFor="icon-button-file">
<InvisInput accept=".json" id="icon-button-file" type="file" onChange={onUpload} />
<Button component="span" startIcon={<Upload />}>Upload</Button>
</label>
</Grid>
<Grid item flexGrow={1}>
<CardDark sx={{ px: 2, py: 1 }}>
<Typography>{filename ? <span><FontAwesomeIcon icon={faFileCode} /> {filename}</span> : <span><FontAwesomeIcon icon={faArrowLeft} /> <Trans t={t} i18nKey="settings:uploadCard.hint" /></span>}</Typography>
</CardDark>
</Grid>
</Grid>
<Typography gutterBottom variant="caption"><Trans t={t} i18nKey="settings:uploadCard.hintPaste" /></Typography>
<Box component="textarea" sx={{ width: "100%", fontFamily: "monospace", minHeight: "10em", mb: 2, resize: "vertical" }} value={data} onChange={e => setdata(e.target.value)} />
{UploadInfo(dataObj) ?? errorMsg}
</CardContent>
{UploadAction(dataObj, reset)}
</CardLight>
}
Example #2
Source File: Files.tsx From NekoMaid with MIT License | 4 votes |
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>)}
<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 #3
Source File: index.tsx From Search-Next with GNU General Public License v3.0 | 4 votes |
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>
);
}