@mui/material#CardActionArea TypeScript Examples

The following examples show how to use @mui/material#CardActionArea. 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: Home.tsx    From mui-toolpad with MIT License 6 votes vote down vote up
function AppCard({ app, onDelete }: AppCardProps) {
  return (
    <Card sx={{ gridColumn: 'span 1' }}>
      <CardActionArea component="a" href={app ? `/deploy/${app.id}` : ''} disabled={!app}>
        <CardContent>
          <Typography gutterBottom variant="h5" component="div">
            {app ? app.name : <Skeleton />}
          </Typography>
          <Typography variant="body2" color="text.secondary">
            {app ? `Created: ${app.createdAt.toLocaleDateString('short')}` : <Skeleton />}
          </Typography>
        </CardContent>
      </CardActionArea>
      <CardActions>
        <Button component="a" href={app ? `/_toolpad/app/${app.id}/editor` : ''} disabled={!app}>
          Edit
        </Button>
        <Button disabled={!app} onClick={onDelete}>
          delete
        </Button>
      </CardActions>
    </Card>
  );
}
Example #2
Source File: AppOverview.tsx    From mui-toolpad with MIT License 6 votes vote down vote up
function PageCard({ page }: PageCardProps) {
  return (
    <Card sx={{ gridColumn: 'span 1' }}>
      <CardActionArea component={Link} to={`/pages/${page.id}`}>
        <CardContent>
          <Typography gutterBottom variant="h5" component="div">
            {page.name}
          </Typography>
          <Typography variant="body2" color="text.secondary">
            {page.attributes.title.value}
          </Typography>
        </CardContent>
      </CardActionArea>
    </Card>
  );
}
Example #3
Source File: BlogEntryPreview.tsx    From firecms with MIT License 6 votes vote down vote up
export function ProductPreview({ productValues }: { productValues: EntityValues<Product> }) {

    if (!productValues)
        return <></>;

    return <CardActionArea style={{
        width: "400px",
        height: "400px",
        margin: "16px",
        boxShadow: "rgb(0 0 0 / 8%) 0px 8px 12px -4px"
    }}>
        <CardContent style={{
            height: "100%",
            display: "flex",
            flexDirection: "column"
        }}>
            <Box flexGrow={1} flexShrink={1} flexBasis={296} p={2}
                 maxHeight={296}>
                <StorageImage storagePath={productValues.main_image}/>
            </Box>
            <Typography gutterBottom
                        variant="h6"
                        noWrap
                        style={{
                            marginTop: "16px"
                        }}>
                {productValues.name}
            </Typography>

            <Typography variant="body2"
                        color="textSecondary"
                        component="div">
                {productValues.price} {productValues.currency}
            </Typography>
        </CardContent>
    </CardActionArea>;

}
Example #4
Source File: WeaponCardNano.tsx    From genshin-optimizer with MIT License 6 votes vote down vote up
export default function WeaponCardNano({ weaponId, showLocation = false, onClick, BGComponent = CardDark, }: Data) {
  const weapon = useWeapon(weaponId)
  const weaponSheet = usePromise(weapon?.key && WeaponSheet.get(weapon.key), [weapon?.key])
  const actionWrapperFunc = useCallback(children => <CardActionArea sx={{ height: "100%" }} onClick={onClick}>{children}</CardActionArea>, [onClick],)
  const UIData = useMemo(() => weaponSheet && weapon && computeUIData([weaponSheet.data, dataObjForWeapon(weapon)]), [weaponSheet, weapon])
  if (!weapon || !weaponSheet || !UIData) return <BGComponent sx={{ height: "100%" }}><Skeleton variant="rectangular" sx={{ width: "100%", height: "100%" }} /></BGComponent>;
  const { refinement, location } = weapon
  return <BGComponent sx={{ height: "100%" }}><ConditionalWrapper condition={!!onClick} wrapper={actionWrapperFunc}  >
    <Box display="flex" height="100%" alignItems="stretch" >
      <Box className={`grad-${weaponSheet.rarity}star`} sx={{ height: "100%", position: "relative", flexGrow: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "flex-end" }} >
        <WeaponNameTooltip sheet={weaponSheet}>
          <Box
            component="img"
            src={weaponSheet.img}
            sx={{ mx: -1, maxHeight: "100%", maxWidth: "100%" }}
          />
        </WeaponNameTooltip>
        <Box sx={{ position: "absolute", width: "100%", height: "100%", p: 0.5, opacity: 0.85, display: "flex", justifyContent: "space-between", pointerEvents: "none" }} >
          <Chip size="small" label={<strong>{WeaponSheet.getLevelString(weapon)}</strong>} color="primary" />
          {showLocation && <Chip size="small" label={<LocationIcon location={location} />} color={"secondary"} sx={{
            overflow: "visible", ".MuiChip-label": {
              overflow: "visible"
            }
          }} />}
        </Box>
        <Box sx={{ position: "absolute", width: "100%", height: "100%", p: 0.5, opacity: 0.85, display: "flex", justifyContent: "flex-end", alignItems: "flex-end" }} >
          {weaponSheet.hasRefinement && <Chip size="small" color="info" label={<strong>R{refinement}</strong>} />}
        </Box>
      </Box>
      <Box display="flex" flexDirection="column" sx={{ p: 1, }}>
        <WeaponStat node={UIData.get(input.weapon.main)} />
        <WeaponStat node={UIData.get(input.weapon.sub)} />
      </Box>
    </Box>
  </ConditionalWrapper></BGComponent >
}
Example #5
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 #6
Source File: AdminPage.tsx    From frontend with MIT License 5 votes vote down vote up
export default function AdminPage() {
  const { t } = useTranslation()
  const router = useRouter()
  const { data: session, status } = useSession()
  if (status !== 'authenticated') {
    return (
      <AdminLayout>
        <AdminContainer title={t('nav.admin.index')}>
          <Box p={3}>
            <Typography variant="h6">Not authenticated</Typography>
          </Box>
        </AdminContainer>
      </AdminLayout>
    )
  }

  if (!isAdmin(session)) {
    return (
      <AdminLayout>
        <AdminContainer title={t('nav.admin.index')}>
          <Box p={3}>
            <Typography variant="h6">Not authorized</Typography>
          </Box>
        </AdminContainer>
      </AdminLayout>
    )
  }

  return (
    <AdminLayout>
      <AdminContainer title={t('nav.admin.index')}>
        <Box p={4}>
          <Typography variant="h6">{'Добре дошли!'}</Typography>
        </Box>
        <Grid container spacing={2} rowSpacing={4} px={4} pb={4}>
          {menuItems.map(({ label, href, icon: Icon }, index) => (
            <Grid xs={12} sm={6} md={4} lg={2} item key={index}>
              <Card sx={{ maxWidth: 345 }}>
                <CardActionArea onClick={() => router.push(href)}>
                  <CardContent>
                    <Box textAlign="center">
                      <Icon fontSize="medium" />
                    </Box>
                    <Typography gutterBottom variant="h6" component="div" textAlign="center">
                      {label}
                    </Typography>
                  </CardContent>
                </CardActionArea>
              </Card>
            </Grid>
          ))}
        </Grid>
      </AdminContainer>
    </AdminLayout>
  )
}
Example #7
Source File: TabTalent.tsx    From genshin-optimizer with MIT License 5 votes vote down vote up
function SkillDisplayCard({ talentKey, subtitle, onClickTitle }: SkillDisplayCardProps) {
  const { data, character: { talent }, characterSheet, characterDispatch } = useContext(DataContext)

  const actionWrapeprFunc = useCallback(
    children => <CardActionArea onClick={onClickTitle}>{children}</CardActionArea>,
    [onClickTitle],
  )

  let header: Displayable | null = null

  if (talentKey in talent) {
    const levelBoost = data.get(input.bonus[talentKey] as NumNode).value
    const level = data.get(input.total[talentKey]).value
    const asc = data.get(input.asc).value
    const setTalentLevel = (tKey, newTalentLevelKey) => {
      talent[tKey] = newTalentLevelKey
      characterDispatch({ talent })
    }
    header = <DropdownButton fullWidth title={`Talent Lv. ${level}`} color={levelBoost ? "info" : "primary"} sx={{ borderRadius: 0 }}>
      {range(1, talentLimits[asc]).map(i =>
        <MenuItem key={i} selected={talent[talentKey] === (i)} disabled={talent[talentKey] === (i)} onClick={() => setTalentLevel(talentKey, i)}>Talent Lv. {i + levelBoost}</MenuItem>)}
    </DropdownButton>
  }
  const talentSheet = characterSheet.getTalentOfKey(talentKey, data.get(input.charEle).value as ElementKey | undefined)

  // Hide header if the header matches the current talent
  const hideHeader = (section: DocumentSection): boolean => {
    let headerAction = section.header?.action
    if (headerAction && (typeof headerAction !== "string")) {
      const key: string = headerAction.props.children.props.key18
      return key.includes(talentKey)
    }
    return false
  }

  return <CardLight sx={{ height: "100%" }}>
    {header}
    <CardContent>
      <ConditionalWrapper condition={!!onClickTitle} wrapper={actionWrapeprFunc} >
        <Grid container sx={{ flexWrap: "nowrap" }}>
          <Grid item>
            <Box component="img" src={talentSheet?.img} sx={{ width: 60, height: "auto" }} />
          </Grid>
          <Grid item flexGrow={1} sx={{ pl: 1 }}>
            <Typography variant="h6">{talentSheet?.name}</Typography>
            <Typography variant="subtitle1">{subtitle}</Typography>
          </Grid>
        </Grid>
      </ConditionalWrapper>
      {/* Display document sections */}
      {talentSheet?.sections ? <DocumentDisplay sections={talentSheet.sections} hideDesc hideHeader={hideHeader} /> : null}
    </CardContent>
  </CardLight>
}
Example #8
Source File: CampaignCard.tsx    From frontend with MIT License 5 votes vote down vote up
export default function CampaignCard({ campaign }: Props) {
  const { t } = useTranslation()

  const target = campaign.targetAmount
  const summary = campaign.summary.find(() => true)
  const pictureUrl = campaignListPictureUrl(campaign)
  const reached = summary ? summary.reachedAmount : 0

  return (
    <StyledCard variant="outlined" className={classes.cardWrapper}>
      <CardActionArea>
        <Link href={routes.campaigns.viewCampaignBySlug(campaign.slug)}>
          <CardMedia
            className={classes.media}
            image={pictureUrl}
            title="campaign image placeholder"
          />
        </Link>
        <CardContent className={classes.cardContent}>
          <Typography textAlign={'left'} gutterBottom variant="h5" component="h2">
            {campaign.title}
          </Typography>
          <Typography textAlign={'left'} variant="body2" color="textSecondary" component="p">
            {campaign.essence}
          </Typography>
        </CardContent>
      </CardActionArea>
      <CardActions className={classes.cardActions}>
        <Grid container justifyContent="space-around">
          <Box p={2} width={1}>
            <CampaignProgress raised={reached} target={target} />
          </Box>
          <Typography variant="subtitle1" component="p" className={classes.progressBar}>
            {t('campaigns:campaign.reached')} <b>{money(reached)}</b> /{' '}
            {t('campaigns:campaign.target')} <b>{money(target)}</b>
          </Typography>
          <Grid item xs={12}>
            <Box mx={2} mb={2}>
              <LinkButton
                fullWidth
                href={routes.campaigns.oneTimeDonation(campaign.slug)}
                variant="contained"
                color="secondary"
                endIcon={<Favorite color="error" />}>
                {t('campaigns:cta.support-now')}
              </LinkButton>
            </Box>
            <Box mx={2} mb={2}>
              <LinkButton
                fullWidth
                href={routes.campaigns.viewCampaignBySlug(campaign.slug)}
                variant="outlined"
                endIcon={<ArrowForwardIosIcon />}>
                {t('campaigns:cta.see-more')}
              </LinkButton>
            </Box>
          </Grid>
        </Grid>
      </CardActions>
    </StyledCard>
  )
}
Example #9
Source File: WeaponSelectionModal.tsx    From genshin-optimizer with MIT License 5 votes vote down vote up
export default function WeaponSelectionModal({ show, onHide, onSelect, filter = () => true, weaponFilter: propWeaponFilter }: WeaponSelectionModalProps) {
  const weaponSheets = usePromise(WeaponSheet.getAll, [])
  const [weaponFilter, setWeaponfilter] = useState<WeaponTypeKey | "">(propWeaponFilter ?? "")

  useEffect(() => propWeaponFilter && setWeaponfilter(propWeaponFilter), [propWeaponFilter])

  const weaponIdList = !weaponSheets ? [] : [...new Set(allWeaponKeys)].filter(wKey => filter(weaponSheets[wKey]))
    .filter(wKey => {
      if (weaponFilter && weaponFilter !== weaponSheets?.[wKey]?.weaponType) return false
      return true
    })
    .sort((a, b) => (weaponSheets?.[b]?.rarity ?? 0) - (weaponSheets?.[a]?.rarity ?? 0))

  if (!weaponSheets) return null

  return <ModalWrapper open={show} onClose={onHide}>
    <CardDark>
      <CardContent sx={{ py: 1 }}>
        <Grid container>
          <Grid item flexGrow={1}>
            <WeaponToggle value={weaponFilter} onChange={setWeaponfilter} disabled={!!propWeaponFilter} size="small" />
          </Grid >
          <Grid item>
            <CloseButton onClick={onHide} />
          </Grid >
        </Grid>
      </CardContent>
      <Divider />
      <CardContent><Grid container spacing={1}>
        {weaponIdList.map(weaponKey => {
          const weaponSheet = weaponSheets[weaponKey]
          return <Grid item key={weaponKey} lg={3} md={4}>
            <CardLight sx={{ height: "100%" }} >
              <CardActionArea onClick={() => { onHide(); onSelect(weaponKey) }} sx={{ display: "flex" }}>
                <Box component="img" src={weaponSheet.img} sx={{ width: 100, height: "auto" }} className={` grad-${weaponSheet.rarity}star`} />
                <Box sx={{ flexGrow: 1, px: 1 }}>
                  <Typography variant="subtitle1">{weaponSheet.name}</Typography>
                  <Typography><ImgIcon src={Assets.weaponTypes?.[weaponSheet.weaponType]} /> <Stars stars={weaponSheet.rarity} colored /></Typography>
                </Box>
              </CardActionArea>
            </CardLight>
          </Grid>
        })}
      </Grid></CardContent>
      <Divider />
      <CardContent sx={{ py: 1 }}>
        <CloseButton large onClick={onHide} />
      </CardContent>
    </CardDark>
  </ModalWrapper>
}
Example #10
Source File: CharacterCard.tsx    From genshin-optimizer with MIT License 5 votes vote down vote up
function Header({ onClick }: { onClick?: (characterKey: CharacterKey, tab: string) => void }) {
  const { data, characterSheet } = useContext(DataContext)
  const characterKey = data.get(input.charKey).value as CharacterKey
  const characterEle = data.get(input.charEle).value as ElementKey
  const characterLevel = data.get(input.lvl).value
  const constellation = data.get(input.constellation).value
  const ascension = data.get(input.asc).value
  const autoBoost = data.get(input.bonus.auto).value
  const skillBoost = data.get(input.bonus.skill).value
  const burstBoost = data.get(input.bonus.burst).value

  const tAuto = data.get(input.total.auto).value
  const tSkill = data.get(input.total.skill).value
  const tBurst = data.get(input.total.burst).value

  const actionWrapperFunc = useCallback(
    children => <CardActionArea onClick={() => characterKey && onClick?.(characterKey, "overview")} sx={{ flexGrow: 1, display: "flex", flexDirection: "column" }}>{children}</CardActionArea>,
    [onClick, characterKey],
  )
  return <ConditionalWrapper condition={!!onClick} wrapper={actionWrapperFunc} >
    <Box display="flex"
      position="relative"
      className={`grad-${characterSheet.rarity}star`}
      sx={{
        "&::before": {
          content: '""',
          display: "block", position: "absolute",
          left: 0, top: 0,
          width: "100%", height: "100%",
          opacity: 0.7,
          backgroundImage: `url(${characterSheet.bannerImg})`, backgroundPosition: "center", backgroundSize: "cover",
        }
      }}
      width="100%" >
      <Box flexShrink={1} sx={{ maxWidth: { xs: "40%", lg: "40%" } }} alignSelf="flex-end" display="flex" flexDirection="column" zIndex={1}>
        <Box
          component="img"
          src={characterSheet.thumbImg}
          width="100%"
          height="auto"
          maxWidth={256}
          sx={{ mt: "auto" }}
        />
      </Box>
      <Box flexGrow={1} sx={{ py: 1, pr: 1 }} display="flex" flexDirection="column" zIndex={1}>
        <Chip label={<Typography variant="subtitle1">{characterSheet.name}</Typography>} size="small" color={characterEle} sx={{ opacity: 0.85 }} />
        <Grid container spacing={1} flexWrap="nowrap">
          <Grid item sx={{ textShadow: "0 0 5px gray" }}>
            <Typography component="span" variant="h6" whiteSpace="nowrap" >Lv. {characterLevel}</Typography>
            <Typography component="span" variant="h6" color="text.secondary">/{ascensionMaxLevel[ascension]}</Typography>
          </Grid>
          <Grid item>
            <Typography variant="h6"><SqBadge>C{constellation}</SqBadge></Typography>
          </Grid>
        </Grid>
        <Grid container spacing={1} flexWrap="nowrap">
          <Grid item>
            <Chip color={autoBoost ? "info" : "secondary"} label={<strong >{tAuto}</strong>} />
          </Grid>
          <Grid item>
            <Chip color={skillBoost ? "info" : "secondary"} label={<strong >{tSkill}</strong>} />
          </Grid>
          <Grid item>
            <Chip color={burstBoost ? "info" : "secondary"} label={<strong >{tBurst}</strong>} />
          </Grid>
        </Grid>
        <Typography mt={1} ><Stars stars={characterSheet.rarity} colored /></Typography>
      </Box>
    </Box>
  </ConditionalWrapper>
}
Example #11
Source File: CharacterCard.tsx    From genshin-optimizer with MIT License 5 votes vote down vote up
export default function CharacterCard({ characterKey, artifactChildren, weaponChildren, characterChildren, onClick, onClickHeader, footer, isTeammateCard }: CharacterCardProps) {
  const { teamData: teamDataContext } = useContext(DataContext)
  const teamData = useTeamData(teamDataContext ? "" : characterKey) ?? (teamDataContext as TeamData | undefined)
  const { character, characterSheet, target: data } = teamData?.[characterKey] ?? {}
  const onClickHandler = useCallback(() => characterKey && onClick?.(characterKey, "overview"), [characterKey, onClick])
  const actionWrapperFunc = useCallback(
    children => <CardActionArea onClick={onClickHandler} sx={{ flexGrow: 1, display: "flex", flexDirection: "column" }}>{children}</CardActionArea>,
    [onClickHandler],
  )
  const characterDispatch = useCharacterReducer(characterKey)
  if (!teamData || !character || !characterSheet || !data) return null;
  const dataContextObj: dataContextObj = {
    character,
    data,
    characterSheet,
    mainStatAssumptionLevel: 0,
    teamData,
    characterDispatch
  }

  return <Suspense fallback={<Skeleton variant="rectangular" sx={{ width: "100%", height: "100%", minHeight: 350 }} />}>
    <DataContext.Provider value={dataContextObj}>
      <CardLight sx={{ height: "100%", display: "flex", flexDirection: "column" }}>
        <Box sx={{ display: "flex", position: "absolute", zIndex: 2, opacity: 0.7 }}>
          <IconButton sx={{ p: 0.5 }} onClick={event => characterDispatch({ favorite: !character.favorite })}>
            {character.favorite ? <Favorite /> : <FavoriteBorder />}
          </IconButton>
        </Box>
        <ConditionalWrapper condition={!!onClick} wrapper={actionWrapperFunc} >
          <Header onClick={!onClick ? onClickHeader : undefined} />
          <CardContent sx={{ width: "100%", display: "flex", flexDirection: "column", gap: 1, flexGrow: 1 }}>
            <Artifacts />
            {!isTeammateCard && <Grid container columns={4} spacing={0.75}>
              <Grid item xs={1} height="100%">
                <WeaponCardPico weaponId={character.equippedWeapon} />
              </Grid>
              {range(0, 2).map(i => <Grid key={i} item xs={1} height="100%"><CharacterCardPico characterKey={character.team[i]} index={i} /></Grid>)}
            </Grid>}
            {isTeammateCard && <WeaponFullCard weaponId={character.equippedWeapon} />}
            {!isTeammateCard && <Stats />}
            {weaponChildren}
            {artifactChildren}
            {characterChildren}
          </CardContent>
        </ConditionalWrapper>
        {footer}
      </CardLight>
    </DataContext.Provider>
  </Suspense>
}
Example #12
Source File: ArtifactCardNano.tsx    From genshin-optimizer with MIT License 5 votes vote down vote up
export default function ArtifactCardNano({ artifactId, slotKey: pSlotKey, mainStatAssumptionLevel = 0, showLocation = false, onClick, BGComponent = CardDark }: Data) {
  const art = useArtifact(artifactId)
  const sheet = usePromise(ArtifactSheet.get(art?.setKey), [art])
  const actionWrapperFunc = useCallback(children => <CardActionArea onClick={onClick} sx={{ height: "100%" }}>{children}</CardActionArea>, [onClick],)
  const theme = useTheme()
  if (!art) return <BGComponent sx={{ display: "flex", height: "100%", alignItems: "center", justifyContent: "center" }}>
    <Box component="img" src={Assets.slot[pSlotKey]} sx={{ width: "25%", height: "auto", opacity: 0.7 }} />
  </BGComponent>

  const { slotKey, rarity, level, mainStatKey, substats, location } = art
  const mainStatLevel = Math.max(Math.min(mainStatAssumptionLevel, rarity * 4), level)
  const mainStatUnit = KeyMap.unit(mainStatKey)
  const levelVariant = "roll" + (Math.floor(Math.max(level, 0) / 4) + 1)
  const element = allElementsWithPhy.find(ele => art.mainStatKey.includes(ele))
  const color = element ? alpha(theme.palette[element].main, 0.6) : alpha(theme.palette.secondary.main, 0.6)
  return <BGComponent sx={{ height: "100%" }}><ConditionalWrapper condition={!!onClick} wrapper={actionWrapperFunc}  >
    <Box display="flex" height="100%">
      <Box className={`grad-${rarity}star`} sx={{ position: "relative", flexGrow: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center" }} >
        <ArtifactSetSlotTooltip slotKey={slotKey} sheet={sheet}>
          <Box
            component="img"
            src={sheet?.slotIcons[slotKey] ?? ""}
            sx={{ m: -1, maxHeight: "110%", maxWidth: "110%" }}
          />
        </ArtifactSetSlotTooltip>
        <Box sx={{ position: "absolute", width: "100%", height: "100%", p: 0.5, opacity: 0.85, display: "flex", justifyContent: "space-between", pointerEvents: "none" }} >
          <Chip size="small" label={<strong>{` +${level}`}</strong>} color={levelVariant as any} />
          {showLocation && <Chip size="small" label={<LocationIcon location={location} />} color={"secondary"} sx={{
            overflow: "visible", ".MuiChip-label": {
              overflow: "visible"
            }
          }} />}
        </Box>
        {/* mainstats */}
        <Chip size="small" sx={{ position: "absolute", bottom: 0, mb: 1, backgroundColor: color }}
          label={<Typography variant="h6" sx={{ display: "flex", gap: 1, px: 1, zIndex: 1 }}>
            <BootstrapTooltip placement="top" title={<Typography>{KeyMap.getArtStr(mainStatKey)}</Typography>} disableInteractive>
              <span>{element ? uncoloredEleIcons[element] : StatIcon[mainStatKey]}</span>
            </BootstrapTooltip>
            <ColorText color={mainStatLevel !== level ? "warning" : undefined}>{cacheValueString(Artifact.mainStatValue(mainStatKey, rarity, mainStatLevel) ?? 0, KeyMap.unit(mainStatKey))}{mainStatUnit}</ColorText>
          </Typography>} />
      </Box>
      {/* substats */}
      <Box display="flex" flexDirection="column" justifyContent="space-between" sx={{ p: 1, }}>
        {substats.map((stat: ICachedSubstat, i: number) => <SubstatDisplay key={i + stat.key} stat={stat} />)}
      </Box>
    </Box>
  </ConditionalWrapper></BGComponent >
}
Example #13
Source File: index.tsx    From Search-Next with GNU General Public License v3.0 5 votes vote down vote up
WebsiteCard: React.FC<WebsiteCardProps> = (props) => {
  const { dataSource } = props;
  const Card = (
    <CardActionArea>
      <a
        className={classNames(
          'p-1.5 shadow-lg rounded text-center flex items-center justify-center',
          css`
            --tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1),
              0 1px 2px 0 ${hexToRgba(dataSource.color ?? '#000', 0.7).rgba} !important;
            border-bottom: 1px solid ${dataSource.color};
            &:hover {
              color: ${dataSource.color};
            }
          `,
        )}
        href={dataSource.url}
        target="_blank"
      >
        <div className="overflow-hidden whitespace-nowrap text-ellipsis">
          {dataSource.name}
        </div>
      </a>
    </CardActionArea>
  );
  return dataSource.intro ? (
    <Tooltip
      title={
        <div
          className={css`
            max-width: 256px;
          `}
        >
          <p>{dataSource.name}</p>
          <div>{dataSource.intro}</div>
        </div>
      }
    >
      {Card}
    </Tooltip>
  ) : (
    Card
  );
}
Example #14
Source File: MarketItem.tsx    From Cromwell with MIT License 5 votes vote down vote up
export default function MarketItem(props: PropsType) {
    const data = props?.data;
    const [installing, setInstalling] = useState(false);
    const [installed, setInstalled] = useState(!!(props.data?.name
        && props?.listItemProps?.installedModules?.find(inst => inst.name === props.data?.name)));

    const installModule = async () => {
        if (!props.listItemProps?.install || !data) return;

        setInstalling(true);
        const success = await props.listItemProps.install(data);
        if (success) setInstalled(true);
        setInstalling(false);
    }

    return (
        <Grid item xs={6} lg={4} className={styles.listItem}>
            <div className={clsx(styles.listItemContent, installing && styles.installing)}>
                {data?.image && (
                    <CardActionArea
                        onClick={() => props.listItemProps?.open(props.data)}
                        className={styles.cardActionArea}
                    >
                        <img src={data.image} className={styles.image} />
                    </CardActionArea>
                )}
                <div className={styles.caption}>
                    <Badge color="secondary" badgeContent={installed ? 'installed' : null}>
                        <Typography gutterBottom variant="h5" component="h3" className={styles.title}>
                            {data?.title ?? ''}
                        </Typography>
                    </Badge>
                    <p className={styles.version}>{data?.version ?? ''}</p>
                    <p className={styles.excerpt}>{data?.excerpt ?? ''}</p>
                </div>
                <div className={styles.actions}>
                    <Button
                        size="small" color="primary" variant="contained"
                        onClick={() => props.listItemProps?.open(props.data)}
                    >Open</Button>
                    <Button
                        disabled={installed || installing}
                        size="small" color="primary" variant="contained"
                        onClick={installModule}
                    >Install</Button>
                </div>
                {installing && (
                    <LinearProgress className={styles.updateProgress} />
                )}
            </div>
        </Grid>
    )
}
Example #15
Source File: itemCard.tsx    From Search-Next with GNU General Public License v3.0 5 votes vote down vote up
ItemCard: React.FC<ItemCardProps> = ({
  title,
  desc,
  icon,
  onClick,
  action,
  size = 'medium',
  ...props
}) => {
  const Content = (
    <CardContent
      className={classNames(
        size === 'medium' && 'px-4 py-3',
        size === 'small' && 'px-3 py-2',
        onClick && 'cursor-pointer',
      )}
      onClick={() => (onClick ? onClick() : null)}
    >
      <div className="flex">
        <div className="flex-grow flex items-center justify-start">
          {icon && <div className="mr-1">{icon}</div>}
          <div>
            <p className="text-sm mb-0">{title}</p>
            {desc && <p className="text-xs mb-0 text-gray-700">{desc}</p>}
          </div>
        </div>
        <div className="flex items-center" onClick={(e) => e.stopPropagation()}>
          {action ? action : <KeyboardArrowRight fontSize="small" />}
        </div>
      </div>
    </CardContent>
  );

  return (
    <StyleCard type="border">
      {action ? (
        Content
      ) : (
        <CardActionArea onClick={() => (onClick ? onClick() : null)}>
          {Content}
        </CardActionArea>
      )}
    </StyleCard>
  );
}
Example #16
Source File: DonationTab.tsx    From frontend with MIT License 4 votes vote down vote up
export default function DonationTab() {
  const router = useRouter()
  const { t } = useTranslation()
  const matches = useMediaQuery(theme.breakpoints.down('md'))

  const { data: user } = getCurrentPerson(!!router.query?.register)
  if (router.query?.register) {
    delete router.query.register
    router.replace({ pathname: router.pathname, query: router.query }, undefined, { shallow: true })
  }
  const { data: userDonations, isLoading: isUserDonationLoading } = useUserDonations()
  const { data: campaigns, isLoading: isCampaignLoading } = useCampaignList()
  return (
    <StyledProfileTab name={ProfileTabs.donations}>
      <Typography className={classes.h1}>{user?.user ? user.user.firstName + ',' : ''}</Typography>
      <Typography variant="h5" fontWeight={'medium'}>
        {t('profile:donations.helpThanks')} ❤️
      </Typography>
      <Grid
        container
        spacing={theme.spacing(2)}
        marginTop={theme.spacing(1)}
        alignItems={'flex-end'}>
        <Grid order={matches ? 3 : 1} item xs={12} md={4}>
          <Card>
            {!isCampaignLoading && campaigns ? (
              <CardActionArea>
                <CardMedia
                  component="img"
                  height="193"
                  image={campaignListPictureUrl(campaigns[0])}
                  alt={campaigns[0].title}
                />
                <CardContent>
                  <Typography gutterBottom variant="h5" component="div">
                    {campaigns[0].title}
                  </Typography>
                  <Typography variant="body2" color="text.secondary">
                    {truncate(campaigns[0].description, { length: 120 })}
                  </Typography>
                </CardContent>
              </CardActionArea>
            ) : (
              <CircularProgress />
            )}
            <CardActions>
              <Button variant="contained" size="medium" color="secondary">
                {t('profile:donations.donateNow')} ❤️
              </Button>
            </CardActions>
          </Card>
        </Grid>
        <Grid order={matches ? 1 : 2} item xs={12} md={8}>
          {!isUserDonationLoading && userDonations ? (
            <Card className={classes.donationsBox}>
              <Box className={classes.donationsBoxRow}>
                <Typography fontWeight="medium" variant="h5">
                  {t('profile:donations.totalDonations')}
                </Typography>
                <Typography fontWeight="medium" variant="h5">
                  {money(userDonations.total)}
                </Typography>
              </Box>
              <Box className={classes.donationsBoxRow}>
                <Box>
                  <Typography variant="h5">{t('profile:donations.recurringDonations')}</Typography>
                  {/* TODO: Use date-fns to format and localize the months,
                   that the user has recurring donations when that is possible */}
                  {/* <Typography>Я, Ф, М, А 2022</Typography> */}
                </Box>
                <Typography fontWeight="medium" variant="h5">
                  {money(sumBy(userDonations.donations, 'amount'))}
                </Typography>
              </Box>
              <Box className={classes.donationsBoxRow}>
                <Typography variant="h5">{t('profile:donations.cardDonations')}</Typography>
                <Typography fontWeight="medium" variant="h5">
                  {money(userDonations.total)}
                </Typography>
              </Box>
              <Box className={classes.donationsBoxRow}>
                <Typography variant="h5">{t('profile:donations.bankDonations')}</Typography>
                <Typography fontWeight="medium" variant="h5">
                  {money(userDonations.total)}
                </Typography>
              </Box>
            </Card>
          ) : (
            <CircularProgress />
          )}
        </Grid>
        <Grid order={matches ? 2 : 3} item xs={12}>
          <DonationTable donations={userDonations?.donations} />
        </Grid>
      </Grid>
    </StyledProfileTab>
  )
}
Example #17
Source File: websiteCardNew.tsx    From Search-Next with GNU General Public License v3.0 4 votes vote down vote up
WebsiteCardNew: React.FC<WebsiteCardNewProps> = (props) => {
  const { datasource } = props;
  const { name, intro, color, url } = datasource;

  const onAdd = () => {
    const res = addSite({
      name,
      url: url.substring(0, url.lastIndexOf('/')),
    });
    if (res) toast.success('添加成功');
  };

  const onCopy = () => {
    if (navigator.clipboard) {
      navigator.clipboard.writeText(url);
      toast.success(`已复制 ${name} (${url})`);
    } else {
      const copy = new Clipboard(`.copy-button_${name}`);
      copy.on('success', (e) => {
        toast.success(`已复制 ${name} (${url})`);
      });
      copy.on('error', function (e) {
        toast.warning(
          `您的浏览器不支持复制功能,请点击跳转到该网站手动复制地址`,
        );
      });
    }
  };

  const onMore = () => {
    toast.warning('功能开发中...');
  };

  return (
    <div
      className={classNames(
        'cursor-pointer shadow-md rounded border-b-2',
        css`
          --tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1),
            0 1px 3px 0 ${hexToRgba(color ?? '#000', 0.45).rgba} !important;
          border-bottom-color: ${color};
        `,
      )}
    >
      <CardActionArea>
        <Tooltip title={intro || '暂无介绍'}>
          <div className="p-3 flex gap-3" onClick={() => window.open(url)}>
            <Avatar
              // style={{ backgroundColor: color }}
              src={getWebIconByUrl(url)}
            >
              {name.split('')[0].toUpperCase()}
            </Avatar>
            <div className="flex-grow overflow-hidden">
              <p className="font-bold text-base whitespace-nowrap overflow-x-hidden">
                {name}
              </p>
              <Overflow>{(intro as any) || ('暂无介绍' as any)}</Overflow>
            </div>
          </div>
        </Tooltip>
      </CardActionArea>
      <div>
        <ButtonGroup
          disableElevation
          variant="text"
          size="small"
          className={classNames(
            'w-full h-full flex',
            css`
              justify-content: flex-end;
              button {
                height: 100%;
                border-right: 0px !important;
              }
            `,
          )}
        >
          <Tooltip title="添加到首页">
            <Button onClick={onAdd}>
              <Add />
            </Button>
          </Tooltip>
          <Tooltip title="复制网站链接">
            <Button
              className={`copy-button_${name}`}
              data-clipboard-text={url}
              onClick={onCopy}
            >
              <CopyAll />
            </Button>
          </Tooltip>
          {false && (
            <Tooltip title="更多">
              <Button onClick={onMore}>
                <MoreHoriz />
              </Button>
            </Tooltip>
          )}
        </ButtonGroup>
      </div>
    </div>
  );
}
Example #18
Source File: ArtifactCard.tsx    From genshin-optimizer with MIT License 4 votes vote down vote up
export default function ArtifactCard({ artifactId, artifactObj, onClick, onDelete, mainStatAssumptionLevel = 0, effFilter = allSubstatFilter, probabilityFilter, disableEditSetSlot = false, editor = false, canExclude = false, canEquip = false, extraButtons }: Data): JSX.Element | null {
  const { t } = useTranslation(["artifact", "ui"]);
  const { database } = useContext(DatabaseContext)
  const databaseArtifact = useArtifact(artifactId)
  const sheet = usePromise(ArtifactSheet.get((artifactObj ?? databaseArtifact)?.setKey), [artifactObj, databaseArtifact])
  const equipOnChar = (charKey: CharacterKey | "") => database.setArtLocation(artifactId!, charKey)
  const editable = !artifactObj
  const [showEditor, setshowEditor] = useState(false)
  const onHideEditor = useCallback(() => setshowEditor(false), [setshowEditor])
  const onShowEditor = useCallback(() => editable && setshowEditor(true), [editable, setshowEditor])

  const wrapperFunc = useCallback(children => <CardActionArea onClick={() => artifactId && onClick?.(artifactId)} sx={{ flexGrow: 1, display: "flex", flexDirection: "column" }} >{children}</CardActionArea>, [onClick, artifactId],)
  const falseWrapperFunc = useCallback(children => <Box sx={{ flexGrow: 1, display: "flex", flexDirection: "column" }} >{children}</Box>, [])

  const art = artifactObj ?? databaseArtifact
  if (!art) return null

  const { id, lock, slotKey, rarity, level, mainStatKey, substats, exclude, location = "" } = art
  const mainStatLevel = Math.max(Math.min(mainStatAssumptionLevel, rarity * 4), level)
  const mainStatUnit = KeyMap.unit(mainStatKey)
  const levelVariant = "roll" + (Math.floor(Math.max(level, 0) / 4) + 1)
  const { currentEfficiency, maxEfficiency } = Artifact.getArtifactEfficiency(art, effFilter)
  const artifactValid = maxEfficiency !== 0
  const slotName = sheet?.getSlotName(slotKey) || "Unknown Piece Name"
  const slotDesc = sheet?.getSlotDesc(slotKey)
  const slotDescTooltip = slotDesc && <InfoTooltip title={<Box>
    <Typography variant='h6'>{slotName}</Typography>
    <Typography>{slotDesc}</Typography>
  </Box>} />
  const setEffects = sheet?.setEffects
  const setDescTooltip = sheet && setEffects && <InfoTooltip title={
    <span>
      {Object.keys(setEffects).map(setNumKey => <span key={setNumKey}>
        <Typography variant="h6"><SqBadge color="success">{t(`artifact:setEffectNum`, { setNum: setNumKey })}</SqBadge></Typography>
        <Typography>{sheet.setEffectDesc(setNumKey as any)}</Typography>
      </span>)}
    </span>
  } />
  return <Suspense fallback={<Skeleton variant="rectangular" sx={{ width: "100%", height: "100%", minHeight: 350 }} />}>
    {editor && <Suspense fallback={false}>
      <ArtifactEditor
        artifactIdToEdit={showEditor ? artifactId : ""}
        cancelEdit={onHideEditor}
        disableEditSetSlot={disableEditSetSlot}
      />
    </Suspense>}
    <CardLight sx={{ height: "100%", display: "flex", flexDirection: "column" }}>
      <ConditionalWrapper condition={!!onClick} wrapper={wrapperFunc} falseWrapper={falseWrapperFunc}>
        <Box className={`grad-${rarity}star`} sx={{ position: "relative", width: "100%" }}>
          {!onClick && <IconButton color="primary" disabled={!editable} onClick={() => database.updateArt({ lock: !lock }, id)} sx={{ position: "absolute", right: 0, bottom: 0, zIndex: 2 }}>
            {lock ? <Lock /> : <LockOpen />}
          </IconButton>}
          <Box sx={{ pt: 2, px: 2, position: "relative", zIndex: 1 }}>
            {/* header */}
            <Box component="div" sx={{ display: "flex", alignItems: "center", gap: 1, mb: 1 }}>
              <Chip size="small" label={<strong>{` +${level}`}</strong>} color={levelVariant as any} />
              <Typography component="span" noWrap sx={{ backgroundColor: "rgba(100,100,100,0.35)", borderRadius: "1em", px: 1 }}><strong>{slotName}</strong></Typography>
              <Box flexGrow={1} sx={{ textAlign: "right" }}>
                {slotDescTooltip}
              </Box>
            </Box>
            <Typography color="text.secondary" variant="body2">
              <SlotNameWithIcon slotKey={slotKey} />
            </Typography>
            <Typography variant="h6" color={`${KeyMap.getVariant(mainStatKey)}.main`}>
              <span>{StatIcon[mainStatKey]} {KeyMap.get(mainStatKey)}</span>
            </Typography>
            <Typography variant="h5">
              <strong>
                <ColorText color={mainStatLevel !== level ? "warning" : undefined}>{cacheValueString(Artifact.mainStatValue(mainStatKey, rarity, mainStatLevel) ?? 0, KeyMap.unit(mainStatKey))}{mainStatUnit}</ColorText>
              </strong>
            </Typography>
            <Stars stars={rarity} colored />
            {/* {process.env.NODE_ENV === "development" && <Typography color="common.black">{id || `""`} </Typography>} */}
          </Box>
          <Box sx={{ height: "100%", position: "absolute", right: 0, top: 0 }}>
            <Box
              component="img"
              src={sheet?.slotIcons[slotKey] ?? ""}
              width="auto"
              height="100%"
              sx={{ float: "right" }}
            />
          </Box>
        </Box>
        <CardContent sx={{ flexGrow: 1, display: "flex", flexDirection: "column", pt: 1, pb: 0, width: "100%" }}>
          {substats.map((stat: ICachedSubstat) => <SubstatDisplay key={stat.key} stat={stat} effFilter={effFilter} rarity={rarity} />)}
          <Box sx={{ display: "flex", my: 1 }}>
            <Typography color="text.secondary" component="span" variant="caption" sx={{ flexGrow: 1 }}>{t`artifact:editor.curSubEff`}</Typography>
            <PercentBadge value={currentEfficiency} max={900} valid={artifactValid} />
          </Box>
          {currentEfficiency !== maxEfficiency && <Box sx={{ display: "flex", mb: 1 }}>
            <Typography color="text.secondary" component="span" variant="caption" sx={{ flexGrow: 1 }}>{t`artifact:editor.maxSubEff`}</Typography>
            <PercentBadge value={maxEfficiency} max={900} valid={artifactValid} />
          </Box>}
          <Box flexGrow={1} />
          {probabilityFilter && <strong>Probability: {(probability(art, probabilityFilter) * 100).toFixed(2)}%</strong>}
          <Typography color="success.main">{sheet?.name ?? "Artifact Set"} {setDescTooltip}</Typography>
        </CardContent>
      </ConditionalWrapper>
      <Box sx={{ p: 1, display: "flex", gap: 1, justifyContent: "space-between", alignItems: "center" }}>
        {editable && canEquip
          ? <CharacterAutocomplete sx={{ flexGrow: 1 }} size="small" showDefault
            defaultIcon={<BusinessCenter />} defaultText={t("ui:inventory")}
            value={location} onChange={equipOnChar} />
          : <LocationName location={location} />}
        {editable && <ButtonGroup sx={{ height: "100%" }}>
          {editor && <Tooltip title={<Typography>{t`artifact:edit`}</Typography>} placement="top" arrow>
            <Button color="info" size="small" onClick={onShowEditor} >
              <FontAwesomeIcon icon={faEdit} className="fa-fw" />
            </Button>
          </Tooltip>}
          {canExclude && <Tooltip title={<Typography>{t`artifact:excludeArtifactTip`}</Typography>} placement="top" arrow>
            <Button onClick={() => database.updateArt({ exclude: !exclude }, id)} color={exclude ? "error" : "success"} size="small" >
              <FontAwesomeIcon icon={exclude ? faBan : faChartLine} className="fa-fw" />
            </Button>
          </Tooltip>}
          {!!onDelete && <Button color="error" size="small" onClick={() => onDelete(id)} disabled={lock}>
            <FontAwesomeIcon icon={faTrashAlt} className="fa-fw" />
          </Button>}
          {extraButtons}
        </ButtonGroup>}
      </Box>
    </CardLight >
  </Suspense>
}
Example #19
Source File: WeaponCard.tsx    From genshin-optimizer with MIT License 4 votes vote down vote up
export default function WeaponCard({ weaponId, onClick, onEdit, onDelete, canEquip = false, extraButtons }: WeaponCardProps) {
  const { t } = useTranslation(["page_weapon", "ui"]);
  const { database } = useContext(DatabaseContext)
  const databaseWeapon = useWeapon(weaponId)
  const weapon = databaseWeapon
  const weaponSheet = usePromise(weapon?.key ? WeaponSheet.get(weapon.key) : undefined, [weapon?.key])

  const filter = useCallback(
    (cs: CharacterSheet) => cs.weaponTypeKey === weaponSheet?.weaponType,
    [weaponSheet],
  )

  const wrapperFunc = useCallback(children => <CardActionArea onClick={() => onClick?.(weaponId)} >{children}</CardActionArea>, [onClick, weaponId],)
  const falseWrapperFunc = useCallback(children => <Box >{children}</Box>, [])

  const equipOnChar = useCallback((charKey: CharacterKey | "") => database.setWeaponLocation(weaponId, charKey), [database, weaponId],)

  const UIData = useMemo(() => weaponSheet && weapon && computeUIData([weaponSheet.data, dataObjForWeapon(weapon)]), [weaponSheet, weapon])

  if (!weapon || !weaponSheet || !UIData) return null;
  const { level, ascension, refinement, id, location = "", lock } = weapon
  const weaponTypeKey = UIData.get(input.weapon.type).value!
  const stats = [input.weapon.main, input.weapon.sub, input.weapon.sub2].map(x => UIData.get(x))
  const img = ascension < 2 ? weaponSheet?.img : weaponSheet?.imgAwaken

  return <Suspense fallback={<Skeleton variant="rectangular" sx={{ width: "100%", height: "100%", minHeight: 300 }} />}>
    <CardLight sx={{ height: "100%", display: "flex", flexDirection: "column", justifyContent: "space-between" }}>
      <ConditionalWrapper condition={!!onClick} wrapper={wrapperFunc} falseWrapper={falseWrapperFunc}>
        <Box className={`grad-${weaponSheet.rarity}star`} sx={{ position: "relative", pt: 2, px: 2, }}>
          {!onClick && <IconButton color="primary" onClick={() => database.updateWeapon({ lock: !lock }, id)} sx={{ position: "absolute", right: 0, bottom: 0, zIndex: 2 }}>
            {lock ? <Lock /> : <LockOpen />}
          </IconButton>}
          <Box sx={{ position: "relative", zIndex: 1 }}>
            <Box component="div" sx={{ display: "flex", alignItems: "center", gap: 1, mb: 1 }}>
              <ImgIcon sx={{ fontSize: "1.5em" }} src={Assets.weaponTypes?.[weaponTypeKey]} />
              <Typography noWrap sx={{ textAlign: "center", backgroundColor: "rgba(100,100,100,0.35)", borderRadius: "1em", px: 1 }}><strong>{weaponSheet.name}</strong></Typography>
            </Box>
            <Typography component="span" variant="h5">Lv. {level}</Typography>
            <Typography component="span" variant="h5" color="text.secondary">/{ascensionMaxLevel[ascension]}</Typography>
            <Typography variant="h6">Refinement <strong>{refinement}</strong></Typography>
            <Typography><Stars stars={weaponSheet.rarity} colored /></Typography>
          </Box>
          <Box sx={{ height: "100%", position: "absolute", right: 0, top: 0 }}>
            <Box
              component="img"
              src={img ?? ""}
              width="auto"
              height="100%"
              sx={{ float: "right" }}
            />
          </Box>
        </Box>
        <CardContent>
          {stats.map(node => {
            if (!node.info.key) return null
            const displayVal = valueString(node.value, node.unit, !node.unit ? 0 : undefined)
            return <Box key={node.info.key} sx={{ display: "flex" }}>
              <Typography flexGrow={1}>{StatIcon[node.info.key!]} {KeyMap.get(node.info.key)}</Typography>
              <Typography>{displayVal}</Typography>
            </Box>
          })}
        </CardContent>
      </ConditionalWrapper>
      <Box sx={{ p: 1, display: "flex", gap: 1, justifyContent: "space-between", alignItems: "center" }}>
        {canEquip
          ? <CharacterAutocomplete size="small" sx={{ flexGrow: 1 }}
            showDefault defaultIcon={<BusinessCenter />} defaultText={t("inventory")}
            value={location} onChange={equipOnChar} filter={filter} />
          : <LocationName location={location} />}
        <ButtonGroup>
          {!!onEdit && <Tooltip title={<Typography>{t`page_weapon:edit`}</Typography>} placement="top" arrow>
            <Button color="info" onClick={() => onEdit(id)} >
              <FontAwesomeIcon icon={faEdit} className="fa-fw" />
            </Button>
          </Tooltip>}
          {!!onDelete && <Button color="error" onClick={() => onDelete(id)} disabled={!!location || lock} >
            <FontAwesomeIcon icon={faTrashAlt} className="fa-fw" />
          </Button>}
          {extraButtons}
        </ButtonGroup>
      </Box>
    </CardLight>
  </Suspense>
}
Example #20
Source File: TabOverview.tsx    From genshin-optimizer with MIT License 4 votes vote down vote up
export default function TabOverview() {
  const { data, characterSheet, character, character: { key: characterKey } } = useContext(DataContext)
  const characterDispatch = useCharacterReducer(characterKey)
  const navigate = useNavigate()
  const { t } = useTranslation("page_character")
  const charEle = data.get(input.charEle).value as ElementKey
  const weaponTypeKey = characterSheet.weaponTypeKey
  const level = data.get(input.lvl).value
  const ascension = data.get(input.asc).value
  const constellation = data.get(input.constellation).value
  const tlvl = {
    auto: data.get(input.total.auto).value,
    skill: data.get(input.total.skill).value,
    burst: data.get(input.total.burst).value,
  }
  const tBoost = {
    auto: data.get(input.bonus.auto).value,
    skill: data.get(input.bonus.skill).value,
    burst: data.get(input.bonus.burst).value,
  }
  return <Grid container spacing={1} sx={{ justifyContent: "center" }}>
    <Grid item xs={8} sm={5} md={4} lg={2.5}  >
      {/* Image card with star and name and level */}
      <CardLight >
        <Box src={characterSheet.cardImg} component="img" width="100%" height="auto" />
        <CardContent>
          <Typography variant="h5" >
            {characterSheet.name}&nbsp;
            <ImgIcon sx={{ pr: 0.5 }} src={Assets.weaponTypes?.[weaponTypeKey]} />
            {StatIcon[charEle]}
            <IconButton sx={{ p: 0.5, mt: -0.5 }} onClick={() => characterDispatch({ favorite: !character.favorite })}>
              {character.favorite ? <Favorite /> : <FavoriteBorder />}
            </IconButton>
          </Typography>
          <Typography variant="h6"><Stars stars={characterSheet.rarity} colored /></Typography>
          <Typography variant="h5">Lvl. {CharacterSheet.getLevelString(level, ascension)}</Typography>
          <CardActionArea sx={{ p: 1 }} onClick={() => navigate("talent")}>
            <Grid container spacing={1} mt={-1}>
              {(["auto", "skill", "burst"] as TalentSheetElementKey[]).map(tKey =>
                <Grid item xs={4} key={tKey}>
                  <Badge badgeContent={tlvl[tKey]} color={tBoost[tKey] ? "info" : "secondary"}
                    overlap="circular"
                    anchorOrigin={{
                      vertical: 'bottom',
                      horizontal: 'right',
                    }}
                    sx={{
                      width: "100%",
                      height: "100%",
                      "& > .MuiBadge-badge": {
                        fontSize: "1.25em",
                        padding: ".25em .4em",
                        borderRadius: ".5em",
                        lineHeight: 1,
                        height: "1.25em"
                      }
                    }}>
                    <Box component="img" src={characterSheet.getTalentOfKey(tKey, charEle)?.img} width="100%" height="auto" />
                  </Badge>
                </Grid>)}
            </Grid>
          </CardActionArea>
          <Typography sx={{ textAlign: "center", mt: 1 }} variant="h6">{characterSheet.constellationName}</Typography>
          <Grid container spacing={1}>
            {range(1, 6).map(i =>
              <Grid item xs={4} key={i}>
                <CardActionArea onClick={() => characterDispatch({ constellation: i === constellation ? i - 1 : i })}>
                  <Box component="img" src={characterSheet.getTalentOfKey(`constellation${i}` as TalentSheetElementKey, charEle)?.img}
                    sx={{
                      ...(constellation >= i ? {} : { filter: "brightness(50%)" })
                    }}
                    width="100%" height="auto" />
                </CardActionArea>
              </Grid>)}
          </Grid>
          <Typography sx={{ textAlign: "center", mt: 1 }} variant="h6">{t("teammates")}</Typography>
          <CardActionArea sx={{ p: 1 }} onClick={() => navigate("teambuffs")}>
            <Grid container columns={3} spacing={1}>
              {range(0, 2).map(i => <Grid key={i} item xs={1} height="100%"><CharacterCardPico characterKey={character.team[i]} index={i} /></Grid>)}
            </Grid>
          </CardActionArea>
        </CardContent>
      </CardLight>
    </Grid>
    <Grid item xs={12} sm={7} md={8} lg={9.5} sx={{
      display: "flex", flexDirection: "column", gap: 1
    }} >
      <Grid container spacing={1} columns={{ xs: 2, sm: 2, md: 3, lg: 4, xl: 6 }}>
        <Grid item xs={1}>
          <WeaponCardNano weaponId={character.equippedWeapon} BGComponent={CardLight} onClick={() => navigate("equip")} />
        </Grid>
        {allSlotKeys.map(slotKey =>
          <Grid item key={slotKey} xs={1} >
            <ArtifactCardNano artifactId={data.get(input.art[slotKey].id).value} slotKey={slotKey} BGComponent={CardLight} onClick={() => navigate("equip")} />
          </Grid>)}
      </Grid>
      <MainStatsCards />
    </Grid>
  </Grid >
}
Example #21
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 #22
Source File: MenuItem.tsx    From Cromwell with MIT License 4 votes vote down vote up
Item = (props: {
    data: TMainMenuItem;
    itemProps?: {
        items: TMainMenuItem[];
        canReorder: boolean;
        refreshList: () => void;
    }
}) => {
    const { refreshList, items, canReorder } = props.itemProps ?? {};
    const item = props.data;

    const forceUpdate = useForceUpdate();
    const classes = useStyles();
    const [_expanded, setExpanded] = React.useState(false);
    const expanded = _expanded && !canReorder;

    if (!item) return null;

    const handleExpandClick = () => {
        if (canReorder) return;
        setExpanded(!_expanded);
    };

    const handleChange = (prop: keyof TMainMenuItem, val: string) => {
        (item as any)[prop] = val;
        forceUpdate();
    }

    const handleRemove = (event) => {
        event.stopPropagation();
        if (items) {
            items.splice(items.indexOf(item), 1);
            refreshList?.();
        }
    }

    return (
        <div className={`${classes.card} PluginMainMenu-paper`}>
            <CardActionArea
                className={classes.cardHeader}
                onClick={handleExpandClick}
            >
                <p className={classes.cardTitle}>{item.title}</p>
                <CardActions disableSpacing className={classes.cardActions}>
                    {!canReorder && (
                        <IconButton
                            className={clsx(classes.expand, {
                                [classes.expandOpen]: expanded,
                            })}
                            aria-expanded={expanded}
                            aria-label="show more"
                        >
                            <ExpandMoreIcon />
                        </IconButton>
                    )}
                    <IconButton onClick={handleRemove}>
                        <HighlightOffIcon />
                    </IconButton>
                </CardActions>
            </CardActionArea>
            <Collapse in={expanded} timeout="auto" unmountOnExit>
                <div className={classes.cardContent}>
                    <TextField label="Title" variant="outlined"
                        value={item.title}
                        className={classes.field}
                        onChange={(e) => { handleChange('title', e.target.value) }}
                    />
                    <TextField label="Link" variant="outlined"
                        value={item.href}
                        className={classes.field}
                        onChange={(e) => { handleChange('href', e.target.value) }}
                    />
                    <TextField label="Columns" variant="outlined"
                        value={item.sublinkCols}
                        className={classes.field}
                        onChange={(e) => { handleChange('sublinkCols', e.target.value) }}
                    />
                    <TextField label="Width in px" variant="outlined"
                        value={item.width}
                        className={classes.field}
                        onChange={(e) => { handleChange('width', e.target.value) }}
                    />
                    <TextField
                        value={item.html}
                        label="Custom HTML"
                        multiline
                        rows={4}
                        variant="outlined"
                        className={classes.field}
                        onChange={(e) => { handleChange('html', e.target.value) }}
                    />
                    <div className={classes.sublinksList}>
                        <h3 className={classes.sublinksTitle}>Sublinks</h3>
                        {item.sublinks && item.sublinks.map((sl, slIndex) => {
                            return (
                                <div className={`${classes.sublinkItem} PluginMainMenu-paper`} >
                                    <TextField label="Sublink title" variant="outlined"
                                        value={sl.title}
                                        className={classes.subField}
                                        onChange={(e) => { if (item.sublinks) item.sublinks[slIndex].title = e.target.value; forceUpdate(); }}
                                    />
                                    <TextField label="Sublink href" variant="outlined"
                                        value={sl.href}
                                        className={classes.subField}
                                        onChange={(e) => { if (item.sublinks) item.sublinks[slIndex].href = e.target.value; forceUpdate(); }}
                                    />
                                    <IconButton onClick={(e) => {
                                        e.stopPropagation();
                                        if (item.sublinks) item.sublinks.splice(slIndex, 1);
                                        refreshList?.();
                                    }}>
                                        <HighlightOffIcon />
                                    </IconButton>
                                </div>
                            )
                        })}
                        <div className={`PluginMainMenu-paper ${classes.card}`}>
                            <MenuItem
                                className={classes.addBtn}
                                onClick={() => {
                                    if (!item.sublinks) item.sublinks = [];
                                    item.sublinks.push({});
                                    forceUpdate();
                                }}>
                                <AddIcon />
                            </MenuItem>
                        </div>
                    </div>
                </div>
            </Collapse>
        </div>
    )
}
Example #23
Source File: FireCMSHomePage.tsx    From firecms with MIT License 4 votes vote down vote up
/**
 * Default entry view for the CMS under the path "/"
 * This components takes navigation as an input and renders cards
 * for each entry, including title and description.
 * @constructor
 * @category Components
 */
export function FireCMSHomePage() {

    const classes = useStyles();
    const navigationContext = useNavigation();
    if (!navigationContext.navigation)
        return <></>;

    const {
        navigationEntries,
        groups
    } = computeTopNavigation(navigationContext, false);

    const allGroups: Array<string | null> = [...groups];
    if (navigationEntries.filter(e => !e.group).length > 0) {
        allGroups.push(null);
    }

    function buildNavigationCard(entry: TopNavigationEntry) {
        return (
            <Grid item xs={12}
                  sm={6}
                  md={4}
                  key={`nav_${entry.group}_${entry.name}`}>
                <Paper variant={"outlined"}>

                    <CardActionArea
                        className={classes.card}
                        component={ReactLink}
                        to={entry.url}>
                        <CardContent
                            className={classes.flexGrow}>

                            <PlaylistPlayIcon color={"disabled"}/>
                            <Typography gutterBottom variant="h5"
                                        component="h2">
                                {entry.name}
                            </Typography>

                            {entry.description && <Typography variant="body2"
                                                              color="textSecondary"
                                                              component="div">
                                <Markdown source={entry.description}/>
                            </Typography>}
                        </CardContent>

                        <CardActions style={{ alignSelf: "flex-end" }}>
                            <Box p={1}>
                                <ArrowForwardIcon color="primary"/>
                            </Box>
                        </CardActions>

                    </CardActionArea>
                </Paper>
            </Grid>
        );
    }

    return (
        <Container>
            {allGroups.map((group, index) => (
                <Box mt={6} mb={6} key={`group_${index}`}>
                    {allGroups.length > 0 && <>
                        <Typography color={"textSecondary"}
                                    className={"weight-500"}>
                            {group?.toUpperCase() ?? "Collections".toUpperCase()}
                        </Typography>
                        <Divider/>
                    </>}

                    <Box mt={2}>
                        <Grid container spacing={2}>
                            {group && navigationEntries
                                .filter((entry) => entry.group === group)
                                .map((entry) => buildNavigationCard(entry))
                            }
                            {!group && navigationEntries
                                .filter((entry) => !entry.group)
                                .map((entry) => buildNavigationCard(entry))
                            }
                        </Grid>
                    </Box>
                </Box>
            ))}
        </Container>
    );
}