@mui/material#List TypeScript Examples
The following examples show how to use
@mui/material#List.
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: GroupFilter.tsx From Tachidesk-WebUI with Mozilla Public License 2.0 | 6 votes |
export default function GroupFilter(props: Props) {
const {
state,
name,
position,
updateFilterValue,
update,
} = props;
const [open, setOpen] = React.useState(false);
const handleClick = () => {
setOpen(!open);
};
return (
<>
<ListItemButton onClick={handleClick}>
<ListItemText primary={name} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={open}>
<List disablePadding>
<Options
sourceFilter={state}
group={position}
updateFilterValue={updateFilterValue}
update={update}
/>
</List>
</Collapse>
</>
);
}
Example #2
Source File: AppNavbar.tsx From sapio-studio with Mozilla Public License 2.0 | 6 votes |
export function AppNavbar(props: {
relayout: () => void;
contract: ContractModel;
bitcoin_node_manager: BitcoinNodeManager;
}): JSX.Element {
const dispatch = useDispatch();
return (
<Paper className="AppNavBar" square={true}>
<List sx={{ textAlign: 'center' }}>
<MainScreens></MainScreens>
</List>
<Divider />
<List>
<ContractMenu relayout={props.relayout} />
<NodeMenu bitcoin_node_manager={props.bitcoin_node_manager} />
<Simulator />
</List>
<Divider />
<List>
<SettingsMenuItem />
</List>
<Divider />
</Paper>
);
}
Example #3
Source File: navigation.tsx From sdk with MIT License | 6 votes |
export function Navigation() {
const links = [{
label: "About",
path: "/about",
default: true
}, {
label: "Connect",
path: "/connect"
}, {
label: "Deploy Collection",
path: "/deploy"
}, {
label: "Mint Token",
path: "/mint"
}, {
label: "Sell",
path: "/sell"
}, {
label: "Buy",
path: "/buy"
}, {
label: "Bid",
path: "/bid"
}, {
label: "Accept Bid",
path: "/accept-bid"
}, {
label: "My Items",
path: "/items"
}]
return (
<List>
{links.map((link) => (
<ListItemLink key={link.path} to={link.path} primary={link.label} default={link.default}/>
))}
</List>
)
}
Example #4
Source File: navigation.tsx From example with MIT License | 6 votes |
export function Navigation() {
const links = [{
label: "About",
path: "/about",
default: true
}, {
label: "Connect",
path: "/connect"
}, {
label: "Deploy Collection",
path: "/deploy"
}, {
label: "Mint Token",
path: "/mint"
}, {
label: "Sell",
path: "/sell"
}, {
label: "Buy",
path: "/buy"
}, {
label: "Bid",
path: "/bid"
}, {
label: "Accept Bid",
path: "/accept-bid"
}]
return (
<List>
{links.map((link) => (
<ListItemLink key={link.path} to={link.path} primary={link.label} default={link.default}/>
))}
</List>
)
}
Example #5
Source File: FileList.tsx From frontend with MIT License | 6 votes |
function FileList({ files, onDelete }: Props) {
return (
<List dense>
{files.map((file, key) => (
<ListItem
key={key}
secondaryAction={
<IconButton edge="end" aria-label="delete" onClick={() => onDelete && onDelete(file)}>
<Delete />
</IconButton>
}>
<ListItemAvatar>
<Avatar>
<UploadFile />
</Avatar>
</ListItemAvatar>
<ListItemText primary={file.type} />
<ListItemText primary={file.name} />
</ListItem>
))}
</List>
)
}
Example #6
Source File: Profile.tsx From abrechnung with GNU Affero General Public License v3.0 | 6 votes |
export default function Profile() {
const user = useRecoilValue(userData);
const isGuest = useRecoilValue(isGuestUser);
useTitle("Abrechnung - Profile");
return (
<MobilePaper>
<Typography component="h3" variant="h5">
Profile
</Typography>
{isGuest && (
<Alert severity="info">
You are a guest user on this Abrechnung and therefore not permitted to create new groups or group
invites.
</Alert>
)}
<List>
<ListItem>
<ListItemText primary="Username" secondary={user.username} />
</ListItem>
<ListItem>
<ListItemText primary="E-Mail" secondary={user.email} />
</ListItem>
<ListItem>
<ListItemText
primary="Registered"
secondary={DateTime.fromISO(user.registered_at).toLocaleString(DateTime.DATETIME_FULL)}
/>
</ListItem>
</List>
</MobilePaper>
);
}
Example #7
Source File: ExpandableListItem.tsx From frontend with MIT License | 6 votes |
ExpandableListItem = ({ header, content }: Props) => {
const [open, setOpen] = useState(false)
return (
<List
sx={{
my: 0,
mx: { xs: 0, md: 3 },
cursor: 'pointer',
}}>
<Paper elevation={1} sx={{ borderRadius: 2 }}>
<Box
sx={{ display: 'flex', alignItems: 'center', px: 3, py: 1 }}
onClick={() => setOpen(!open)}>
<ListItemText
primary={header}
primaryTypographyProps={{
variant: 'subtitle1',
color: `${withAccentColor(open)}`,
}}
/>
{open ? (
<ExpandLess sx={{ color: `${withAccentColor(open)}` }} />
) : (
<ExpandMore sx={{ color: `${withAccentColor(open)}` }} />
)}
</Box>
<Collapse in={open}>
<List>
<Box sx={{ pl: { xs: 3, md: 6 }, pb: 2, pr: 2 }}>{content}</Box>
</List>
</Collapse>
</Paper>
</List>
)
}
Example #8
Source File: AccountTransactionList.tsx From abrechnung with GNU Affero General Public License v3.0 | 6 votes |
export default function AccountTransactionList({ group, accountID }) {
const transactions = useRecoilValue(accountTransactions({ groupID: group.id, accountID: accountID }));
const clearingAccounts = useRecoilValue(clearingAccountsInvolvingUser({ groupID: group.id, accountID: accountID }));
const combinedList: ArrayAccountsAndTransactions = (transactions as ArrayAccountsAndTransactions)
.concat(clearingAccounts)
.sort((f1, f2) => f2.last_changed.toSeconds() - f1.last_changed.toSeconds());
return (
<List>
{combinedList.map((entry) => {
if (entry.type === "clearing") {
return (
<AccountClearingListEntry key={entry.id} accountID={accountID} group={group} account={entry} />
);
}
return (
<AccountTransactionListEntry
key={entry.id}
accountID={accountID}
group={group}
transaction={entry}
/>
);
})}
</List>
);
}
Example #9
Source File: Vault.tsx From NekoMaid with MIT License | 6 votes |
Groups: React.FC<{ plugin: Plugin, id: string | undefined, onClose: () => void, groups: GroupInfo[] }> =
({ plugin, id, onClose, groups }) => {
const [loading, setLoading] = useState(true)
const [playerGroups, setPlayerGroups] = useState<Record<string, true>>({ })
const refresh = () => {
setLoading(true)
plugin.emit('vault:playerGroup', (res: string[]) => {
if (!res) return
const obj: Record<string, true> = { }
res.forEach(it => (obj[it] = true))
setPlayerGroups(obj)
setLoading(false)
}, id, null, 0)
}
useEffect(() => {
setPlayerGroups({})
if (!id) return
refresh()
}, [id])
return <Dialog onClose={onClose} open={!!id}>
<DialogTitle>{lang.vault.whosPermissionGroup(id!)}</DialogTitle>
<List sx={{ pt: 0 }}>
{groups.map(it => <ListItem onClick={() => { }} key={it.id}>
<ListItemIcon><Checkbox
tabIndex={-1}
disabled={loading}
checked={!!playerGroups[it.id]}
onChange={e => plugin.emit('vault:playerGroup', (res: boolean) => {
action(res)
refresh()
}, id, it.id, e.target.checked ? 1 : 2)}
/></ListItemIcon>
<ListItemText primary={it.id} />
</ListItem>)}
</List>
<DialogActions><Button onClick={onClose}>{minecraft['gui.back']}</Button></DialogActions>
</Dialog>
}
Example #10
Source File: WidgetsList.tsx From fluttertemplates.dev with MIT License | 6 votes |
function WidgetsList(props: WidgetsListProps) {
return (
<div
style={{
width: `100%`,
marginTop: "1rem",
marginLeft: "1rem",
}}
>
<Typography
variant="h4"
style={{
fontWeight: "bold",
}}
>
{props.componentSubgroup.title}
</Typography>
<List>
{props.componentSubgroup.widgets.map((item, index) => (
<WidgetDemoBlock
key={`widget_${index}_${item.title}`}
title={item.title}
codeUrl={item.codeUrl}
demoUrl={item.demoUrl}
rawCodeUrl={item.rawCodeUrl}
description={item.description}
id={item.id.split("/").slice(-1)[0]}
/>
))}
</List>
</div>
);
}
Example #11
Source File: FieldDisplay.tsx From genshin-optimizer with MIT License | 6 votes |
FieldDisplayList = styled(List)(({ theme }) => ({
borderRadius: theme.shape.borderRadius,
overflow: "hidden",
margin: 0,
'> .MuiListItem-root:nth-of-type(even)': {
backgroundColor: theme.palette.contentDark.main
},
'> .MuiListItem-root:nth-of-type(odd)': {
backgroundColor: theme.palette.contentDarker.main
},
}))
Example #12
Source File: Dashboard.tsx From NekoMaid with MIT License | 5 votes |
Players: React.FC<{ players?: CurrentStatus['players'] }> = React.memo(({ players }) => {
const his = useHistory()
const plugin = usePlugin()
const globalData = useGlobalData()
const [page, setPage] = useState(1)
const [id, update] = useState(0)
return <Card>
<CardHeader title={lang.dashboard.onlinePlayers} />
<Divider />
<CardContent>
{players?.length === 0
? <Empty />
: <>
<List sx={{ paddingTop: 0 }}>
{players
? players.slice((page - 1) * 8, page * 8).map(p => {
const name = typeof p === 'string' ? p : p.name
return <Tooltip key={name} title={'IP: ' + ((p as any).ip || lang.unknown)}>
<ListItem
secondaryAction={<>
<IconButton
edge='end'
size='small'
onClick={() => dialog(lang.dashboard.confirmKick(<span className='bold'>{name}</span>), lang.reason)
.then(it => it != null && plugin.emit('dashboard:kick', (res: boolean) => {
action(res)
if (!players) return
players.splice(players.indexOf(it!), 1)
update(id + 1)
}, name, it || null))
}
><ExitToApp /></IconButton>
<IconButton edge='end' onClick={() => his.push('/NekoMaid/playerList/' + name)} size='small'><MoreHoriz /></IconButton>
</>
}
>
<ListItemAvatar>
<Avatar
src={getSkin(globalData, name, true)}
imgProps={{ crossOrigin: 'anonymous', onClick () { his.push('/NekoMaid/playerList/' + name) }, style: { width: 40, height: 40 } }}
sx={{ cursor: 'pointer' }}
variant='rounded'
/>
</ListItemAvatar>
<ListItemText primary={name} />
</ListItem>
</Tooltip>
})
: <LoadingList />
}
</List>
{players && <Pagination
page={page}
onChange={(_, it) => setPage(it)}
count={Math.max(Math.ceil(players.length / 8), 1)}
sx={{ display: 'flex', justifyContent: 'flex-end' }}
/>}
</>}
</CardContent>
</Card>
})
Example #13
Source File: SortFilter.tsx From Tachidesk-WebUI with Mozilla Public License 2.0 | 5 votes |
export default function SortFilter(props: Props) {
const {
values,
name,
state,
position,
group,
updateFilterValue,
update,
} = props;
const [val, setval] = React.useState(state);
const [open, setOpen] = React.useState(false);
const handleClick = () => {
setOpen(!open);
};
if (values) {
const handleChange = (event:
React.MouseEvent<HTMLDivElement, MouseEvent>, index: number) => {
const tmp = val;
if (tmp.index === index) {
tmp.ascending = !tmp.ascending;
} else {
tmp.ascending = true;
}
tmp.index = index;
setval(tmp);
const upd = update.filter((e: {
position: number; group: number | undefined;
}) => !(position === e.position && group === e.group));
updateFilterValue([...upd, { position, state: JSON.stringify(tmp), group }]);
};
const ret = (
<FormControl fullWidth>
<ListItemButton onClick={handleClick}>
<ListItemText primary={name} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={open}>
<List>
{values.map((value: string, index: number) => {
let icon;
if (val.index === index) {
icon = val.ascending ? (<ArrowUpwardIcon color="primary" />)
: (<ArrowDownwardIcon color="primary" />);
}
return (
<ListItem disablePadding key={`${name} ${value}`}>
<ListItemButton
onClick={(event) => handleChange(event, index)}
>
<ListItemIcon>
{icon}
</ListItemIcon>
<ListItemText primary={value} />
</ListItemButton>
</ListItem>
);
})}
</List>
</Collapse>
</FormControl>
);
return (
<Box key={name} sx={{ display: 'flex', flexDirection: 'column', minWidth: 120 }}>
{ret}
</Box>
);
}
return (<></>);
}
Example #14
Source File: ContextMenu.tsx From multi-downloader-nx with MIT License | 5 votes |
function ContextMenu<T extends HTMLElement, >(props: ContextMenuProps<T>) {
const [anchor, setAnchor] = React.useState( { x: 0, y: 0 } );
const [show, setShow] = React.useState(false);
React.useEffect(() => {
const { popupItem: ref } = props;
if (ref.current === null)
return;
const listener = (ev: MouseEvent) => {
ev.preventDefault();
setAnchor({ x: ev.x + 10, y: ev.y + 10 });
setShow(true);
}
ref.current.addEventListener('contextmenu', listener);
return () => {
if (ref.current)
ref.current.removeEventListener('contextmenu', listener)
};
}, [ props.popupItem ])
return show ? <Box sx={{ zIndex: 9999, p: 1, background: 'rgba(0, 0, 0, 0.75)', backdropFilter: 'blur(5px)', position: 'fixed', left: anchor.x, top: anchor.y }}>
<List sx={{ p: 0, m: 0 }}>
{props.options.map((item, i) => {
return item === 'divider' ? <Divider key={`ContextMenu_Divider_${i}_${item}`}/> :
<Button color='inherit' key={`ContextMenu_Value_${i}_${item}`} onClick={() => {
item.onClick();
setShow(false);
}} sx={buttonSx}>
{item.text}
</Button>
})}
<Divider />
<Button fullWidth color='inherit' onClick={() => setShow(false)} sx={buttonSx} >
Close
</Button>
</List>
</Box> : <></>
}
Example #15
Source File: Header.tsx From genshin-optimizer with MIT License | 5 votes |
function MobileHeader({ anchor, currentTab }) {
const [mobileOpen, setMobileOpen] = useState(false);
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen);
};
const { t } = useTranslation("ui")
return <>
<AppBar position="fixed" sx={{ bgcolor: "#343a40" }} elevation={0} >
<Drawer
anchor="right"
variant="temporary"
open={mobileOpen}
onClose={handleDrawerToggle}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
>
<List>
<ListItemButton key="home" component={RouterLink} to={'/'} selected={currentTab === ""} disabled={currentTab === ""} onClick={handleDrawerToggle} >
<ListItemText>{t("pageTitle")}</ListItemText>
</ListItemButton >
{content.map(({ i18Key, value, to, svg }) =>
<ListItemButton key={value} component={RouterLink} to={to} selected={currentTab === value} disabled={currentTab === value} onClick={handleDrawerToggle} >
<ListItemIcon><FontAwesomeIcon icon={svg} /></ListItemIcon>
<ListItemText>{t(i18Key)}</ListItemText>
</ListItemButton >)}
</List>
<Divider />
<List>
{links.map(({ i18Key, href, svg, label }) =>
<ListItemButton key={label} component="a" href={href} target="_blank" onClick={e => ReactGA.outboundLink({ label }, () => { })} >
<ListItemIcon><FontAwesomeIcon icon={svg} /></ListItemIcon>
<ListItemText>{t(i18Key)}</ListItemText>
</ListItemButton >)}
</List>
</Drawer>
<Toolbar>
<Button variant="text" sx={{ color: "white" }} component={RouterLink} to="/">
<Typography variant="h6" noWrap component="div">
<Trans t={t} i18nKey="pageTitle">Genshin Optimizer</Trans>
</Typography>
</Button>
<Box flexGrow={1} />
<IconButton
color="inherit"
aria-label="open drawer"
edge="end"
onClick={handleDrawerToggle}
>
<MenuIcon />
</IconButton>
</Toolbar>
</AppBar>
{/* add a blank toolbar to keep space and provide a scroll anchor */}
<Toolbar id={anchor} />
</>
}
Example #16
Source File: TLSCertificate.tsx From console with GNU Affero General Public License v3.0 | 5 votes |
TLSCertificate = ({
classes,
certificateInfo,
onDelete = () => {},
}: ITLSCertificate) => {
const certificates = certificateInfo.domains || [];
return (
<Chip
key={certificateInfo.name}
variant="outlined"
color="primary"
className={classes.certificateWrapper}
label={
<Container>
<Grid item xs={1} className={classes.certificateIcon}>
<CertificateIcon />
</Grid>
<Grid item xs={11} className={classes.certificateInfo}>
<Typography variant="subtitle1" display="block" gutterBottom>
{certificateInfo.name}
</Typography>
<Box className={classes.certificateExpiry}>
<EventBusyIcon color="inherit" fontSize="small" />
<span className={"label"}>Expiry: </span>
<span>
<Moment format="YYYY/MM/DD">{certificateInfo.expiry}</Moment>
</span>
</Box>
<Divider />
<br />
<Box className={classes.certificateDomains}>
<span className="label">{`${certificates.length} Domain (s):`}</span>
</Box>
<List className={classes.certificatesList}>
{certificates.map((dom) => (
<ListItem className={classes.certificatesListItem}>
<ListItemAvatar>
<LanguageIcon />
</ListItemAvatar>
<ListItemText primary={dom} />
</ListItem>
))}
</List>
</Grid>
</Container>
}
onDelete={onDelete}
/>
);
}
Example #17
Source File: QueryStateEditor.tsx From mui-toolpad with MIT License | 5 votes |
export default function QueryStateEditor() {
const dom = useDom();
const state = usePageEditorState();
const domApi = useDomApi();
const [editedState, setEditedState] = React.useState<NodeId | null>(null);
const editedStateNode = editedState ? appDom.getNode(dom, editedState, 'queryState') : null;
const handleEditStateDialogClose = React.useCallback(() => setEditedState(null), []);
const page = appDom.getNode(dom, state.nodeId, 'page');
const { queryStates = [] } = appDom.getChildNodes(dom, page) ?? [];
// To keep dialog content around during closing animation
const lastEditedStateNode = useLatest(editedStateNode);
const handleRemove = React.useCallback(() => {
if (editedStateNode) {
domApi.removeNode(editedStateNode.id);
}
handleEditStateDialogClose();
}, [editedStateNode, handleEditStateDialogClose, domApi]);
return queryStates.length > 0 ? (
<Stack spacing={1} alignItems="start">
<List>
{queryStates.map((stateNode) => {
return (
<ListItem key={stateNode.id} button onClick={() => setEditedState(stateNode.id)}>
{stateNode.name}
</ListItem>
);
})}
</List>
{lastEditedStateNode ? (
<Dialog
fullWidth
maxWidth="lg"
open={!!editedStateNode}
onClose={handleEditStateDialogClose}
scroll="body"
>
<DialogTitle>Edit Query State ({lastEditedStateNode.id})</DialogTitle>
<DialogContent>
<QueryStateNodeEditor node={lastEditedStateNode} />
</DialogContent>
<DialogActions>
<Button color="inherit" variant="text" onClick={handleEditStateDialogClose}>
Close
</Button>
<Button onClick={handleRemove}>Remove</Button>
</DialogActions>
</Dialog>
) : null}
</Stack>
) : null;
}
Example #18
Source File: index.tsx From Search-Next with GNU General Public License v3.0 | 5 votes |
NavigationPage: React.FC<PageProps> = (props) => {
const menu: Classify[] = navigations;
const history = useNavigate();
const location = useLocation();
const params = useParams();
const [selected, setSelected] = React.useState<Classify>({} as Classify);
const changeSelect = (path: string, type: 'push' | 'replace' = 'replace') => {
const find = menu.find((i) => i.path === path);
if (find) {
const path = `${basePath}/${find.path}`;
if (type === 'push') history(path);
if (type === 'replace') history(path, { replace: true });
setSelected(find);
}
};
const menuChange = (id: string, item: Classify) => {
changeSelect(item.path, 'push');
};
React.useEffect(() => {
const path = location.pathname;
if (path === basePath + '/*') {
history(`${basePath}/${menu[0].path}`, { replace: true });
setSelected(menu[0]);
} else {
const { classify } = params;
changeSelect(classify || '');
}
}, []);
return (
<MenuLayoutNew
{...props}
mode="page"
menu={menu as MenuLayoutMenu[]}
pathname={basePath}
onChange={menuChange}
menuFooter={
<List dense>
<MenuListItem
icon={<InsertComment />}
primary="提交网站"
onClick={() => history('/help/commit_website')}
/>
</List>
}
>
{Recursion(selected)}
</MenuLayoutNew>
);
}
Example #19
Source File: AdminLayout.tsx From frontend with MIT License | 5 votes |
export default function AdminLayout({ children }: Props) {
const theme = useTheme()
const initialOpen = useMemo<boolean>(() => {
const item = typeof window !== 'undefined' ? window.localStorage.getItem('menu-open') : false
if (item) {
return Boolean(JSON.parse(item))
}
return false
}, [])
const [open, setOpen] = useState<boolean>(initialOpen)
useEffect(() => {
if (typeof window !== 'undefined') {
window.localStorage.setItem('menu-open', JSON.stringify(open))
}
}, [open])
const toggleMenu = useCallback(() => setOpen((open) => !open), [])
return (
<StyledBox className={classes.wrapper}>
<AdminAppBar isOpen={open}>
<Box className={classes.appbarHeader}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<IconButton>
<Notifications color="info" />
</IconButton>
<PrivateMenu />
</Box>
</Box>
</AdminAppBar>
<Drawer variant="permanent" open={open} theme={theme}>
<DrawerHeader />
<List sx={{ p: '2rem .5rem', height: '100%', position: 'relative' }}>
{items.map(({ items, menu, icon }, index) => (
<HoverMenu isOpen={open} key={index} menu={menu} icon={icon} items={items} />
))}
<CustomListItem icon={open ? <MenuOpen /> : <ChevronRight />} onClick={toggleMenu} />
<CustomListItem
icon={<Settings />}
label={'Настройки'}
sx={{ position: 'absolute', bottom: '1rem' }}
/>
</List>
</Drawer>
<Box component="main" sx={{ flexGrow: 1 }}>
<DrawerHeader />
{children}
</Box>
<PanelFooter>
<Button sx={{ color: 'white' }}>
<GppGood />
</Button>
<Typography color="white">{'Вие сте логнат като администратор'}</Typography>
</PanelFooter>
<Snackbar />
</StyledBox>
)
}
Example #20
Source File: WalletDialog.tsx From wallet-adapter with Apache License 2.0 | 5 votes |
WalletDialog: FC<WalletDialogProps> = ({
title = 'Select your wallet',
featuredWallets = 3,
onClose,
...props
}) => {
const { wallets, select } = useWallet();
const { open, setOpen } = useWalletDialog();
const [expanded, setExpanded] = useState(false);
const [featured, more] = useMemo(
() => [wallets.slice(0, featuredWallets), wallets.slice(featuredWallets)],
[wallets, featuredWallets]
);
const handleClose = useCallback(
(event: SyntheticEvent, reason?: 'backdropClick' | 'escapeKeyDown') => {
if (onClose) onClose(event, reason!);
if (!event.defaultPrevented) setOpen(false);
},
[setOpen, onClose]
);
const handleWalletClick = useCallback(
(event: SyntheticEvent, walletName: WalletName) => {
select(walletName);
handleClose(event);
},
[select, handleClose]
);
const handleExpandClick = useCallback(() => setExpanded(!expanded), [setExpanded, expanded]);
return (
<RootDialog open={open} onClose={handleClose} {...props}>
<DialogTitle>
{title}
<IconButton onClick={handleClose} size="large">
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent>
<List>
{featured.map((wallet) => (
<WalletListItem
key={wallet.adapter.name}
onClick={(event) => handleWalletClick(event, wallet.adapter.name)}
wallet={wallet}
/>
))}
{more.length ? (
<>
<Collapse in={expanded} timeout="auto" unmountOnExit>
<List>
{more.map((wallet) => (
<WalletListItem
key={wallet.adapter.name}
onClick={(event) => handleWalletClick(event, wallet.adapter.name)}
wallet={wallet}
/>
))}
</List>
</Collapse>
<ListItem>
<Button onClick={handleExpandClick}>
{expanded ? 'Less' : 'More'} options
{expanded ? <CollapseIcon /> : <ExpandIcon />}
</Button>
</ListItem>
</>
) : null}
</List>
</DialogContent>
</RootDialog>
);
}
Example #21
Source File: index.tsx From Search-Next with GNU General Public License v3.0 | 5 votes |
MenuLayout: React.FC<MenuLayoutProps> = ({
menu,
menuFooter,
basePath = '',
showCopyright = true,
children,
title,
onChange,
...props
}) => {
const location = useLocation();
const history = useNavigate();
const [selected, setSelected] = React.useState<MenuLayoutMenu>(
[] as unknown as MenuLayoutMenu,
);
React.useEffect(() => {
const state = location?.state as RouteState;
if (state && state?.search) {
const sel = menu.find((i) => i.id === state?.search);
const selected = sel ? sel : menu[0];
setSelected(selected);
if (onChange) onChange(selected.id, selected);
} else {
setSelected(menu[0]);
if (onChange) onChange(menu[0].id, menu[0]);
}
}, []);
return (
<div className="flex h-screen bg-gray-50">
<div
className={classNames(
'max-w-xs flex-grow py-7 px-7 pl-12 border-gray-200 border-r flex flex-col',
!!menuFooter && 'pb-2',
)}
>
<div className="flex items-center">
<IconButton size="small" onClick={() => history(-1)}>
<ArrowBack />
</IconButton>
<span className="text-xl font-semibold">{title}</span>
</div>
<List dense className="flex-grow">
{menu.map((i, j) => (
<MenuListItem
key={j}
selected={i.id === selected.id}
onClick={() => {
setSelected(i);
if (onChange) onChange(i.id, i);
}}
icon={i.icon}
primary={i.name}
/>
))}
</List>
<div>{menuFooter}</div>
</div>
<div className="flex-grow h-full flex flex-col">
<div
className="flex-grow overflow-y-auto pl-12 pb-8"
style={{ scrollSnapType: 'proximity' }}
>
{children}
</div>
{showCopyright && (
<div className="text-center h-8 max-w-4xl">
<Copyright />
</div>
)}
</div>
</div>
);
}
Example #22
Source File: SidebarGroupList.tsx From abrechnung with GNU Affero General Public License v3.0 | 5 votes |
export default function SidebarGroupList({ group = null }) {
const groups = useRecoilValue(groupList);
const isGuest = useRecoilValue(isGuestUser);
const [showGroupCreationModal, setShowGroupCreationModal] = useState(false);
const openGroupCreateModal = () => {
setShowGroupCreationModal(true);
};
const closeGroupCreateModal = (evt, reason) => {
if (reason !== "backdropClick") {
setShowGroupCreationModal(false);
}
};
return (
<>
<List sx={{ pt: 0 }}>
<ListItem sx={{ pt: 0, pb: 0 }}>
<ListItemText secondary="Groups" />
</ListItem>
{groups.map((it) => (
<ListItemLink key={it.id} to={`/groups/${it.id}`} selected={group && group.id === it.id}>
<ListItemText primary={it.name} />
</ListItemLink>
))}
{!isGuest && (
<ListItem sx={{ padding: 0 }}>
<Grid container justifyContent="center">
<IconButton size="small" onClick={openGroupCreateModal}>
<Add />
</IconButton>
</Grid>
</ListItem>
)}
</List>
{!isGuest && <GroupCreateModal show={showGroupCreationModal} onClose={closeGroupCreateModal} />}
</>
);
}
Example #23
Source File: SkeletonComponent.tsx From firecms with MIT License | 5 votes |
function renderMap<T extends object>(property: MapProperty<T>, size: PreviewSize) {
if (!property.properties)
return <></>;
let mapProperties: string[];
if (!size) {
mapProperties = Object.keys(property.properties);
} else {
if (property.previewProperties)
mapProperties = property.previewProperties as string[];
else
mapProperties = Object.keys(property.properties).slice(0, 3);
}
if (size)
return (
<List>
{mapProperties && mapProperties.map((key: string) => (
<ListItem key={property.title + key}>
<SkeletonComponent
property={(property.properties as any)[key]}
size={"small"}/>
</ListItem>
))}
</List>
);
return (
<Table size={"small"}>
<TableBody>
{mapProperties &&
mapProperties.map((key, index) => {
return (
<TableRow key={`table_${property.title}_${index}`}
sx={{
"&:last-child th, &:last-child td": {
borderBottom: 0
}
}}>
<TableCell key={`table-cell-title-${key}`}
component="th">
<Skeleton variant="text"/>
</TableCell>
<TableCell key={`table-cell-${key}`} component="th">
<SkeletonComponent
property={(property.properties as any)[key]}
size={"small"}/>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
);
}
Example #24
Source File: StatsDialog.tsx From GTAV-NativeDB with MIT License | 5 votes |
export default function StatsDialog({ open, onClose }: Props) {
const stats = useStats()
return (
<Dialog open={open} onClose={onClose} fullWidth maxWidth="xs">
<DialogTitle>
Stats
</DialogTitle>
<List dense>
<ListItem sx={{ px: 3 }} >
<ListItemText
primary="Namespaces"
secondary={stats.namespaces}
/>
</ListItem>
<ListItem sx={{ px: 3 }} >
<ListItemText
primary="Natives"
secondary={stats.natives}
/>
</ListItem>
<ListItem sx={{ px: 3 }} >
<ListItemText
primary="Comments"
secondary={stats.comments}
/>
</ListItem>
<ListItem sx={{ px: 3 }} >
<ListItemText
primary="Known names"
secondary={`${stats.knownNames.confirmed} (${stats.knownNames.total})`}
/>
</ListItem>
</List>
<DialogActions>
<Button onClick={onClose}>Close</Button>
</DialogActions>
</Dialog>
)
}
Example #25
Source File: GroupInvite.tsx From abrechnung with GNU Affero General Public License v3.0 | 5 votes |
export default function GroupInvite() {
const [group, setGroup] = useState(null);
const [error, setError] = useState(null);
const history = useHistory();
const params = useParams();
const inviteToken = params["inviteToken"];
useTitle("Abrechnung - Join Group");
useEffect(() => {
fetchGroupPreview({ token: inviteToken })
.then((res) => {
setGroup(res);
setError(null);
})
.catch((err) => {
setError(err);
setGroup(null);
});
}, [setGroup, setError, history, inviteToken]);
const join = () => {
joinGroup({ token: inviteToken })
.then((value) => {
setError(null);
history.push("/");
})
.catch((err) => {
setError(err);
});
};
return (
<MobilePaper>
{error !== null ? (
<Alert severity="error">{error}</Alert>
) : group === null ? (
<Loading />
) : (
<>
<Typography variant="h5">
<h4>You have been invited to group {group.name}</h4>
</Typography>
<List>
<ListItem>
<ListItemText primary="Name" secondary={group.name} />
</ListItem>
<ListItem>
<ListItemText primary="Description" secondary={group.description} />
</ListItem>
<ListItem>
<ListItemText primary="Created At" secondary={group.created_at} />
</ListItem>
<ListItem>
<ListItemText primary="Invitation Description" secondary={group.invite_description} />
</ListItem>
<ListItem>
<ListItemText primary="Invitation Valid Until" secondary={group.invite_valid_until} />
</ListItem>
<ListItem>
<ListItemText
primary="Invitation Single Use"
secondary={group.invite_single_use ? "yes" : "no"}
/>
</ListItem>
</List>
<Grid container={true} sx={{ justifyContent: "center" }}>
<Button color="primary" onClick={join}>
Join
</Button>
</Grid>
</>
)}
</MobilePaper>
);
}
Example #26
Source File: NativeInfo.tsx From GTAV-NativeDB with MIT License | 4 votes |
export default function NativeInfo() {
const { native: nativeHash } = useParams<{ native?: string }>()
const [usageNotFound, setUsageNotFound] = useState(false)
const settings = useSettings()
const native = useNative(nativeHash ?? '')
const copyToClipboard = useCopyToClipboard()
const onShare = useCallback(() => {
copyToClipboard(createShareUrl(`/natives/${nativeHash}`))
}, [copyToClipboard, nativeHash])
const onUsageNotFound = useCallback(() => {
setUsageNotFound(true)
}, [setUsageNotFound])
useEffect(() => {
setUsageNotFound(false)
}, [nativeHash])
if (!nativeHash) {
return (
<Box sx={{ p: 2 }}>
<NoNativeSelected />
</Box>
)
}
if (!native) {
return (
<Box sx={{ p: 2 }}>
<NativeNotFound nativeHash={nativeHash} />
</Box>
)
}
return (
<Box sx={{ p: 2 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, pb: 1 }}>
<Tooltip title="Copy Link">
<IconButton onClick={onShare} size="small" aria-label="copy link">
<ShareIcon />
</IconButton>
</Tooltip>
<Typography
sx={{
textOverflow: 'ellipsis',
overflow: 'hidden'
}}
variant="h5"
component="h1"
>
{native.name}
</Typography>
</Box>
<Stack spacing={2}>
<Paper sx={{ p: 2 }}>
<NativeDetails
hash={native.hash}
jhash={native.jhash}
build={native.build}
variant="body2"
/>
<NativeDefinition
name={native.name}
params={native.params}
returnType={native.returnType}
variant="body2"
/>
</Paper>
{native.comment && (
<div>
<Typography variant="subtitle1" gutterBottom>
Comment
</Typography>
<Paper sx={{ p: 2 }}>
<NativeComment variant="body2">
{native.comment}
</NativeComment>
</Paper>
</div>
)}
{native.examples && !_.isEmpty(native.examples) && (
<div>
<Typography variant="subtitle1" gutterBottom>
Examples
</Typography>
<Paper>
<CodeExamples
examples={native.examples}
/>
</Paper>
</div>
)}
{native.oldNames && (
<div>
<Typography variant="subtitle1" gutterBottom>
Old name{native.oldNames?.length !== 1 ? 's' : ''}
</Typography>
<Paper>
<List>
{native.oldNames.map(oldName => (
<ListItem key={oldName} dense>
<ListItemText primary={oldName} />
</ListItem>
))}
</List>
</Paper>
</div>
)}
{(!usageNotFound && _.includes(settings.sources, NativeSources.DottieDot)) && (
<div>
<Typography variant="subtitle1" gutterBottom>
Script usage
</Typography>
<Paper>
<NativeUsage
onNotFound={onUsageNotFound}
nativeHash={nativeHash}
/>
</Paper>
</div>
)}
</Stack>
</Box>
)
}
Example #27
Source File: PlayerList.tsx From NekoMaid with MIT License | 4 votes |
PlayerInfo: React.FC<{ name?: string }> = React.memo(({ name }) => {
const plugin = usePlugin()
const globalData = useGlobalData()
const [open, setOpen] = useState(false)
const [info, setInfo] = useState<IPlayerInfo | undefined>()
const refresh = () => plugin.emit('playerList:query', setInfo, name)
useEffect(() => {
setInfo(undefined)
if (name) refresh()
}, [name])
return name && info
? <>
<Divider />
<List
sx={{ width: '100%' }}
component='nav'
subheader={<ListSubheader component='div' sx={{ backgroundColor: 'inherit' }}>{lang.playerList.details}</ListSubheader>}
>
<ListItem>
<ListItemIcon><AssignmentInd /></ListItemIcon>
<ListItemText primary={globalData.onlineMode
? <Link underline='hover' rel='noopener' target='_blank' href={'https://namemc.com/profile/' + info.id}>{info.id}</Link>
: info.id
} />
</ListItem>
{!info.hasPlayedBefore && <ListItem>
<ListItemIcon><ErrorOutline color='error' /></ListItemIcon>
<ListItemText primary={lang.playerList.hasNotPlayed} />
</ListItem>}
{info.ban != null && <ListItem>
<ListItemIcon><Block color='error' /></ListItemIcon>
<ListItemText primary={lang.playerList.banned + (info.ban ? ': ' + info.ban : '')} />
</ListItem>}
{info.whitelisted && <ListItem>
<ListItemIcon><Star color='warning' /></ListItemIcon>
<ListItemText primary={lang.playerList.whitelisted} />
</ListItem>}
{info.isOP && <ListItem>
<ListItemIcon><Security color='primary' /></ListItemIcon>
<ListItemText primary={lang.playerList.op} />
</ListItem>}
{info.hasPlayedBefore && <>
<ListItemButton onClick={() => setOpen(!open)}>
<ListItemIcon><Equalizer /></ListItemIcon>
<ListItemText primary={minecraft['gui.stats']} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={open} timeout="auto" unmountOnExit>
<List component='div' dense disablePadding>
{[
minecraft['stat.minecraft.play_time'] + ': ' + dayjs.duration(info.playTime / 20, 'seconds').humanize(),
lang.playerList.firstPlay + ': ' + dayjs(info.firstPlay).fromNow(),
lang.playerList.lastPlay + ': ' + dayjs(info.lastOnline).fromNow(),
minecraft['stat.minecraft.leave_game'] + ': ' + info.quit,
minecraft['stat.minecraft.deaths'] + ': ' + info.death,
minecraft['stat.minecraft.player_kills'] + ': ' + info.playerKill,
minecraft['stat.minecraft.mob_kills'] + ': ' + info.entityKill,
lang.playerList.tnt + ': ' + info.tnt
].map((it, i) => <ListItem key={i} sx={{ pl: 4 }}>
<ListItemIcon>{icons[i]}</ListItemIcon>
<ListItemText primary={it} />
</ListItem>)}
</List>
</Collapse>
</>}
</List>
<CardActions disableSpacing sx={{ justifyContent: 'flex-end' }}>
<Tooltip title={lang.playerList[info.whitelisted ? 'clickToRemoveWhitelist' : 'clickToAddWhitelist']}>
<IconButton onClick={() => whitelist(name, plugin, refresh, !info.whitelisted)}>
{info.whitelisted ? <Star color='warning' /> : <StarBorder />}
</IconButton>
</Tooltip>
<Tooltip title={lang.playerList[info.ban == null ? 'clickToBan' : 'clickToPardon']}>
<IconButton onClick={() => banPlayer(name, plugin, refresh, info.ban == null)}>
<Block color={info.ban == null ? undefined : 'error'} />
</IconButton>
</Tooltip>
</CardActions>
</>
: <></>
})
Example #28
Source File: Layout.tsx From abrechnung with GNU Affero General Public License v3.0 | 4 votes |
export default function Layout({ group = null, children, ...props }) {
const authenticated = useRecoilValue(isAuthenticated);
const [anchorEl, setAnchorEl] = useState(null);
const theme: Theme = useTheme();
const dotsMenuOpen = Boolean(anchorEl);
const cfg = useRecoilValue(config);
const location = useLocation();
const [mobileOpen, setMobileOpen] = useState(true);
const { window } = props;
const handleProfileMenuOpen = (event) => {
setAnchorEl(event.currentTarget);
};
const handleDotsMenuClose = (event) => {
setAnchorEl(null);
};
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen);
};
const drawer = (
<div style={{ height: "100%" }}>
<Toolbar />
<Divider />
{group != null && (
<List sx={{ pb: 0 }}>
<ListItemLink
to={`/groups/${group.id}/`}
selected={
location.pathname === `/groups/${group.id}/` || location.pathname === `/groups/${group.id}`
}
>
<ListItemIcon>
<Paid />
</ListItemIcon>
<ListItemText primary="Transactions" />
</ListItemLink>
<ListItemLink
to={`/groups/${group.id}/balances`}
selected={location.pathname.startsWith(`/groups/${group.id}/balances`)}
>
<ListItemIcon>
<BarChart />
</ListItemIcon>
<ListItemText primary="Balances" />
</ListItemLink>
<ListItemLink
to={`/groups/${group.id}/accounts`}
selected={location.pathname.startsWith(`/groups/${group.id}/accounts`)}
>
<ListItemIcon>
<AccountBalance />
</ListItemIcon>
<ListItemText primary="Accounts" />
</ListItemLink>
<ListItemLink
to={`/groups/${group.id}/detail`}
selected={location.pathname.startsWith(`/groups/${group.id}/detail`)}
>
<ListItemIcon>
<AdminPanelSettings />
</ListItemIcon>
<ListItemText primary="Group Settings" />
</ListItemLink>
<ListItemLink
to={`/groups/${group.id}/members`}
selected={location.pathname.startsWith(`/groups/${group.id}/members`)}
>
<ListItemIcon>
<People />
</ListItemIcon>
<ListItemText primary="Group Members" />
</ListItemLink>
<ListItemLink
to={`/groups/${group.id}/invites`}
selected={location.pathname.startsWith(`/groups/${group.id}/invites`)}
>
<ListItemIcon>
<Mail />
</ListItemIcon>
<ListItemText primary="Group Invites" />
</ListItemLink>
<ListItemLink
to={`/groups/${group.id}/log`}
selected={location.pathname.startsWith(`/groups/${group.id}/log`)}
>
<ListItemIcon>
<Message />
</ListItemIcon>
<ListItemText primary="Group Log" />
</ListItemLink>
<Divider />
</List>
)}
<SidebarGroupList group={group} />
<Box
sx={{
display: "flex",
position: "absolute",
width: "100%",
justifyContent: "center",
bottom: 0,
padding: 1,
borderTop: 1,
borderColor: theme.palette.divider,
}}
>
{cfg.imprintURL && (
<Link href={cfg.imprintURL} target="_blank" sx={{ mr: 2 }}>
imprint
</Link>
)}
<Tooltip title="Source Code">
<Link sx={{ ml: 1 }} target="_blank" href={cfg.sourceCodeURL}>
<GitHub />
</Link>
</Tooltip>
{cfg.issueTrackerURL && (
<Tooltip title="Bug reports">
<Link sx={{ ml: 1 }} target="_blank" href={cfg.issueTrackerURL}>
<BugReport />
</Link>
</Tooltip>
)}
</Box>
</div>
);
const container = window !== undefined ? () => window().document.body : undefined;
return (
<Box sx={{ display: "flex" }}>
<CssBaseline />
<AppBar
position="fixed"
sx={{
// width: {sm: `calc(100% - ${drawerWidth}px)`},
// ml: {sm: `${drawerWidth}px`},
zIndex: (theme) => theme.zIndex.drawer + 1,
}}
>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={handleDrawerToggle}
edge="start"
sx={{ mr: 2, display: { sm: "none" } }}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
Abrechnung
</Typography>
{authenticated ? (
<div>
<IconButton
aria-label="account of current user"
aria-controls="menu-appbar"
aria-haspopup="true"
onClick={handleProfileMenuOpen}
color="inherit"
>
<AccountCircleIcon />
</IconButton>
<Menu
id="menu-appbar"
open={dotsMenuOpen}
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
keepMounted
anchorEl={anchorEl}
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
onClose={handleDotsMenuClose}
>
<MenuItem component={RouterLink} to="/profile">
Profile
</MenuItem>
<MenuItem component={RouterLink} to="/profile/settings">
Settings
</MenuItem>
<MenuItem component={RouterLink} to="/profile/sessions">
Sessions
</MenuItem>
<MenuItem component={RouterLink} to="/profile/change-email">
Change E-Mail
</MenuItem>
<MenuItem component={RouterLink} to="/profile/change-password">
Change Password
</MenuItem>
<Divider />
<MenuItem component={RouterLink} to="/logout">
<ListItemIcon>
<Logout fontSize="small" />
</ListItemIcon>
<ListItemText>Sign out</ListItemText>
</MenuItem>
</Menu>
</div>
) : (
<Button component={RouterLink} color="inherit" to="/login">
Login
</Button>
)}
</Toolbar>
</AppBar>
{authenticated ? (
<Box component="nav" sx={{ width: { sm: drawerWidth }, flexShrink: { sm: 0 } }}>
<Drawer
container={container}
variant="temporary"
open={mobileOpen}
onClose={handleDrawerToggle}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
sx={{
display: { xs: "block", sm: "none" },
"& .MuiDrawer-paper": {
boxSizing: "border-box",
width: drawerWidth,
},
}}
>
{drawer}
</Drawer>
<Drawer
variant="permanent"
sx={{
flexShrink: 0,
display: { xs: "none", sm: "block" },
"& .MuiDrawer-paper": {
boxSizing: "border-box",
width: drawerWidth,
},
}}
open
>
{drawer}
</Drawer>
</Box>
) : null}
<Box
component="main"
sx={{
flexGrow: 1,
width: { sm: `calc(100% - ${drawerWidth}px)` },
}}
>
<Toolbar />
<Banner />
<Container maxWidth="lg" sx={{ padding: { xs: 0, md: 1, lg: 3 } }}>
{children}
</Container>
</Box>
</Box>
);
}
Example #29
Source File: Worlds.tsx From NekoMaid with MIT License | 4 votes |
Worlds: React.FC = () => {
const plugin = usePlugin()
const globalData = useGlobalData()
const [worlds, setWorlds] = useState<World[]>([])
const [selected, setSelected] = useState('')
const [open, setOpen] = useState(false)
const update = () => plugin.emit('worlds:fetch', (data: World[]) => {
setWorlds(data)
if (data.length) setSelected(old => data.some(it => it.id === old) ? old : '')
})
useEffect(() => {
const offUpdate = plugin.on('worlds:update', update)
update()
return () => { offUpdate() }
}, [])
const sw = worlds.find(it => it.id === selected)
const getSwitch = (name: string, configId = name) => sw
? <ListItem
secondaryAction={<Switch disabled={!globalData.hasMultiverse} checked={(sw as any)[name]}
onChange={e => {
plugin.emit('worlds:set', sw.id, configId, e.target.checked.toString())
success()
}}
/>}><ListItemText primary={(lang.worlds as any)[name]} /></ListItem>
: null
return <Box sx={{ minHeight: '100%', py: 3 }}>
<Toolbar />
<Container maxWidth={false}>
<Grid container spacing={3}>
<Grid item lg={8} md={12} xl={9} xs={12}>
<Card>
<CardHeader title={lang.worlds.title} />
<Divider />
<Box sx={{ position: 'relative' }}>
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell padding='checkbox' />
<TableCell>{lang.worlds.name}</TableCell>
{globalData.hasMultiverse && <TableCell>{lang.worlds.alias}</TableCell>}
<TableCell>{lang.worlds.players}</TableCell>
<TableCell>{lang.worlds.chunks}</TableCell>
<TableCell>{lang.worlds.entities}</TableCell>
<TableCell>{lang.worlds.tiles}</TableCell>
<TableCell>{lang.worlds.time}</TableCell>
<TableCell>{lang.worlds.weather}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{worlds.map(it => <TableRow key={it.id}>
<TableCell padding='checkbox'><Checkbox checked={selected === it.id} onClick={() => setSelected(it.id)} /></TableCell>
<TableCell><Tooltip title={it.id}><span>{it.name}</span></Tooltip></TableCell>
{globalData.hasMultiverse && <TableCell>{it.alias}
<IconButton size='small' onClick={() => dialog(lang.inputValue, lang.worlds.alias).then(res => {
if (res == null) return
plugin.emit('worlds:set', it.id, 'alias', res)
success()
})}><Edit fontSize='small' /></IconButton>
</TableCell>}
<TableCell>{it.players}</TableCell>
<TableCell>{it.chunks}</TableCell>
<TableCell>{it.entities}</TableCell>
<TableCell>{it.tiles}</TableCell>
<TableCell><Countdown time={it.time} max={24000} interval={50} /></TableCell>
<TableCell><IconButton size='small' onClick={() => {
plugin.emit('worlds:weather', it.id)
success()
}}>
{React.createElement((it.weather === 1 ? WeatherRainy : it.weather === 2 ? WeatherLightningRainy : WbSunny) as any)}
</IconButton></TableCell>
</TableRow>)}
</TableBody>
</Table>
</TableContainer>
</Box>
</Card>
</Grid>
<Grid item lg={4} md={6} xl={3} xs={12}>
<Card>
<CardHeader
title={lang.operations}
sx={{ position: 'relative' }}
action={<Tooltip title={lang.worlds.save} placement='left'>
<IconButton
size='small'
onClick={() => {
if (!sw) return
plugin.emit('worlds:save', sw.id)
success()
}}
sx={cardActionStyles}
><Save /></IconButton>
</Tooltip>}
/>
<Divider />
<Box sx={{ position: 'relative' }}>
{sw
? <List sx={{ width: '100%' }} component='nav'>
<ListItem secondaryAction={<ToggleButtonGroup
exclusive
color='primary'
size='small'
value={sw.difficulty}
onChange={(_, value) => {
plugin.emit('worlds:difficulty', sw.id, value)
success()
}}
>
{difficulties.map(it => <ToggleButton value={it.toUpperCase()} key={it}>{minecraft['options.difficulty.' + it]}</ToggleButton>)}
</ToggleButtonGroup>}><ListItemText primary={minecraft['options.difficulty']} /></ListItem>
<ListItem secondaryAction={<Switch checked={sw.pvp} onChange={e => {
plugin.emit('worlds:pvp', sw.id, e.target.checked)
success()
}} />}><ListItemText primary='PVP' /></ListItem>
{getSwitch('allowAnimals', 'spawning.animals.spawn')}
{getSwitch('allowMonsters', 'spawning.monsters.spawn')}
{globalData.hasMultiverse && <>
{getSwitch('allowFlight')}
{getSwitch('autoHeal')}
{getSwitch('hunger')}
</>}
<ListItem secondaryAction={globalData.canSetViewDistance
? <IconButton
onClick={() => dialog({
content: lang.inputValue,
input: {
error: true,
type: 'number',
helperText: lang.invalidValue,
validator: (it: string) => /^\d+$/.test(it) && +it > 1 && +it < 33
}
}).then(res => {
if (!res) return
plugin.emit('worlds:viewDistance', sw.id, parseInt(res as any))
success()
})}
><Edit /></IconButton>
: undefined}>
<ListItemText primary={lang.worlds.viewDistance + ': ' + sw.viewDistance} />
</ListItem>
<ListItem><ListItemText primary={minecraft['selectWorld.enterSeed']} secondary={sw.seed} /></ListItem>
<ListItemButton onClick={() => setOpen(!open)}>
<ListItemText primary={minecraft['selectWorld.gameRules']} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={open} timeout="auto" unmountOnExit>
<List component='div' dense disablePadding>
{sw.rules.map(([key, value]) => {
const isTrue = value === 'true'
const isBoolean = isTrue || value === 'false'
const isNumber = /^\d+$/.test(value)
return <ListItem
key={key}
sx={{ pl: 4 }}
secondaryAction={isBoolean
? <Switch
checked={isTrue}
onChange={e => {
plugin.emit('worlds:rule', sw.id, key, e.target.checked.toString())
success()
}}
/>
: <IconButton
onClick={() => dialog({
content: lang.inputValue,
input: isNumber
? {
error: true,
type: 'number',
helperText: lang.invalidValue,
validator: (it: string) => /^\d+$/.test(it)
}
: { }
}).then(res => {
if (res == null) return
plugin.emit('worlds:rule', sw.id, key, res)
success()
})}
><Edit /></IconButton>}
>
<ListItemText primary={(minecraft['gamerule.' + key] || key) + (isBoolean ? '' : ': ' + value)} />
</ListItem>
})}
</List>
</Collapse>
</List>
: <CardContent><Empty /></CardContent>
}
</Box>
</Card>
</Grid>
</Grid>
</Container>
</Box>
}