@mui/material#Skeleton TypeScript Examples
The following examples show how to use
@mui/material#Skeleton.
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: Autocomplete.tsx From Cromwell with MIT License | 6 votes |
constructor(props: any) {
super(props);
this.state = {
searchOpen: false,
isLoading: false,
}
for (let i = 0; i < 5; i++) {
this.listSkeleton.push(<Skeleton key={i} variant="text" height="20px" style={{ margin: '5px 20px' }} />)
}
}
Example #2
Source File: CharacterSelectionModal.tsx From genshin-optimizer with MIT License | 6 votes |
function CharacterBtn({ onClick, characterKey, characterSheet }: { onClick: () => void, characterKey: CharacterKey, characterSheet: CharacterSheet }) {
const teamData = useTeamData(characterKey)
const { database } = useContext(DatabaseContext)
const characterDispatch = useCharacterReducer(characterKey)
const favorite = database._getChar(characterKey)?.favorite
const { target: data } = teamData?.[characterKey] ?? {}
const rarity = characterSheet.rarity
return <Suspense fallback={<Skeleton variant="rectangular" height={130} />}><Box>
{favorite !== undefined && <Box display="flex" position="absolute" alignSelf="start" zIndex={1}>
<IconButton sx={{ p: 0.5 }} onClick={() => characterDispatch({ favorite: !favorite })}>
{favorite ? <Favorite /> : <FavoriteBorder />}
</IconButton>
</Box>}
<CardActionArea onClick={onClick} >
<CardLight sx={{ display: "flex", alignItems: "center" }} >
<Box component="img" src={characterSheet.thumbImg} sx={{ width: 130, height: "auto" }} className={`grad-${rarity}star`} />
<Box sx={{ pl: 1 }}>
<Typography><strong>{characterSheet.name}</strong></Typography>
{data ? <>
<Typography variant="h6"> {characterSheet.elementKey && StatIcon[characterSheet.elementKey]} <ImgIcon src={Assets.weaponTypes?.[characterSheet.weaponTypeKey]} />{` `}{CharacterSheet.getLevelString(data.get(input.lvl).value, data.get(input.asc).value)}</Typography>
<Typography >
<SqBadge color="success">{`C${data.get(input.constellation).value}`}</SqBadge>{` `}
<SqBadge color={data.get(input.bonus.auto).value ? "info" : "secondary"}><strong >{data.get(input.total.auto).value}</strong></SqBadge>{` `}
<SqBadge color={data.get(input.bonus.skill).value ? "info" : "secondary"}><strong >{data.get(input.total.skill).value}</strong></SqBadge>{` `}
<SqBadge color={data.get(input.bonus.burst).value ? "info" : "secondary"}><strong >{data.get(input.total.burst).value}</strong></SqBadge>
</Typography>
</> : <>
<Typography variant="h6"><SqBadge color="primary">NEW</SqBadge></Typography>
</>}
<small><Stars stars={rarity} colored /></small>
</Box>
</CardLight>
</CardActionArea >
</Box></Suspense>
}
Example #3
Source File: ThemeMarket.tsx From Cromwell with MIT License | 6 votes |
preloader = (<div className={styles.listContainer}> {Array(4).fill(1).map((it, index) => { return ( <Grid key={index} item xs={6} lg={4} className={styles.listItem}> <Skeleton variant="rectangular" height="388px" width="100%" style={{ margin: '0 10px 20px 10px' }} > </Skeleton> </Grid> ) })} </div>)
Example #4
Source File: CharacterCardPico.tsx From genshin-optimizer with MIT License | 6 votes |
export default function CharacterCardPico({ characterKey = "", index = -1 }: { characterKey: CharacterKey | "", index?: number }) {
const teammateSheet = usePromise(CharacterSheet.get(characterKey), [characterKey])
const character = useCharacter(characterKey)
if (teammateSheet && character) {
const title = <Suspense fallback={<Skeleton variant="text" width={100} />}>
<Typography>{teammateSheet.elementKey && StatIcon[teammateSheet.elementKey]} {teammateSheet.name}</Typography>
</Suspense>
return <CardDark sx={{ maxWidth: 128, position: "relative", display: "flex", flexDirection: "column", }}>
<BootstrapTooltip placement="top" title={title}>
<Box display="flex" className={`grad-${teammateSheet.rarity}star`}>
<Box
component="img"
src={teammateSheet.thumbImgSide}
maxWidth="100%"
maxHeight="100%"
sx={{ transform: "scale(1.4)", transformOrigin: "bottom" }}
/>
</Box>
</BootstrapTooltip>
<Typography variant='subtitle1' sx={{ position: "absolute", mt: -0.2, lineHeight: 1, pointerEvents: "none" }}>
<SqBadge color="primary" >{character.level}/{ascensionMaxLevel[character.ascension]}</SqBadge>
</Typography>
<Typography variant='subtitle1' sx={{ position: "absolute", bottom: 0, right: 0, lineHeight: 1, pointerEvents: "none" }}>
<SqBadge color="secondary" >C{character.constellation}</SqBadge>
</Typography>
</CardDark>
} else {
return <CardDark sx={{ display: "flex", alignItems: "center", justifyContent: "center", py: "12.5%" }}>
<Box component="img" src={Assets.team[`team${index + 2}`]} sx={{ width: "75%", height: "auto", opacity: 0.7 }} />
</CardDark>
}
}
Example #5
Source File: PagePanel.tsx From mui-toolpad with MIT License | 6 votes |
export default function PagePanel({ appId, className, sx }: ComponentPanelProps) {
const { data: app, isLoading } = client.useQuery('getApp', [appId]);
return (
<PagePanelRoot className={className} sx={sx}>
<Box sx={{ px: 2, py: 1 }}>
{isLoading ? <Skeleton variant="text" /> : <Typography>{app?.name}</Typography>}
</Box>
<Divider />
<HierarchyExplorer appId={appId} />
</PagePanelRoot>
);
}
Example #6
Source File: PluginMarket.tsx From Cromwell with MIT License | 6 votes |
preloader = (<div className={styles.listContainer}> {Array(4).fill(1).map((it, index) => { return ( <Grid key={index} item xs={6} lg={4} className={styles.listItem}> <Skeleton variant="rectangular" height="388px" width="100%" style={{ margin: '0 10px 20px 10px' }} > </Skeleton> </Grid> ) })} </div>)
Example #7
Source File: Image.tsx From mui-toolpad with MIT License | 6 votes |
function Image({ sx: sxProp, src, width, height, alt, loading: loadingProp, fit }: ImageProps) {
const sx: SxProps = React.useMemo(
() => ({
...sxProp,
width,
height,
position: 'relative',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}),
[sxProp, width, height],
);
const [imgLoading, setImgLoading] = React.useState(true);
const handleLoad = React.useCallback(() => setImgLoading(false), []);
const loading = loadingProp || imgLoading;
return (
<Box sx={sx}>
{loading ? <Skeleton variant="rectangular" width={width} height={height} /> : null}
<Box
component="img"
src={src}
alt={alt}
sx={{
width: '100%',
height: '100%',
objectFit: fit,
position: 'absolute',
visibility: loading ? 'hidden' : 'visible',
}}
onLoad={handleLoad}
/>
</Box>
);
}
Example #8
Source File: ModalWrapper.tsx From genshin-optimizer with MIT License | 6 votes |
export default function ModalWrapper({ children, containerProps, ...props }: ModalWrapperProps) {
return <ScrollModal {...props}>
<ModalContainer {...containerProps}>
<Suspense fallback={<CardLight><CardContent><Skeleton variant="rectangular" width="100%" height={300} /></CardContent></CardLight>}>
{children}
</Suspense>
</ModalContainer>
</ScrollModal>
}
Example #9
Source File: LoadingImplementedChart.tsx From your_spotify with GNU General Public License v3.0 | 6 votes |
export default function LoadingImplementedChart({
title,
className,
}: LoadingImplementedChartProps) {
return (
<ChartCard title={title} className={className}>
<Skeleton variant="rectangular" height="100%" width="100%" />
</ChartCard>
);
}
Example #10
Source File: index.tsx From genshin-optimizer with MIT License | 6 votes |
export default function CharacterDisplay() {
const navigate = useNavigate();
let { characterKey } = useParams<{ characterKey?: CharacterKey }>();
const invalidKey = !allCharacterKeys.includes(characterKey as any ?? "")
if (invalidKey)
return <Navigate to="/characters" />
return <Box my={1} display="flex" flexDirection="column" gap={1}>
{characterKey && <Suspense fallback={<Skeleton variant="rectangular" width="100%" height={1000} />}>
<CharacterDisplayCard characterKey={characterKey} onClose={() => navigate("/characters")} />
</Suspense>}
</Box>
}
Example #11
Source File: Sidebar.tsx From airmessage-web with Apache License 2.0 | 6 votes |
function ConversationSkeleton() {
return (
<div className={styles.skeletonMain}>
<Skeleton variant="circular" width={40} height={40} animation={false} />
<div className={styles.skeletonText}>
<Skeleton variant="text" animation={false} />
<Skeleton variant="text" animation={false} />
</div>
</div>
);
}
Example #12
Source File: AsyncPreviewComponent.tsx From firecms with MIT License | 6 votes |
function AsyncPreviewComponentInternal<M extends { [Key: string]: any }>(
{
builder
}: AsyncPreviewComponentProps): JSX.Element {
const [loading, setLoading] = useState<boolean>(true);
const [result, setResult] = useState<React.ReactNode>(null);
useEffect(() => {
let unmounted = false;
builder
.then((res) => {
if (!unmounted) {
setLoading(false);
setResult(res);
}
})
.catch(error => {
setLoading(false);
console.error(error);
});
return () => {
unmounted = true;
};
}, [builder]);
if (loading)
return <Skeleton variant="text"/>;
return <React.Fragment>{result}</React.Fragment>;
}
Example #13
Source File: CompareBuildButton.tsx From genshin-optimizer with MIT License | 6 votes |
export default function CompareBuildButton({ artId, weaponId }: { artId?: string, weaponId?: string }) {
const { t } = useTranslation("page_character")
const [show, onShow, onHide] = useBoolState(false)
const { database } = useContext(DatabaseContext)
const { character, character: { key: characterKey, equippedArtifacts }, characterSheet, data: oldData, mainStatAssumptionLevel, characterDispatch } = useContext(DataContext)
const build = useMemo(() => {
const newArt = database._getArt(artId ?? "")
const artmap = objectMap(equippedArtifacts, (id, slot) => slot === newArt?.slotKey ? newArt : database._getArt(id))
return Object.values(artmap)
}, [database, equippedArtifacts, artId])
const teamData = useTeamData(characterKey, mainStatAssumptionLevel, build, weaponId ? database._getWeapon(weaponId) : undefined)
if (!teamData) return null
return <>
<ModalWrapper open={show} onClose={onHide} containerProps={{ maxWidth: "xl" }}>
<Suspense fallback={<Skeleton variant="rectangular" width="100%" height={600} />}>
<DataContext.Provider value={{ characterSheet, character, characterDispatch, mainStatAssumptionLevel, data: teamData[characterKey]!.target, teamData, oldData }}>
<BuildDisplayItem compareBuild={true} extraButtons={<><HitModeToggle size="small" /><ReactionToggle size="small" /><Button size='small' color="error" onClick={onHide} ><Close /></Button></>} />
</DataContext.Provider>
</Suspense>
</ModalWrapper>
<Tooltip title={<Typography>{t`tabEquip.compare`}</Typography>} placement="top" arrow>
<Button color="info" size="small" onClick={onShow} ><Difference /></Button>
</Tooltip>
</>
}
Example #14
Source File: SongsListened.tsx From your_spotify with GNU General Public License v3.0 | 5 votes |
export default function SongsListened({ className }: SongsListenedProps) {
const { interval, unit } = useSelector(selectRawIntervalDetail);
const result = useAPI(api.songsPer, interval.start, interval.end, Timesplit.all);
const lastPeriod = useMemo(() => getLastPeriod(interval.start, interval.end), [interval]);
const resultOld = useAPI(api.songsPer, lastPeriod.start, lastPeriod.end, Timesplit.all);
if (!result || !resultOld) {
return (
<TitleCard title="Songs listened" className={className} fade>
<div className={s.root}>
<Text className={s.number}>
<Skeleton width={50} />
</Text>
<Text>
<Skeleton width={200} />
</Text>
</div>
</TitleCard>
);
}
const count = result[0]?.count ?? 0;
const oldCount = resultOld[0]?.count ?? 0;
const different = result[0]?.differents ?? 0;
const percentMore = getPercentMore(oldCount, count);
return (
<TitleCard title="Songs listened" className={className} fade>
<div className={s.root}>
<div className={s.twonumbers}>
<Text className={s.number}>{count}</Text>
<Text className={s.number}>{different} diff.</Text>
</div>
<Text>
<Text
element="strong"
className={clsx({
[s.more]: percentMore >= 0,
[s.less]: percentMore < 0,
})}>
{Math.abs(percentMore)}%
</Text>
<Text element="span">
{percentMore < 0 ? 'less' : 'more'} than last {unit}
</Text>
</Text>
</div>
</TitleCard>
);
}
Example #15
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 #16
Source File: TableStorageUpload.tsx From firecms with MIT License | 5 votes |
export function StorageUploadProgress({
storagePath,
entry,
metadata,
onFileUploadComplete,
size
}: StorageUploadItemProps) {
const storage = useStorageSource();
const snackbarContext = useSnackbarController();
const [error, setError] = React.useState<string>();
const [loading, setLoading] = React.useState<boolean>(false);
const mounted = React.useRef(false);
const upload = useCallback((file: File, fileName?: string) => {
setError(undefined);
setLoading(true);
storage.uploadFile({ file, fileName, path: storagePath, metadata })
.then(async ({ path }) => {
console.debug("Upload successful");
await onFileUploadComplete(path, entry, metadata);
if (mounted.current)
setLoading(false);
})
.catch((e) => {
console.error("Upload error", e);
if (mounted.current) {
setError(e.message);
setLoading(false);
}
snackbarContext.open({
type: "error",
title: "Error uploading file",
message: e.message
});
});
}, [entry, metadata, onFileUploadComplete, snackbarContext, storage, storagePath]);
useEffect(() => {
mounted.current = true;
if (entry.file)
upload(entry.file, entry.fileName);
return () => {
mounted.current = false;
};
}, [entry.file, entry.fileName, upload]);
const imageSize = useMemo(() => getThumbnailMeasure(size), [size]);
return (
<Box m={1} sx={{
width: imageSize,
height: imageSize
}}>
{loading && <Skeleton variant="rectangular" sx={{
width: imageSize,
height: imageSize
}}/>}
{error && <p>Error uploading file: {error}</p>}
</Box>
);
}
Example #17
Source File: AttributesPage.tsx From Cromwell with MIT License | 5 votes |
export default function AttributesPage() {
const attributes = useRef<TAttribute[] | null>(null);
const graphClient = getGraphQLClient();
const forceUpdate = useForceUpdate();
const [isLoading, setIsLoading] = useState<boolean>(true);
const getAttributes = async () => {
try {
const attrs = await graphClient?.getAttributes();
if (attrs && Array.isArray(attrs)) attributes.current = attrs;
} catch (e) {
console.error(e);
}
setIsLoading(false);
}
useEffect(() => {
getAttributes();
}, []);
const handleAddAttribute = () => {
attributes.current.unshift({
key: '',
values: [],
type: 'radio'
} as any);
forceUpdate();
}
const handleRemove = (attribute: TAttribute) => {
attributes.current = attributes.current.filter(attr => attr !== attribute);
forceUpdate();
}
return (
<div className={styles.Attributes}>
<div className={styles.header}>
<div>
<p className={clsx(commonStyles.pageTitle, styles.pageTitle)}>Attributes</p>
</div>
<Button variant="contained"
size="small"
onClick={handleAddAttribute}
>Add new</Button>
</div>
<Grid
spacing={3}
container
className={styles.list}
>
{attributes.current && attributes.current.map((attribute, index) => (
<Grid item xs={12} sm={6} key={attribute.id ?? 'temp_' + index}>
<div className={styles.listItem} key={attribute.id ?? index} >
<AttributeItem data={attribute} handleRemove={handleRemove} />
</div>
</Grid>
))}
{isLoading && Array(3).fill(1).map((it, index) => {
return (
<Grid item xs={12} sm={12} md={6} key={index}>
<Skeleton key={index} className={styles.listItem} variant="rectangular" height="315px" width="100%" style={{ margin: '0 10px 20px 10px' }} > </Skeleton>
</Grid>
)
})}
</Grid>
</div>
)
}
Example #18
Source File: Loading.tsx From NekoMaid with MIT License | 5 votes |
LoadingList: React.FC<{ count?: number }> = ({ count = 3 }) => <>{Array.from({ length: count }, (_, i) => <ListItem key={i}>
<ListItemAvatar><Skeleton animation='wave' variant='circular' width={40} height={40} /></ListItemAvatar>
<ListItemText primary={<Skeleton animation='wave' />} />
</ListItem>)}</>
Example #19
Source File: DataGrid.tsx From mui-toolpad with MIT License | 5 votes |
function SkeletonLoadingOverlay() {
const apiRef = useGridApiContext();
const dimensions = apiRef.current?.getRootDimensions();
const viewportHeight = dimensions?.viewportInnerSize.height ?? 0;
const rowHeight = gridDensityRowHeightSelector(apiRef);
const skeletonRowsCount = Math.ceil(viewportHeight / rowHeight);
const totalWidth = gridColumnsTotalWidthSelector(apiRef);
const positions = gridColumnPositionsSelector(apiRef);
const inViewportCount = React.useMemo(
() => positions.filter((value) => value <= totalWidth).length,
[totalWidth, positions],
);
const columns = apiRef.current.getVisibleColumns().slice(0, inViewportCount);
const children = React.useMemo(() => {
// reseed random number generator to create stable lines betwen renders
const random = randomBetween(12345, 25, 75);
const array: React.ReactNode[] = [];
for (let i = 0; i < skeletonRowsCount; i += 1) {
for (const column of columns) {
const width = Math.round(random());
array.push(
<SkeletonCell key={`col-${column.field}-${i}`} sx={{ justifyContent: column.align }}>
<Skeleton sx={{ mx: 1 }} width={`${width}%`} />
</SkeletonCell>,
);
}
array.push(<SkeletonCell key={`fill-${i}`} />);
}
return array;
}, [skeletonRowsCount, columns]);
const rowsCount = apiRef.current.getRowsCount();
return rowsCount > 0 ? (
<LinearProgress />
) : (
<div
style={{
display: 'grid',
gridTemplateColumns: `${columns
.map(({ computedWidth }) => `${computedWidth}px`)
.join(' ')} 1fr`,
gridAutoRows: `${rowHeight}px`,
}}
>
{children}
</div>
);
}
Example #20
Source File: ArtistsListened.tsx From your_spotify with GNU General Public License v3.0 | 5 votes |
export default function ArtistsListened({ className }: ArtistsListenedProps) {
const { interval, unit } = useSelector(selectRawIntervalDetail);
const result = useAPI(api.differentArtistsPer, interval.start, interval.end, Timesplit.all);
const lastPeriod = useMemo(() => getLastPeriod(interval.start, interval.end), [interval]);
const resultOld = useAPI(
api.differentArtistsPer,
lastPeriod.start,
lastPeriod.end,
Timesplit.all,
);
if (!result || !resultOld) {
return (
<TitleCard title="Artists listened" className={className} fade>
<div className={s.root}>
<Text className={s.number}>
<Skeleton width={50} />
</Text>
<Text>
<Skeleton width={200} />
</Text>
</div>
</TitleCard>
);
}
const count = result[0]?.differents ?? 0;
const oldCount = resultOld[0]?.differents ?? 0;
const percentMore = getPercentMore(oldCount, count);
return (
<TitleCard title="Artists listened" className={className} fade>
<div className={s.root}>
<Text className={s.number}>{count} different</Text>
<Text>
<Text
element="strong"
className={clsx({
[s.more]: percentMore >= 0,
[s.less]: percentMore < 0,
})}>
{Math.abs(percentMore)}%
</Text>
<Text>
{percentMore < 0 ? 'less' : 'more'} than last {unit}
</Text>
</Text>
</div>
</TitleCard>
);
}
Example #21
Source File: Dashboard.tsx From NekoMaid with MIT License | 4 votes |
Dashboard: React.FC = () => {
const plugin = usePlugin()
const { version, hasGeoIP } = useGlobalData()
const [status, setStatus] = useState<Status[]>([])
const [current, setCurrent] = useState<CurrentStatus | undefined>()
useEffect(() => {
const offSetStatus = plugin.once('dashboard:info', setStatus)
const offCurrent = plugin.on('dashboard:current', (data: CurrentStatus) => setCurrent(old => {
if (old && isEqual(old.players, data.players)) data.players = old.players
return data
}))
plugin.switchPage('dashboard')
return () => {
offSetStatus()
offCurrent()
}
}, [])
const playerCount = current?.players?.length || 0
const prev = status[status.length - 1]?.players || 0
const percent = (prev ? playerCount / prev - 1 : playerCount) * 100
const tpsColor = !current || current.tps >= 18 ? green : current.tps >= 15 ? yellow : red
return <Box sx={{ minHeight: '100%', py: 3 }}>
<Toolbar />
<Container maxWidth={false}>
<Grid container spacing={3}>
<Grid item lg={3} sm={6} xl={3} xs={12}>
<TopCard
title={lang.dashboard.version}
content={current ? version : <Skeleton animation='wave' width={150} />}
icon={<Handyman />}
color={orange[600]}
>
<Box sx={{ pt: 2, display: 'flex', alignItems: 'flex-end' }}>
{!current || current.behinds < 0
? <Refresh htmlColor={blue[900]} />
: current?.behinds === 0
? <Check htmlColor={green[900]} />
: <Update htmlColor={yellow[900]} />}
<Typography color='textSecondary' variant='caption'> {!current || current.behinds === -3
? lang.dashboard.updateChecking
: current.behinds < 0
? <Link underline='hover' color='inherit' sx={{ cursor: 'pointer' }} onClick={() => {
toast(lang.dashboard.updateChecking)
plugin.emit('dashboard:checkUpdate')
}}>{lang.dashboard.updateFailed}</Link>
: current.behinds === 0 ? lang.dashboard.updated : lang.dashboard.behinds(current.behinds)}</Typography>
</Box>
</TopCard>
</Grid>
<Grid item lg={3} sm={6} xl={3} xs={12}>
<TopCard
title={lang.dashboard.onlinePlayers}
content={current ? playerCount : <Skeleton animation='wave' width={150} />}
icon={<People />}
color={deepPurple[600]}
>
<Box sx={{ pt: 2, display: 'flex', alignItems: 'flex-end' }}>
{percent === 0 ? <Remove color='primary' /> : percent < 0 ? <ArrowDownward color='error' /> : <ArrowUpward color='success' />}
<Typography
sx={{ color: (percent === 0 ? blue : percent < 0 ? red : green)[900], mr: 1 }}
variant='body2'
>{Math.abs(percent).toFixed(0)}%</Typography>
<Typography color='textSecondary' variant='caption'>{lang.dashboard.lastHour}</Typography>
</Box>
</TopCard>
</Grid>
<Grid item lg={3} sm={6} xl={3} xs={12}>
<TopCard
title='TPS'
content={current ? (current.tps === -1 ? '?' : current.tps.toFixed(2)) : <Skeleton animation='wave' width={150} />}
icon={!current || current.tps >= 18 || current.tps === -1
? <SentimentVerySatisfied />
: current.tps >= 15 ? <SentimentSatisfied /> : <SentimentDissatisfied />}
color={tpsColor[600]}
>
<Box sx={{ pt: 2.1, display: 'flex', alignItems: 'flex-end' }}>
<Typography
sx={{ color: tpsColor[900], mr: 1 }}
variant='body2'
>{!current || current.mspt === -1 ? '?' : current.mspt.toFixed(2) + 'ms'}</Typography>
<Typography color='textSecondary' variant='caption'>{lang.dashboard.mspt}</Typography>
</Box>
</TopCard>
</Grid>
<Grid item lg={3} sm={6} xl={3} xs={12}>
<TopCard
title={lang.dashboard.uptime}
content={current ? <Uptime time={current.time} /> : <Skeleton animation='wave' width={150} />}
icon={<AccessTime />}
color={blue[600]}
>
<Box sx={{ pt: 2.7, display: 'flex', alignItems: 'center' }}>
<Typography color='textSecondary' variant='caption' sx={{ marginRight: 1 }}>{lang.dashboard.memory}</Typography>
<Tooltip title={current?.totalMemory ? prettyBytes(current.memory) + ' / ' + prettyBytes(current.totalMemory) : ''}>
<LinearProgress
variant='determinate'
value={current?.totalMemory ? current.memory / current.totalMemory * 100 : 0}
sx={{ flex: '1' }}
/>
</Tooltip>
</Box>
</TopCard>
</Grid>
<Grid item lg={8} md={12} xl={9} xs={12}>{useMemo(() => <Charts data={status} />, [status])}</Grid>
<Grid item lg={4} md={12} xl={3} xs={12}><Players players={current?.players} /></Grid>
{hasGeoIP && current?.players && typeof current.players[0] !== 'string' && <Grid item xs={12}>
<Accordion TransitionProps={{ unmountOnExit: true }} disableGutters>
<AccordionSummary expandIcon={<ExpandMore />}>
<Typography>{lang.dashboard.playersDistribution}</Typography>
</AccordionSummary>
<Divider />
<WorldMap players={current.players as Player[]} />
</Accordion>
</Grid>}
</Grid>
</Container>
</Box>
}
Example #22
Source File: index.tsx From genshin-optimizer with MIT License | 4 votes |
export default function PageWeapon() {
const { t } = useTranslation(["page_weapon", "ui"]);
const { database } = useContext(DatabaseContext)
const [state, stateDisplatch] = useDBState("WeaponDisplay", initialState)
const [newWeaponModalShow, setnewWeaponModalShow] = useState(false)
const [dbDirty, forceUpdate] = useForceUpdate()
const invScrollRef = useRef<HTMLDivElement>(null)
const [pageIdex, setpageIdex] = useState(0)
//set follow, should run only once
useEffect(() => {
ReactGA.send({ hitType: "pageview", page: '/weapon' })
return database.followAnyWeapon(forceUpdate)
}, [forceUpdate, database])
const brPt = useMediaQueryUp()
const maxNumToDisplay = numToShowMap[brPt]
const weaponSheets = usePromise(WeaponSheet.getAll, [])
const deleteWeapon = useCallback(async (key) => {
const weapon = database._getWeapon(key)
if (!weapon) return
const name = i18next.t(`weapon_${weapon.key}_gen:name`)
if (!window.confirm(`Are you sure you want to remove ${name}?`)) return
database.removeWeapon(key)
if (state.editWeaponId === key)
stateDisplatch({ editWeaponId: "" })
}, [state.editWeaponId, stateDisplatch, database])
const editWeapon = useCallback(key => {
stateDisplatch({ editWeaponId: key })
}, [stateDisplatch])
const newWeapon = useCallback(
(weaponKey: WeaponKey) => {
editWeapon(database.createWeapon(initialWeapon(weaponKey)))
},
[database, editWeapon])
const { sortType, ascending, weaponType, rarity } = state
const sortConfigs = useMemo(() => weaponSheets && weaponSortConfigs(weaponSheets), [weaponSheets])
const filterConfigs = useMemo(() => weaponSheets && weaponFilterConfigs(weaponSheets), [weaponSheets])
const { weaponIdList, totalWeaponNum } = useMemo(() => {
const weapons = database._getWeapons()
const totalWeaponNum = weapons.length
if (!sortConfigs || !filterConfigs) return { weaponIdList: [], totalWeaponNum }
const weaponIdList = weapons.filter(filterFunction({ weaponType, rarity }, filterConfigs))
.sort(sortFunction(sortType, ascending, sortConfigs)).map(weapon => weapon.id);
return dbDirty && { weaponIdList, totalWeaponNum }
}, [dbDirty, database, sortConfigs, filterConfigs, sortType, ascending, rarity, weaponType])
const { weaponIdsToShow, numPages, currentPageIndex } = useMemo(() => {
const numPages = Math.ceil(weaponIdList.length / maxNumToDisplay)
const currentPageIndex = clamp(pageIdex, 0, numPages - 1)
return { weaponIdsToShow: weaponIdList.slice(currentPageIndex * maxNumToDisplay, (currentPageIndex + 1) * maxNumToDisplay), numPages, currentPageIndex }
}, [weaponIdList, pageIdex, maxNumToDisplay])
//for pagination
const totalShowing = weaponIdList.length !== totalWeaponNum ? `${weaponIdList.length}/${totalWeaponNum}` : `${totalWeaponNum}`
const setPage = useCallback(
(e, value) => {
invScrollRef.current?.scrollIntoView({ behavior: "smooth" })
setpageIdex(value - 1);
},
[setpageIdex, invScrollRef],
)
const resetEditWeapon = useCallback(() => stateDisplatch({ editWeaponId: "" }), [stateDisplatch])
const { editWeaponId } = state
// Validate weaponId to be an actual weapon
useEffect(() => {
if (!editWeaponId) return
if (!database._getWeapon(editWeaponId))
resetEditWeapon()
}, [database, editWeaponId, resetEditWeapon])
return <Box my={1} display="flex" flexDirection="column" gap={1}>
{/* editor/character detail display */}
<Suspense fallback={false}>
<WeaponEditor
weaponId={editWeaponId}
footer
onClose={resetEditWeapon}
/>
</Suspense>
<CardDark ref={invScrollRef} sx={{ p: 2, pb: 1 }}>
<Grid container spacing={1} sx={{ mb: 1 }}>
<Grid item>
<WeaponToggle sx={{ height: "100%" }} onChange={weaponType => stateDisplatch({ weaponType })} value={weaponType} size="small" />
</Grid>
<Grid item flexGrow={1}>
<SolidToggleButtonGroup sx={{ height: "100%" }} onChange={(e, newVal) => stateDisplatch({ rarity: newVal })} value={rarity} size="small">
{allRarities.map(star => <ToggleButton key={star} value={star}><Box display="flex" gap={1}><strong>{star}</strong><Stars stars={1} /></Box></ToggleButton>)}
</SolidToggleButtonGroup>
</Grid>
<Grid item >
<SortByButton sx={{ height: "100%" }} sortKeys={weaponSortKeys}
value={sortType} onChange={sortType => stateDisplatch({ sortType })}
ascending={ascending} onChangeAsc={ascending => stateDisplatch({ ascending })}
/>
</Grid>
</Grid>
<Grid container alignItems="flex-end">
<Grid item flexGrow={1}>
<Pagination count={numPages} page={currentPageIndex + 1} onChange={setPage} />
</Grid>
<Grid item>
<ShowingWeapon numShowing={weaponIdsToShow.length} total={totalShowing} t={t} />
</Grid>
</Grid>
</CardDark>
<Suspense fallback={<Skeleton variant="rectangular" sx={{ width: "100%", height: "100%", minHeight: 500 }} />}>
<Grid container spacing={1} columns={columns}>
<Grid item xs={1}>
<CardDark sx={{ height: "100%", width: "100%", minHeight: 300, display: "flex", flexDirection: "column" }}>
<CardContent>
<Typography sx={{ textAlign: "center" }}>Add New Weapon</Typography>
</CardContent>
<WeaponSelectionModal show={newWeaponModalShow} onHide={() => setnewWeaponModalShow(false)} onSelect={newWeapon} />
<Box sx={{
flexGrow: 1,
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
>
<Button onClick={() => setnewWeaponModalShow(true)} color="info" sx={{ borderRadius: "1em" }}>
<Typography variant="h1"><FontAwesomeIcon icon={faPlus} className="fa-fw" /></Typography>
</Button>
</Box>
</CardDark>
</Grid>
{weaponIdsToShow.map(weaponId =>
<Grid item key={weaponId} xs={1} >
<WeaponCard
weaponId={weaponId}
onDelete={deleteWeapon}
onEdit={editWeapon}
canEquip
/>
</Grid>)}
</Grid>
</Suspense>
{numPages > 1 && <CardDark><CardContent>
<Grid container alignItems="flex-end">
<Grid item flexGrow={1}>
<Pagination count={numPages} page={currentPageIndex + 1} onChange={setPage} />
</Grid>
<Grid item>
<ShowingWeapon numShowing={weaponIdsToShow.length} total={totalShowing} t={t} />
</Grid>
</Grid>
</CardContent></CardDark>}
</Box>
}
Example #23
Source File: outline-card.tsx From Search-Next with GNU General Public License v3.0 | 4 votes |
OutlineCard: React.FC<OutlineCardProps> = ({
label,
id,
value,
tip,
disabled = false,
loading = false,
fullWidth = false,
children,
onChange,
}) => {
const [radioChecked, setChecked] = React.useState<boolean>(id === value);
React.useEffect(() => {
value && setChecked(value === id);
}, [value]);
React.useEffect(() => {
if (radioChecked) {
if (onChange && id) onChange(id);
}
}, [radioChecked]);
const radio = () => {
return (
<input
className={classNames(
'rounded w-full cursor-pointer h-full m-0 p-0 z-10 overflow-hidden absolute appearance-none bg-transparent transition-all border',
!loading && radioChecked
? 'border-primary shadow-inner'
: 'border-gray-200 shadow-sm',
)}
type="radio"
id={id}
name={value}
checked={radioChecked}
onChange={() => {}}
onClick={() => setChecked(true)}
disabled={disabled}
/>
);
};
return (
<div
className={classNames(
'p-0 mb-0 h-full transition-all rounded',
fullWidth && 'w-full',
)}
>
<div
className={classNames(
'inline-block relative min-w-min min-h-full items-stretch justify-start h-full',
fullWidth && 'w-full',
)}
>
{tip ? <Tooltip title={tip}>{radio()}</Tooltip> : radio()}
<label
slot="label"
htmlFor={id}
className={classNames(
'inline-block p-0 text-sm leading-5 text-primary',
fullWidth && 'w-full',
)}
>
<div className="px-2 pt-2 pb-1">
<div className="border border-gray-300 rounded">
{loading ? (
<Skeleton variant="rectangular" width={136} height={76} />
) : (
children
)}
</div>
{label && (
<div>
<label className="mt-1 overflow-hidden max-w-full whitespace-nowrap text-ellipsis text-xs leading-4 inline-block p-0 text-primary">
{loading ? (
<Skeleton variant="text" width={100} height={16} />
) : (
label
)}
</label>
</div>
)}
</div>
</label>
</div>
</div>
);
}
Example #24
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 #25
Source File: index.tsx From genshin-optimizer with MIT License | 4 votes |
export default function PageCharacter(props) {
// TODO: #412 We shouldn't be loading all the character translation files. Should have a separate lookup file for character name.
const { t } = useTranslation(["page_character", ...allCharacterKeys.map(k => `char_${k}_gen`)])
const { database } = useContext(DatabaseContext)
const [state, stateDispatch] = useDBState("CharacterDisplay", initialState)
const [searchTerm, setSearchTerm] = useState("")
const deferredSearchTerm = useDeferredValue(searchTerm)
const [pageIdex, setpageIdex] = useState(0)
const invScrollRef = useRef<HTMLDivElement>(null)
const setPage = useCallback(
(e, value) => {
invScrollRef.current?.scrollIntoView({ behavior: "smooth" })
setpageIdex(value - 1);
},
[setpageIdex, invScrollRef],
)
const brPt = useMediaQueryUp()
const maxNumToDisplay = numToShowMap[brPt]
const [newCharacter, setnewCharacter] = useState(false)
const [dbDirty, forceUpdate] = useForceUpdate()
//set follow, should run only once
useEffect(() => {
ReactGA.send({ hitType: "pageview", page: '/characters' })
return database.followAnyChar(forceUpdate)
}, [forceUpdate, database])
const characterSheets = usePromise(CharacterSheet.getAll, [])
const deleteCharacter = useCallback(async (cKey: CharacterKey) => {
const chararcterSheet = await CharacterSheet.get(cKey)
let name = chararcterSheet?.name
//use translated string
if (typeof name === "object")
name = i18next.t(`char_${cKey}_gen:name`)
if (!window.confirm(t("removeCharacter", { value: name }))) return
database.removeChar(cKey)
}, [database, t])
const editCharacter = useCharSelectionCallback()
const navigate = useNavigate()
const { element, weaponType } = state
const sortConfigs = useMemo(() => characterSheets && characterSortConfigs(database, characterSheets), [database, characterSheets])
const filterConfigs = useMemo(() => characterSheets && characterFilterConfigs(database, characterSheets), [database, characterSheets])
const { charKeyList, totalCharNum } = useMemo(() => {
const chars = database._getCharKeys()
const totalCharNum = chars.length
if (!sortConfigs || !filterConfigs) return { charKeyList: [], totalCharNum }
const charKeyList = database._getCharKeys()
.filter(filterFunction({ element, weaponType, favorite: "yes", name: deferredSearchTerm }, filterConfigs))
.sort(sortFunction(state.sortType, state.ascending, sortConfigs))
.concat(
database._getCharKeys()
.filter(filterFunction({ element, weaponType, favorite: "no", name: deferredSearchTerm }, filterConfigs))
.sort(sortFunction(state.sortType, state.ascending, sortConfigs)))
return dbDirty && { charKeyList, totalCharNum }
},
[dbDirty, database, sortConfigs, state.sortType, state.ascending, element, filterConfigs, weaponType, deferredSearchTerm])
const { charKeyListToShow, numPages, currentPageIndex } = useMemo(() => {
const numPages = Math.ceil(charKeyList.length / maxNumToDisplay)
const currentPageIndex = clamp(pageIdex, 0, numPages - 1)
return { charKeyListToShow: charKeyList.slice(currentPageIndex * maxNumToDisplay, (currentPageIndex + 1) * maxNumToDisplay), numPages, currentPageIndex }
}, [charKeyList, pageIdex, maxNumToDisplay])
const totalShowing = charKeyList.length !== totalCharNum ? `${charKeyList.length}/${totalCharNum}` : `${totalCharNum}`
return <Box my={1} display="flex" flexDirection="column" gap={1}>
<CardDark ref={invScrollRef} ><CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
<Grid container spacing={1}>
<Grid item>
<WeaponToggle sx={{ height: "100%" }} onChange={weaponType => stateDispatch({ weaponType })} value={state.weaponType} size="small" />
</Grid>
<Grid item>
<ElementToggle sx={{ height: "100%" }} onChange={element => stateDispatch({ element })} value={state.element} size="small" />
</Grid>
<Grid item flexGrow={1}>
<TextField
autoFocus
value={searchTerm}
onChange={(e: ChangeEvent<HTMLTextAreaElement>) => setSearchTerm(e.target.value)}
label={t("characterName")}
/>
</Grid>
<Grid item >
<SortByButton sx={{ height: "100%" }}
sortKeys={characterSortKeys} value={state.sortType} onChange={sortType => stateDispatch({ sortType })}
ascending={state.ascending} onChangeAsc={ascending => stateDispatch({ ascending })} />
</Grid>
</Grid>
<Grid container alignItems="flex-end">
<Grid item flexGrow={1}>
<Pagination count={numPages} page={currentPageIndex + 1} onChange={setPage} />
</Grid>
<Grid item>
<ShowingCharacter numShowing={charKeyListToShow.length} total={totalShowing} t={t} />
</Grid>
</Grid>
</CardContent></CardDark>
<Suspense fallback={<Skeleton variant="rectangular" sx={{ width: "100%", height: "100%", minHeight: 5000 }} />}>
<Grid container spacing={1} columns={columns}>
<Grid item xs={1} >
<CardDark sx={{ height: "100%", minHeight: 400, width: "100%", display: "flex", flexDirection: "column" }}>
<CardContent>
<Typography sx={{ textAlign: "center" }}><Trans t={t} i18nKey="addNew" /></Typography>
</CardContent>
<CharacterSelectionModal newFirst show={newCharacter} onHide={() => setnewCharacter(false)} onSelect={editCharacter} />
<Box sx={{
flexGrow: 1,
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
>
<Button onClick={() => setnewCharacter(true)} color="info" sx={{ borderRadius: "1em" }}>
<Typography variant="h1"><FontAwesomeIcon icon={faPlus} className="fa-fw" /></Typography>
</Button>
</Box>
</CardDark>
</Grid>
{charKeyListToShow.map(charKey =>
<Grid item key={charKey} xs={1} >
<CharacterCard
characterKey={charKey}
onClick={() => navigate(`${charKey}`)}
footer={<><Divider /><Box sx={{ py: 1, px: 2, display: "flex", gap: 1, justifyContent: "space-between" }}>
<BootstrapTooltip placement="top" title={<Typography>{t("tabs.talent")}</Typography>}>
<IconButton onClick={() => navigate(`${charKey}/talent`)}>
<FactCheck />
</IconButton>
</BootstrapTooltip>
<BootstrapTooltip placement="top" title={<Typography>{t("tabs.equip")}</Typography>}>
<IconButton onClick={() => navigate(`${charKey}/equip`)} >
<Checkroom />
</IconButton>
</BootstrapTooltip>
<BootstrapTooltip placement="top" title={<Typography>{t("tabs.teambuffs")}</Typography>}>
<IconButton onClick={() => navigate(`${charKey}/teambuffs`)} >
<Groups />
</IconButton>
</BootstrapTooltip>
<BootstrapTooltip placement="top" title={<Typography>{t("tabs.optimize")}</Typography>}>
<IconButton onClick={() => navigate(`${charKey}/optimize`)} >
<Calculate />
</IconButton>
</BootstrapTooltip>
<Divider orientation="vertical" />
<BootstrapTooltip placement="top" title={<Typography>{t("delete")}</Typography>}>
<IconButton color="error" onClick={() => deleteCharacter(charKey)}>
<DeleteForever />
</IconButton>
</BootstrapTooltip>
</Box></>}
/>
</Grid>)}
</Grid>
</Suspense>
{numPages > 1 && <CardDark ><CardContent>
<Grid container alignItems="flex-end">
<Grid item flexGrow={1}>
<Pagination count={numPages} page={currentPageIndex + 1} onChange={setPage} />
</Grid>
<Grid item>
<ShowingCharacter numShowing={charKeyListToShow.length} total={totalShowing} t={t} />
</Grid>
</Grid>
</CardContent></CardDark>}
</Box>
}
Example #26
Source File: Download.tsx From website with Apache License 2.0 | 4 votes |
Download = ({ showMore }: IDownloadProps) => {
const { releases, isError, isLoading } = useGithubReleases();
const [modalActive, setModalActive] = useState(false);
const Router = useRouter();
const getDate = (date: Date) => {
date = new Date(date);
return new Date(
date.getFullYear(),
date.getMonth(),
date.getDate(),
).toLocaleString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
});
};
const closeModal = useCallback(() => setModalActive(false), []);
const openModal = useCallback(() => setModalActive(true), []);
return (
<>
<AnimatePresence>
{modalActive ? (
<StyledModal
initial={modalContainerAnimation.initial}
animate={modalContainerAnimation.isOpen}
exit={modalContainerAnimation.exit}
transition={{ duration: 0.2 }}
open={modalActive}
onClose={closeModal}
PaperComponent={({ children }) => (
<StyledPaper
initial={modalAnimation.initial}
animate={modalAnimation.isOpen}
exit={modalAnimation.exit}
transition={{ duration: 0.2 }}
>
{children}
</StyledPaper>
)}
>
<h1>Support dahliaOS</h1>
<p>
Donating to dahliaOS will help us purchase devices for testing and
cover web hosting fees, so we can continue work on our amazing
software!
</p>
<DialogActions>
<DialogButton disableGradient onClick={closeModal}>
No thanks
</DialogButton>
<DialogButton
disableGradient={false}
onClick={() => Router.replace("/donate")}
autoFocus
>
Donate
</DialogButton>
</DialogActions>
</StyledModal>
) : null}
</AnimatePresence>
{isError ? (
<Card isError>
<ErrorContainer>
<StyledErrorIcon />
<br />
<ErrorMessage>
An error occurred whilst fetching GitHub's API!
</ErrorMessage>
</ErrorContainer>
</Card>
) : null}
{releases?.length ? (
<>
<Card>
<Latest>
<TextContainer>
<Header>Latest</Header>
<ReleaseName>
{releases[0].name} ({getDate(releases[0].published_at)})
</ReleaseName>
<Changelogs>
{releases[0].body
.substring(releases[0].body.indexOf("+ "))
.replace(/(?:\r\n|\r|\n)/g, "\n")}
</Changelogs>
<ReadMoreContainer>
<ReadMoreButton href={releases[0].html_url}>
Read more
</ReadMoreButton>
</ReadMoreContainer>
</TextContainer>
<ButtonContainer>
{releases[0].assets.map(asset => (
<StyledButton
key={asset.name}
href={asset.browser_download_url}
onClick={openModal}
>
{asset.name.includes("efi")
? "Download (EFI)"
: "Download (Legacy)"}
</StyledButton>
))}
</ButtonContainer>
</Latest>
<Older>
<TextContainer>
<OlderHeader>Older updates</OlderHeader>
<UpdateContainer>
{releases.map((oldRelease, i) => {
if (i === 0 || i > 4) return;
return (
<OlderUpdate key={i}>
<OlderUpdateTextWrapper>
<OlderUpdateTitle>{oldRelease.name}</OlderUpdateTitle>
<OlderUpdateDate>
{getDate(oldRelease.published_at)}
</OlderUpdateDate>
</OlderUpdateTextWrapper>
<OlderBtns>
{oldRelease.assets.map(asset => (
<StyledButton
key={asset.name}
href={asset.browser_download_url}
disableGradient={!asset.name.includes("efi")}
onClick={openModal}
>
{asset.name.includes("efi") ? "EFI" : "Legacy"}
</StyledButton>
))}
</OlderBtns>
</OlderUpdate>
);
})}
</UpdateContainer>
</TextContainer>
</Older>
</Card>
</>
) : isLoading ? (
<Card>
<Latest>
<TextContainer>
<Header>
<Skeleton
variant="text"
animation="wave"
width={"25%"}
height={50}
/>
</Header>
<ReleaseName>
<Skeleton
variant="text"
animation="wave"
width={"48%"}
height={25}
/>
</ReleaseName>
<Changelogs>
<Skeleton
variant="text"
animation="wave"
width={"100%"}
height={20}
/>
<Skeleton
variant="text"
animation="wave"
width={"98%"}
height={20}
/>
<Skeleton
variant="text"
animation="wave"
width={"95%"}
height={20}
/>
<Skeleton
variant="text"
animation="wave"
width={"93%"}
height={20}
/>
<Skeleton
variant="text"
animation="wave"
width={"87%"}
height={20}
/>
<Skeleton
variant="text"
animation="wave"
width={"85%"}
height={20}
/>
<Skeleton
variant="text"
animation="wave"
width={"20%"}
height={20}
/>
</Changelogs>
<Link target="_blank">
<ReadMoreButton>
<Skeleton
variant="text"
animation="wave"
width={100}
height={55}
/>
</ReadMoreButton>
</Link>
</TextContainer>
<ButtonContainer>
<Skeleton
variant="rectangular"
animation="wave"
width={"20%"}
height={35}
style={{ display: "inline-block" }}
/>
<Skeleton
variant="rectangular"
animation="wave"
width={"20%"}
height={35}
style={{ display: "inline-block", marginLeft: 15 }}
/>
</ButtonContainer>
</Latest>
<Older>
<TextContainer>
<OlderHeader>Older updates</OlderHeader>
<UpdateContainer>
{[...Array(5)].map((oldRelease, i) => {
if (i === 0 || i > 4) return;
return (
<OlderUpdate key={i}>
<OlderUpdateTextWrapper>
<OlderUpdateTitle>
<Skeleton
variant="text"
animation="wave"
width={"25%"}
height={25}
/>
</OlderUpdateTitle>
<OlderUpdateDate>
<Skeleton
variant="text"
animation="wave"
width={"25%"}
height={20}
/>
</OlderUpdateDate>
</OlderUpdateTextWrapper>
<OlderBtns>
<Skeleton
variant="rectangular"
animation="wave"
width={"20%"}
height={15}
style={{ display: "inline-block" }}
/>
<Skeleton
variant="rectangular"
animation="wave"
width={"20%"}
height={15}
style={{ display: "inline-block", marginLeft: 15 }}
/>
</OlderBtns>
</OlderUpdate>
);
})}
</UpdateContainer>
</TextContainer>
</Older>
</Card>
) : null}
{/* Will implement soon */}
{showMore ? null : null}
</>
);
}
Example #27
Source File: Queue.tsx From multi-downloader-nx with MIT License | 4 votes |
Queue: React.FC = () => {
const data = useDownloadManager();
const [{ queue }, dispatch] = useStore();
return data || queue.length > 0 ? <>
{data && <Box sx={{ mb: 1, height: 200, display: 'grid', gridTemplateColumns: '20% 1fr', gap: 1 }}>
<img src={data.downloadInfo.image} height='200px' width='100%' />
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr max-content' }}>
<Typography variant='h5' color='text.primary'>
{data.downloadInfo.title}
</Typography>
<Typography variant='h5' color='text.primary'>
Language: {data.downloadInfo.language.name}
</Typography>
</Box>
<Typography variant='h6' color='text.primary'>
{data.downloadInfo.parent.title}
</Typography>
</Box>
<LinearProgress variant='determinate' sx={{ height: '10px' }} value={(typeof data.progress.percent === 'string' ? parseInt(data.progress.percent) : data.progress.percent)} />
<Box>
<Typography variant="body1" color='text.primary'>
{data.progress.cur}MB / {(data.progress.total)}MB ({data.progress.percent}% | {formatTime(data.progress.time)} | {(data.progress.downloadSpeed / 1024 / 1024).toFixed(2)} MB/s)
</Typography>
</Box>
</Box>
</Box>}
{queue.length && <Divider variant="fullWidth" />}
{queue.map((queueItem, index, { length }) => {
return <Box key={`queue_item_${index}`}>
<Box sx={{ height: 200, display: 'grid', gridTemplateColumns: '20% 1fr', gap: 1, mb: 1, mt: 1 }}>
<img src={queueItem.image} height='200px' width='100%' />
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 200px' }}>
<Typography variant='h5' color='text.primary'>
{queueItem.title}
</Typography>
<Typography variant='h5' color='text.primary'>
Languages: {queueItem.dubLang.join(', ')}
</Typography>
</Box>
<Typography variant='h6' color='text.primary'>
{queueItem.parent.title}
</Typography>
</Box>
<Typography variant='body1' color='text.primary'>
S{queueItem.parent.season}E{queueItem.episode} <br />
Quality: {queueItem.q}
</Typography>
<Button onClick={() => {
const override = [...queue];
dispatch({
type: 'queue',
payload: override,
extraInfo: {
force: true
}
});
}} sx={{ position: 'relative', left: '50%', transform: 'translateX(-50%)', width: '60%' }} variant="outlined" color="warning">
Remove from Queue
</Button>
</Box>
</Box>
{index < length - 1 && <Divider variant="fullWidth" />}
</Box>;
})}
</> : <Box>
<Typography color='text.primary' variant='h4'>
Selected episodes will be shown here
</Typography>
<Box sx={{ height: 200, display: 'grid', gridTemplateColumns: '20% 1fr', gap: 1 }}>
<Skeleton variant='rectangular' height={'100%'}/>
<Box sx={{ display: 'grid', gridTemplateRows: '33% 1fr', gap: 1 }}>
<Skeleton variant='text' height={'100%'} />
<Skeleton variant='text' height={'100%'} />
</Box>
</Box>
</Box>
}
Example #28
Source File: ThemeList.tsx From Cromwell with MIT License | 4 votes |
render() {
const { isLoading, packages, installedThemes, cmsConfig, isChangingTheme } = this.state;
return (
<div className={styles.ThemeList} ref={this.pageRef}>
<Button
className={styles.addBtn}
onClick={this.handleOpenMarket}
variant="contained"
color="primary"
startIcon={<AddCircleOutlineIcon />}
>Add themes</Button>
{isLoading && (
<div className={styles.list}>
{Array(2).fill(1).map((it, index) => (
<Skeleton key={index} variant="rectangular" height="388px" width="300px" style={{ margin: '0 10px 20px 10px' }} > </Skeleton>
))}
</div>
)}
{!isLoading &&
<div className={styles.list}>
{packages.map(info => {
const isActive = Boolean(cmsConfig && cmsConfig.themeName === info.name);
const entity = installedThemes?.find(ent => ent.name === info.name);
const isInstalled = entity?.isInstalled ?? false;
const availableUpdate = this.themeUpdates[info.name];
const isUnderUpdate = this.themeUnderUpdate[info.name];
return (
<div className={`${styles.themeCard} ${commonStyles.paper}`} key={info.name}>
<CardActionArea
className={styles.cardActionArea}
style={{ opacity: isUnderUpdate ? 0.5 : 1 }}
>
<div
style={{ backgroundImage: `url("data:image/png;base64,${info.image}")` }}
className={styles.themeImage}
></div>
<CardContent className={styles.mainInfo}>
<Badge color="secondary" badgeContent={isActive ? 'Active' : null}>
<Typography gutterBottom variant="h5" component="h2" className={styles.themeTitle}>
{info.title}
</Typography>
</Badge>
<p className={styles.version}
onClick={this.handleShowUpdate(entity, info, availableUpdate)}
style={{ cursor: availableUpdate ? 'pointer' : 'initial' }}
>{(info?.version ?? '') +
(availableUpdate ? ' > ' + availableUpdate.version + ' Open info' : '')}</p>
<Typography variant="body2" color="textSecondary" component="p">
{info.excerpt}
</Typography>
</CardContent>
</CardActionArea>
<CardActions
style={{ opacity: isUnderUpdate ? 0.5 : 1 }}
className={styles.themeActions}
disableSpacing
>
{!isInstalled && (
<Button
disabled={isUnderUpdate || isChangingTheme}
size="small" color="primary" variant="contained"
onClick={this.handleActivateTheme(info.name)}
>Install theme</Button>
)}
{isInstalled && isActive && (
<Button
disabled={isUnderUpdate || isChangingTheme}
size="small" color="primary" variant="contained"
onClick={() => {
const route = `${themeEditPageInfo.baseRoute}`;
this.props.history.push(route);
}}
>
Edit theme
</Button>
)}
{availableUpdate && (
<Button
disabled={isUnderUpdate || isChangingTheme}
size="small" color="primary" variant="contained"
onClick={() => this.startUpdate(info)}
>Update</Button>
)}
{isInstalled && !isActive && (
<Button size="small" color="primary" variant="contained"
onClick={() => this.handleSetActiveTheme(info)}
disabled={isUnderUpdate || isChangingTheme}
>Set active</Button>
)}
<Button size="small" color="primary" variant="outlined"
disabled={isUnderUpdate || isChangingTheme}
onClick={() => this.handleDelete(info)}
>Delete</Button>
<Button size="small" color="primary" variant="outlined"
onClick={() => this.openTheme(info)}
>Info</Button>
{isUnderUpdate && (
<LinearProgress className={styles.updateProgress} />
)}
</CardActions>
</div>
)
})}
</div>}
<LoadingStatus isActive={isChangingTheme} />
{/* <ManagerLogger isActive={isChangingTheme} /> */}
<Modal
open={!!this.state.updateModalInfo}
onClose={() => this.setState({ updateModalInfo: null })}
className={commonStyles.center}
blurSelector="#root"
>
<UpdateModalContent
underUpdate={this.themeUnderUpdate}
{...(this.state?.updateModalInfo ?? {})}
onStartUpdate={this.startUpdate}
onClose={() => this.setState({ updateModalInfo: null })}
/>
</Modal>
<Modal
open={!!this.state.openedTheme}
blurSelector="#root"
className={commonStyles.center}
onClose={() => this.setState({ openedTheme: undefined })}
>
{this.state?.openedTheme && (
<MarketModal
installedModules={this.state?.installedThemes ?? []}
data={this.state.openedTheme}
noInstall
/>
)}
</Modal>
</div>
)
}
Example #29
Source File: ReferencePreview.tsx From firecms with MIT License | 4 votes |
function ReferencePreviewComponent<M extends { [Key: string]: any }>(
{
value,
property,
onClick,
size,
onHover
}: ReferencePreviewProps) {
if (typeof property.path !== "string") {
throw Error("Picked the wrong component ReferencePreviewComponent");
}
const reference: EntityReference = value;
const previewProperties = property.previewProperties;
const navigationContext = useNavigation();
const sideEntityController = useSideEntityController();
const collectionResolver = navigationContext.getCollectionResolver<M>(property.path);
if (!collectionResolver) {
throw Error(`Couldn't find the corresponding collection view for the path: ${property.path}`);
}
const schema = collectionResolver.schema;
const {
entity,
dataLoading,
dataLoadingError
} = useEntityFetch({
path: reference.path,
entityId: reference.id,
schema: collectionResolver.schemaResolver,
useCache: true
});
const listProperties = useMemo(() => {
let res = previewProperties;
if (!res || !res.length) {
res = Object.keys(schema.properties);
}
if (size === "small" || size === "regular")
res = res.slice(0, 3);
else if (size === "tiny")
res = res.slice(0, 1);
return res;
}, [previewProperties, schema.properties, size]);
let body: JSX.Element;
function buildError(error: string, tooltip?: string) {
return <ErrorView error={error} tooltip={tooltip}/>;
}
if (!value) {
body = buildError("Reference not set");
}
// currently not happening since this gets filtered out in PreviewComponent
else if (!(value instanceof EntityReference)) {
body = buildError("Unexpected value", JSON.stringify(value));
} else if (entity && !entity.values) {
body = buildError("Reference does not exist", reference.path);
} else {
body = (
<>
<Box sx={{
display: "flex",
flexDirection: "column",
flexGrow: 1,
maxWidth: "calc(100% - 60px)",
margin: 1
}}>
{size !== "tiny" && (
value
? <Box sx={{
display: size !== "regular" ? "block" : undefined,
whiteSpace: size !== "regular" ? "nowrap" : undefined,
overflow: size !== "regular" ? "hidden" : undefined,
textOverflow: size !== "regular" ? "ellipsis" : undefined
}}>
<Typography variant={"caption"}
className={"mono"}>
{value.id}
</Typography>
</Box>
: <Skeleton variant="text"/>)}
{listProperties && listProperties.map((key) => {
const childProperty = schema.properties[key as string];
if (!childProperty) return null;
return (
<div key={"ref_prev_" + (key as string)}>
{entity
? <PreviewComponent name={key as string}
value={entity.values[key as string]}
property={childProperty as AnyProperty}
size={"tiny"}/>
: <SkeletonComponent
property={childProperty as AnyProperty}
size={"tiny"}/>
}
</div>
);
})}
</Box>
<Box sx={{
margin: "auto"
}}>
{entity &&
<Tooltip title={`See details for ${entity.id}`}>
<IconButton
size={size === "tiny" ? "small" : "medium"}
onClick={(e) => {
e.stopPropagation();
sideEntityController.open({
entityId: entity.id,
path: entity.path,
schema: schema,
overrideSchemaRegistry: false
});
}}>
<KeyboardTabIcon fontSize={"small"}/>
</IconButton>
</Tooltip>}
</Box>
</>
);
}
return (
<Paper elevation={0} sx={(theme) => {
const clickableStyles = onClick
? {
tabindex: 0,
backgroundColor: onHover ? (theme.palette.mode === "dark" ? lighten(theme.palette.background.default, 0.1) : darken(theme.palette.background.default, 0.15)) : darken(theme.palette.background.default, 0.1),
transition: "background-color 300ms ease, box-shadow 300ms ease",
boxShadow: onHover ? "0 0 0 2px rgba(128,128,128,0.05)" : undefined,
cursor: onHover ? "pointer" : undefined
}
: {};
return ({
width: "100%",
display: "flex",
color: "#838383",
backgroundColor: darken(theme.palette.background.default, 0.1),
borderRadius: "2px",
overflow: "hidden",
padding: size === "regular" ? 1 : 0,
itemsAlign: size === "tiny" ? "center" : undefined,
fontWeight: theme.typography.fontWeightMedium,
...clickableStyles
});
}}
onClick={onClick}>
{body}
</Paper>
);
}