@mui/material#Menu TypeScript Examples
The following examples show how to use
@mui/material#Menu.
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: WalletMultiButton.tsx From wallet-adapter with Apache License 2.0 | 6 votes |
StyledMenu = styled(Menu)(({ theme }: { theme: Theme }) => ({
'& .MuiList-root': {
padding: 0,
},
'& .MuiListItemIcon-root': {
marginRight: theme.spacing(),
minWidth: 'unset',
'& .MuiSvgIcon-root': {
width: 20,
height: 20,
},
},
}))
Example #2
Source File: AppNavbar.tsx From sapio-studio with Mozilla Public License 2.0 | 6 votes |
function Simulator() {
const dispatch = useDispatch();
const simulateRef = React.useRef<HTMLLIElement>(null);
const [sim_open, setSimOpen] = React.useState(false);
return (
<div>
<ListItem
disableGutters
button={false}
key={'Simulate'}
onClick={() => setSimOpen(true)}
ref={simulateRef}
>
<ListItemIcon></ListItemIcon>
<ListItemText primary={'Simulate'} />
</ListItem>
<Menu
anchorEl={simulateRef.current}
anchorOrigin={{
vertical: 'center',
horizontal: 'right',
}}
keepMounted
open={sim_open}
onClose={() => setSimOpen(false)}
>
<MenuItem
onClick={() => {
setSimOpen(false);
dispatch(toggle_showing());
}}
>
Timing
</MenuItem>
</Menu>
</div>
);
}
Example #3
Source File: PublicMenu.tsx From frontend with MIT License | 6 votes |
export default function PublicMenu() {
const { t } = useTranslation()
const { data: session } = useSession()
const [anchorEl, setAnchorEl] = useState<Element | null>(null)
const handleMenu = (event: React.MouseEvent) => setAnchorEl(event.currentTarget)
const handleClose = () => setAnchorEl(null)
if (session) {
return null
}
return (
<StyledGrid item>
<IconButton onClick={handleMenu} size="large">
<AccountCircle sx={{ fill: theme.palette.info.light }} />
</IconButton>
<Menu
open={Boolean(anchorEl)}
keepMounted
id="menu-appbar"
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
transformOrigin={{ vertical: 'top', horizontal: 'right' }}>
<LinkMenuItem href={routes.login} className={classes.dropdownLinkButton}>
<Typography variant="button" className={classes.dropdownLinkText}>
{t('nav.login')}
</Typography>
</LinkMenuItem>
<LinkMenuItem href={routes.register} className={classes.dropdownLinkButton}>
<Typography variant="button" className={classes.dropdownLinkText}>
{t('nav.register')}
</Typography>
</LinkMenuItem>
</Menu>
</StyledGrid>
)
}
Example #4
Source File: GenericMenu.tsx From frontend with MIT License | 6 votes |
export default function GenericMenu({ label, children }: Props) {
const [anchorEl, setAnchorEl] = useState<Element | null>(null)
const open = Boolean(anchorEl)
const handleMenu = (event: React.MouseEvent) => setAnchorEl(event.currentTarget)
const handleClose = () => setAnchorEl(null)
return (
<>
<Button
variant="text"
color="primary"
onClick={handleMenu}
sx={{ whiteSpace: 'nowrap', px: 2 }}
endIcon={open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}>
{label}
</Button>
<Menu
keepMounted
id="menu-donation"
anchorEl={anchorEl}
elevation={6}
onClose={handleClose}
open={Boolean(anchorEl)}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
transformOrigin={{ vertical: 'top', horizontal: 'right' }}>
{children}
</Menu>
</>
)
}
Example #5
Source File: AppNavbar.tsx From sapio-studio with Mozilla Public License 2.0 | 5 votes |
function NodeMenu(props: { bitcoin_node_manager: BitcoinNodeManager }) {
const dispatch = useDispatch();
const nodeRef = React.useRef<HTMLLIElement>(null);
const [node_open, setNodeOpen] = React.useState(false);
const close = () => setNodeOpen(false);
return (
<>
<ListItem
disableGutters
button={false}
key={'Bitcoin Node'}
onClick={() => setNodeOpen(true)}
ref={nodeRef}
>
<ListItemIcon></ListItemIcon>
<ListItemText primary={'Bitcoin Node'} />
</ListItem>
<Menu
anchorEl={nodeRef.current}
anchorOrigin={{
vertical: 'center',
horizontal: 'right',
}}
keepMounted
open={node_open}
onClose={close}
>
<MenuItem
onClick={async () => {
close();
const addr =
await props.bitcoin_node_manager.get_new_address();
window.electron.write_clipboard(addr);
}}
>
Get New Address to Clipboard
</MenuItem>
<MenuItem
onClick={() => {
close();
props.bitcoin_node_manager
.generate_blocks(10)
.catch((err) => console.error(err));
}}
>
Generate 10 Blocks
</MenuItem>
<Divider />
<MenuItem
onClick={() => {
close();
dispatch(toggle_status_bar());
}}
>
Toggle Status
</MenuItem>
</Menu>
</>
);
}
Example #6
Source File: PlayBar.tsx From rewind with MIT License | 5 votes |
function MoreMenu() {
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
const handleClick = (event: any) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const analyzer = useAnalysisApp();
const handleTakeScreenshot = () => {
analyzer.screenshotTaker.takeScreenshot();
handleClose();
};
const [helpOpen, setHelpOpen] = useState(false);
const handleOpenHelp = () => {
setHelpOpen(true);
handleClose();
};
return (
<>
<HelpModalDialog isOpen={helpOpen} onClose={() => setHelpOpen(false)} />
<IconButton
aria-label="more"
id="long-button"
aria-controls="long-menu"
// aria-expanded={open ? "true" : undefined}
aria-haspopup="true"
onClick={handleClick}
onFocus={ignoreFocus}
>
<MoreVert />
</IconButton>
<Menu
open={open}
onClose={handleClose}
anchorEl={anchorEl}
anchorOrigin={{
vertical: "top",
horizontal: "center",
}}
transformOrigin={{
vertical: "bottom",
horizontal: "center",
}}
>
<MenuItem onClick={handleTakeScreenshot}>
<ListItemIcon>
<PhotoCamera />
</ListItemIcon>
<ListItemText>Take Screenshot</ListItemText>
</MenuItem>
<MenuItem onClick={handleOpenHelp}>
<ListItemIcon>
<Help />
</ListItemIcon>
<ListItemText>Help</ListItemText>
</MenuItem>
</Menu>
</>
);
}
Example #7
Source File: PlayBar.tsx From rewind with MIT License | 5 votes |
function BaseSpeedButton(props: BaseSpeedButtonProps) {
const { value, onChange } = props;
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
const handleClick = (event: any) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const formatSpeed = (s: number) => `${s}x`;
// Floating point issues?
return (
<>
<Button
sx={{
color: "text.primary",
textTransform: "none",
fontSize: "1em",
// minWidth: "0",
// px: 2,
}}
size={"small"}
onClick={handleClick}
onFocus={ignoreFocus}
>
{formatSpeed(value)}
{/*<Typography>{formatSpeed(value)}</Typography>*/}
</Button>
<Menu
open={open}
onClose={handleClose}
anchorEl={anchorEl}
anchorOrigin={{
vertical: "top",
horizontal: "center",
}}
transformOrigin={{
vertical: "bottom",
horizontal: "center",
}}
>
<MenuList>
{ALLOWED_SPEEDS.map((s) => (
<MenuItem
key={s}
onClick={() => {
onChange(s);
handleClose();
}}
sx={{ width: "120px", maxWidth: "100%" }}
>
<ListItemText>{formatSpeed(s)}</ListItemText>
<Typography variant="body2" color="text.secondary">
{speedLabels[s] ?? ""}
</Typography>
</MenuItem>
))}
</MenuList>
</Menu>
</>
);
}
Example #8
Source File: PrivateMenu.tsx From frontend with MIT License | 5 votes |
export default function PrivateMenu() {
const { t } = useTranslation()
const { data: session, status } = useSession()
const [anchorEl, setAnchorEl] = useState<Element | null>(null)
const handleMenu = (event: React.MouseEvent) => setAnchorEl(event.currentTarget)
const handleClose = () => setAnchorEl(null)
if (!session) {
return null
}
const title = `${session.name}\n(${session.email})`
return (
<StyledGrid item>
<IconButton onClick={handleMenu} size="large">
{session?.user?.picture ? (
<Avatar title={title} alt={title} src={session?.user?.picture} />
) : (
<AccountCircle sx={{ fill: theme.palette.info.light }} />
)}
</IconButton>
<Menu
keepMounted
id="menu-appbar"
anchorEl={anchorEl}
onClose={handleClose}
open={Boolean(anchorEl)}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
transformOrigin={{ vertical: 'top', horizontal: 'right' }}>
<LinkMenuItem href={routes.profile.index} className={classes.dropdownLinkText}>
<Typography variant="button">{t('nav.profile')}</Typography>
</LinkMenuItem>
{status === 'authenticated' && isAdmin(session) && (
<LinkMenuItem href={routes.admin.index} className={classes.dropdownLinkText}>
<Typography variant="button">{t('nav.admin.index')}</Typography>
</LinkMenuItem>
)}
<LinkMenuItem href={routes.logout} className={classes.dropdownLinkText}>
<Typography variant="button">{t('nav.logout')}</Typography>
</LinkMenuItem>
</Menu>
</StyledGrid>
)
}
Example #9
Source File: DropdownButton.tsx From genshin-optimizer with MIT License | 5 votes |
export default function DropdownButton({ title, children, id = "dropdownbtn", ...props }: DropdownButtonProps) {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = useCallback(
(event: React.MouseEvent<HTMLButtonElement>) => setAnchorEl(event.currentTarget),
[setAnchorEl],
)
const handleClose = useCallback(
() => setAnchorEl(null),
[setAnchorEl],
)
return <Suspense fallback={<Button endIcon={<KeyboardArrowDown />}{...props}><Skeleton width={50} /></Button>} >
<Button
{...props}
id={id}
aria-controls="basic-menu"
aria-haspopup="true"
aria-expanded={open ? 'true' : undefined}
onClick={handleClick}
endIcon={<KeyboardArrowDown />}
>
{title}
</Button>
<Menu
id="basic-menu"
anchorEl={anchorEl}
open={open}
onClose={handleClose}
MenuListProps={{
'aria-labelledby': id,
}}
onClick={handleClose}
>
{/* set Skeleton to be really high so the taller dropdowns can still be placed properly... */}
<Suspense fallback={<Skeleton width="100%" height="1000" />}>
{children}
</Suspense>
</Menu>
</Suspense>
}
Example #10
Source File: InputUnitMenu.tsx From console with GNU Affero General Public License v3.0 | 5 votes |
InputUnitMenu = ({
classes,
id,
unitSelected,
unitsList,
disabled = false,
onUnitChange,
}: IInputUnitBox) => {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = (newUnit: string) => {
setAnchorEl(null);
if (newUnit !== "" && onUnitChange) {
onUnitChange(newUnit);
}
};
return (
<Fragment>
<button
id={`${id}-button`}
aria-controls={`${id}-menu`}
aria-haspopup="true"
aria-expanded={open ? "true" : undefined}
onClick={handleClick}
className={classes.buttonTrigger}
disabled={disabled}
type={"button"}
>
{unitSelected}
</button>
<Menu
id={`${id}-menu`}
aria-labelledby={`${id}-button`}
anchorEl={anchorEl}
open={open}
onClose={() => {
handleClose("");
}}
anchorOrigin={{
vertical: "bottom",
horizontal: "center",
}}
transformOrigin={{
vertical: "top",
horizontal: "center",
}}
>
{unitsList.map((unit) => (
<MenuItem
onClick={() => handleClose(unit.value)}
key={`itemUnit-${unit.value}-${unit.label}`}
>
{unit.label}
</MenuItem>
))}
</Menu>
</Fragment>
);
}
Example #11
Source File: LocaleMenu.tsx From frontend with MIT License | 5 votes |
export default function LocaleMenu() {
const router = useRouter()
const { t } = useTranslation()
const [anchorEl, setAnchorEl] = useState<Element | null>(null)
const handleMenu = (event: React.MouseEvent) => setAnchorEl(event.currentTarget)
const handleClose = () => setAnchorEl(null)
const changeLang = useCallback(
(locale: string) => (event: React.MouseEvent) => {
event.preventDefault()
// Same route different language
router.push(router.route, router.asPath, { locale })
setAnchorEl(null)
},
[],
)
if (!router.locale) {
return null
}
return (
<>
<Button variant="text" size="small" onClick={handleMenu}>
{router.locale.toUpperCase()}
</Button>
<Menu
open={Boolean(anchorEl)}
keepMounted
id="menu-appbar"
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
transformOrigin={{ vertical: 'top', horizontal: 'right' }}>
<MenuItem href={router.route} component="a" onClick={changeLang('bg')}>
{t('BG')}
</MenuItem>
<MenuItem href={router.route} component="a" onClick={changeLang('en')}>
{t('EN')}
</MenuItem>
</Menu>
</>
)
}
Example #12
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 #13
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 #14
Source File: AppNavbar.tsx From sapio-studio with Mozilla Public License 2.0 | 4 votes |
function ContractMenu(props: { relayout: () => void }) {
const dispatch = useDispatch();
const workspace = useSelector(selectWorkspace);
const contractRef = React.useRef<HTMLLIElement>(null);
const [contracts_open, setContractsOpen] = React.useState(false);
return (
<div>
<ListItem
disableGutters
button={false}
key={'Contract'}
onClick={() => setContractsOpen(true)}
ref={contractRef}
>
<ListItemIcon></ListItemIcon>
<ListItemText primary={'Contract'} />
</ListItem>
<Menu
anchorEl={contractRef.current}
anchorOrigin={{
vertical: 'center',
horizontal: 'right',
}}
keepMounted
open={contracts_open}
onClose={() => setContractsOpen(false)}
>
<MenuItem
onClick={() => {
setContractsOpen(false);
dispatch(open_modal('LoadHex'));
}}
>
Open Contract from Clipboard
</MenuItem>
<MenuItem
onClick={() => {
setContractsOpen(false);
dispatch(create_contract_from_file());
}}
>
Open Contract from File
</MenuItem>
<MenuItem
onClick={() => {
setContractsOpen(false);
dispatch(open_modal('SaveHex'));
}}
>
Save Contract
</MenuItem>
<MenuItem
onClick={() => {
setContractsOpen(false);
window.electron.sapio.load_wasm_plugin(workspace);
}}
>
Load WASM Plugin
</MenuItem>
<MenuItem
onClick={async () => {
setContractsOpen(false);
const apis =
await window.electron.sapio.load_contract_list(
workspace
);
if ('err' in apis) {
alert(apis.err);
return;
}
dispatch(set_apis(apis.ok));
dispatch(switch_showing('ContractCreator'));
}}
>
Create New Contract
</MenuItem>
<MenuItem
onClick={() => {
setContractsOpen(false);
dispatch(recreate_contract());
}}
>
Recreate Last Contract
</MenuItem>
<Divider />
<MenuItem
onClick={() => {
props.relayout();
}}
>
Repair Layout
</MenuItem>
</Menu>
</div>
);
}
Example #15
Source File: PlayerList.tsx From NekoMaid with MIT License | 4 votes |
Players: React.FC = () => {
const his = useHistory()
const plugin = usePlugin()
const [page, setPage] = useState(0)
const [loading, setLoading] = useState(true)
const [state, setState] = useState<number | null>(null)
const [activedPlayer, setActivedPlayer] = useState<PlayerData | null>(null)
const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null)
const [data, setData] = useState<{ count: number, players: PlayerData[] }>(() => ({ count: 0, players: [] }))
const globalData = useGlobalData()
const { hasWhitelist } = globalData
const refresh = () => {
setLoading(true)
plugin.emit('playerList:fetchPage', (it: any) => {
if (it.players == null) it.players = []
setData(it)
setLoading(false)
}, page, state === 1 || state === 2 ? state : 0, null)
}
useMemo(refresh, [page, state])
const close = () => {
setAnchorEl(null)
setActivedPlayer(null)
}
return <Card>
<CardHeader
title={lang.playerList.title}
action={
<ToggleButtonGroup
size='small'
color={(state === 1 ? 'warning' : state === 2 ? 'error' : undefined) as any}
value={state}
exclusive
onChange={(_, it) => {
if (it === 3) return
setState(it)
if (state === 3) refresh()
}}
>
<ToggleButton disabled={loading} value={1}><Star /></ToggleButton>
<ToggleButton disabled={loading} value={2}><Block /></ToggleButton>
<ToggleButton disabled={loading} value={3} onClick={() => state !== 3 && dialog(lang.playerList.nameToSearch, lang.username)
.then(filter => {
if (filter == null) return
his.push('/NekoMaid/playerList/' + filter)
setState(3)
setLoading(true)
plugin.emit('playerList:fetchPage', (it: any) => {
if (it.players == null) it.players = []
setPage(0)
setData(it)
setLoading(false)
}, page, 0, filter.toLowerCase())
})}><Search /></ToggleButton>
</ToggleButtonGroup>
}
/>
<Divider />
<Box sx={{ position: 'relative' }}>
<CircularLoading loading={loading} />
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell padding='checkbox' />
<TableCell>{lang.username}</TableCell>
<TableCell align='right'>{minecraft['stat.minecraft.play_time']}</TableCell>
<TableCell align='right'>{lang.playerList.lastPlay}</TableCell>
<TableCell align='right'>{lang.operations}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{data.players.map(it => <TableRow key={it.name}>
<TableCell sx={{ cursor: 'pointer', padding: theme => theme.spacing(1, 1, 1, 2) }} onClick={() => his.push('/NekoMaid/playerList/' + it.name)}>
<Avatar src={getSkin(globalData, it.name, true)} imgProps={{ crossOrigin: 'anonymous', style: { width: 40, height: 40 } }} variant='rounded' />
</TableCell>
<TableCell>{it.name}</TableCell>
<TableCell align='right'>{dayjs.duration(it.playTime / 20, 'seconds').humanize()}</TableCell>
<TableCell align='right'>{dayjs(it.lastOnline).fromNow()}</TableCell>
<TableCell align='right'>
{(state === 1 || hasWhitelist) && <Tooltip title={lang.playerList[it.whitelisted ? 'clickToRemoveWhitelist' : 'clickToAddWhitelist']}>
<IconButton onClick={() => whitelist(it.name, plugin, refresh, !it.whitelisted)}>
{it.whitelisted ? <Star color='warning' /> : <StarBorder />}
</IconButton>
</Tooltip>}
<Tooltip title={it.ban == null ? lang.playerList.clickToBan : lang.playerList.banned + ': ' + it.ban}>
<IconButton onClick={() => banPlayer(it.name, plugin, refresh, it.ban == null)}>
<Block color={it.ban == null ? undefined : 'error'} />
</IconButton>
</Tooltip>
{actions.length
? <IconButton onClick={e => {
setActivedPlayer(anchorEl ? null : it)
setAnchorEl(anchorEl ? null : e.currentTarget)
}}><MoreHoriz /></IconButton>
: null}
</TableCell>
</TableRow>)}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={[]}
component='div'
count={data.count}
rowsPerPage={10}
page={page}
onPageChange={(_, it) => !loading && setPage(it)}
/>
</Box>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={() => setAnchorEl(null)}
>{actions.map((It, i) => <It key={i} onClose={close} player={activedPlayer} />)}</Menu>
</Card>
}
Example #16
Source File: Files.tsx From NekoMaid with MIT License | 4 votes |
Files: React.FC = () => {
const plugin = usePlugin()
const theme = useTheme()
const his = useHistory()
const loc = useLocation()
const drawerWidth = useDrawerWidth()
const tree = useRef<HTMLHRElement | null>(null)
const editor = useRef<UnControlled | null>(null)
const prevExpanded = useRef<string[]>([])
const dirs = useRef<Record<string, boolean>>({ })
// eslint-disable-next-line func-call-spacing
const loading = useRef<Record<string, () => Promise<void>> & { '!#LOADING'?: boolean }>({ })
const [id, setId] = useState(0)
const [curPath, setCurPath] = useState('')
const [progress, setProgress] = useState(-1)
const [copyPath, setCopyPath] = useState('')
const [expanded, setExpanded] = useState<string[]>([])
const [compressFile, setCompressFile] = useState<string | null>(null)
const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null)
const isDir = !!dirs.current[curPath]
const dirPath = isDir ? curPath : curPath.substring(0, curPath.lastIndexOf('/'))
const spacing = theme.spacing(3)
const refresh = () => {
loading.current = { }
dirs.current = { }
prevExpanded.current = []
setCurPath('')
setExpanded([])
setId(id + 1)
}
useEffect(() => {
if (!tree.current) return
const resize = () => {
if (!tree.current) return
const height = tree.current.style.maxHeight = (window.innerHeight - tree.current.offsetTop - parseInt(spacing)) + 'px'
const style = (editor as any).current?.editor?.display?.wrapper?.style
if (style) style.height = height
}
resize()
window.addEventListener('resize', resize)
return window.removeEventListener('resize', resize)
}, [tree.current, spacing])
return <Box sx={{ height: '100vh', py: 3 }}>
<Toolbar />
<Container maxWidth={false}>
<Grid container spacing={3} sx={{ width: { sm: `calc(100vw - ${drawerWidth}px - ${theme.spacing(3)})` } }}>
<Grid item lg={4} md={12} xl={3} xs={12}>
<Card sx={{ minHeight: 400 }}>
<CardHeader
title={lang.files.filesList}
sx={{ position: 'relative' }}
action={<Box sx={{ position: 'absolute', right: theme.spacing(1), top: '50%', transform: 'translateY(-50%)' }}
>
<Tooltip title={lang.files.delete}><span>
<IconButton
disabled={!curPath}
size='small'
onClick={() => dialog({
okButton: { color: 'error' },
content: <>{lang.files.confirmDelete(<span className='bold'>{curPath}</span>)}
<span className='bold' style={{ color: theme.palette.error.main }}>({lang.unrecoverable})</span></>
}).then(it => it && plugin.emit('files:update', (res: boolean) => {
action(res)
if (!res) return
refresh()
if (loc.pathname.replace(/^\/NekoMaid\/files\/?/, '') === curPath) his.push('/NekoMaid/files')
}, curPath))}
><DeleteForever /></IconButton>
</span></Tooltip>
<Tooltip title={lang.files.createFile}>
<IconButton size='small' onClick={() => fileNameDialog(lang.files.createFile, curPath)
.then(it => it != null && his.push(`/NekoMaid/files/${dirPath ? dirPath + '/' : ''}${it}`))}>
<Description /></IconButton></Tooltip>
<Tooltip title={lang.files.createFolder}>
<IconButton size='small' onClick={() => fileNameDialog(lang.files.createFolder, curPath)
.then(it => it != null && plugin.emit('files:createDirectory', (res: boolean) => {
action(res)
if (res) refresh()
}, dirPath + '/' + it))}><CreateNewFolder /></IconButton></Tooltip>
<Tooltip title={lang.more}>
<IconButton size='small' onClick={e => setAnchorEl(anchorEl ? null : e.currentTarget)}><MoreHoriz /></IconButton>
</Tooltip>
</Box>} />
<Divider />
<TreeView
ref={tree}
defaultCollapseIcon={<ArrowDropDown />}
defaultExpandIcon={<ArrowRight />}
sx={{ flexGrow: 1, width: '100%', overflowY: 'auto' }}
expanded={expanded}
onNodeToggle={(_: any, it: string[]) => {
const l = loading.current
if (it.length < prevExpanded.current.length || !l[it[0]]) {
setExpanded(it)
prevExpanded.current = it
return
}
l[it[0]]().then(() => {
prevExpanded.current.unshift(it[0])
setExpanded([...prevExpanded.current])
delete l[it[0]]
})
delete l[it[0]]
}}
onNodeSelect={(_: any, it: string) => {
setCurPath(it[0] === '/' ? it.slice(1) : it)
if (dirs.current[it] || loading.current['!#LOADING']) return
if (it.startsWith('/')) it = it.slice(1)
his.push('/NekoMaid/files/' + it)
}}
>
<Item plugin={plugin} path='' loading={loading.current} dirs={dirs.current} key={id} />
</TreeView>
</Card>
</Grid>
<Grid item lg={8} md={12} xl={9} xs={12} sx={{ maxWidth: `calc(100vw - ${theme.spacing(1)})`, paddingBottom: 3 }}>
<Editor plugin={plugin} editorRef={editor} loading={loading.current} dirs={dirs.current} refresh={refresh} />
</Grid>
</Grid>
</Container>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={() => setAnchorEl(null)}
anchorOrigin={anchorOrigin}
transformOrigin={anchorOrigin}
>
<MenuItem onClick={() => {
refresh()
setAnchorEl(null)
}}><ListItemIcon><Refresh /></ListItemIcon>{lang.refresh}</MenuItem>
<MenuItem disabled={!curPath} onClick={() => {
setAnchorEl(null)
fileNameDialog(lang.files.rename, curPath).then(it => it != null && plugin.emit('files:rename', (res: boolean) => {
action(res)
if (res) refresh()
}, curPath, dirPath + '/' + it))
}}><ListItemIcon><DriveFileRenameOutline /></ListItemIcon>{lang.files.rename}</MenuItem>
<MenuItem disabled={!curPath} onClick={() => {
setAnchorEl(null)
setCopyPath(curPath)
}}>
<ListItemIcon><FileCopy /></ListItemIcon>{lang.files.copy}
</MenuItem>
<MenuItem disabled={!copyPath} onClick={() => {
setAnchorEl(null)
toast(lang.files.pasting)
plugin.emit('files:copy', (res: boolean) => {
action(res)
refresh()
}, copyPath, dirPath)
}}>
<ListItemIcon><ContentPaste /></ListItemIcon>{lang.files.paste}
</MenuItem>
<MenuItem disabled={progress !== -1} component='label' htmlFor='NekoMaid-files-upload-input' onClick={() => setAnchorEl(null)}>
<ListItemIcon><Upload /></ListItemIcon>{progress === -1 ? lang.files.upload : `${lang.files.uploading} (${progress.toFixed(2)}%)`}
</MenuItem>
<MenuItem disabled={isDir} onClick={() => {
setAnchorEl(null)
toast(lang.files.downloading)
plugin.emit('files:download', (res: ArrayBuffer | null) => {
if (res) window.open(address! + 'Download/' + res, '_blank')
else failed()
}, curPath)
}}><ListItemIcon><Download /></ListItemIcon>{lang.files.download}</MenuItem>
<MenuItem onClick={() => {
setAnchorEl(null)
setCompressFile(curPath)
}}><ListItemIcon><Inbox /></ListItemIcon>{lang.files.compress}</MenuItem>
<MenuItem onClick={() => {
setAnchorEl(null)
toast(lang.files.uncompressing)
plugin.emit('files:compress', (res: boolean) => {
action(res)
refresh()
}, curPath)
}}><ListItemIcon><Outbox /></ListItemIcon>{lang.files.decompress}</MenuItem>
</Menu>
<Input id='NekoMaid-files-upload-input' type='file' sx={{ display: 'none' }} onChange={e => {
const elm = e.target as HTMLInputElement
const file = elm.files?.[0]
elm.value = ''
if (!file) return
const size = file.size
if (size > 128 * 1024 * 1024) return failed(lang.files.uploadTooBig)
toast(lang.files.uploading)
const name = dirPath + '/' + file.name
if (dirs.current[name] != null) return failed(lang.files.exists)
plugin.emit('files:upload', (res: string | null) => {
if (!res) return failed(lang.files.exists)
const formdata = new FormData()
formdata.append('file', file)
const xhr = new XMLHttpRequest()
setProgress(0)
xhr.open('put', address! + 'Upload/' + res)
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) return
setProgress(-1)
action(xhr.status === 200)
refresh()
}
xhr.upload.onprogress = e => e.lengthComputable && setProgress(e.loaded / e.total * 100)
xhr.send(formdata)
}, name[0] === '/' ? name.slice(1) : name)
}} />
<CompressDialog file={compressFile} path={dirPath} dirs={dirs.current} onClose={() => setCompressFile(null)} refresh={refresh} plugin={plugin} />
</Box>
}
Example #17
Source File: CollectionRowActions.tsx From firecms with MIT License | 4 votes |
/**
*
* @param entity
* @param isSelected
* @param selectionEnabled
* @param size
* @param toggleEntitySelection
* @param onCopyClicked
* @param onEditClicked
* @param onDeleteClicked
* @constructor
*
* @category Collection components
*/
export function CollectionRowActions<M extends { [Key: string]: any }>({
entity,
isSelected,
selectionEnabled,
size,
toggleEntitySelection,
onCopyClicked,
onEditClicked,
onDeleteClicked
}:
{
entity: Entity<M>,
size: CollectionSize,
isSelected?: boolean,
selectionEnabled?: boolean,
toggleEntitySelection?: (selectedEntity: Entity<M>) => void
onEditClicked?: (selectedEntity: Entity<M>) => void,
onCopyClicked?: (selectedEntity: Entity<M>) => void,
onDeleteClicked?: (selectedEntity: Entity<M>) => void,
}) {
const editEnabled = Boolean(onEditClicked);
const copyEnabled = Boolean(onCopyClicked);
const deleteEnabled = Boolean(onDeleteClicked);
const classes = useTableStyles();
const [anchorEl, setAnchorEl] = React.useState<any | null>(null);
const openMenu = useCallback((event: React.MouseEvent) => {
setAnchorEl(event.currentTarget);
event.stopPropagation();
}, [setAnchorEl]);
const closeMenu = useCallback(() => {
setAnchorEl(null);
}, [setAnchorEl]);
const onCheckboxChange = (event: React.ChangeEvent) => {
if (toggleEntitySelection)
toggleEntitySelection(entity);
event.stopPropagation();
};
const onDeleteClick = useCallback((event: MouseEvent) => {
event.stopPropagation();
if (onDeleteClicked)
onDeleteClicked(entity);
setAnchorEl(null);
}, [entity, onDeleteClicked, setAnchorEl]);
const onCopyClick = useCallback((event: MouseEvent) => {
event.stopPropagation();
if (onCopyClicked)
onCopyClicked(entity);
setAnchorEl(null);
}, [entity, onCopyClicked, setAnchorEl]);
return (
<div className={classes.cellButtonsWrap}>
{(editEnabled || deleteEnabled || selectionEnabled) &&
<div className={classes.cellButtons}
>
{editEnabled &&
<Tooltip title={`Edit ${entity.id}`}>
<IconButton
onClick={(event: MouseEvent) => {
event.stopPropagation();
if (onEditClicked)
onEditClicked(entity);
}}
size="large">
<KeyboardTab/>
</IconButton>
</Tooltip>
}
{selectionEnabled &&
<Tooltip title={`Select ${entity.id}`}>
<Checkbox
checked={isSelected}
onChange={onCheckboxChange}
/>
</Tooltip>}
{(copyEnabled || deleteEnabled) &&
<IconButton onClick={openMenu} size="large">
<MoreVert/>
</IconButton>
}
{(copyEnabled || deleteEnabled) && <Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={closeMenu}
elevation={2}
>
{deleteEnabled && <MenuItem onClick={onDeleteClick}>
<ListItemIcon>
<Delete/>
</ListItemIcon>
<ListItemText primary={"Delete"}/>
</MenuItem>}
{copyEnabled && <MenuItem onClick={onCopyClick}>
<ListItemIcon>
<FileCopy/>
</ListItemIcon>
<ListItemText primary="Copy"/>
</MenuItem>}
</Menu>}
</div>}
{size !== "xs" && (
<div className={classes.cellButtonsId}>
{entity
? <Typography
className={"mono"}
variant={"caption"}
color={"textSecondary"}> {entity.id} </Typography>
: <Skeleton variant="text"/>
}
</div>
)}
</div>
);
}
Example #18
Source File: UploadFilesButton.tsx From console with GNU Affero General Public License v3.0 | 4 votes |
UploadFilesButton = ({
uploadPath,
bucketName,
forceDisable = false,
uploadFileFunction,
uploadFolderFunction,
classes,
}: IUploadFilesButton) => {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const openUploadMenu = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const handleCloseUpload = () => {
setAnchorEl(null);
};
const uploadObjectAllowed = hasPermission(uploadPath, [
IAM_SCOPES.S3_PUT_OBJECT,
]);
const uploadFolderAllowed = hasPermission(
bucketName,
[IAM_SCOPES.S3_PUT_OBJECT],
false,
true
);
const uploadEnabled: boolean = uploadObjectAllowed || uploadFolderAllowed;
return (
<Fragment>
<RBIconButton
id={"upload-main"}
tooltip={"Upload Files"}
aria-controls={`upload-main-menu`}
aria-haspopup="true"
aria-expanded={openUploadMenu ? "true" : undefined}
onClick={handleClick}
text={"Upload"}
icon={<UploadIcon />}
color="primary"
variant={"contained"}
disabled={forceDisable || !uploadEnabled}
/>
<Menu
id={`upload-main-menu`}
aria-labelledby={`upload-main`}
anchorEl={anchorEl}
open={openUploadMenu}
onClose={() => {
handleCloseUpload();
}}
anchorOrigin={{
vertical: "bottom",
horizontal: "center",
}}
transformOrigin={{
vertical: "top",
horizontal: "center",
}}
>
<MenuItem
onClick={() => {
uploadFileFunction(handleCloseUpload);
}}
disabled={!uploadObjectAllowed || forceDisable}
>
<ListItemIcon className={classes.listUploadIcons}>
<UploadIcon />
</ListItemIcon>
<ListItemText>Upload File</ListItemText>
</MenuItem>
<MenuItem
onClick={() => {
uploadFolderFunction(handleCloseUpload);
}}
disabled={!uploadFolderAllowed || forceDisable}
>
<ListItemIcon className={classes.listUploadIcons}>
<UploadFolderIcon />
</ListItemIcon>
<ListItemText>Upload Folder</ListItemText>
</MenuItem>
</Menu>
</Fragment>
);
}
Example #19
Source File: Navbar.tsx From website with Apache License 2.0 | 4 votes |
Navbar = ({
rootPageHasAnimation,
}: {
rootPageHasAnimation?: boolean;
}) => {
const [drawerState, setDrawerState] = useState(false);
const [toggleMoreIcon, setToggleMoreIcon] = useState(false);
const [initialPageWidth, setInitialPageWidth] = useState(0);
const theme = useTheme();
const ref = useRef<HTMLButtonElement | null>(null);
/* In the future we should come up with a different solution that doesn't
rerender this component, its not much of a problem right now but later it
*may* become a problem
- Cody
*/
const scrollPos = useScrollPosition(15);
const toggleDrawer = useCallback(
(open: boolean = false) => setDrawerState(open ?? !drawerState),
[drawerState],
);
useEffect(() => {
// This is used for checking if its mobile or not
setInitialPageWidth(window.innerWidth);
}, []);
return (
<Wrapper>
<SkipNavLink
contentId="skipNav"
style={{
background: theme.background.backgroundColor,
color:
theme.type === "dark"
? theme.text.textColorExtremelyLight
: theme.text.textColorDark,
zIndex: 9999999,
}}
/>
<Drawer
anchor="left"
open={drawerState}
onClose={() => toggleDrawer(false)}
>
<DrawerLogoContainer>
<DrawerLogo
alt="dahliaOS logo"
src={
theme.type === "dark"
? "/images/logos/logo-white.png"
: "/images/logos/logo-color.png"
}
/>
</DrawerLogoContainer>
<Container>
<Link href="/#features">Features</Link>
<Link href="/#download">Download</Link>
<Link href="mailto:[email protected]">Contact</Link>
<Link href="https://github.com/orgs/dahliaos/people" target="_blank">
Developers
</Link>
<Link href="https://docs.dahliaOS.io">Documentation</Link>
<Divider />
<Category>Find us on</Category>
<Link href="/discord" target="_blank">
Discord
</Link>
<Link href="/github" target="_blank">
GitHub
</Link>
<Link href="/reddit" target="_blank">
Reddit
</Link>
<Link href="/telegram" target="_blank">
Telegram
</Link>
<Link href="/facebook" target="_blank">
Facebook
</Link>
<Link href="/instagram" target="_blank">
Instagram
</Link>
<Link href="/twitter" target="_blank">
Twitter
</Link>
<Divider />
<Category>For developers</Category>
<Link href="/github">Source Code</Link>
<Link href="/discord">Join Our Team</Link>
</Container>
</Drawer>
<StyledAppBar
rootPageHasAnimation={
initialPageWidth < 1075 ? false : rootPageHasAnimation
}
position="fixed"
scrollPos={scrollPos}
>
<StyledToolbar scrollPos={scrollPos}>
<IconButton
edge="start"
color="inherit"
aria-label="menu"
onClick={() => toggleDrawer(true)}
>
<MenuIcon
style={
theme.type === "dark"
? undefined
: { color: theme.text.textColorDark }
}
/>
</IconButton>
<AppBarLogoLinkContainer href="/">
<AppBarLogo
alt="dahliaOS logo"
src={
theme.type === "dark"
? "/images/logos/logo-white.png"
: "/images/logos/logo-color.png"
}
draggable={false}
/>
</AppBarLogoLinkContainer>
<DesktopNav>
<AppBarLink href="/#features">Features</AppBarLink>
<AppBarLink href="/#download">Download</AppBarLink>
<AppBarLink href="https://web.dahliaOS.io" target="_blank">
Demo
</AppBarLink>
<AppBarLink
href="https://github.com/orgs/dahliaos/people"
target="_blank"
>
Developers
</AppBarLink>
<AppBarLink href="https://docs.dahliaOS.io">
Documentation
</AppBarLink>
<IconButton
ref={ref}
aria-label="nav-more"
aria-haspopup="true"
onClick={() => setToggleMoreIcon(true)}
>
<MoreVert style={{ color: theme.text.textColorLight }} />
</IconButton>
<Menu
open={toggleMoreIcon}
onClose={() => setToggleMoreIcon(false)}
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
anchorEl={ref.current}
keepMounted
>
<MenuItem>
<MenuLink href="/github" target="_blank">
Source Code
</MenuLink>
</MenuItem>
<MenuItem disabled>Screenshots</MenuItem>
</Menu>
</DesktopNav>
</StyledToolbar>
</StyledAppBar>
<SkipNavContent id="skipNav" />
</Wrapper>
);
}
Example #20
Source File: CommentItemModule.tsx From bouncecode-cms with GNU General Public License v3.0 | 4 votes |
function CommentItemModule({post, comment}: ICommentItemModule) {
const [moreVert, setMoreVert] = React.useState(null);
const openMoreVert = Boolean(moreVert);
const toggleMoreVert = open => {
return event => {
if (open) {
setMoreVert(event.currentTarget);
} else {
setMoreVert(null);
}
};
};
const [commentDrawer, setCommentDrawer] = React.useState(null);
const toggleCommentDrawer = open => {
return event => {
if (
event.type === 'keydown' &&
(event.key === 'Tab' || event.key === 'Shift')
) {
return;
}
setCommentDrawer(open);
};
};
const [
emotionMutation,
{loading: emotionLoading},
] = useCommentEmotionMutation();
const [
undoEmotionMutation,
{loading: undoEmotionLoading},
] = useCommentEmotionUndoMutation();
const updateEmotion = emotion => {
return () => {
console.log(myEmotion);
console.log(emotion);
if (myEmotion === emotion.toLowerCase()) {
undoEmotionMutation({
variables: {
where: {
id: comment.id,
},
},
refetchQueries: [
{
query: CommentMyEmotionDocument,
variables: {where: {id: comment.id}},
},
],
});
} else {
emotionMutation({
variables: {
where: {
id: comment.id,
},
data: {emotion},
},
refetchQueries: [
{
query: CommentMyEmotionDocument,
variables: {where: {id: comment.id}},
},
],
});
}
};
};
const {
data: myEmotionData,
loading: myEmotionLoading,
} = useCommentMyEmotionQuery({
variables: {where: {id: comment.id}},
});
const myEmotion = myEmotionData?.commentMyEmotion?.emotion;
return (
<Grid container direction="column" spacing={1}>
<Grid item>
<Grid container spacing={2}>
<Grid item>
<Avatar></Avatar>
</Grid>
<Grid item xs>
<Grid container direction="column" spacing={1}>
<Grid item>
<Grid container spacing={2} alignItems="center">
<Grid item>
<Typography variant="body2">
{comment.user.email}
</Typography>
</Grid>
<Grid item>
<Typography variant="caption">
{formatDistance(
new Date(comment.createdDate),
new Date(),
{addSuffix: true},
)}
</Typography>
</Grid>
</Grid>
</Grid>
<Grid item>
<Typography variant="body1">{comment.text}</Typography>
</Grid>
<Grid
container
justifyContent="space-between"
alignItems="center">
<Grid item>
<Grid container alignItems="center">
<Grid item>
<Button
variant="text"
size="small"
startIcon={<ThumbUpOutlined />}
onClick={updateEmotion('LIKE')}
disabled={
myEmotionLoading ||
emotionLoading ||
undoEmotionLoading
}>
{comment.like || ''}
</Button>
</Grid>
<Grid item>
<Button
variant="text"
size="small"
startIcon={<ThumbDownOutlined />}
onClick={updateEmotion('UNLIKE')}
disabled={
myEmotionLoading ||
emotionLoading ||
undoEmotionLoading
}>
{comment.unlike || ''}
</Button>
</Grid>
{post ? (
<Grid item>
<Button
variant="text"
size="small"
onClick={toggleCommentDrawer(true)}
disabled>
답글
</Button>
<Drawer
anchor="bottom"
open={commentDrawer}
onClose={toggleCommentDrawer(false)}>
<Container maxWidth="md">
<Box pt={2} pb={2}>
<CommentCreateFormModule />
</Box>
</Container>
</Drawer>
</Grid>
) : (
undefined
)}
</Grid>
</Grid>
<Grid item>
<IconButton onClick={toggleMoreVert(true)} disabled>
<MoreVert fontSize="small" />
</IconButton>
<Menu
anchorEl={moreVert}
open={openMoreVert}
onClose={toggleMoreVert(false)}>
<MenuItem
// selected={option === 'Pyxis'}
onClick={toggleMoreVert(false)}>
<ListItemIcon>
<AssistantPhotoOutlined />
</ListItemIcon>
<Typography variant="inherit">신고</Typography>
</MenuItem>
</Menu>
</Grid>
</Grid>
{/* {post ? (
<Grid item>
<Grid container direction="column" spacing={2}>
<Grid item>
<Button
variant="text"
size="small"
startIcon={<KeyboardArrowDown />}>
답글 1개 보기
</Button>
</Grid>
<Grid item>
<CommentItemModule comment />
</Grid>
</Grid>
</Grid>
) : (
undefined
)} */}
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
);
}
Example #21
Source File: Sidebar.tsx From airmessage-web with Apache License 2.0 | 4 votes |
export default function Sidebar(props: {
conversations: Conversation[] | undefined;
selectedConversation?: number;
onConversationSelected: (id: number) => void;
onCreateSelected: () => void;
errorBanner?: ConnectionErrorCode;
needsServerUpdate?: boolean;
}) {
const displaySnackbar = useContext(SnackbarContext);
const [overflowMenu, setOverflowMenu] = useState<HTMLElement | null>(null);
const openOverflowMenu = useCallback((event: React.MouseEvent<HTMLElement>) => {
setOverflowMenu(event.currentTarget);
}, [setOverflowMenu]);
const closeOverflowMenu = useCallback(() => {
setOverflowMenu(null);
}, [setOverflowMenu]);
const [isChangelogDialog, showChangelogDialog, hideChangelogDialog] = useSidebarDialog(closeOverflowMenu);
const [isFeedbackDialog, showFeedbackDialog, hideFeedbackDialog] = useSidebarDialog(closeOverflowMenu);
const [isSignOutDialog, showSignOutDialog, hideSignOutDialog] = useSidebarDialog(closeOverflowMenu);
const [isRemoteUpdateDialog, showRemoteUpdateDialog, hideRemoteUpdateDialog] = useSidebarDialog();
const [isUpdateRequiredDialog, showUpdateRequiredDialog, hideUpdateRequiredDialog] = useSidebarDialog();
const [faceTimeLinkDialog, setFaceTimeLinkDialog] = useState<string | undefined>(undefined);
//Keep track of remote updates
const [remoteUpdate, setRemoteUpdate] = useState<ServerUpdateData | undefined>(undefined);
useEffect(() => {
const listener: RemoteUpdateListener = {onUpdate: setRemoteUpdate};
ConnectionManager.addRemoteUpdateListener(listener);
return () => ConnectionManager.removeRemoteUpdateListener(listener);
}, [setRemoteUpdate]);
const [remoteUpdateCache, setRemoteUpdateCache] = useState<ServerUpdateData>({
id: 0, notes: "", protocolRequirement: [], remoteInstallable: false, version: ""
});
useEffect(() => {
if(remoteUpdate !== undefined) {
setRemoteUpdateCache(remoteUpdate);
}
}, [remoteUpdate, setRemoteUpdateCache]);
//Keep track of whether FaceTime is supported
const isFaceTimeSupported = useIsFaceTimeSupported();
const [isFaceTimeLinkLoading, setFaceTimeLinkLoading] = useState(false);
const createFaceTimeLink = useCallback(async () => {
setFaceTimeLinkLoading(true);
try {
const link = await ConnectionManager.requestFaceTimeLink();
//Prefer web share, fall back to displaying a dialog
if(navigator.share) {
await navigator.share({text: link});
} else {
setFaceTimeLinkDialog(link);
}
} catch(error) {
if(error === FaceTimeLinkErrorCode.Network) {
displaySnackbar({message: "Failed to get FaceTime link: no connection to server"});
} else if(error === FaceTimeLinkErrorCode.External) {
displaySnackbar({message: "Failed to get FaceTime link: an external error occurred"});
}
} finally {
setFaceTimeLinkLoading(false);
}
}, [setFaceTimeLinkLoading, displaySnackbar]);
return (
<div className={styles.sidebar}>
<ChangelogDialog isOpen={isChangelogDialog} onDismiss={hideChangelogDialog} />
<FeedbackDialog isOpen={isFeedbackDialog} onDismiss={hideFeedbackDialog} />
<SignOutDialog isOpen={isSignOutDialog} onDismiss={hideSignOutDialog} />
<RemoteUpdateDialog isOpen={isRemoteUpdateDialog} onDismiss={hideRemoteUpdateDialog} update={remoteUpdateCache} />
<UpdateRequiredDialog isOpen={isUpdateRequiredDialog} onDismiss={hideUpdateRequiredDialog} />
<FaceTimeLinkDialog isOpen={faceTimeLinkDialog !== undefined} onDismiss={() => setFaceTimeLinkDialog(undefined)} link={faceTimeLinkDialog ?? ""} />
<Toolbar className={styles.sidebarToolbar}>
<AirMessageLogo />
<div style={{flexGrow: 1}} />
{isFaceTimeSupported && (
<IconButton
size="large"
onClick={createFaceTimeLink}
disabled={isFaceTimeLinkLoading}>
<VideoCallOutlined />
</IconButton>
)}
<IconButton
size="large"
onClick={props.onCreateSelected}
disabled={props.conversations === undefined}>
<AddRounded />
</IconButton>
<IconButton
aria-haspopup="true"
size="large"
onClick={openOverflowMenu}
disabled={props.conversations === undefined}>
<MoreVertRounded />
</IconButton>
<Menu
anchorEl={overflowMenu}
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
keepMounted
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
open={!!overflowMenu}
onClose={closeOverflowMenu}>
<MenuItem onClick={showChangelogDialog}>What's new</MenuItem>
<MenuItem onClick={showFeedbackDialog}>Help and feedback</MenuItem>
<MenuItem onClick={showSignOutDialog}>Sign out</MenuItem>
</Menu>
</Toolbar>
{props.errorBanner !== undefined && <ConnectionBanner error={props.errorBanner} /> }
{remoteUpdate !== undefined && (
<SidebarBanner
icon={<Update />}
message="A server update is available"
button="Details"
onClickButton={showRemoteUpdateDialog} />
)}
{props.needsServerUpdate && (
<SidebarBanner
icon={<SyncProblem />}
message="Your server needs to be updated"
button="Details"
onClickButton={showUpdateRequiredDialog} />
)}
{props.conversations !== undefined ? (
<Flipper
className={styles.sidebarList}
flipKey={props.conversations.map(conversation => conversation.localID).join(" ")}>
<List>
{props.conversations.map((conversation) =>
<Flipped key={conversation.localID} flipId={conversation.localID}>
{flippedProps => (
<ListConversation
conversation={conversation}
selected={conversation.localID === props.selectedConversation}
highlighted={conversation.unreadMessages}
onSelected={() => props.onConversationSelected(conversation.localID)}
flippedProps={flippedProps as Record<string, unknown>} />
)}
</Flipped>
)}
</List>
</Flipper>
) : (
<div className={styles.sidebarListLoading}>
{[...Array(16)].map((element, index) => <ConversationSkeleton key={`skeleton-${index}`} />)}
</div>
)}
</div>
);
}
Example #22
Source File: GridLayouts.tsx From Tachidesk-WebUI with Mozilla Public License 2.0 | 4 votes |
export default function SourceGridLayout() {
const { options, setOptions } = useLibraryOptionsContext();
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);
const handleClick = (event: any) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
function setGridContextOptions(
e: React.ChangeEvent<HTMLInputElement>,
checked: boolean,
) {
if (checked) {
setOptions((prev: any) => ({ ...prev, SourcegridLayout: parseInt(e.target.name, 10) }));
}
}
return (
<>
<IconButton
onClick={handleClick}
size="small"
sx={{ ml: 2 }}
aria-controls={open ? 'account-menu' : undefined}
aria-haspopup="true"
aria-expanded={open ? 'true' : undefined}
>
<ViewModuleIcon />
</IconButton>
<Menu
id="basic-menu"
anchorEl={anchorEl}
open={open}
onClose={handleClose}
MenuListProps={{ 'aria-labelledby': 'basic-button' }}
>
<MenuItem onClick={handleClose}>
<FormControlLabel
label="Compact grid"
control={(
<Radio
name="0"
// eslint-disable-next-line max-len
checked={options.SourcegridLayout === 0 || options.SourcegridLayout === undefined}
onChange={setGridContextOptions}
/>
)}
/>
</MenuItem>
<MenuItem onClick={handleClose}>
<FormControlLabel
label="Comfortable grid"
control={(
<Radio
name="1"
checked={options.SourcegridLayout === 1}
onChange={setGridContextOptions}
/>
)}
/>
</MenuItem>
<MenuItem onClick={handleClose}>
<FormControlLabel
label="List"
control={(
<Radio
name="2"
checked={options.SourcegridLayout === 2}
onChange={setGridContextOptions}
/>
)}
/>
</MenuItem>
</Menu>
</>
);
}
Example #23
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 #24
Source File: Mobile.tsx From GTAV-NativeDB with MIT License | 4 votes |
function Mobile({ ...rest }: AppBarProps) {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const [settingsOpen, setSettingsOpen] = useState(false)
const [searchOpen, setSearchOpen] = useState(false)
const [statsDialog, setStatsDialog] = useState(false)
const settings = useAppBarSettings()
const handleSettingsOpen = useCallback(() => {
setSettingsOpen(true)
}, [setSettingsOpen])
const handleSettingsClose = useCallback(() => {
setSettingsOpen(false)
}, [setSettingsOpen])
const handleMenuOpen = useCallback((event: MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget)
}, [setAnchorEl])
const handleMenuClose = useCallback(() => {
setAnchorEl(null)
}, [setAnchorEl])
const handleSearchOpen = useCallback(() => {
setSearchOpen(true)
}, [setSearchOpen])
const handleSearchClose = useCallback(() => {
setSearchOpen(false)
}, [setSearchOpen])
const handleStatsDialogOpen = useCallback(() => {
setStatsDialog(true)
}, [setStatsDialog])
const handleStatsDialogClose = useCallback(() => {
setStatsDialog(false)
}, [setStatsDialog])
const actions: AppBarActionProps[] = useMemo(() => [
...(settings.actions ?? []),
{
text: 'Settings',
mobileIcon: SettingsIcon,
buttonProps: {
onClick: handleSettingsOpen
}
},
{
text: 'Stats',
mobileIcon: StatsIcon,
buttonProps: {
onClick: handleStatsDialogOpen
}
},
{
text: 'Generate Code',
mobileIcon: CodeIcon,
buttonProps: {
href: '/generate-code'
}
},
{
text: 'View on Github',
mobileIcon: GithubIcon,
buttonProps: {
href: 'https://github.com/DottieDot/GTAV-NativeDB',
target: '_blank'
}
}
], [settings, handleSettingsOpen, handleStatsDialogOpen])
return (
<Box {...rest}>
<StatsDialog open={statsDialog} onClose={handleStatsDialogClose} />
<SettingsDrawer open={settingsOpen} onClose={handleSettingsClose} />
<MaterialAppBar position="sticky">
{settings.search && (
<Box sx={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}>
<Zoom in={searchOpen}>
<Box sx={{ height: '100%', position: 'relative', zIndex: 1 }}>
<MobileSearch
search={settings.search}
onClose={handleSearchClose}
/>
</Box>
</Zoom>
</Box>
)}
<Toolbar>
<Typography variant="h6" component="div">
<Link
to="/natives"
color="inherit"
underline="none"
component={RouterLink}
>
{settings?.title ?? 'GTA V Native Reference'}
</Link>
</Typography>
<Box
sx={{ display: 'flex', flex: 1 }}
/>
<StatusButton />
{settings?.search && (
<IconButton onClick={handleSearchOpen} aria-label="search">
<SearchIcon />
</IconButton>
)}
<IconButton onClick={handleMenuOpen} aria-label="more">
<MoreIcon />
</IconButton>
<Menu
anchorEl={anchorEl}
open={!!anchorEl}
onClose={handleMenuClose}
onClick={handleMenuClose}
>
{actions.map(action => (
<AppBarAction
key={action.text}
{...action}
/>
))}
</Menu>
</Toolbar>
</MaterialAppBar>
</Box>
)
}