@mui/icons-material#Refresh TypeScript Examples
The following examples show how to use
@mui/icons-material#Refresh.
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: OpenInv.tsx From NekoMaid with MIT License | 5 votes |
OpenInv: React.FC = () => {
const plugin = usePlugin()
const [inv, setInv] = useState<Array<Item | null>>([])
const [ender, setEnder] = useState<Array<Item | null>>([])
const { name: player } = useParams<{ name: string }>()
useEffect(() => {
if (player) plugin.emit('openInv:fetchInv', setInv, player).emit('openInv:fetchEnderChest', setEnder, player)
}, [player])
const mapToInv = (inv: Array<Item | null>, type: InvType) => {
const update = (res: boolean) => {
if (!res) failed()
if (type === InvType.PLAYER) plugin.emit('openInv:fetchInv', setInv, player)
else plugin.emit('openInv:fetchEnderChest', setEnder, player)
}
const updateWithAction = (res: boolean) => {
action(res)
if (type === InvType.PLAYER) plugin.emit('openInv:fetchInv', setInv, player)
else plugin.emit('openInv:fetchEnderChest', setEnder, player)
}
return player
? inv.map((it, i) => <React.Fragment key={i}><ItemViewer
item={it}
data={{ type, solt: i, player }}
onDrag={() => plugin.emit('openInv:set', update, type, player, i, null, -1)}
onDrop={(item, obj) => plugin.emit('openInv:set', update, type,
player, i, JSON.stringify(item), obj?.type === type && obj?.player === player ? obj.solt : -1)}
onEdit={item => item !== false && plugin.emit('openInv:set', updateWithAction, type, player, i, item && JSON.stringify(item), -1)}
/>{!((i + 1) % 9) && <br />}</React.Fragment>)
: <Empty title={lang.openInv.notSelected} />
}
return <Box sx={{ minHeight: '100%', py: 3 }}>
<Toolbar />
<Container maxWidth={false}>
<Grid container spacing={3}>
<Grid item lg={6} md={12} xl={6} xs={12}>
<Card>
<CardHeader
title={lang.openInv.whosBackpack(player || minecraft['entity.minecraft.player'])}
sx={{ position: 'relative' }}
action={<IconButton
size='small'
disabled={!player}
sx={cardActionStyles}
onClick={() => {
success()
plugin.emit('openInv:fetchInv', setInv, player)
}}
><Refresh /></IconButton>}
/>
<Divider />
<CardContent sx={{ whiteSpace: 'nowrap', overflowX: 'auto' }}>{mapToInv(inv, InvType.PLAYER)}</CardContent>
</Card>
</Grid>
<Grid item lg={6} md={12} xl={6} xs={12}>
<Card>
<CardHeader
title={lang.openInv.whosEnderChest(player || minecraft['entity.minecraft.player'])}
sx={{ position: 'relative' }}
action={<IconButton
size='small'
disabled={!player}
sx={cardActionStyles}
onClick={() => {
success()
plugin.emit('openInv:fetchEnderChest', setEnder, player)
}}
><Refresh /></IconButton>}
/>
<Divider />
<CardContent sx={{ whiteSpace: 'nowrap', overflowX: 'auto' }}>{mapToInv(ender, InvType.ENDER_CHEST)}</CardContent>
</Card>
</Grid>
</Grid>
</Container>
</Box>
}
Example #2
Source File: Profiler.tsx From NekoMaid with MIT License | 5 votes |
Profiler: React.FC = () => { const plugin = usePlugin() const theme = useTheme() const globalData = useGlobalData() const [tab, setTab] = useState(0) const [key, setTKey] = useState(0) const [status, setStatus] = useState(!!globalData.profilerStarted) useEffect(() => { const off = plugin.on('profiler:status', setStatus) return () => { off() } }, []) const transitionDuration = { enter: theme.transitions.duration.enteringScreen, exit: theme.transitions.duration.leavingScreen } const Elm = components[tab] const createFab = (onClick: any, children: any, show: boolean) => <Zoom in={show} timeout={transitionDuration} style={{ transitionDelay: (show ? transitionDuration.exit : 0) + 'ms' }} unmountOnExit > <Fab color='primary' sx={{ position: 'fixed', bottom: { xs: 16, sm: 40 }, right: { xs: 16, sm: 40 }, zIndex: 3 }} onClick={onClick} >{children}</Fab> </Zoom> return <Box sx={{ minHeight: status || !tab ? '100%' : undefined }}> <Toolbar /> <Paper square variant='outlined' sx={{ margin: '0 -1px', position: 'fixed', width: 'calc(100% + 1px)', zIndex: 3 }}> <Tabs value={tab} onChange={(_, it) => setTab(it)} variant='scrollable' scrollButtons='auto'> <Tab label={lang.profiler.summary} /> <Tab label='Timings' disabled={!globalData.hasTimings} /> <Tab label={lang.profiler.plugins} /> <Tab label={lang.profiler.carbageCollection} /> <Tab label={lang.profiler.entities} /> <Tab label={lang.profiler.heap} /> <Tab label={lang.profiler.threads} /> </Tabs> </Paper> <Tabs /> {tab < 4 && !status ? <Box sx={{ textAlign: 'center', marginTop: '40vh' }}>{lang.profiler.notStarted}</Box> : Elm && <Elm key={key} />} {createFab(() => plugin.emit('profiler:status', !status), status ? <Stop /> : <PlayArrow />, tab < 4)} {createFab(() => setTKey(key + 1), <Refresh />, tab >= 4)} </Box> }
Example #3
Source File: BlockEditor.tsx From NekoMaid with MIT License | 4 votes |
BlockEditor: React.FC = () => {
const theme = useTheme()
const plugin = usePlugin()
const his = useHistory()
const loc = useLocation()
const globalData = useGlobalData()
const drawerWidth = useDrawerWidth()
const [block, setBlock] = useState<Block>()
const [types, setTypes] = useState<string[]>([])
const [worlds, setWorlds] = useState<string[]>([])
const params = { world: '', x: 0, y: 0, z: 0 }
if (loc.pathname.startsWith('/NekoMaid/block/')) {
const arr = loc.pathname.split('/')
if (arr.length > 6) {
params.world = arr[3]
params.x = +arr[4]
params.y = +arr[5]
params.z = +arr[6]
} else his.push('/NekoMaid/block')
}
useEffect(() => {
const off = plugin.emit('item:blocks', (types: string[], worlds: string[]) => {
setTypes(types)
setWorlds(worlds)
})
.on('block:select', (world, x, y, z) => his.push(`/NekoMaid/block/${world}/${x}/${y}/${z}`))
return () => void off()
}, [])
const update = () => {
if (params.world) {
plugin.emit('block:fetch', (block: Block) => {
if (!block) {
failed()
his.push('/NekoMaid/block')
return
}
if (globalData.hasNBTAPI && block.nbt) block.nbt = stringify(parse(block.nbt), { pretty: true })
setBlock(block)
}, params.world, params.x, params.y, params.z)
}
}
const updateWithAction = (res: boolean) => {
action(res)
update()
}
useEffect(update, [params.world, params.x, params.y, params.z])
return <Box sx={{ minHeight: '100%', py: 3 }}>
<Toolbar />
<Container maxWidth={false}>
<Grid container spacing={3} sx={{ width: { sm: `calc(100vw - ${drawerWidth}px - ${theme.spacing(3)})` } }}>
<Grid item lg={6} md={12} xl={6} xs={12}>
<Card sx={{ '& .CodeMirror-dialog, .CodeMirror-scrollbar-filler': { backgroundColor: theme.palette.background.paper + '!important' } }}>
<CardHeader
title={lang.blockEditor.title}
sx={{ position: 'relative' }}
action={<Box sx={cardActionStyles}>
<IconButton
size='small'
disabled={!block || (!block.data && !block.nbt)}
onClick={() => block && plugin.emit('block:save', (res: boolean) => {
action(res)
update()
}, params.world, params.x, params.y, params.z, block.nbt || null, block.data || null)}
><Save /></IconButton>
<IconButton
size='small'
disabled={!block}
onClick={() => {
update()
success()
}}
><Refresh /></IconButton>
</Box>}
/>
<Divider />
{block
? <>
<CardContent sx={{ display: 'flex', width: '100%', justifyContent: 'center' }}>
<ItemViewer item={{ type: block.type }} />
<Autocomplete
options={types}
sx={{ maxWidth: 300, marginLeft: 1, flexGrow: 1 }}
value={block.type}
onChange={(_, it) => it && plugin.emit('block:type', (res: boolean) => {
action(res)
update()
}, params.world, params.x, params.y, params.z, (block.type = it))}
getOptionLabel={it => {
const locatedName = getName(it.toLowerCase())
return (locatedName ? locatedName + ' ' : '') + it
}}
renderInput={(params) => <TextField {...params} label={lang.itemEditor.itemType} size='small' variant='standard' />}
/>
</CardContent>
{block.data != null && <Accordion sx={{ '&::before': { opacity: '1!important' } }} disableGutters>
<AccordionSummary expandIcon={<ExpandMore />}><Typography>{lang.data}</Typography></AccordionSummary>
<AccordionDetails sx={{ padding: 0, '& .CodeMirror': { width: '100%', height: 350 } }}>
<UnControlled
value={block.data}
options={{
mode: 'javascript',
phrases: lang.codeMirrorPhrases,
theme: theme.palette.mode === 'dark' ? 'material' : 'one-light'
}}
onChange={(_: any, __: any, data: string) => (block.data = data)}
/>
</AccordionDetails>
</Accordion>}
{block.nbt != null && <Accordion sx={{ '&::before': { opacity: '1!important', display: '!important' } }} disableGutters>
<AccordionSummary expandIcon={<ExpandMore />}><Typography>NBT</Typography></AccordionSummary>
<AccordionDetails sx={{ padding: 0, '& .CodeMirror': { width: '100%', height: 350 } }}>
<UnControlled
value={block.nbt}
options={{
mode: 'javascript',
phrases: lang.codeMirrorPhrases,
theme: theme.palette.mode === 'dark' ? 'material' : 'one-light'
}}
onChange={(_: any, __: any, data: string) => (block.nbt = data)}
/>
</AccordionDetails>
</Accordion>}
</>
: <CardContent>{worlds.length ? <BlockSelector worlds={worlds} /> : <Empty />}</CardContent>}
</Card>
</Grid>
{block?.inventory?.length
? <Grid item lg={6} md={12} xl={6} xs={12}>
<Card>
<CardHeader
title={minecraft[('container.' + block.inventoryType || '').toLowerCase()] || lang.blockEditor.container}
sx={{ position: 'relative' }}
/>
<Divider />
<CardContent sx={{ whiteSpace: 'nowrap', overflowX: 'auto', textAlign: 'center' }}>
{block.inventory.map((it, i) => <React.Fragment key={i}><ItemViewer
item={it}
data={{ type: InvType.BLOCK, solt: i, ...params }}
onDrag={() => plugin.emit('block:setItem', update, params.world, params.x, params.y, params.z, i, null, -1)}
onDrop={(item, obj) => plugin.emit('block:setItem', update, params.world, params.x, params.y, params.z, i,
JSON.stringify(item), compare(obj, params) ? obj.solt : -1)}
onEdit={item => item !== false && plugin.emit('block:setItem', updateWithAction, params.world, params.x, params.y,
params.z, i, item && JSON.stringify(item), -1)}
/>{!((i + 1) % 9) && <br />}</React.Fragment>)}
</CardContent>
</Card>
</Grid>
: undefined}
</Grid>
</Container>
</Box>
}
Example #4
Source File: Dashboard.tsx From NekoMaid with MIT License | 4 votes |
Dashboard: React.FC = () => {
const plugin = usePlugin()
const { version, hasGeoIP } = useGlobalData()
const [status, setStatus] = useState<Status[]>([])
const [current, setCurrent] = useState<CurrentStatus | undefined>()
useEffect(() => {
const offSetStatus = plugin.once('dashboard:info', setStatus)
const offCurrent = plugin.on('dashboard:current', (data: CurrentStatus) => setCurrent(old => {
if (old && isEqual(old.players, data.players)) data.players = old.players
return data
}))
plugin.switchPage('dashboard')
return () => {
offSetStatus()
offCurrent()
}
}, [])
const playerCount = current?.players?.length || 0
const prev = status[status.length - 1]?.players || 0
const percent = (prev ? playerCount / prev - 1 : playerCount) * 100
const tpsColor = !current || current.tps >= 18 ? green : current.tps >= 15 ? yellow : red
return <Box sx={{ minHeight: '100%', py: 3 }}>
<Toolbar />
<Container maxWidth={false}>
<Grid container spacing={3}>
<Grid item lg={3} sm={6} xl={3} xs={12}>
<TopCard
title={lang.dashboard.version}
content={current ? version : <Skeleton animation='wave' width={150} />}
icon={<Handyman />}
color={orange[600]}
>
<Box sx={{ pt: 2, display: 'flex', alignItems: 'flex-end' }}>
{!current || current.behinds < 0
? <Refresh htmlColor={blue[900]} />
: current?.behinds === 0
? <Check htmlColor={green[900]} />
: <Update htmlColor={yellow[900]} />}
<Typography color='textSecondary' variant='caption'> {!current || current.behinds === -3
? lang.dashboard.updateChecking
: current.behinds < 0
? <Link underline='hover' color='inherit' sx={{ cursor: 'pointer' }} onClick={() => {
toast(lang.dashboard.updateChecking)
plugin.emit('dashboard:checkUpdate')
}}>{lang.dashboard.updateFailed}</Link>
: current.behinds === 0 ? lang.dashboard.updated : lang.dashboard.behinds(current.behinds)}</Typography>
</Box>
</TopCard>
</Grid>
<Grid item lg={3} sm={6} xl={3} xs={12}>
<TopCard
title={lang.dashboard.onlinePlayers}
content={current ? playerCount : <Skeleton animation='wave' width={150} />}
icon={<People />}
color={deepPurple[600]}
>
<Box sx={{ pt: 2, display: 'flex', alignItems: 'flex-end' }}>
{percent === 0 ? <Remove color='primary' /> : percent < 0 ? <ArrowDownward color='error' /> : <ArrowUpward color='success' />}
<Typography
sx={{ color: (percent === 0 ? blue : percent < 0 ? red : green)[900], mr: 1 }}
variant='body2'
>{Math.abs(percent).toFixed(0)}%</Typography>
<Typography color='textSecondary' variant='caption'>{lang.dashboard.lastHour}</Typography>
</Box>
</TopCard>
</Grid>
<Grid item lg={3} sm={6} xl={3} xs={12}>
<TopCard
title='TPS'
content={current ? (current.tps === -1 ? '?' : current.tps.toFixed(2)) : <Skeleton animation='wave' width={150} />}
icon={!current || current.tps >= 18 || current.tps === -1
? <SentimentVerySatisfied />
: current.tps >= 15 ? <SentimentSatisfied /> : <SentimentDissatisfied />}
color={tpsColor[600]}
>
<Box sx={{ pt: 2.1, display: 'flex', alignItems: 'flex-end' }}>
<Typography
sx={{ color: tpsColor[900], mr: 1 }}
variant='body2'
>{!current || current.mspt === -1 ? '?' : current.mspt.toFixed(2) + 'ms'}</Typography>
<Typography color='textSecondary' variant='caption'>{lang.dashboard.mspt}</Typography>
</Box>
</TopCard>
</Grid>
<Grid item lg={3} sm={6} xl={3} xs={12}>
<TopCard
title={lang.dashboard.uptime}
content={current ? <Uptime time={current.time} /> : <Skeleton animation='wave' width={150} />}
icon={<AccessTime />}
color={blue[600]}
>
<Box sx={{ pt: 2.7, display: 'flex', alignItems: 'center' }}>
<Typography color='textSecondary' variant='caption' sx={{ marginRight: 1 }}>{lang.dashboard.memory}</Typography>
<Tooltip title={current?.totalMemory ? prettyBytes(current.memory) + ' / ' + prettyBytes(current.totalMemory) : ''}>
<LinearProgress
variant='determinate'
value={current?.totalMemory ? current.memory / current.totalMemory * 100 : 0}
sx={{ flex: '1' }}
/>
</Tooltip>
</Box>
</TopCard>
</Grid>
<Grid item lg={8} md={12} xl={9} xs={12}>{useMemo(() => <Charts data={status} />, [status])}</Grid>
<Grid item lg={4} md={12} xl={3} xs={12}><Players players={current?.players} /></Grid>
{hasGeoIP && current?.players && typeof current.players[0] !== 'string' && <Grid item xs={12}>
<Accordion TransitionProps={{ unmountOnExit: true }} disableGutters>
<AccordionSummary expandIcon={<ExpandMore />}>
<Typography>{lang.dashboard.playersDistribution}</Typography>
</AccordionSummary>
<Divider />
<WorldMap players={current.players as Player[]} />
</Accordion>
</Grid>}
</Grid>
</Container>
</Box>
}
Example #5
Source File: EntityEditor.tsx From NekoMaid with MIT License | 4 votes |
EntityEditor: React.FC = () => {
const theme = useTheme()
const plugin = usePlugin()
const his = useHistory()
const loc = useLocation()
const globalData = useGlobalData()
const drawerWidth = useDrawerWidth()
const [customName, setCustomName] = useState('')
const [entity, setEntity] = useState<Entity>()
let id: string | null = null
if (loc.pathname.startsWith('/NekoMaid/entity/')) {
const arr = loc.pathname.split('/')
if (arr.length > 3) id = arr[3]
}
useEffect(() => {
const off = plugin.on('entity:select', id => his.push('/NekoMaid/entity/' + id))
return () => void off()
}, [])
const update = () => {
if (id) {
plugin.emit('entity:fetch', (entity: Entity) => {
if (!entity) {
failed()
his.push('/NekoMaid/entity')
return
}
if (globalData.hasNBTAPI && entity.nbt) entity.nbt = stringify(parse(entity.nbt), { pretty: true })
setCustomName(entity.customName || '')
setEntity(entity)
}, id)
}
}
const updateWithAction = (res: boolean) => {
action(res)
update()
}
useEffect(update, [id])
return <Box sx={{ minHeight: '100%', py: 3 }}>
<Toolbar />
<Container maxWidth={false}>
<Grid container spacing={3} sx={{ width: { sm: `calc(100vw - ${drawerWidth}px - ${theme.spacing(3)})` } }}>
<Grid item lg={6} md={12} xl={6} xs={12}>
<Card>
<CardHeader
title={(entity && minecraft['entity.minecraft.' + entity.type.toLowerCase()]) || lang.entityEditor.title}
sx={{ position: 'relative' }}
action={<Box sx={cardActionStyles}>
<IconButton
size='small'
disabled={!entity}
onClick={() => entity && plugin.emit('entity:save', (res: boolean) => {
action(res)
update()
}, id, entity.nbt || null, customName || null)}
><Save /></IconButton>
<IconButton
size='small'
disabled={!entity}
onClick={() => {
update()
success()
}}
><Refresh /></IconButton>
</Box>}
/>
<Divider />
{entity
? <>
<CardContent>
<Grid container>
<Grid item lg={6} md={6} xl={6} xs={12}>
<TextField
size='small'
label={lang.entityEditor.customName}
value={customName}
sx={{ width: '90%' }}
onChange={e => setCustomName(e.target.value)}
/>
</Grid>
{values.map(it => <Grid item lg={6} md={6} xl={6} xs={12} key={it}>
<FormControlLabel
control={<Switch checked={(entity as any)[it]} />}
label={(lang.entityEditor as any)[it]}
onChange={(e: any) => plugin.emit('entity:set', (res: boolean) => {
action(res)
update()
}, id, it, e.target.checked)}
/>
</Grid>)}
</Grid>
</CardContent>
{entity.nbt != null && <Accordion sx={{ '&::before': { opacity: '1!important' } }} disableGutters>
<AccordionSummary expandIcon={<ExpandMore />}><Typography>NBT</Typography></AccordionSummary>
<AccordionDetails sx={{
padding: 0,
'& .CodeMirror': { width: '100%', height: 350 },
'& .CodeMirror-dialog, .CodeMirror-scrollbar-filler': { backgroundColor: theme.palette.background.paper + '!important' }
}}>
<UnControlled
value={entity.nbt}
options={{
mode: 'javascript',
phrases: lang.codeMirrorPhrases,
theme: theme.palette.mode === 'dark' ? 'material' : 'one-light'
}}
onChange={(_: any, __: any, data: string) => (entity.nbt = data)}
/>
</AccordionDetails>
</Accordion>}
</>
: <CardContent><EntitySelector /></CardContent>}
</Card>
</Grid>
{entity?.inventory?.length
? <Grid item lg={6} md={12} xl={6} xs={12}>
<Card>
<CardHeader
title={lang.entityEditor.container}
sx={{ position: 'relative' }}
/>
<Divider />
<CardContent sx={{ whiteSpace: 'nowrap', overflowX: 'auto', textAlign: 'center' }}>
{entity.inventory.map((it, i) => <React.Fragment key={i}><ItemViewer
item={it}
data={{ type: InvType.ENTITY, solt: i, id }}
onDrag={() => plugin.emit('entity:setItem', update, id, i, null, -1)}
onDrop={(item, obj) => plugin.emit('entity:setItem', update, id, i, JSON.stringify(item),
obj?.type === InvType.ENTITY && obj.id === id ? obj.solt : -1)}
onEdit={item => item !== false && plugin.emit('entity:setItem', updateWithAction, id, i, item && JSON.stringify(item), -1)}
/>{!((i + 1) % 9) && <br />}</React.Fragment>)}
</CardContent>
</Card>
</Grid>
: undefined}
</Grid>
</Container>
</Box>
}
Example #6
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 #7
Source File: weatherCard.tsx From Search-Next with GNU General Public License v3.0 | 4 votes |
WeatherCard: FC<WeatherCardProps> = (props) => {
const {
weather = {} as QWeatherNow,
city = {} as QWeatherCity,
loading = false,
onRefresh,
apiKey,
} = props;
const extraList: {
field: keyof Now;
unit: string;
label?: string;
labelBuilder?: () => string;
}[] = [
{
label: '体感',
field: 'feelsLike',
unit: '℃',
},
{
labelBuilder: () => weather.now?.windDir,
field: 'windScale',
unit: '级',
},
{
label: '湿度',
field: 'humidity',
unit: '%',
},
{
label: '气压',
field: 'pressure',
unit: 'hPa',
},
];
const showLoading = useMemo(() => {
if (loading) return loading;
if (Object.keys(weather).length === 0 || Object.keys(city).length === 0)
return true;
return false;
}, [loading, weather, city]);
return (
<BorderCard>
<Loading loading={showLoading}>
<a className="px-4 py-2 block" href={weather.fxLink} target="_blank">
<div className="flex gap-1 font-semibold text-lg items-center">
<div>{city.location?.[0].adm2}</div>
<div>{city.location?.[0].name}</div>
{apiKey && (
<IconButton
size="small"
onClick={(e) => {
e.preventDefault();
if (onRefresh) onRefresh();
}}
>
<Refresh />
</IconButton>
)}
</div>
<div className="flex items-center justify-center gap-6 mb-2">
<div className="flex items-center gap-3 mb-2">
<div>
{weather.now?.icon && (
<img
className="w-16 h-16"
src={getWeatherIcon(weather.now?.icon)}
/>
)}
</div>
<div>
<div className="flex gap-1">
<div className="text-5xl font-semibold">
{weather.now?.temp}
<span className="text-sm align-top">℃</span>
</div>
</div>
<div>{weather.now?.text}</div>
</div>
</div>
<div className="bg-gray-500 w-1 h-9"></div>
<ul className="flex gap-4">
{extraList.map((i, j) => (
<li key={j}>
<div className="text-xl font-semibold mb-2">
{weather.now?.[i.field]}
{i.unit}
</div>
<div className="text-sm">
{i.labelBuilder ? i.labelBuilder() : i.label}
</div>
</li>
))}
</ul>
</div>
</a>
</Loading>
</BorderCard>
);
}