@mui/material#ListItemButton TypeScript Examples
The following examples show how to use
@mui/material#ListItemButton.
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: LibraryOptions.tsx From Tachidesk-WebUI with Mozilla Public License 2.0 | 6 votes |
function sortsTab(currentTab: number) {
const { options: { sorts, sortDesc }, setOption } = useLibraryOptionsContext();
const handleChange = (event: React.MouseEvent<HTMLDivElement, MouseEvent>, index: string) => {
if (sorts === index) {
setOption('sortDesc', (sortDes) => !sortDes);
} else {
setOption('sortDesc', false);
}
setOption('sorts', index);
};
return (
<>
<TabPanel index={1} currentIndex={currentTab}>
<Stack direction="column">
{
['sortToRead', 'sortAlph', 'sortID'].map((e) => {
let icon;
if (sorts === e) {
icon = !sortDesc ? (<ArrowUpwardIcon color="primary" />)
: (<ArrowDownwardIcon color="primary" />);
}
icon = icon === undefined && sortDesc === undefined && e === 'sortID' ? (<ArrowDownwardIcon color="primary" />) : icon;
return (
<ListItem disablePadding>
<ListItemButton onClick={(event) => handleChange(event, e)}>
<ListItemIcon>{icon}</ListItemIcon>
<ListItemText primary={e} />
</ListItemButton>
</ListItem>
);
})
}
</Stack>
</TabPanel>
</>
);
}
Example #2
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 #3
Source File: CustomListItem.tsx From frontend with MIT License | 6 votes |
function CustomListItem({ label, icon, selected, ...props }: Props) {
return (
<ListItem disablePadding {...props}>
<ListItemButton selected={selected}>
<ListItemIcon
title={label}
sx={(theme) => ({
color: selected ? theme.palette.primary.main : theme.palette.action.active,
})}>
{icon}
</ListItemIcon>
{label && (
<ListItemText
primary={label}
primaryTypographyProps={{ color: selected ? 'primary' : undefined }}
/>
)}
</ListItemButton>
</ListItem>
)
}
Example #4
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 #5
Source File: ListConversation.tsx From airmessage-web with Apache License 2.0 | 5 votes |
export default function ListConversation(props: {conversation: Conversation, selected?: boolean, highlighted?: boolean, onSelected: () => void, flippedProps?: Record<string, unknown>}) {
//Getting the conversation title
const [title, setConversationTitle] = useState(ConversationUtils.getFallbackTitle(props.conversation));
useEffect(() => {
//Updating the conversation's name if it has one
if(props.conversation.name) {
setConversationTitle(props.conversation.name);
return;
}
//Building the conversation title
ConversationUtils.getMemberTitle(props.conversation.members).then((title) => setConversationTitle(title));
}, [props.conversation.name, props.conversation.members]);
const primaryStyle: TypographyProps = props.highlighted ? {
color: "primary",
sx: {
fontSize: "1rem",
fontWeight: "bold"
}
} : {
sx: {
fontSize: "1rem",
fontWeight: 500
}
};
const secondaryStyle: TypographyProps = props.highlighted ? {
color: "textPrimary",
sx: {
fontWeight: "bold"
}
} : {};
return (
<div className={styles.containerOuter} {...props.flippedProps}>
<ListItemButton
className={styles.containerInner}
key={props.conversation.localID}
onClick={props.onSelected}
selected={props.selected}
sx={{
"&&.Mui-selected, &&.Mui-selected:hover": {
backgroundColor: "action.selected"
},
"&&:hover": {
backgroundColor: "action.hover"
}
}}>
<ListItemAvatar>
<GroupAvatar members={props.conversation.members} />
</ListItemAvatar>
<ListItemText className={styles.textPreview} primary={title} primaryTypographyProps={primaryStyle} secondary={previewString(props.conversation.preview)} secondaryTypographyProps={secondaryStyle} />
<Typography className={styles.textTime} variant="body2" color="textSecondary">{getLastUpdateStatusTime(props.conversation.preview.date)}</Typography>
</ListItemButton>
</div>
);
}
Example #6
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 #7
Source File: HoverMenu.tsx From frontend with MIT License | 5 votes |
export default function HoverMenu({ menu, items, icon: Icon, isOpen }: Props) {
const router = useRouter()
const [anchorMenu, setAnchorMenu] = useState<null | HTMLElement>(null)
const isSelected = useMemo(
() => items.filter((item) => item.href !== '#' && router.asPath.includes(item.href)).length > 0,
[items],
)
const handleOpenMenu = (event: React.MouseEvent<HTMLElement>) => {
setAnchorMenu(event.currentTarget)
}
const handleCloseMenu = () => setAnchorMenu(null)
return (
<Root>
<ListItemButton
selected={isSelected}
onClick={handleOpenMenu}
sx={{ borderRadius: '0 25px 25px 0' }}>
<ListItemIcon
title={menu}
sx={(theme) => ({
minWidth: theme.spacing(4),
color: isSelected ? theme.palette.primary.main : theme.palette.action.active,
})}>
{<Icon />}
</ListItemIcon>
{isOpen && (
<ListItemText
primary={menu}
primaryTypographyProps={{ color: isSelected ? 'primary' : undefined }}
/>
)}
{isOpen && (
<ChevronRightIcon
color={
anchorMenu ? (isSelected ? 'primary' : 'action') : isSelected ? 'primary' : 'disabled'
}
/>
)}
</ListItemButton>
<Menu
keepMounted
id="menu-appbar"
anchorEl={anchorMenu}
onClose={handleCloseMenu}
open={Boolean(anchorMenu)}
className={isOpen ? classes.open : classes.close}
anchorOrigin={{ vertical: 'top', horizontal: 'left' }}
transformOrigin={{ vertical: 'center', horizontal: 'left' }}>
{items.map(({ label, icon: Icon, href }) => (
<CustomListItem
key={label}
sx={{ p: 0 }}
selected={href !== '#' && router.asPath.includes(href)}
icon={<Icon />}
label={label}
onClick={() => router.push(href)}
/>
))}
</Menu>
</Root>
)
}
Example #8
Source File: Settings.tsx From Tachidesk-WebUI with Mozilla Public License 2.0 | 4 votes |
export default function Settings() {
const { setTitle, setAction } = useContext(NavbarContext);
useEffect(() => { setTitle('Settings'); setAction(<></>); }, []);
const { darkTheme, setDarkTheme } = useContext(DarkTheme);
const [serverAddress, setServerAddress] = useLocalStorage<String>('serverBaseURL', '');
const [showNsfw, setShowNsfw] = useLocalStorage<boolean>('showNsfw', true);
const [useCache, setUseCache] = useLocalStorage<boolean>('useCache', true);
const [dialogOpen, setDialogOpen] = useState(false);
const [dialogValue, setDialogValue] = useState(serverAddress);
const [dialogOpenItemWidth, setDialogOpenItemWidth] = useState(false);
const [ItemWidth, setItemWidth] = useLocalStorage<number>('ItemWidth', 300);
const [DialogItemWidth, setDialogItemWidth] = useState(ItemWidth);
const handleDialogOpen = () => {
setDialogValue(serverAddress);
setDialogOpen(true);
};
const handleDialogCancel = () => {
setDialogOpen(false);
};
const handleDialogSubmit = () => {
setDialogOpen(false);
setServerAddress(dialogValue);
};
const handleDialogOpenItemWidth = () => {
setDialogItemWidth(ItemWidth);
setDialogOpenItemWidth(true);
};
const handleDialogCancelItemWidth = () => {
setDialogOpenItemWidth(false);
};
const handleDialogSubmitItemWidth = () => {
setDialogOpenItemWidth(false);
setItemWidth(DialogItemWidth);
};
const handleDialogResetItemWidth = () => {
setDialogOpenItemWidth(false);
setItemWidth(300);
};
const handleChange = (event: Event, newValue: number | number[]) => {
setDialogItemWidth(newValue as number);
};
return (
<>
<List sx={{ padding: 0 }}>
<ListItemLink to="/settings/categories">
<ListItemIcon>
<ListAltIcon />
</ListItemIcon>
<ListItemText primary="Categories" />
</ListItemLink>
<ListItemLink to="/settings/backup">
<ListItemIcon>
<BackupIcon />
</ListItemIcon>
<ListItemText primary="Backup" />
</ListItemLink>
<ListItem>
<ListItemIcon>
<Brightness6Icon />
</ListItemIcon>
<ListItemText primary="Dark Theme" />
<ListItemSecondaryAction>
<Switch
edge="end"
checked={darkTheme}
onChange={() => setDarkTheme(!darkTheme)}
/>
</ListItemSecondaryAction>
</ListItem>
<ListItemButton>
<ListItemIcon>
<ViewModuleIcon />
</ListItemIcon>
<ListItemText
primary="Manga Item width"
secondary={`px:${ItemWidth}`}
onClick={() => {
handleDialogOpenItemWidth();
}}
/>
</ListItemButton>
<ListItem>
<ListItemIcon>
<FavoriteIcon />
</ListItemIcon>
<ListItemText
primary="Show NSFW"
secondary="Hide NSFW extensions and sources"
/>
<ListItemSecondaryAction>
<Switch
edge="end"
checked={showNsfw}
onChange={() => setShowNsfw(!showNsfw)}
/>
</ListItemSecondaryAction>
</ListItem>
<ListItem>
<ListItemIcon>
<CachedIcon />
</ListItemIcon>
<ListItemText
primary="Use image cache"
secondary="Disabling image cache makes images load faster if you have a slow disk,
but uses it much more internet traffic in turn"
/>
<ListItemSecondaryAction>
<Switch
edge="end"
checked={useCache}
onChange={() => setUseCache(!useCache)}
/>
</ListItemSecondaryAction>
</ListItem>
<ListItem>
<ListItemIcon>
<DnsIcon />
</ListItemIcon>
<ListItemText primary="Server Address" secondary={serverAddress} />
<ListItemSecondaryAction>
<IconButton
onClick={() => {
handleDialogOpen();
}}
size="large"
>
<EditIcon />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
<ListItemLink to="/settings/about">
<ListItemIcon>
<InfoIcon />
</ListItemIcon>
<ListItemText primary="About" />
</ListItemLink>
</List>
<Dialog open={dialogOpen} onClose={handleDialogCancel}>
<DialogContent>
<DialogContentText>
Enter Server Address
</DialogContentText>
<TextField
autoFocus
margin="dense"
id="name"
label="Server Address"
type="text"
fullWidth
value={dialogValue}
placeholder="http://127.0.0.1:4567"
onChange={(e) => setDialogValue(e.target.value)}
/>
</DialogContent>
<DialogActions>
<Button onClick={handleDialogCancel} color="primary">
Cancel
</Button>
<Button onClick={handleDialogSubmit} color="primary">
Set
</Button>
</DialogActions>
</Dialog>
<Dialog open={dialogOpenItemWidth} onClose={handleDialogCancelItemWidth}>
<DialogTitle>
Manga Item width
</DialogTitle>
<DialogContent
sx={{
width: '98%',
margin: 'auto',
}}
>
<TextField
sx={{
width: '100%',
margin: 'auto',
}}
autoFocus
value={DialogItemWidth}
type="number"
onChange={(e) => setDialogItemWidth(parseInt(e.target.value, 10))}
/>
<Slider
aria-label="Manga Item width"
defaultValue={300}
value={DialogItemWidth}
step={10}
min={100}
max={1000}
onChange={handleChange}
/>
</DialogContent>
<DialogActions>
<Button onClick={handleDialogResetItemWidth} color="primary">
Reset to Default
</Button>
<Button onClick={handleDialogCancelItemWidth} color="primary">
Cancel
</Button>
<Button onClick={handleDialogSubmitItemWidth} color="primary">
OK
</Button>
</DialogActions>
</Dialog>
</>
);
}
Example #9
Source File: Header.tsx From metaplex with Apache License 2.0 | 4 votes |
Header = ({ narrow }: { narrow: boolean }) => {
const navs = [
{
href: `/entanglement/`,
innerNarrow: 'About',
inner: <HomeIcon />,
},
{
href: `/entanglement/create`,
inner: 'Create',
},
{
href: `/entanglement/show`,
inner: 'Show',
},
{
href: `/entanglement/swap`,
inner: 'Swap',
},
{
href: `/entanglement/search`,
inner: 'Search',
},
{
href: `/entanglement/wizard`,
inner: 'Wizard',
},
];
const [drawerOpen, setDrawerOpen] = React.useState(false);
const toggleDrawer = open => event => {
if (
event.type === 'keydown' &&
(event.key === 'Tab' || event.key === 'Shift')
) {
return;
}
setDrawerOpen(open);
};
return (
<Box
sx={{
height: '52px',
display: 'flex',
bgcolor: 'action.disabledBackground',
overflow: 'auto',
}}
>
{narrow ? (
<React.Fragment>
<Button onClick={toggleDrawer(true)}>
<MenuIcon />
</Button>
<Drawer open={drawerOpen} onClose={toggleDrawer(false)}>
<Box
sx={{ width: 250 }}
role="presentation"
onClick={toggleDrawer(false)}
onKeyDown={toggleDrawer(false)}
>
<List>
<ListItem>
<ListItemText
primary="Token Entangler"
primaryTypographyProps={{
fontSize: '1.2rem',
fontWeight: 'medium',
letterSpacing: 0,
}}
/>
</ListItem>
<Divider />
{navs.map((nav, idx) => {
return (
<Link to={nav.href} key={idx} style={{ color: 'inherit' }}>
<ListItemButton>
{nav.innerNarrow || nav.inner}
</ListItemButton>
</Link>
);
})}
</List>
</Box>
</Drawer>
</React.Fragment>
) : (
<Stack
direction="row"
spacing={2}
sx={{
display: 'flex',
justifyContent: 'flex-start',
alignItems: 'center',
marginLeft: '36px',
}}
>
{navs.map((nav, idx) => {
return (
<Link to={nav.href} key={idx}>
<Button variant="outlined" style={{ minWidth: 0 }}>
{nav.inner}
</Button>
</Link>
);
})}
</Stack>
)}
<Box sx={{ flexGrow: 1, minWidth: '36px' }}></Box>
<Settings narrow={narrow} />
</Box>
);
}
Example #10
Source File: index.tsx From metaplex with Apache License 2.0 | 4 votes |
Settings = ({ narrow }: { narrow: boolean }) => {
const { disconnect, publicKey } = useWallet();
const { setEndpoint, env, endpoint } = useConnectionConfig();
const { setVisible } = useWalletModal();
const open = React.useCallback(() => setVisible(true), [setVisible]);
const { setModal } = useModal();
const theme = useTheme();
const colorModeCtx = useColorMode();
const handleConnect = React.useCallback(() => {
setModal(ModalEnum.WALLET);
setVisible(true);
}, [setModal, setVisible]);
const connectedActions = [
{
click: async () => {
if (publicKey) {
await navigator.clipboard.writeText(publicKey.toBase58());
notify({
message: 'Wallet update',
description: 'Address copied to clipboard',
});
}
},
innerNarrow: () =>
`Copy Address (${publicKey && shortenAddress(publicKey.toBase58())})`,
inner: function ConnectedWalletCopyC() {
return (
<React.Fragment>
<CopyOutlined />
{publicKey && shortenAddress(publicKey.toBase58())}
</React.Fragment>
);
},
},
{
click: open,
inner: () => 'Change\u00A0Wallet',
},
{
click: () => disconnect().catch(),
inner: () => `Disconnect\u00A0(${env})`,
expandedExtra: {
// these are interepreted as props. TODO: specific types
color: 'error' as any,
variant: 'contained' as any,
},
},
];
const [drawerOpen, setDrawerOpen] = React.useState(false);
const [envCollapseOpen, setEnvCollapseOpen] = React.useState(false);
const hackySkipSet = 'hackySkipSet';
const toggleDrawer = open => event => {
if (
event.type === 'keydown' &&
(event.key === 'Tab' || event.key === 'Shift')
) {
return;
}
if (event.target.classList.contains(hackySkipSet)) {
return;
}
setDrawerOpen(open);
};
const drawerC = inner => {
return (
<React.Fragment>
<Button onClick={toggleDrawer(true)}>
<AccountBalanceWalletIcon />
</Button>
<Drawer anchor="right" open={drawerOpen} onClose={toggleDrawer(false)}>
<Box
sx={{ width: 250 }}
role="presentation"
onClick={toggleDrawer(false)}
onKeyDown={toggleDrawer(false)}
>
{inner}
</Box>
</Drawer>
</React.Fragment>
);
};
const themeSwitch = (
<Button
sx={{ ml: 1 }}
onClick={colorModeCtx.toggleColorMode}
color="inherit"
>
{theme.palette.mode === 'dark' ? (
<Brightness7Icon />
) : (
<Brightness4Icon />
)}
</Button>
);
if (narrow) {
const listHead = (
<ListItem>
<ListItemText
primary="Wallet"
primaryTypographyProps={{
fontSize: '1.2rem',
fontWeight: 'medium',
letterSpacing: 0,
}}
/>
</ListItem>
);
return (
<React.Fragment>
{!publicKey &&
drawerC(
<List>
{listHead}
<Divider />
<ListItemButton
onClick={() => setEnvCollapseOpen(!envCollapseOpen)}
className={hackySkipSet}
>
Change Network
{envCollapseOpen ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={envCollapseOpen} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
{ENDPOINTS.map(p => (
<ListItemButton
selected={endpoint === p.endpoint}
onClick={() => setEndpoint(p.endpoint)}
key={p.name}
sx={{ pl: 4 }}
className={hackySkipSet}
>
{p.name}
</ListItemButton>
))}
</List>
</Collapse>
<ListItemButton onClick={handleConnect}>Connect</ListItemButton>
</List>,
)}
{publicKey &&
drawerC(
<List>
{listHead}
<Divider />
{connectedActions.map((a, idx) => {
return (
<ListItemButton onClick={a.click} key={idx}>
{(a.innerNarrow && a.innerNarrow()) || a.inner()}
</ListItemButton>
);
})}
</List>,
)}
{themeSwitch}
</React.Fragment>
);
} else {
return (
<Stack
direction="row"
spacing={2}
sx={{
display: 'flex',
justifyContent: 'flex-end',
alignItems: 'center',
marginRight: '36px',
}}
>
{!publicKey && (
<React.Fragment>
<FormControl variant="standard" style={{ minWidth: '10ch' }}>
<Select
id="connected-env-select"
onChange={e => {
setEndpoint(e.target.value);
}}
value={endpoint}
>
{ENDPOINTS.map(({ name, endpoint }) => (
<MenuItem key={name} value={endpoint}>
{name}
</MenuItem>
))}
</Select>
</FormControl>
<Link underline="none">
<Button variant="contained" onClick={handleConnect}>
Connect
</Button>
</Link>
</React.Fragment>
)}
{publicKey &&
connectedActions.map((a, idx) => {
return (
<Button
key={idx}
variant="outlined"
onClick={a.click}
{...a.expandedExtra}
>
{a.inner()}
</Button>
);
})}
{themeSwitch}
</Stack>
);
}
}
Example #11
Source File: GridColumns.tsx From mui-toolpad with MIT License | 4 votes |
function GridColumnsPropEditor({
label,
nodeId,
value = [],
onChange,
disabled,
}: EditorProps<GridColumns>) {
const { bindings } = usePageEditorState();
const [editColumnsDialogOpen, setEditColumnsDialogOpen] = React.useState(false);
const [editedIndex, setEditedIndex] = React.useState<number | null>(null);
const editedColumn = typeof editedIndex === 'number' ? value[editedIndex] : null;
React.useEffect(() => {
if (editColumnsDialogOpen) {
setEditedIndex(null);
}
}, [editColumnsDialogOpen]);
const [menuAnchorEl, setMenuAnchorEl] = React.useState<null | HTMLElement>(null);
const menuOpen = Boolean(menuAnchorEl);
const handleMenuClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setMenuAnchorEl(event.currentTarget);
};
const handleClose = () => {
setMenuAnchorEl(null);
};
const rowsValue = nodeId && bindings[`${nodeId}.props.rows`];
const definedRows: unknown = rowsValue?.value;
const columnSuggestions = React.useMemo(() => {
const inferred = inferColumns(Array.isArray(definedRows) ? definedRows : []);
const existingFields = new Set(value.map(({ field }) => field));
return inferred.filter((column) => !existingFields.has(column.field));
}, [definedRows, value]);
const handleCreateColumn = React.useCallback(
(suggestion: GridColDef) => () => {
const existingFields = new Set(value.map(({ field }) => field));
const newFieldName = generateUniqueString(suggestion.field, existingFields);
const newValue = [...value, { ...suggestion, field: newFieldName }];
onChange(newValue);
setEditedIndex(newValue.length - 1);
handleClose();
},
[value, onChange],
);
const handleColumnItemClick = React.useCallback(
(index: number) => () => {
setEditedIndex(index);
},
[],
);
const handleColumnChange = React.useCallback(
(newValue: GridColDef) => {
onChange(value.map((column, i) => (i === editedIndex ? newValue : column)));
},
[editedIndex, onChange, value],
);
const handleColumnDelete = React.useCallback(
(deletedIndex: number) => (event: React.MouseEvent) => {
event.stopPropagation();
onChange(value.filter((column, i) => i !== deletedIndex));
},
[onChange, value],
);
return (
<React.Fragment>
<Button onClick={() => setEditColumnsDialogOpen(true)}>{label}</Button>
<Dialog
fullWidth
open={editColumnsDialogOpen}
onClose={() => setEditColumnsDialogOpen(false)}
>
{editedColumn ? (
<React.Fragment>
<DialogTitle>
<IconButton aria-label="Back" onClick={() => setEditedIndex(null)}>
<ArrowBackIcon />
</IconButton>
Edit column {editedColumn.field}
</DialogTitle>
<DialogContent>
<Stack gap={1} py={1}>
<TextField
label="field"
value={editedColumn.field}
disabled={disabled}
onChange={(event) =>
handleColumnChange({ ...editedColumn, field: event.target.value })
}
/>
<TextField
select
fullWidth
label="type"
value={editedColumn.type ?? ''}
disabled={disabled}
onChange={(event) =>
handleColumnChange({ ...editedColumn, type: event.target.value })
}
>
{COLUMN_TYPES.map((type) => (
<MenuItem key={type} value={type}>
{type}
</MenuItem>
))}
</TextField>
<TextField
select
fullWidth
label="align"
value={editedColumn.align ?? ''}
disabled={disabled}
onChange={(event) =>
handleColumnChange({
...editedColumn,
align: (event.target.value as GridAlignment) || undefined,
})
}
>
{ALIGNMENTS.map((alignment) => (
<MenuItem key={alignment} value={alignment}>
{alignment}
</MenuItem>
))}
</TextField>
<TextField
label="width"
type="number"
value={editedColumn.width}
disabled={disabled}
onChange={(event) =>
handleColumnChange({ ...editedColumn, width: Number(event.target.value) })
}
/>
</Stack>
</DialogContent>
</React.Fragment>
) : (
<React.Fragment>
<DialogTitle>Edit columns</DialogTitle>
<DialogContent>
<IconButton aria-label="Add column" onClick={handleMenuClick} disabled={disabled}>
<AddIcon />
</IconButton>
<Menu
id="new-column-menu"
anchorEl={menuAnchorEl}
open={menuOpen}
onClose={handleClose}
MenuListProps={{
'aria-labelledby': 'basic-button',
}}
>
{columnSuggestions.map((suggestion) => (
<MenuItem key={suggestion.field} onClick={handleCreateColumn(suggestion)}>
{suggestion.field}
</MenuItem>
))}
<MenuItem onClick={handleCreateColumn({ field: 'new' })}>new column</MenuItem>
</Menu>
<List>
{value.map((colDef, i) => {
return (
<ListItem
key={colDef.field}
disableGutters
onClick={handleColumnItemClick(i)}
secondaryAction={
<IconButton
aria-label="Remove column"
edge="end"
onClick={handleColumnDelete(i)}
>
<DeleteIcon />
</IconButton>
}
>
<ListItemButton>
<ListItemText primary={colDef.field} />
</ListItemButton>
</ListItem>
);
})}
</List>
</DialogContent>
</React.Fragment>
)}
<DialogActions>
<Button color="inherit" variant="text" onClick={() => setEditColumnsDialogOpen(false)}>
Close
</Button>
</DialogActions>
</Dialog>
</React.Fragment>
);
}
Example #12
Source File: SelectOptions.tsx From mui-toolpad with MIT License | 4 votes |
function SelectOptionsPropEditor({ label, value = [], onChange, }: EditorProps<(string | SelectOption)[]>) { const [editOptionsDialogOpen, setEditOptionsDialogOpen] = React.useState(false); const optionInputRef = React.useRef<HTMLInputElement | null>(null); const [editingIndex, setEditingIndex] = React.useState<number | null>(null); const editingOption: SelectOption | null = React.useMemo(() => { if (typeof editingIndex === 'number') { const option: SelectOption | string = value[editingIndex]; if (typeof option === 'string') { return { value: option, label: '', }; } return option; } return null; }, [editingIndex, value]); const handleOptionTextInput = React.useCallback( (event: React.KeyboardEvent<HTMLDivElement>) => { if (event.key === 'Enter') { onChange([...value, (event.target as HTMLInputElement).value]); if (optionInputRef.current) { optionInputRef.current.value = ''; } } }, [onChange, value], ); const handleOptionDelete = React.useCallback( (deletedIndex: number) => (event: React.MouseEvent) => { event.stopPropagation(); onChange(value.filter((column, i) => i !== deletedIndex)); }, [onChange, value], ); const handleDeleteAll = React.useCallback(() => { onChange([]); }, [onChange]); const handleOptionItemClick = React.useCallback( (index: number) => () => { setEditingIndex(index); }, [], ); const handleOptionChange = React.useCallback( (newValue: SelectOption) => { onChange(value.map((option, i) => (i === editingIndex ? newValue : option))); }, [editingIndex, onChange, value], ); const handleEditOptionsDialogClose = React.useCallback(() => { setEditingIndex(null); setEditOptionsDialogOpen(false); }, []); return ( <React.Fragment> <Button variant="outlined" color="inherit" fullWidth onClick={() => setEditOptionsDialogOpen(true)} > {label} </Button> <Dialog fullWidth open={editOptionsDialogOpen} onClose={() => setEditOptionsDialogOpen(false)} > {editingOption ? ( <React.Fragment> <DialogTitle> <IconButton aria-label="Back" onClick={() => setEditingIndex(null)}> <ArrowBackIcon /> </IconButton> Edit option “{editingOption.value}” </DialogTitle> <DialogContent> <Stack gap={1} py={1}> <TextField label="Value" value={editingOption.value} onChange={(event) => { handleOptionChange({ ...editingOption, value: event.target.value }); }} /> <TextField label="Label" value={editingOption.label} onChange={(event) => { handleOptionChange({ ...editingOption, label: event.target.value }); }} /> </Stack> </DialogContent> </React.Fragment> ) : ( <React.Fragment> <DialogTitle> Edit options {value.length > 0 ? ( <Button aria-label="Delete all options" variant="text" color="inherit" onClick={handleDeleteAll} sx={{ position: 'absolute', right: 16, top: 16, }} > Delete All </Button> ) : null} </DialogTitle> <DialogContent> {value.length > 0 ? ( <List> {value.map((option, i) => { return ( <ListItem key={i} disableGutters onClick={handleOptionItemClick(i)} secondaryAction={ <IconButton aria-label="Delete option" edge="end" onClick={handleOptionDelete(i)} > <DeleteIcon /> </IconButton> } > <ListItemButton> <ListItemText primary={ typeof option === 'string' ? option : (option as SelectOption).value } secondary={ typeof option === 'object' ? `Label: "${(option as SelectOption).label}"` : null } /> </ListItemButton> </ListItem> ); })} </List> ) : null} <TextField fullWidth sx={{ m: 1 }} variant="outlined" inputRef={optionInputRef} onKeyUp={handleOptionTextInput} label="Add option" helperText="Press “Enter” / “Return” to add" /> </DialogContent> </React.Fragment> )} <DialogActions> <Button color="inherit" variant="text" onClick={handleEditOptionsDialogClose}> Close </Button> </DialogActions> </Dialog> </React.Fragment> ); }
Example #13
Source File: Config.tsx From NekoMaid with MIT License | 4 votes |
configs.push({
title: lang.config.serverConfig,
component () {
const plugin = usePlugin()
const globalData = useGlobalData()
const [flag, update] = useState(0)
const [info, setInfo] = useState<Record<string, string>>({ })
const [open, setOpen] = useState(false)
const [canGetData, setCanGetData] = useState(true)
const [loading, setLoading] = useState(false)
const setValue = (field: string, value: any, isGlobal = true) => {
plugin.emit('server:set', field, value)
success()
if (isGlobal) {
(globalData as any)[field] = value
update(flag + 1)
location.reload()
}
}
const createEditButtom = (field: string, isGlobal?: boolean, isInt = true) => <IconButton
onClick={() => dialog(
{
content: lang.inputValue,
input: isInt
? {
error: true,
type: 'number',
helperText: lang.invalidValue,
validator: (it: string) => /^\d+$/.test(it) && +it >= 0
}
: { }
}).then(res => res != null && setValue(field, isInt ? parseInt(res as any) : (res || null), isGlobal))}
><Edit /></IconButton>
const infoElm: JSX.Element[] = []
for (const key in info) {
const name = (lang.config as any)[key]
infoElm.push(<ListItem key={key} sx={{ pl: 4 }}>
<ListItemText
primary={key === 'isAikarFlags' ? <Link href='https://mcflags.emc.gs' target='_blank' rel='noopener'>{name}</Link> : name}
secondary={info[key].toString()}
/>
</ListItem>)
}
return <List>
<CircularLoading loading={loading} />
<ListItem secondaryAction={globalData.canSetMaxPlayers
? createEditButtom('maxPlayers')
: undefined}>
<ListItemText primary={lang.config.maxPlayers + ': ' + globalData.maxPlayers} />
</ListItem>
<ListItem secondaryAction={createEditButtom('spawnRadius')}>
<ListItemText primary={lang.config.spawnRadius + ': ' + globalData.spawnRadius} />
</ListItem>
<ListItem secondaryAction={createEditButtom('motd', false, false)}>
<ListItemText primary={lang.config.motd} />
</ListItem>
<ListItem secondaryAction={<Switch checked={globalData.hasWhitelist} onChange={e => setValue('hasWhitelist', e.target.checked)} />}>
<ListItemText primary={lang.config.whitelist} />
</ListItem>
{canGetData && <>
<ListItemButton onClick={() => {
if (infoElm.length) setOpen(!open)
else {
setLoading(true)
plugin.emit('server:fetchInfo', (data: any) => {
setLoading(false)
if (!data) {
failed(lang.unsupported)
setCanGetData(false)
return
}
setInfo(data)
setOpen(true)
})
}
}}>
<ListItemIcon><Equalizer /></ListItemIcon>
<ListItemText primary={lang.info} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={open} timeout='auto' unmountOnExit>
<List component='div' dense disablePadding>{infoElm}</List>
</Collapse>
</>}
</List>
}
},
{
title: lang.history,
component () {
const [cur, update] = useState(0)
const list: ServerRecord[] = JSON.parse(localStorage.getItem('NekoMaid:servers') || '[]')
return <List>
{list.sort((a, b) => b.time - a.time).map(it => {
const i = it.address.indexOf('?')
return <ListItem
disablePadding
key={it.address}
secondaryAction={<IconButton edge='end' size='small' onClick={() => {
localStorage.setItem('NekoMaid:servers', JSON.stringify(list.filter(s => s.address !== it.address)))
success()
update(cur + 1)
}}><Delete /></IconButton>}
>
<ListItemButton onClick={() => {
location.hash = ''
location.search = it.address
}} dense>
<ListItemAvatar><Avatar src={it.icon} variant='rounded'><HelpOutline /></Avatar></ListItemAvatar>
<ListItemText primary={<Tooltip title={it.address.slice(i + 1)}>
<span>{it.address.slice(0, i)}</span></Tooltip>} secondary={dayjs(it.time).fromNow()} />
</ListItemButton>
</ListItem>
})}
</List>
}
},
{
title: lang.config.theme,
component () {
const color = localStorage.getItem('NekoMaid:color') || 'blue'
return <CardContent sx={{ textAlign: 'center' }}>
<Box>
<ToggleButtonGroup exclusive value={localStorage.getItem('NekoMaid:colorMode') || ''} onChange={(_, it) => {
localStorage.setItem('NekoMaid:colorMode', it)
location.reload()
}}>
<ToggleButton value='light'><Brightness7 /> {lang.config.light}</ToggleButton>
<ToggleButton value=''><SettingsBrightness /> {lang.config.system}</ToggleButton>
<ToggleButton value='dark'><Brightness4 /> {lang.config.dark}</ToggleButton>
</ToggleButtonGroup>
</Box>
<Paper sx={{ marginTop: 2, width: '176px', overflow: 'hidden', display: 'inline-block' }}>
{Object.keys(colors).slice(1, 17).map((key, i) => {
const checked = color === key
const elm = <Box
key={key}
onClick={() => {
localStorage.setItem('NekoMaid:color', key)
location.reload()
}}
sx={{
backgroundColor: (colors as any)[key][600],
width: '44px',
height: '44px',
display: 'inline-block',
cursor: 'pointer'
}}
><Check htmlColor='white' sx={{ top: '10px', position: 'relative', opacity: checked ? 1 : 0 }} /></Box>
return (i + 1) % 4 === 0 ? <React.Fragment key={key}>{elm}<br /></React.Fragment> : elm
})}
</Paper>
</CardContent>
}
})
Example #14
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 #15
Source File: Scheduler.tsx From NekoMaid with MIT License | 4 votes |
Scheduler: React.FC = () => {
const plugin = usePlugin()
const [id, setId] = useState(-1)
let [tasks, setTasks] = useState<Task[]>([])
const [name, setName] = useState('')
const [cron, setCron] = useState('')
const [values, setValues] = useState('')
const [whenIdle, setWhenIdle] = useState(false)
const [cronError, setCronError] = useState('')
const save = () => plugin.emit('scheduler:update', (res: boolean) => {
action(res)
plugin.emit('scheduler:fetch', setTasks)
}, JSON.stringify(tasks))
useEffect(() => { plugin.emit('scheduler:fetch', setTasks) }, [])
return <Box sx={{ minHeight: '100%', py: 3 }}>
<Toolbar />
<Container maxWidth={false}>
<Grid container spacing={3}>
<Grid item lg={4} md={12} xl={4} xs={12}>
<Card>
<CardHeader
title={lang.scheduler.title}
sx={{ position: 'relative' }}
action={<IconButton
size='small'
onClick={() => {
const task = {
name: lang.scheduler.newTask,
cron: '*/1 * * * *',
enabled: true,
whenIdle: false,
values: ['/say Hello, %server_tps% (PlaceholderAPI)', 'This is a chat message']
}
setTasks([...tasks, task])
setId(tasks.length)
setCronError('')
setCron(task.cron)
setName(task.name)
setValues(task.values.join('\n'))
setWhenIdle(false)
}}
sx={cardActionStyles}
><Add /></IconButton>}
/>
<Divider />
{tasks.length
? <List
sx={{ width: '100%' }}
component='nav'
>
{tasks.map((it, i) => <ListItem
key={i}
disablePadding
secondaryAction={<IconButton
edge='end'
onClick={() => dialog(lang.scheduler.confirmDelete)
.then(it => {
if (it == null) return
setTasks((tasks = tasks.filter((_, id) => i !== id)))
save()
})}
><Delete /></IconButton>}
sx={{ position: 'relative' }}
>
<ListItemIcon sx={{ paddingLeft: 2, position: 'absolute' }}>
<Checkbox
edge='start'
checked={it.enabled}
tabIndex={-1}
/>
</ListItemIcon>
<ListItemButton onClick={() => {
setId(i)
setCronError('')
setCron(tasks[i].cron)
setName(tasks[i].name)
setValues(tasks[i].values.join('\n'))
setWhenIdle(!!tasks[i].whenIdle)
}}><ListItemText inset primary={it.name} /></ListItemButton >
</ListItem>)}
</List>
: <CardContent><Empty /></CardContent>}
</Card>
</Grid>
<Grid item lg={8} md={12} xl={8} xs={12}>
<Card>
<CardHeader
title={lang.scheduler.editor}
sx={{ position: 'relative' }}
action={<IconButton
size='small'
onClick={() => {
tasks[id].values = values.split('\n')
tasks[id].cron = cron
tasks[id].name = name
tasks[id].whenIdle = whenIdle
save()
}}
sx={cardActionStyles}
disabled={!tasks[id] || !!cronError}
><Save /></IconButton>}
/>
<Divider />
<CardContent>
{tasks[id]
? <>
<TextField
required
fullWidth
variant='standard'
label={lang.scheduler.name}
value={name}
onChange={e => setName(e.target.value)}
/>
<TextField
fullWidth
multiline
rows={4}
value={values}
sx={{ marginTop: 3 }}
label={lang.scheduler.content}
onChange={e => setValues(e.target.value)}
/>
<FormControlLabel
control={<Switch checked={whenIdle} />}
label={lang.scheduler.whenIdle}
onChange={(e: any) => setWhenIdle(e.target.checked)}
/>
</>
: <Empty title={lang.scheduler.notSelected} />}
</CardContent>
{tasks[id] && <>
<Divider textAlign='left'>{lang.scheduler.timer}</Divider>
<CardContent>
<Box sx={{
'& .MuiTextField-root': { backgroundColor: 'inherit!important' },
'& .MuiOutlinedInput-input': { color: 'inherit!important' },
'& .MuiTypography-h6': { color: theme => theme.palette.primary.main + '!important' }
}}>
<Cron cron={cron} setCron={setCron} setCronError={setCronError} locale={currentLanguage as any} isAdmin />
</Box>
</CardContent>
</>}
</Card>
</Grid>
</Grid>
</Container>
</Box>
}
Example #16
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>
}