@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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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">
            &nbsp;{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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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>
            &nbsp;{percentMore < 0 ? 'less' : 'more'} than last {unit}
          </Text>
        </Text>
      </div>
    </TitleCard>
  );
}
Example #21
Source File: Dashboard.tsx    From NekoMaid with MIT License 4 votes vote down vote up
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'>&nbsp;{!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 vote down vote up
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 vote down vote up
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 vote down vote up
/**
 *
 * @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 vote down vote up
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 vote down vote up
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&apos;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 vote down vote up
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 vote down vote up
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 vote down vote up
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>
    );

}