@mui/material#ListItemText TypeScript Examples
The following examples show how to use
@mui/material#ListItemText.
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: ArtifactSlotDropdown.tsx From genshin-optimizer with MIT License | 6 votes |
export default function ArtifactSlotDropdown({ slotKey = "", onChange, hasUnselect = false, ...props }: ArtifactSlotDropdownProps) {
const { t } = useTranslation(["artifact", "ui"]);
return <DropdownButton
title={slotKey ? t(`artifact:slotName:${slotKey}`) : t('artifact:slot')}
color={slotKey ? "success" : "primary"}
startIcon={slotKey ? artifactSlotIcon(slotKey) : undefined}
{...props}
>
{hasUnselect && <MenuItem selected={slotKey === ""} disabled={slotKey === ""} onClick={() => onChange("")} >
<ListItemIcon>
<Replay />
</ListItemIcon>
<ListItemText>
{t`ui:unselect`}
</ListItemText>
</MenuItem>}
{hasUnselect && <Divider />}
{allSlotKeys.map(key =>
<MenuItem key={key} selected={slotKey === key} disabled={slotKey === key} onClick={() => onChange(key)} >
<ListItemIcon>
{artifactSlotIcon(key)}
</ListItemIcon>
<ListItemText>
{t(`artifact:slotName:${key}`)}
</ListItemText>
</MenuItem>)}
</DropdownButton>
}
Example #2
Source File: index.tsx From Search-Next with GNU General Public License v3.0 | 6 votes |
MenuListItem: React.FC<MenuListItemProps> = ({
icon,
primary,
...props
}) => {
return (
<ListItem {...props}>
<ListItemIcon className="min-w-min mr-2">{icon}</ListItemIcon>
<ListItemText primary={primary} />
</ListItem>
);
}
Example #3
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 #4
Source File: list-item-link.tsx From example with MIT License | 6 votes |
export function ListItemLink(props: IListItemLinkProps) {
const { icon, primary, to } = props
const location = useLocation();
const renderLink = React.useMemo(
() =>
React.forwardRef(function Link(itemProps, ref) {
return <RouterLink to={to} ref={ref as any} {...itemProps} role={undefined} />
}),
[to],
)
return (
<li>
<ListItem
button
component={renderLink}
selected={
location.pathname === to ||
(props.default && (location.pathname === "" || location.pathname === "/"))
}>
{icon ? <ListItemIcon>{icon}</ListItemIcon> : null}
<ListItemText primary={primary} />
</ListItem>
</li>
)
}
Example #5
Source File: FileList.tsx From frontend with MIT License | 6 votes |
function FileList({ files, onDelete }: Props) {
return (
<List dense>
{files.map((file, key) => (
<ListItem
key={key}
secondaryAction={
<IconButton edge="end" aria-label="delete" onClick={() => onDelete && onDelete(file)}>
<Delete />
</IconButton>
}>
<ListItemAvatar>
<Avatar>
<UploadFile />
</Avatar>
</ListItemAvatar>
<ListItemText primary={file.type} />
<ListItemText primary={file.name} />
</ListItem>
))}
</List>
)
}
Example #6
Source File: Vault.tsx From NekoMaid with MIT License | 6 votes |
Groups: React.FC<{ plugin: Plugin, id: string | undefined, onClose: () => void, groups: GroupInfo[] }> =
({ plugin, id, onClose, groups }) => {
const [loading, setLoading] = useState(true)
const [playerGroups, setPlayerGroups] = useState<Record<string, true>>({ })
const refresh = () => {
setLoading(true)
plugin.emit('vault:playerGroup', (res: string[]) => {
if (!res) return
const obj: Record<string, true> = { }
res.forEach(it => (obj[it] = true))
setPlayerGroups(obj)
setLoading(false)
}, id, null, 0)
}
useEffect(() => {
setPlayerGroups({})
if (!id) return
refresh()
}, [id])
return <Dialog onClose={onClose} open={!!id}>
<DialogTitle>{lang.vault.whosPermissionGroup(id!)}</DialogTitle>
<List sx={{ pt: 0 }}>
{groups.map(it => <ListItem onClick={() => { }} key={it.id}>
<ListItemIcon><Checkbox
tabIndex={-1}
disabled={loading}
checked={!!playerGroups[it.id]}
onChange={e => plugin.emit('vault:playerGroup', (res: boolean) => {
action(res)
refresh()
}, id, it.id, e.target.checked ? 1 : 2)}
/></ListItemIcon>
<ListItemText primary={it.id} />
</ListItem>)}
</List>
<DialogActions><Button onClick={onClose}>{minecraft['gui.back']}</Button></DialogActions>
</Dialog>
}
Example #7
Source File: MenuItemWithImage.tsx From genshin-optimizer with MIT License | 6 votes |
export default function MenuItemWithImage({ value, image = "", text, theme, isSelected, addlElement, props }: MenuItemWithImageProps) {
return <MenuItem key={value} value={value} {...props}>
<ListItemIcon>{image}</ListItemIcon>
<ListItemText primaryTypographyProps={{ style: { fontWeight: isSelected ? theme.typography.fontWeightMedium : theme.typography.fontWeightRegular } }}>
{text}
</ListItemText>
{addlElement && addlElement}
</MenuItem>
}
Example #8
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 #9
Source File: Profile.tsx From abrechnung with GNU Affero General Public License v3.0 | 6 votes |
export default function Profile() {
const user = useRecoilValue(userData);
const isGuest = useRecoilValue(isGuestUser);
useTitle("Abrechnung - Profile");
return (
<MobilePaper>
<Typography component="h3" variant="h5">
Profile
</Typography>
{isGuest && (
<Alert severity="info">
You are a guest user on this Abrechnung and therefore not permitted to create new groups or group
invites.
</Alert>
)}
<List>
<ListItem>
<ListItemText primary="Username" secondary={user.username} />
</ListItem>
<ListItem>
<ListItemText primary="E-Mail" secondary={user.email} />
</ListItem>
<ListItem>
<ListItemText
primary="Registered"
secondary={DateTime.fromISO(user.registered_at).toLocaleString(DateTime.DATETIME_FULL)}
/>
</ListItem>
</List>
</MobilePaper>
);
}
Example #10
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 #11
Source File: StatsDialog.tsx From GTAV-NativeDB with MIT License | 5 votes |
export default function StatsDialog({ open, onClose }: Props) {
const stats = useStats()
return (
<Dialog open={open} onClose={onClose} fullWidth maxWidth="xs">
<DialogTitle>
Stats
</DialogTitle>
<List dense>
<ListItem sx={{ px: 3 }} >
<ListItemText
primary="Namespaces"
secondary={stats.namespaces}
/>
</ListItem>
<ListItem sx={{ px: 3 }} >
<ListItemText
primary="Natives"
secondary={stats.natives}
/>
</ListItem>
<ListItem sx={{ px: 3 }} >
<ListItemText
primary="Comments"
secondary={stats.comments}
/>
</ListItem>
<ListItem sx={{ px: 3 }} >
<ListItemText
primary="Known names"
secondary={`${stats.knownNames.confirmed} (${stats.knownNames.total})`}
/>
</ListItem>
</List>
<DialogActions>
<Button onClick={onClose}>Close</Button>
</DialogActions>
</Dialog>
)
}
Example #12
Source File: IrregularityFile.tsx From frontend with MIT License | 5 votes |
export default function IrregularityFile({ file, irregularityId }: Props) {
const { t } = useTranslation('irregularity')
const queryClient = useQueryClient()
const router = useRouter()
const mutation = useMutation<AxiosResponse<IrregularityFileResponse>, AxiosError<ApiErrors>>({
mutationFn: deleteIrregularityFile(file.id),
onError: () => AlertStore.show(t('admin.alerts.error'), 'error'),
onSuccess: () => {
AlertStore.show(t('admin.alerts.delete-file'), 'success')
queryClient.invalidateQueries(endpoints.irregularity.viewIrregularity(irregularityId).url)
router.push(routes.admin.irregularity.index)
},
})
const deleteFileHandler = () => {
mutation.mutate()
}
return (
<ListItem key={file.id}>
<ListItemAvatar>
<Avatar>
<FilePresentIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary={file.filename} />
<></>
<Tooltip
title={
'Note: This link is public on the API side! Need to correct before official release!'
}>
<Button>
{/* TODO: to be discussed. Tracked in issue: https://github.com/podkrepi-bg/frontend/issues/811 */}
<a style={{ color: 'red' }} href={API_URL + `/irregularity-file/${file.id}`}>
{t('admin.cta.download') + '*'}
</a>
</Button>
</Tooltip>
<Button onClick={deleteFileHandler}>{t('admin.cta.delete')}</Button>
</ListItem>
)
}
Example #13
Source File: Dashboard.tsx From NekoMaid with MIT License | 5 votes |
Players: React.FC<{ players?: CurrentStatus['players'] }> = React.memo(({ players }) => {
const his = useHistory()
const plugin = usePlugin()
const globalData = useGlobalData()
const [page, setPage] = useState(1)
const [id, update] = useState(0)
return <Card>
<CardHeader title={lang.dashboard.onlinePlayers} />
<Divider />
<CardContent>
{players?.length === 0
? <Empty />
: <>
<List sx={{ paddingTop: 0 }}>
{players
? players.slice((page - 1) * 8, page * 8).map(p => {
const name = typeof p === 'string' ? p : p.name
return <Tooltip key={name} title={'IP: ' + ((p as any).ip || lang.unknown)}>
<ListItem
secondaryAction={<>
<IconButton
edge='end'
size='small'
onClick={() => dialog(lang.dashboard.confirmKick(<span className='bold'>{name}</span>), lang.reason)
.then(it => it != null && plugin.emit('dashboard:kick', (res: boolean) => {
action(res)
if (!players) return
players.splice(players.indexOf(it!), 1)
update(id + 1)
}, name, it || null))
}
><ExitToApp /></IconButton>
<IconButton edge='end' onClick={() => his.push('/NekoMaid/playerList/' + name)} size='small'><MoreHoriz /></IconButton>
</>
}
>
<ListItemAvatar>
<Avatar
src={getSkin(globalData, name, true)}
imgProps={{ crossOrigin: 'anonymous', onClick () { his.push('/NekoMaid/playerList/' + name) }, style: { width: 40, height: 40 } }}
sx={{ cursor: 'pointer' }}
variant='rounded'
/>
</ListItemAvatar>
<ListItemText primary={name} />
</ListItem>
</Tooltip>
})
: <LoadingList />
}
</List>
{players && <Pagination
page={page}
onChange={(_, it) => setPage(it)}
count={Math.max(Math.ceil(players.length / 8), 1)}
sx={{ display: 'flex', justifyContent: 'flex-end' }}
/>}
</>}
</CardContent>
</Card>
})
Example #14
Source File: TLSCertificate.tsx From console with GNU Affero General Public License v3.0 | 5 votes |
TLSCertificate = ({
classes,
certificateInfo,
onDelete = () => {},
}: ITLSCertificate) => {
const certificates = certificateInfo.domains || [];
return (
<Chip
key={certificateInfo.name}
variant="outlined"
color="primary"
className={classes.certificateWrapper}
label={
<Container>
<Grid item xs={1} className={classes.certificateIcon}>
<CertificateIcon />
</Grid>
<Grid item xs={11} className={classes.certificateInfo}>
<Typography variant="subtitle1" display="block" gutterBottom>
{certificateInfo.name}
</Typography>
<Box className={classes.certificateExpiry}>
<EventBusyIcon color="inherit" fontSize="small" />
<span className={"label"}>Expiry: </span>
<span>
<Moment format="YYYY/MM/DD">{certificateInfo.expiry}</Moment>
</span>
</Box>
<Divider />
<br />
<Box className={classes.certificateDomains}>
<span className="label">{`${certificates.length} Domain (s):`}</span>
</Box>
<List className={classes.certificatesList}>
{certificates.map((dom) => (
<ListItem className={classes.certificatesListItem}>
<ListItemAvatar>
<LanguageIcon />
</ListItemAvatar>
<ListItemText primary={dom} />
</ListItem>
))}
</List>
</Grid>
</Container>
}
onDelete={onDelete}
/>
);
}
Example #15
Source File: Header.tsx From genshin-optimizer with MIT License | 5 votes |
function MobileHeader({ anchor, currentTab }) {
const [mobileOpen, setMobileOpen] = useState(false);
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen);
};
const { t } = useTranslation("ui")
return <>
<AppBar position="fixed" sx={{ bgcolor: "#343a40" }} elevation={0} >
<Drawer
anchor="right"
variant="temporary"
open={mobileOpen}
onClose={handleDrawerToggle}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
>
<List>
<ListItemButton key="home" component={RouterLink} to={'/'} selected={currentTab === ""} disabled={currentTab === ""} onClick={handleDrawerToggle} >
<ListItemText>{t("pageTitle")}</ListItemText>
</ListItemButton >
{content.map(({ i18Key, value, to, svg }) =>
<ListItemButton key={value} component={RouterLink} to={to} selected={currentTab === value} disabled={currentTab === value} onClick={handleDrawerToggle} >
<ListItemIcon><FontAwesomeIcon icon={svg} /></ListItemIcon>
<ListItemText>{t(i18Key)}</ListItemText>
</ListItemButton >)}
</List>
<Divider />
<List>
{links.map(({ i18Key, href, svg, label }) =>
<ListItemButton key={label} component="a" href={href} target="_blank" onClick={e => ReactGA.outboundLink({ label }, () => { })} >
<ListItemIcon><FontAwesomeIcon icon={svg} /></ListItemIcon>
<ListItemText>{t(i18Key)}</ListItemText>
</ListItemButton >)}
</List>
</Drawer>
<Toolbar>
<Button variant="text" sx={{ color: "white" }} component={RouterLink} to="/">
<Typography variant="h6" noWrap component="div">
<Trans t={t} i18nKey="pageTitle">Genshin Optimizer</Trans>
</Typography>
</Button>
<Box flexGrow={1} />
<IconButton
color="inherit"
aria-label="open drawer"
edge="end"
onClick={handleDrawerToggle}
>
<MenuIcon />
</IconButton>
</Toolbar>
</AppBar>
{/* add a blank toolbar to keep space and provide a scroll anchor */}
<Toolbar id={anchor} />
</>
}
Example #16
Source File: Facet.tsx From cli with Apache License 2.0 | 5 votes |
FacetRenderer: FunctionComponent<FacetRendererProps> = (props) => {
const {controller} = props;
const [state, setState] = useState(controller.state);
useEffect(
() => controller.subscribe(() => setState(controller.state)),
[controller]
);
const toggleSelect = (value: FacetValue) => {
controller.toggleSelect(value);
};
const showMore = () => {
controller.showMoreValues();
};
const showLess = () => {
controller.showLessValues();
};
return (
<Box mb={5} mr={3} p={1}>
<Box pb={1}>
<Typography variant="h6" component="h6">
{props.title}
</Typography>
</Box>
<Divider />
<List dense>
{state.values.map((value: FacetValue) => {
const labelId = `checkbox-list-label-${value}`;
return (
<ListItem
style={{padding: 0}}
key={value.value}
role={undefined}
button
onClick={() => toggleSelect(value)}
>
<Checkbox
size="small"
edge="start"
checked={controller.isValueSelected(value)}
tabIndex={-1}
disableRipple
inputProps={{'aria-labelledby': labelId}}
/>
<ListItemText
className="truncate inline"
primary={`${value.value}`}
secondary={`(${value.numberOfResults})`}
/>
</ListItem>
);
})}
</List>
{state.canShowLessValues && (
<Button size="small" onClick={() => showLess()}>
Show Less
</Button>
)}
{state.canShowMoreValues && (
<Button size="small" onClick={() => showMore()}>
Show More
</Button>
)}
</Box>
);
}
Example #17
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 #18
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 #19
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 #20
Source File: AccountTransactionListEntry.tsx From abrechnung with GNU Affero General Public License v3.0 | 5 votes |
export default function AccountTransactionListEntry({ group, transaction, accountID }) {
return (
<ListItemLink to={`/groups/${group.id}/transactions/${transaction.id}`}>
<ListItemAvatar sx={{ minWidth: { xs: "40px", md: "56px" } }}>
{transaction.type === "purchase" ? (
<Tooltip title="Purchase">
<PurchaseIcon color="primary" />
</Tooltip>
) : transaction.type === "transfer" ? (
<Tooltip title="Money Transfer">
<TransferIcon color="primary" />
</Tooltip>
) : (
<Tooltip title="Unknown Transaction Type">
<HelpOutline color="primary" />
</Tooltip>
)}
</ListItemAvatar>
<ListItemText
primary={
<>
{transaction.is_wip && (
<Chip color="info" variant="outlined" label="WIP" size="small" sx={{ mr: 3 }} />
)}
<Typography variant="body1" component="span">
{transaction.description}
</Typography>
</>
}
secondary={DateTime.fromISO(transaction.billed_at).toLocaleString(DateTime.DATE_FULL)}
/>
<ListItemText>
<Typography align="right" variant="body2">
<Typography
component="span"
sx={{ color: (theme) => balanceColor(transaction.account_balances[accountID].total, theme) }}
>
{transaction.account_balances[accountID].total.toFixed(2)} {group.currency_symbol}
</Typography>
<br />
<Typography component="span" sx={{ typography: "body2", color: "text.secondary" }}>
last changed:{" "}
{DateTime.fromISO(transaction.last_changed).toLocaleString(DateTime.DATETIME_FULL)}
</Typography>
</Typography>
</ListItemText>
</ListItemLink>
);
}
Example #21
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 #22
Source File: TableSelect.tsx From firecms with MIT License | 4 votes |
export function TableSelect(props: {
name: string;
enumValues: EnumValues;
error: Error | undefined;
multiple: boolean;
disabled: boolean;
small: boolean;
internalValue: string | number | string[] | number[] | undefined;
valueType: "string" | "number";
updateValue: (newValue: (string | number | string[] | number[] | null)) => void;
focused: boolean;
onBlur?: React.FocusEventHandler<HTMLInputElement | HTMLTextAreaElement>;
setPreventOutsideClick: (value: any) => void;
}) {
const {
name,
enumValues,
error,
internalValue,
disabled,
small,
focused,
updateValue,
multiple,
setPreventOutsideClick,
valueType
} = props;
const [open, setOpen] = useState<boolean>(false);
const handleOpen = () => {
setPreventOutsideClick(true);
setOpen(true);
};
const handleClose = () => {
setPreventOutsideClick(false);
setOpen(false);
};
const classes = useInputStyles();
const validValue = (Array.isArray(internalValue) && multiple) ||
(!Array.isArray(internalValue) && !multiple);
const ref = React.createRef<HTMLInputElement>();
useEffect(() => {
if (ref.current && focused) {
ref.current?.focus({ preventScroll: true });
}
}, [focused, ref]);
return (
<Select
variant={"standard"}
key={`table_select_${name}`}
inputRef={ref}
className={classes.select}
classes={{ select: classes.selectRoot }}
open={open}
disabled={disabled}
multiple={multiple}
onClose={handleClose}
onOpen={handleOpen}
fullWidth
inputProps={{
style: {
height: "100%"
}
}}
disableUnderline
error={!!error}
value={validValue
? (multiple ? (internalValue as any[]).map(v => v.toString()) : internalValue)
: (multiple ? [] : "")}
onChange={(evt) => {
if (valueType === "number") {
if (multiple) {
const newValue = (evt.target.value as string[]).map((v) => parseFloat(v));
updateValue(newValue);
} else {
updateValue(parseFloat(evt.target.value as string));
}
} else if (valueType === "string") {
if (!evt.target.value)
updateValue(null)
else
updateValue(evt.target.value);
} else {
throw Error("Missing mapping in TableSelect");
}
}}
renderValue={(enumKey: any) => {
if (multiple && Array.isArray(enumKey)) {
return <ArrayEnumPreview value={enumKey}
name={name}
enumValues={enumValues}
size={small ? "small" : "regular"}/>;
} else {
return <EnumValuesChip
enumKey={enumKey}
enumValues={enumValues}
small={small}/>;
}
}
}>
{enumToObjectEntries(enumValues).map(([key, labelOrConfig]) => {
const chip = <EnumValuesChip
enumKey={key}
enumValues={enumValues}
small={true}/>;
if (multiple) {
return (
<MenuItem key={`select-${name}-${key}`}
value={key}
disabled={isEnumValueDisabled(labelOrConfig)}
dense={true}>
<Checkbox
checked={Array.isArray(internalValue) && (internalValue as any[]).map(v => v.toString()).includes(key.toString())}/>
<ListItemText primary={chip}/>
</MenuItem>
);
} else {
return (
<MenuItem key={`select-${name}-${key}`} value={key}
disabled={isEnumValueDisabled(labelOrConfig)}
dense={true}>
{chip}
</MenuItem>
);
}
})}
</Select>
);
}
Example #23
Source File: index.tsx From ExpressLRS-Configurator with GNU General Public License v3.0 | 4 votes |
Sidebar: FunctionComponent = () => {
const location = useLocation();
const configuratorActive =
matchPath(location.pathname, '/configurator') !== null;
const backpackActive = matchPath(location.pathname, '/backpack') !== null;
// const settingsActive = matchPath(location.pathname, '/settings') !== null;
const logsActive = matchPath(location.pathname, '/logs') !== null;
const serialMonitorActive =
matchPath(location.pathname, '/serial-monitor') !== null;
const supportActive = matchPath(location.pathname, '/support') !== null;
const { appStatus } = useAppState();
const navigationEnabled = appStatus !== AppStatus.Busy;
return (
<Drawer sx={styles.drawer} variant="permanent">
<Toolbar />
<Divider />
<Box sx={styles.drawerContainer}>
<List>
<ListItem
component={Link}
to="/configurator"
selected={configuratorActive}
sx={styles.menuItem}
button
disabled={!navigationEnabled}
>
<ListItemIcon>
<BuildIcon />
</ListItemIcon>
<ListItemText primary="Configurator" />
</ListItem>
<ListItem
component={Link}
to="/backpack"
selected={backpackActive}
sx={styles.menuItem}
button
disabled={!navigationEnabled}
>
<ListItemIcon>
<BackpackIcon />
</ListItemIcon>
<ListItemText primary="Backpack" />
</ListItem>
{/* <ListItem */}
{/* component={Link} */}
{/* to="/settings" */}
{/* selected={settingsActive} */}
{/* sx={styles.menuItem} */}
{/* button */}
{/* > */}
{/* <ListItemIcon> */}
{/* <SettingsIcon /> */}
{/* </ListItemIcon> */}
{/* <ListItemText primary="Settings" /> */}
{/* </ListItem> */}
<ListItem
component={Link}
to="/logs"
selected={logsActive}
sx={styles.menuItem}
button
disabled={!navigationEnabled}
>
<ListItemIcon>
<ListIcon />
</ListItemIcon>
<ListItemText primary="Logs" />
</ListItem>
<ListItem
component={Link}
to="/serial-monitor"
selected={serialMonitorActive}
sx={styles.menuItem}
button
disabled={!navigationEnabled}
>
<ListItemIcon>
<DvrIcon />
</ListItemIcon>
<ListItemText primary="Serial Monitor" />
</ListItem>
<ListItem
component={Link}
to="/support"
selected={supportActive}
sx={styles.menuItem}
button
disabled={!navigationEnabled}
>
<ListItemIcon>
<HelpIcon />
</ListItemIcon>
<ListItemText primary="Support" />
</ListItem>
</List>
</Box>
</Drawer>
);
}
Example #24
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>
}
Example #25
Source File: NativeInfo.tsx From GTAV-NativeDB with MIT License | 4 votes |
export default function NativeInfo() {
const { native: nativeHash } = useParams<{ native?: string }>()
const [usageNotFound, setUsageNotFound] = useState(false)
const settings = useSettings()
const native = useNative(nativeHash ?? '')
const copyToClipboard = useCopyToClipboard()
const onShare = useCallback(() => {
copyToClipboard(createShareUrl(`/natives/${nativeHash}`))
}, [copyToClipboard, nativeHash])
const onUsageNotFound = useCallback(() => {
setUsageNotFound(true)
}, [setUsageNotFound])
useEffect(() => {
setUsageNotFound(false)
}, [nativeHash])
if (!nativeHash) {
return (
<Box sx={{ p: 2 }}>
<NoNativeSelected />
</Box>
)
}
if (!native) {
return (
<Box sx={{ p: 2 }}>
<NativeNotFound nativeHash={nativeHash} />
</Box>
)
}
return (
<Box sx={{ p: 2 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, pb: 1 }}>
<Tooltip title="Copy Link">
<IconButton onClick={onShare} size="small" aria-label="copy link">
<ShareIcon />
</IconButton>
</Tooltip>
<Typography
sx={{
textOverflow: 'ellipsis',
overflow: 'hidden'
}}
variant="h5"
component="h1"
>
{native.name}
</Typography>
</Box>
<Stack spacing={2}>
<Paper sx={{ p: 2 }}>
<NativeDetails
hash={native.hash}
jhash={native.jhash}
build={native.build}
variant="body2"
/>
<NativeDefinition
name={native.name}
params={native.params}
returnType={native.returnType}
variant="body2"
/>
</Paper>
{native.comment && (
<div>
<Typography variant="subtitle1" gutterBottom>
Comment
</Typography>
<Paper sx={{ p: 2 }}>
<NativeComment variant="body2">
{native.comment}
</NativeComment>
</Paper>
</div>
)}
{native.examples && !_.isEmpty(native.examples) && (
<div>
<Typography variant="subtitle1" gutterBottom>
Examples
</Typography>
<Paper>
<CodeExamples
examples={native.examples}
/>
</Paper>
</div>
)}
{native.oldNames && (
<div>
<Typography variant="subtitle1" gutterBottom>
Old name{native.oldNames?.length !== 1 ? 's' : ''}
</Typography>
<Paper>
<List>
{native.oldNames.map(oldName => (
<ListItem key={oldName} dense>
<ListItemText primary={oldName} />
</ListItem>
))}
</List>
</Paper>
</div>
)}
{(!usageNotFound && _.includes(settings.sources, NativeSources.DottieDot)) && (
<div>
<Typography variant="subtitle1" gutterBottom>
Script usage
</Typography>
<Paper>
<NativeUsage
onNotFound={onUsageNotFound}
nativeHash={nativeHash}
/>
</Paper>
</div>
)}
</Stack>
</Box>
)
}
Example #26
Source File: GroupList.tsx From abrechnung with GNU Affero General Public License v3.0 | 4 votes |
export default function GroupList() {
const [showGroupCreationModal, setShowGroupCreationModal] = useState(false);
const [showGroupDeletionModal, setShowGroupDeletionModal] = useState(false);
const [groupToDelete, setGroupToDelete] = useState(null);
const groups = useRecoilValue(groupList);
const isGuest = useRecoilValue(isGuestUser);
const openGroupDeletionModal = (groupID) => {
setGroupToDelete(groups.find((group) => group.id === groupID));
setShowGroupDeletionModal(true);
};
const closeGroupDeletionModal = () => {
setShowGroupDeletionModal(false);
setGroupToDelete(null);
};
const openGroupCreateModal = () => {
setShowGroupCreationModal(true);
};
const closeGroupCreateModal = (evt, reason) => {
if (reason !== "backdropClick") {
setShowGroupCreationModal(false);
}
};
return (
<MobilePaper>
<Typography component="h3" variant="h5">
Groups
</Typography>
{isGuest && (
<Alert severity="info">
You are a guest user on this Abrechnung and therefore not permitted to create new groups.
</Alert>
)}
<List>
{groups.length === 0 ? (
<ListItem key={0}>
<span>No Groups</span>
</ListItem>
) : (
groups.map((group) => {
return (
<ListItem sx={{ padding: 0 }} key={group.id}>
<ListItemLink to={`/groups/${group.id}`}>
<ListItemText primary={group.name} secondary={group.description} />
</ListItemLink>
<ListItemSecondaryAction>
<IconButton
edge="end"
aria-label="delete-group"
onClick={() => openGroupDeletionModal(group.id)}
>
<Delete />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
);
})
)}
</List>
{!isGuest && (
<>
<Grid container justifyContent="center">
<IconButton color="primary" onClick={openGroupCreateModal}>
<Add />
</IconButton>
</Grid>
<GroupCreateModal show={showGroupCreationModal} onClose={closeGroupCreateModal} />
</>
)}
<GroupDeleteModal
show={showGroupDeletionModal}
onClose={closeGroupDeletionModal}
groupToDelete={groupToDelete}
/>
</MobilePaper>
);
}
Example #27
Source File: InlineDonation.tsx From frontend with MIT License | 4 votes |
export default function InlineDonation({ campaign }: Props) {
const { t } = useTranslation()
const router = useRouter()
const [showDonationPriceList, setDonationPriceList] = useState(false)
const onClick = () => setDonationPriceList(true)
const target = campaign.targetAmount
const summary = campaign.summary.find(() => true)
const reached = summary?.reachedAmount ?? 0
const donors = summary?.donors ?? 0
const { data: prices } = useSinglePriceList()
const {
data: donations,
error: donationHistoryError,
isLoading: isDonationHistoryLoading,
} = useCampaignDonationHistory(campaign.id)
const sortedPrices = useMemo(() => {
if (!prices) return []
return prices?.sort((a, b) => {
if (a.unit_amount === null || b.unit_amount === null) return 0
return a.unit_amount - b.unit_amount
})
}, [prices])
return (
<StyledGrid item xs={12} mt={5} p={3} className={classes.inlineDonationWrapper}>
<Grid mb={2}>
<Typography component="span" className={classes.reachedMoney}>
{money(reached)}
</Typography>{' '}
{t('campaigns:campaign.from')}{' '}
<Typography component="span" className={classes.targetMoney}>
{money(target)}
</Typography>
</Grid>
<CampaignProgress raised={reached} target={target} />
<Grid display="inline-block" m={3} ml={0}>
<Typography className={classes.donorsSharesCount}>{donors}</Typography>
<Typography>{t('campaigns:campaign.donors')}</Typography>
</Grid>
<Grid display="inline-block" m={3} ml={0}>
<Typography className={classes.donorsSharesCount}>{0}</Typography>
<Typography>{t('campaigns:campaign.shares')}</Typography>
</Grid>
<Grid container gap={2}>
<LinkButton
fullWidth
href="#"
variant="outlined"
startIcon={<ShareIcon />}
color="secondary">
{t('campaigns:cta.share')}
</LinkButton>
<LinkButton
fullWidth
href="#"
onClick={onClick}
variant="contained"
color="secondary"
startIcon={<Favorite color="action" />}>
{t('common:support')}
</LinkButton>
{showDonationPriceList && (
<List className={classes.donationPriceList}>
{sortedPrices.map((price, index) => {
if (!price) return null
return (
<ListItem button key={index}>
<ListItemText
onClick={() =>
router.push({
pathname: routes.campaigns.oneTimeDonation(campaign.slug),
query: {
price: price.id,
},
})
}
primary={`${(price.unit_amount ?? 100) / 100} лв.`}
secondary={price.metadata.title}
/>
</ListItem>
)
})}
</List>
)}
</Grid>
{donationHistoryError ? (
'Error fetching donation history'
) : isDonationHistoryLoading ? (
<CircularProgress sx={{ display: 'block', margin: `${theme.spacing(3)} auto` }} />
) : (
<DonorsAndDonations donations={donations} />
)}
{/* <pre>{JSON.stringify(prices, null, 2)}</pre> */}
</StyledGrid>
)
}
Example #28
Source File: JumpToNamespace.tsx From GTAV-NativeDB with MIT License | 4 votes |
function JumpToNamespace({ namespaces, onNamespaceClicked }: Props) {
const namespaceArray = useMemo(() => Object.values(namespaces), [namespaces])
const [filter, setFilter] = useState('')
const filteredNamespaces = useMemo(
() => namespaceArray
.filter(ns => ns.name.toLowerCase()
.indexOf(filter.toLowerCase()) !== -1),
[filter, namespaceArray]
)
const [dialogOpen, setDialogOpen] = useState(false)
const handleFilterChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
setFilter(e.target.value)
}, [setFilter])
const handleDialogOpen = useCallback(() => {
setDialogOpen(true)
}, [setDialogOpen])
const handleDialogClose = useCallback(() => {
setDialogOpen(false)
setFilter('')
}, [setDialogOpen])
const handleNamespaceSelected = useCallback((namespace: string) => {
onNamespaceClicked(namespace)
handleDialogClose()
}, [handleDialogClose, onNamespaceClicked])
useSetAppBarSettings('JumpToNamespace', {
actions: [
{
text: 'Jump to namespace',
mobileIcon: JumpToNamespaceIcon,
buttonProps: {
onClick: handleDialogOpen
}
}
]
})
const onKeyDown = useCallback((e: KeyboardEvent<HTMLDivElement>) => {
if (e.key === 'Enter' && filter && filteredNamespaces) {
handleNamespaceSelected(filteredNamespaces[0].name)
e.preventDefault()
}
}, [handleNamespaceSelected, filter, filteredNamespaces])
useHotkeys('ctrl+g', () => {
handleDialogOpen()
}, {
filter: (event: globalThis.KeyboardEvent) => {
event.preventDefault()
return true
},
}, [handleDialogOpen])
return (
<Fragment>
<Dialog open={dialogOpen} onClose={handleDialogClose} maxWidth="xs" fullWidth>
<TextField
label="Filter"
variant="filled"
value={filter}
onChange={handleFilterChange}
onKeyDown={onKeyDown}
fullWidth
autoFocus
/>
<List sx={{ height: 200, overflowY: 'scroll' }}>
{filteredNamespaces.map(({ name, natives }, index) => (
<ListItem
key={name}
onClick={() => handleNamespaceSelected(name)}
selected={!!filter && index === 0}
button
>
<ListItemText
primary={name}
secondary={`${natives.length} ${natives.length === 1 ? 'native' : 'natives'}`}
/>
</ListItem>
))}
</List>
</Dialog>
</Fragment>
)
}
Example #29
Source File: GroupInvites.tsx From abrechnung with GNU Affero General Public License v3.0 | 4 votes |
export default function GroupInvites({ group }) {
const [showModal, setShowModal] = useState(false);
const invites = useRecoilValue(groupInvites(group.id));
const members = useRecoilValue(groupMembers(group.id));
const userPermissions = useRecoilValue(currUserPermissions(group.id));
const isGuest = useRecoilValue(isGuestUser);
useTitle(`${group.name} - Invite Links`);
const deleteToken = (id) => {
deleteGroupInvite({ groupID: group.id, inviteID: id }).catch((err) => {
toast.error(err);
});
};
const getMemberUsername = (member_id) => {
const member = members.find((member) => member.user_id === member_id);
if (member === undefined) {
return "unknown";
}
return member.username;
};
const selectLink = (event) => {
const node = event.target;
const selection = window.getSelection();
const range = document.createRange();
range.selectNodeContents(node);
selection.removeAllRanges();
selection.addRange(range);
};
const copyToClipboard = (content) => {
navigator.clipboard.writeText(content);
toast.info("Link copied to clipboard!");
};
return (
<MobilePaper>
<Typography component="h3" variant="h5">
Active Invite Links
</Typography>
{isGuest && (
<Alert severity="info">
You are a guest user on this Abrechnung and therefore not permitted to create group invites.
</Alert>
)}
<List>
{invites.length === 0 ? (
<ListItem>
<ListItemText primary="No Links" />
</ListItem>
) : (
invites.map((invite) => (
<ListItem key={invite.id}>
<ListItemText
primary={
invite.token === null ? (
<span>token hidden, was created by another member</span>
) : (
<span onClick={selectLink}>
{window.location.origin}/invite/
{invite.token}
</span>
)
}
secondary={
<>
{invite.description}, created by {getMemberUsername(invite.created_by)}, valid
until{" "}
{DateTime.fromISO(invite.valid_until).toLocaleString(DateTime.DATETIME_FULL)}
{invite.single_use && ", single use"}
{invite.join_as_editor && ", join as editor"}
</>
}
/>
{userPermissions.can_write && (
<ListItemSecondaryAction>
<IconButton
color="primary"
onClick={() =>
copyToClipboard(`${window.location.origin}/invite/${invite.token}`)
}
>
<ContentCopy />
</IconButton>
<IconButton color="error" onClick={() => deleteToken(invite.id)}>
<Delete />
</IconButton>
</ListItemSecondaryAction>
)}
</ListItem>
))
)}
</List>
{userPermissions.can_write && !isGuest && (
<>
<Grid container justifyContent="center">
<IconButton color="primary" onClick={() => setShowModal(true)}>
<Add />
</IconButton>
</Grid>
<InviteLinkCreate show={showModal} onClose={() => setShowModal(false)} group={group} />
</>
)}
</MobilePaper>
);
}