@mui/icons-material#Replay TypeScript Examples
The following examples show how to use
@mui/icons-material#Replay.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: ArtifactSlotDropdown.tsx From genshin-optimizer with MIT License | 6 votes |
export default function ArtifactSlotDropdown({ slotKey = "", onChange, hasUnselect = false, ...props }: ArtifactSlotDropdownProps) {
const { t } = useTranslation(["artifact", "ui"]);
return <DropdownButton
title={slotKey ? t(`artifact:slotName:${slotKey}`) : t('artifact:slot')}
color={slotKey ? "success" : "primary"}
startIcon={slotKey ? artifactSlotIcon(slotKey) : undefined}
{...props}
>
{hasUnselect && <MenuItem selected={slotKey === ""} disabled={slotKey === ""} onClick={() => onChange("")} >
<ListItemIcon>
<Replay />
</ListItemIcon>
<ListItemText>
{t`ui:unselect`}
</ListItemText>
</MenuItem>}
{hasUnselect && <Divider />}
{allSlotKeys.map(key =>
<MenuItem key={key} selected={slotKey === key} disabled={slotKey === key} onClick={() => onChange(key)} >
<ListItemIcon>
{artifactSlotIcon(key)}
</ListItemIcon>
<ListItemText>
{t(`artifact:slotName:${key}`)}
</ListItemText>
</MenuItem>)}
</DropdownButton>
}
Example #2
Source File: StatInput.tsx From genshin-optimizer with MIT License | 6 votes |
export default function StatInput({ name, children, value, placeholder, defaultValue = 0, onValueChange, percent = false, disabled = false, onReset, ...restProps }: StatInputInput) {
return <FlexButtonGroup {...restProps} >
{children}
<TextButton sx={{ whiteSpace: "nowrap" }} >
{name}
</TextButton>
<CustomNumberInputButtonGroupWrapper sx={{ flexBasis: 30, flexGrow: 1 }} >
<CustomNumberInput
sx={{ px: 1 }}
inputProps={{
sx: { textAlign: "right" }
}}
float={percent}
placeholder={placeholder}
value={value}
onChange={onValueChange}
disabled={disabled}
endAdornment={percent ? "%" : undefined}
/>
</CustomNumberInputButtonGroupWrapper>
<Button onClick={() => onReset ? onReset() : onValueChange(defaultValue)} disabled={disabled || value === defaultValue} >
<Replay />
</Button>
</FlexButtonGroup>
}
Example #3
Source File: ArtifactFilter.tsx From genshin-optimizer with MIT License | 6 votes |
export default function ArtifactFilter({ filterOption, filterOptionDispatch, filterDispatch, numShowing, total, }:
{ filterOption: FilterOption, filterOptionDispatch: (any) => void, filterDispatch: (any) => void, numShowing: number, total: number }) {
const { t } = useTranslation(["artifact", "ui"])
return <Suspense fallback={<Skeleton variant="rectangular" width="100%" height={300} />}>
<CardDark >
<CardContent>
<Grid container>
<Grid item >
<Typography variant="h6"><Trans t={t} i18nKey="artifactFilter">Artifact Filter</Trans></Typography>
</Grid>
<Grid item flexGrow={1} display="flex" justifyContent="center" alignItems="center">
{numShowing !== total && <Typography>Filtered {numShowing} / {total}</Typography>}
</Grid>
<Grid item>
<Button size="small" color="error" onClick={() => filterDispatch({ type: "reset" })} startIcon={<Replay />}>
<Trans t={t} i18nKey="ui:reset" />
</Button>
</Grid>
</Grid>
<Suspense fallback={<Skeleton variant="rectangular" width="100%" height={200} />}>
<ArtifactFilterDisplay filterOption={filterOption} filterOptionDispatch={filterOptionDispatch} />
</Suspense>
</CardContent>
</CardDark>
</Suspense>
}
Example #4
Source File: ArtifactSetConditional.tsx From genshin-optimizer with MIT License | 5 votes |
function ArtConditionalModal({ open, onClose, artifactCondCount }: {
open: boolean, onClose: () => void, artifactCondCount: number
}) {
const dataContext = useContext(DataContext)
const { character, characterDispatch } = dataContext
const artifactSheets = usePromise(ArtifactSheet.getAll, [])
const resetArtConds = useCallback(() => {
const conditional = Object.fromEntries(Object.entries(character.conditional).filter(([k, v]) => !allArtifactSets.includes(k as any)))
characterDispatch({ conditional })
}, [character, characterDispatch]);
if (!artifactSheets) return null
const artSetKeyList = Object.entries(ArtifactSheet.setKeysByRarities(artifactSheets)).reverse().flatMap(([, sets]) => sets)
return <ModalWrapper open={open} onClose={onClose} ><CardDark>
<CardContent>
<Grid container spacing={1}>
<Grid item flexGrow={1}>
<Typography variant="h6">Default Artifact Set Effects {!!artifactCondCount && <SqBadge color="success">{artifactCondCount} Selected</SqBadge>}</Typography>
</Grid>
<Grid item>
<Button onClick={resetArtConds} color="error" startIcon={<Replay />}>Reset All</Button>
</Grid>
<Grid item>
<CloseButton onClick={onClose} />
</Grid>
</Grid>
</CardContent>
<Divider />
<CardContent>
<CardLight sx={{ mb: 1 }}>
<CardContent>
<Typography>Some artifacts provide conditional stats. This windows allows you to select those stats, so they can take effect during build calculation, when artifact sets are not specified.</Typography>
</CardContent>
</CardLight>
<Grid container spacing={1}>
{artSetKeyList.map(setKey => {
const sheet: ArtifactSheet = artifactSheets[setKey]
// Don't display if no conditional in artifact
if (!Object.values(sheet.setEffects).some(entry => entry.document && entry.document.some(d => "states" in d))) return null
return <Grid item key={setKey} xs={6} lg={4}>
<CardLight sx={{ height: "100%" }}>
<Box className={`grad-${sheet.rarity[0]}star`} width="100%" sx={{ display: "flex" }} >
<Box component="img" src={sheet.defIconSrc} sx={{ height: 100, width: "auto" }} />
<Box sx={{ flexGrow: 1, px: 1, display: "flex", flexDirection: "column", justifyContent: "center" }}>
<Typography variant="h6">{sheet.name ?? ""}</Typography>
<Box display="flex" gap={1}>
<Typography variant="subtitle1">{sheet.rarity.map((ns, i) => <span key={ns}>{ns} <Stars stars={1} /> {i < (sheet.rarity.length - 1) ? "/ " : null}</span>)}</Typography>
{/* If there is ever a 2-set conditional, we will need to change this */}
<InfoTooltip title={<Typography><Translate ns={`artifact_${setKey}_gen`} key18={"setEffects.4"} /></Typography>} />
</Box>
</Box>
</Box>
<DataContext.Provider value={fakeData(dataContext) /* TODO: Do we need to Memo this? */}>
<CardContent sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
{Object.keys(sheet.setEffects)
.filter(setNumKey => sheet.setEffects[setNumKey]?.document
.some(doc => "states" in doc)
)
.map(setNumKey =>
<SetEffectDisplay key={setNumKey} setKey={setKey} setNumKey={parseInt(setNumKey) as SetNum} hideHeader conditionalsOnly />
)
}
</CardContent>
</DataContext.Provider>
</CardLight>
</Grid>
})}
</Grid>
</CardContent>
<Divider />
<CardContent sx={{ py: 1 }}>
<CloseButton large onClick={onClose} />
</CardContent>
</CardDark></ModalWrapper>
}
Example #5
Source File: MainStatSelectionCard.tsx From genshin-optimizer with MIT License | 5 votes |
export default function MainStatSelectionCard({ mainStatKeys, onChangeMainStatKey, disabled = false, }: {
mainStatKeys: BuildSetting["mainStatKeys"]
onChangeMainStatKey: (slotKey: SlotKey, mainStatKey?: MainStatKey) => void
disabled?: boolean
}) {
const { t } = useTranslation("artifact")
return <Box display="flex" flexDirection="column" gap={1}>
{artifactsSlotsToSelectMainStats.map(slotKey => {
const numSel = mainStatKeys[slotKey].length
return <Box key={slotKey}>
<Divider />
<CardContent sx={{ pt: 1 }}>
<Box sx={{ display: "flex", gap: 1, alignItems: "center", pb: 1 }}>
<BootstrapTooltip placement="top" title={<Typography>{t(`slotName.${slotKey}`)}</Typography>}>
<span>{artifactSlotIcon(slotKey)}</span>
</BootstrapTooltip>
<Box flexGrow={1}>
<SqBadge color="info">{numSel ? `${numSel} Selected` : `Any`}</SqBadge>
</Box>
<Button color="error" size="small" disabled={!mainStatKeys[slotKey].length || disabled} sx={{ mt: -1, mb: -1 }}
onClick={() => onChangeMainStatKey(slotKey)}>
<Replay />
</Button>
</Box>
<Grid container spacing={1}>
{Artifact.slotMainStats(slotKey).map((mainStatKey, i) => {
const element = allElementsWithPhy.find(ele => mainStatKey.includes(ele))
const color = mainStatKeys[slotKey].includes(mainStatKey)
? element ?? "success"
: "secondary"
return <Grid item key={mainStatKey} flexGrow={1} xs={i < 3 ? 4 : undefined} >
<BootstrapTooltip placement="top" title={<Typography><strong>{KeyMap.getArtStr(mainStatKey)}</strong></Typography>} disableInteractive>
<Button fullWidth size="small" color={color} sx={{ fontSize: "1.2em", height: "100%", pointerEvents: disabled ? "none" : undefined, cursor: disabled ? "none" : undefined }}
onClick={() => onChangeMainStatKey(slotKey, mainStatKey)}>
{element ? uncoloredEleIcons[element] : StatIcon[mainStatKey]}
</Button>
</BootstrapTooltip>
</Grid>
})}
</Grid>
</CardContent>
</Box>
})}
</Box >
}
Example #6
Source File: UseEquipped.tsx From genshin-optimizer with MIT License | 5 votes |
export default function UseEquipped({ useEquippedArts, buildSettingsDispatch, disabled }) {
const { t } = useTranslation("page_character")
const { character: { key: characterKey } } = useContext(DataContext)
const { database } = useContext(DatabaseContext)
const [show, onOpen, onClose] = useBoolState(false)
const [{ equipmentPriority: tempEquipmentPriority }, setOptimizeDBState] = useOptimizeDBState()
//Basic validate for the equipmentPrio list to remove dups and characters that doesnt exist.
const equipmentPriority = useMemo(() => [...new Set(tempEquipmentPriority)].filter(ck => database._getChar(ck)), [database, tempEquipmentPriority])
const setPrio = useCallback((equipmentPriority: CharacterKey[]) => setOptimizeDBState({ equipmentPriority }), [setOptimizeDBState])
const setPrioRank = useCallback((fromIndex, toIndex) => {
const arr = [...equipmentPriority]
var element = arr[fromIndex];
arr.splice(fromIndex, 1);
arr.splice(toIndex, 0, element);
setPrio(arr)
}, [equipmentPriority, setPrio])
const removePrio = useCallback((fromIndex) => {
const arr = [...equipmentPriority]
arr.splice(fromIndex, 1)
setPrio(arr)
}, [equipmentPriority, setPrio])
const addPrio = useCallback((ck: CharacterKey) => setPrio([...equipmentPriority, ck]), [equipmentPriority, setPrio])
const resetPrio = useCallback(() => setPrio([]), [setPrio])
const numAbove = useMemo(() => {
let numAbove = equipmentPriority.length
const index = equipmentPriority.indexOf(characterKey)
if (index >= 0) numAbove = index
return numAbove
}, [characterKey, equipmentPriority])
const numUseEquippedChar = useMemo(() => {
return database._getCharKeys().length - 1 - numAbove
}, [numAbove, database])
const numUnlisted = useMemo(() => {
return database._getCharKeys().length - equipmentPriority.length
}, [equipmentPriority, database])
return <Box display="flex" gap={1}>
<ModalWrapper open={show} onClose={onClose} containerProps={{ maxWidth: "sm" }}><CardDark>
<CardContent>
<Grid container spacing={1}>
<Grid item flexGrow={1}>
<Typography variant="h6"><Trans t={t} i18nKey="tabOptimize.useEquipped.modal.title">Character Priority for Equipped Artifacts</Trans></Typography>
</Grid>
<Grid item sx={{ mb: -1 }}>
<CloseButton onClick={onClose} />
</Grid>
</Grid>
</CardContent>
<Divider />
<CardContent>
<CardLight sx={{ mb: 1 }}>
<CardContent>
<Typography gutterBottom><Trans t={t} i18nKey="tabOptimize.useEquipped.modal.desc1">When generating a build, the Optimizer will only consider equipped artifacts from characters below the current character or those not on the list.</Trans></Typography>
<Typography gutterBottom><Trans t={t} i18nKey="tabOptimize.useEquipped.modal.desc2">If the current character is not on the list, the Optimizer will only consider equipped artifacts from others characters that are not on the list.</Trans></Typography>
</CardContent>
</CardLight>
<Box display="flex" flexDirection="column" gap={2}>
{equipmentPriority.map((ck, i) =>
<SelectItem key={ck} characterKey={ck} rank={i + 1} maxRank={equipmentPriority.length} setRank={(num) => num && setPrioRank(i, num - 1)} onRemove={() => removePrio(i)} numAbove={numAbove} />)}
<Box sx={{ display: "flex", gap: 1 }}>
<NewItem onAdd={addPrio} list={equipmentPriority} />
<Button color="error" onClick={resetPrio} startIcon={<Replay />}><Trans t={t} i18nKey="tabOptimize.useEquipped.modal.clearList">Clear List</Trans></Button>
</Box>
{!!numUseEquippedChar && <SqBadge color="success"><Typography><Trans t={t} i18nKey="tabOptimize.useEquipped.modal.usingNum" count={numUnlisted}>Using artifacts from <strong>{{ count: numUnlisted }}</strong> unlisted characters</Trans></Typography></SqBadge>}
</Box>
</CardContent>
</CardDark ></ModalWrapper>
<ButtonGroup sx={{ display: "flex", width: "100%" }}>
<Button sx={{ flexGrow: 1 }} onClick={() => buildSettingsDispatch({ useEquippedArts: !useEquippedArts })} disabled={disabled} startIcon={useEquippedArts ? <CheckBox /> : <CheckBoxOutlineBlank />} color={useEquippedArts ? "success" : "secondary"}>
<Box>
<span><Trans t={t} i18nKey="tabOptimize.useEquipped.title">Use Equipped Artifacts</Trans></span>
{useEquippedArts && <SqBadge><Trans t={t} i18nKey="tabOptimize.useEquipped.usingNum" count={numUseEquippedChar}>Using from <strong>{{ count: numUseEquippedChar }}</strong> characters</Trans></SqBadge>}
</Box>
</Button>
{useEquippedArts && <Button sx={{ flexShrink: 1 }} color="info" onClick={onOpen}><Settings /></Button>}
</ButtonGroup>
</Box>
}
Example #7
Source File: ArtifactEditor.tsx From genshin-optimizer with MIT License | 4 votes |
export default function ArtifactEditor({ artifactIdToEdit = "", cancelEdit, allowUpload = false, allowEmpty = false, disableEditSetSlot: disableEditSlotProp = false }:
{ artifactIdToEdit?: string, cancelEdit: () => void, allowUpload?: boolean, allowEmpty?: boolean, disableEditSetSlot?: boolean }) {
const { t } = useTranslation("artifact")
const artifactSheets = usePromise(ArtifactSheet.getAll, [])
const { database } = useContext(DatabaseContext)
const [show, setShow] = useState(false)
const [dirtyDatabase, setDirtyDatabase] = useForceUpdate()
useEffect(() => database.followAnyArt(setDirtyDatabase), [database, setDirtyDatabase])
const [editorArtifact, artifactDispatch] = useReducer(artifactReducer, undefined)
const artifact = useMemo(() => editorArtifact && parseArtifact(editorArtifact), [editorArtifact])
const [modalShow, setModalShow] = useState(false)
const [{ processed, outstanding }, dispatchQueue] = useReducer(queueReducer, { processed: [], outstanding: [] })
const firstProcessed = processed[0] as ProcessedEntry | undefined
const firstOutstanding = outstanding[0] as OutstandingEntry | undefined
const processingImageURL = usePromise(firstOutstanding?.imageURL, [firstOutstanding?.imageURL])
const processingResult = usePromise(firstOutstanding?.result, [firstOutstanding?.result])
const remaining = processed.length + outstanding.length
const image = firstProcessed?.imageURL ?? processingImageURL
const { artifact: artifactProcessed, texts } = firstProcessed ?? {}
// const fileName = firstProcessed?.fileName ?? firstOutstanding?.fileName ?? "Click here to upload Artifact screenshot files"
const disableEditSetSlot = disableEditSlotProp || !!artifact?.location
useEffect(() => {
if (!artifact && artifactProcessed)
artifactDispatch({ type: "overwrite", artifact: artifactProcessed })
}, [artifact, artifactProcessed, artifactDispatch])
useEffect(() => {
const numProcessing = Math.min(maxProcessedCount - processed.length, maxProcessingCount, outstanding.length)
const processingCurrent = numProcessing && !outstanding[0].result
outstanding.slice(0, numProcessing).forEach(processEntry)
if (processingCurrent)
dispatchQueue({ type: "processing" })
}, [processed.length, outstanding])
useEffect(() => {
if (processingResult)
dispatchQueue({ type: "processed", ...processingResult })
}, [processingResult, dispatchQueue])
const uploadFiles = useCallback((files: FileList) => {
setShow(true)
dispatchQueue({ type: "upload", files: [...files].map(file => ({ file, fileName: file.name })) })
}, [dispatchQueue, setShow])
const clearQueue = useCallback(() => dispatchQueue({ type: "clear" }), [dispatchQueue])
useEffect(() => {
const pasteFunc = (e: any) => uploadFiles(e.clipboardData.files)
allowUpload && window.addEventListener('paste', pasteFunc);
return () => {
if (allowUpload) window.removeEventListener('paste', pasteFunc)
}
}, [uploadFiles, allowUpload])
const onUpload = useCallback(
e => {
uploadFiles(e.target.files)
e.target.value = null // reset the value so the same file can be uploaded again...
},
[uploadFiles],
)
const { old, oldType }: { old: ICachedArtifact | undefined, oldType: "edit" | "duplicate" | "upgrade" | "" } = useMemo(() => {
const databaseArtifact = dirtyDatabase && artifactIdToEdit && database._getArt(artifactIdToEdit)
if (databaseArtifact) return { old: databaseArtifact, oldType: "edit" }
if (artifact === undefined) return { old: undefined, oldType: "" }
const { duplicated, upgraded } = dirtyDatabase && database.findDuplicates(artifact)
return { old: duplicated[0] ?? upgraded[0], oldType: duplicated.length !== 0 ? "duplicate" : "upgrade" }
}, [artifact, artifactIdToEdit, database, dirtyDatabase])
const { artifact: cachedArtifact, errors } = useMemo(() => {
if (!artifact) return { artifact: undefined, errors: [] as Displayable[] }
const validated = validateArtifact(artifact, artifactIdToEdit)
if (old) {
validated.artifact.location = old.location
validated.artifact.exclude = old.exclude
}
return validated
}, [artifact, artifactIdToEdit, old])
// Overwriting using a different function from `databaseArtifact` because `useMemo` does not
// guarantee to trigger *only when* dependencies change, which is necessary in this case.
useEffect(() => {
if (artifactIdToEdit === "new") {
setShow(true)
artifactDispatch({ type: "reset" })
}
const databaseArtifact = artifactIdToEdit && dirtyDatabase && database._getArt(artifactIdToEdit)
if (databaseArtifact) {
setShow(true)
artifactDispatch({ type: "overwrite", artifact: deepClone(databaseArtifact) })
}
}, [artifactIdToEdit, database, dirtyDatabase])
const sheet = artifact ? artifactSheets?.[artifact.setKey] : undefined
const reset = useCallback(() => {
cancelEdit?.();
dispatchQueue({ type: "pop" })
artifactDispatch({ type: "reset" })
}, [cancelEdit, artifactDispatch])
const update = useCallback((newValue: Partial<IArtifact>) => {
const newSheet = newValue.setKey ? artifactSheets![newValue.setKey] : sheet!
function pick<T>(value: T | undefined, available: readonly T[], prefer?: T): T {
return (value && available.includes(value)) ? value : (prefer ?? available[0])
}
if (newValue.setKey) {
newValue.rarity = pick(artifact?.rarity, newSheet.rarity, Math.max(...newSheet.rarity) as ArtifactRarity)
newValue.slotKey = pick(artifact?.slotKey, newSheet.slots)
}
if (newValue.rarity)
newValue.level = artifact?.level ?? 0
if (newValue.level)
newValue.level = clamp(newValue.level, 0, 4 * (newValue.rarity ?? artifact!.rarity))
if (newValue.slotKey)
newValue.mainStatKey = pick(artifact?.mainStatKey, Artifact.slotMainStats(newValue.slotKey))
if (newValue.mainStatKey) {
newValue.substats = [0, 1, 2, 3].map(i =>
(artifact && artifact.substats[i].key !== newValue.mainStatKey) ? artifact!.substats[i] : { key: "", value: 0 })
}
artifactDispatch({ type: "update", artifact: newValue })
}, [artifact, artifactSheets, sheet, artifactDispatch])
const setSubstat = useCallback((index: number, substat: ISubstat) => {
artifactDispatch({ type: "substat", index, substat })
}, [artifactDispatch])
const isValid = !errors.length
const canClearArtifact = (): boolean => window.confirm(t`editor.clearPrompt` as string)
const { rarity = 5, level = 0, slotKey = "flower" } = artifact ?? {}
const { currentEfficiency = 0, maxEfficiency = 0 } = cachedArtifact ? Artifact.getArtifactEfficiency(cachedArtifact, allSubstatFilter) : {}
const preventClosing = processed.length || outstanding.length
const onClose = useCallback(
(e) => {
if (preventClosing) e.preventDefault()
setShow(false)
cancelEdit()
}, [preventClosing, setShow, cancelEdit])
const theme = useTheme();
const grmd = useMediaQuery(theme.breakpoints.up('md'));
const element = artifact ? allElementsWithPhy.find(ele => artifact.mainStatKey.includes(ele)) : undefined
const color = artifact
? element ?? "success"
: "primary"
return <ModalWrapper open={show} onClose={onClose} >
<Suspense fallback={<Skeleton variant="rectangular" sx={{ width: "100%", height: show ? "100%" : 64 }} />}><CardDark >
<UploadExplainationModal modalShow={modalShow} hide={() => setModalShow(false)} />
<CardHeader
title={<Trans t={t} i18nKey="editor.title" >Artifact Editor</Trans>}
action={<CloseButton disabled={!!preventClosing} onClick={onClose} />}
/>
<CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
<Grid container spacing={1} columns={{ xs: 1, md: 2 }} >
{/* Left column */}
<Grid item xs={1} display="flex" flexDirection="column" gap={1}>
{/* set & rarity */}
<ButtonGroup sx={{ display: "flex", mb: 1 }}>
{/* Artifact Set */}
<ArtifactSetSingleAutocomplete
size="small"
disableClearable
artSetKey={artifact?.setKey ?? ""}
setArtSetKey={setKey => update({ setKey: setKey as ArtifactSetKey })}
sx={{ flexGrow: 1 }}
disabled={disableEditSetSlot}
/>
{/* rarity dropdown */}
<ArtifactRarityDropdown rarity={artifact ? rarity : undefined} onChange={r => update({ rarity: r })} filter={r => !!sheet?.rarity?.includes?.(r)} disabled={disableEditSetSlot || !sheet} />
</ButtonGroup>
{/* level */}
<Box component="div" display="flex">
<CustomNumberTextField id="filled-basic" label="Level" variant="filled" sx={{ flexShrink: 1, flexGrow: 1, mr: 1, my: 0 }} margin="dense" size="small"
value={level} disabled={!sheet} placeholder={`0~${rarity * 4}`} onChange={l => update({ level: l })}
/>
<ButtonGroup >
<Button onClick={() => update({ level: level - 1 })} disabled={!sheet || level === 0}>-</Button>
{rarity ? [...Array(rarity + 1).keys()].map(i => 4 * i).map(i => <Button key={i} onClick={() => update({ level: i })} disabled={!sheet || level === i}>{i}</Button>) : null}
<Button onClick={() => update({ level: level + 1 })} disabled={!sheet || level === (rarity * 4)}>+</Button>
</ButtonGroup>
</Box>
{/* slot */}
<Box component="div" display="flex">
<ArtifactSlotDropdown disabled={disableEditSetSlot || !sheet} slotKey={slotKey} onChange={slotKey => update({ slotKey })} />
<CardLight sx={{ p: 1, ml: 1, flexGrow: 1 }}>
<Suspense fallback={<Skeleton width="60%" />}>
<Typography color="text.secondary">
{sheet?.getSlotName(artifact!.slotKey) ? <span><ImgIcon src={sheet.slotIcons[artifact!.slotKey]} /> {sheet?.getSlotName(artifact!.slotKey)}</span> : t`editor.unknownPieceName`}
</Typography>
</Suspense>
</CardLight>
</Box>
{/* main stat */}
<Box component="div" display="flex">
<DropdownButton startIcon={element ? uncoloredEleIcons[element] : (artifact?.mainStatKey ? StatIcon[artifact.mainStatKey] : undefined)}
title={<b>{artifact ? KeyMap.getArtStr(artifact.mainStatKey) : t`mainStat`}</b>} disabled={!sheet} color={color} >
{Artifact.slotMainStats(slotKey).map(mainStatK =>
<MenuItem key={mainStatK} selected={artifact?.mainStatKey === mainStatK} disabled={artifact?.mainStatKey === mainStatK} onClick={() => update({ mainStatKey: mainStatK })} >
<ListItemIcon>{StatIcon[mainStatK]}</ListItemIcon>
<ListItemText>{KeyMap.getArtStr(mainStatK)}</ListItemText>
</MenuItem>)}
</DropdownButton>
<CardLight sx={{ p: 1, ml: 1, flexGrow: 1 }}>
<Typography color="text.secondary">
{artifact ? `${cacheValueString(Artifact.mainStatValue(artifact.mainStatKey, rarity, level), KeyMap.unit(artifact.mainStatKey))}${KeyMap.unit(artifact.mainStatKey)}` : t`mainStat`}
</Typography>
</CardLight>
</Box>
{/* Current/Max Substats Efficiency */}
<SubstatEfficiencyDisplayCard valid={isValid} efficiency={currentEfficiency} t={t} />
{currentEfficiency !== maxEfficiency && <SubstatEfficiencyDisplayCard max valid={isValid} efficiency={maxEfficiency} t={t} />}
{/* Image OCR */}
{allowUpload && <CardLight>
<CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
{/* TODO: artifactDispatch not overwrite */}
<Suspense fallback={<Skeleton width="100%" height="100" />}>
<Grid container spacing={1} alignItems="center">
<Grid item flexGrow={1}>
<label htmlFor="contained-button-file">
<InputInvis accept="image/*" id="contained-button-file" multiple type="file" onChange={onUpload} />
<Button component="span" startIcon={<PhotoCamera />}>
Upload Screenshot (or Ctrl-V)
</Button>
</label>
</Grid>
<Grid item>
<Button color="info" sx={{ px: 2, minWidth: 0 }} onClick={() => setModalShow(true)}><Typography><FontAwesomeIcon icon={faQuestionCircle} /></Typography></Button>
</Grid>
</Grid>
{image && <Box display="flex" justifyContent="center">
<Box component="img" src={image} width="100%" maxWidth={350} height="auto" alt="Screenshot to parse for artifact values" />
</Box>}
{remaining > 0 && <CardDark sx={{ pl: 2 }} ><Grid container spacing={1} alignItems="center" >
{!firstProcessed && firstOutstanding && <Grid item>
<CircularProgress size="1em" />
</Grid>}
<Grid item flexGrow={1}>
<Typography>
<span>
Screenshots in file-queue: <b>{remaining}</b>
{/* {process.env.NODE_ENV === "development" && ` (Debug: Processed ${processed.length}/${maxProcessedCount}, Processing: ${outstanding.filter(entry => entry.result).length}/${maxProcessingCount}, Outstanding: ${outstanding.length})`} */}
</span>
</Typography>
</Grid>
<Grid item>
<Button size="small" color="error" onClick={clearQueue}>Clear file-queue</Button>
</Grid>
</Grid></CardDark>}
</Suspense>
</CardContent>
</CardLight>}
</Grid>
{/* Right column */}
<Grid item xs={1} display="flex" flexDirection="column" gap={1}>
{/* substat selections */}
{[0, 1, 2, 3].map((index) => <SubstatInput key={index} index={index} artifact={cachedArtifact} setSubstat={setSubstat} />)}
{texts && <CardLight><CardContent>
<div>{texts.slotKey}</div>
<div>{texts.mainStatKey}</div>
<div>{texts.mainStatVal}</div>
<div>{texts.rarity}</div>
<div>{texts.level}</div>
<div>{texts.substats}</div>
<div>{texts.setKey}</div>
</CardContent></CardLight>}
</Grid>
</Grid>
{/* Duplicate/Updated/Edit UI */}
{old && <Grid container sx={{ justifyContent: "space-around" }} spacing={1} >
<Grid item xs={12} md={5.5} lg={4} ><CardLight>
<Typography sx={{ textAlign: "center" }} py={1} variant="h6" color="text.secondary" >{oldType !== "edit" ? (oldType === "duplicate" ? t`editor.dupArt` : t`editor.upArt`) : t`editor.beforeEdit`}</Typography>
<ArtifactCard artifactObj={old} />
</CardLight></Grid>
{grmd && <Grid item md={1} display="flex" alignItems="center" justifyContent="center" >
<CardLight sx={{ display: "flex" }}><ChevronRight sx={{ fontSize: 40 }} /></CardLight>
</Grid>}
<Grid item xs={12} md={5.5} lg={4} ><CardLight>
<Typography sx={{ textAlign: "center" }} py={1} variant="h6" color="text.secondary" >{t`editor.preview`}</Typography>
<ArtifactCard artifactObj={cachedArtifact} />
</CardLight></Grid>
</Grid>}
{/* Error alert */}
{!isValid && <Alert variant="filled" severity="error" >{errors.map((e, i) => <div key={i}>{e}</div>)}</Alert>}
{/* Buttons */}
<Grid container spacing={2}>
<Grid item>
{oldType === "edit" ?
<Button startIcon={<Add />} onClick={() => {
database.updateArt(editorArtifact!, old!.id);
if (allowEmpty) reset()
else {
setShow(false)
cancelEdit()
}
}} disabled={!editorArtifact || !isValid} color="primary">
{t`editor.btnSave`}
</Button> :
<Button startIcon={<Add />} onClick={() => {
database.createArt(artifact!);
if (allowEmpty) reset()
else {
setShow(false)
cancelEdit()
}
}} disabled={!artifact || !isValid} color={oldType === "duplicate" ? "warning" : "primary"}>
{t`editor.btnAdd`}
</Button>}
</Grid>
<Grid item flexGrow={1}>
{allowEmpty && <Button startIcon={<Replay />} disabled={!artifact} onClick={() => { canClearArtifact() && reset() }} color="error">{t`editor.btnClear`}</Button>}
</Grid>
<Grid item>
{process.env.NODE_ENV === "development" && <Button color="info" startIcon={<Shuffle />} onClick={async () => artifactDispatch({ type: "overwrite", artifact: await randomizeArtifact() })}>{t`editor.btnRandom`}</Button>}
</Grid>
{old && oldType !== "edit" && <Grid item>
<Button startIcon={<Update />} onClick={() => { database.updateArt(editorArtifact!, old.id); allowEmpty ? reset() : setShow(false) }} disabled={!editorArtifact || !isValid} color="success">{t`editor.btnUpdate`}</Button>
</Grid>}
</Grid>
</CardContent>
</CardDark ></Suspense>
</ModalWrapper>
}
Example #8
Source File: index.tsx From genshin-optimizer with MIT License | 4 votes |
export default function PageArtifact() {
const [{ tcMode }] = useDBState("GlobalSettings", initGlobalSettings)
const { t } = useTranslation(["artifact", "ui"]);
const { database } = useContext(DatabaseContext)
const [state, setState] = useDBState("ArtifactDisplay", initialState)
const stateDispatch = useCallback(
action => {
if (action.type === "reset") setState(initialArtifactSortFilter())
else setState(action)
},
[setState],
)
const brPt = useMediaQueryUp()
const maxNumArtifactsToDisplay = numToShowMap[brPt]
const { effFilter, filterOption, ascending, probabilityFilter } = state
let { sortType } = state
const showProbability = tcMode && sortType === "probability"
//force the sortType back to a normal value after exiting TC mode
if (sortType === "probability" && !tcMode) stateDispatch({ sortType: artifactSortKeys[0] })
const [pageIdex, setpageIdex] = useState(0)
const invScrollRef = useRef<HTMLDivElement>(null)
const [dbDirty, forceUpdate] = useForceUpdate()
const effFilterSet = useMemo(() => new Set(effFilter), [effFilter]) as Set<SubstatKey>
const deleteArtifact = useCallback((id: string) => database.removeArt(id), [database])
useEffect(() => {
ReactGA.send({ hitType: "pageview", page: '/artifact' })
return database.followAnyArt(forceUpdate)
}, [database, forceUpdate])
const filterOptionDispatch = useCallback((action) => {
stateDispatch({
filterOption: {
...filterOption,
...action
}
})
}, [stateDispatch, filterOption])
const setProbabilityFilter = useCallback(probabilityFilter => stateDispatch({ probabilityFilter }), [stateDispatch],)
const noArtifact = useMemo(() => !database._getArts().length, [database])
const sortConfigs = useMemo(() => artifactSortConfigs(effFilterSet, probabilityFilter), [effFilterSet, probabilityFilter])
const filterConfigs = useMemo(() => artifactFilterConfigs(), [])
const { artifactIds, totalArtNum } = useMemo(() => {
const { sortType = artifactSortKeys[0], ascending = false, filterOption } = state
let allArtifacts = database._getArts()
const filterFunc = filterFunction(filterOption, filterConfigs)
const sortFunc = sortFunction(sortType, ascending, sortConfigs)
//in probability mode, filter out the artifacts that already reach criteria
if (showProbability) {
allArtifacts.forEach(art => (art as any).probability = probability(art, probabilityFilter))
allArtifacts = allArtifacts.filter(art => (art as any).probability && (art as any).probability !== 1)
}
const artifactIds = allArtifacts.filter(filterFunc).sort(sortFunc).map(art => art.id)
return { artifactIds, totalArtNum: allArtifacts.length, ...dbDirty }//use dbDirty to shoo away warnings!
}, [state, dbDirty, database, sortConfigs, filterConfigs, probabilityFilter, showProbability])
const { artifactIdsToShow, numPages, currentPageIndex } = useMemo(() => {
const numPages = Math.ceil(artifactIds.length / maxNumArtifactsToDisplay)
const currentPageIndex = clamp(pageIdex, 0, numPages - 1)
return { artifactIdsToShow: artifactIds.slice(currentPageIndex * maxNumArtifactsToDisplay, (currentPageIndex + 1) * maxNumArtifactsToDisplay), numPages, currentPageIndex }
}, [artifactIds, pageIdex, maxNumArtifactsToDisplay])
//for pagination
const totalShowing = artifactIds.length !== totalArtNum ? `${artifactIds.length}/${totalArtNum}` : `${totalArtNum}`
const setPage = useCallback(
(e, value) => {
invScrollRef.current?.scrollIntoView({ behavior: "smooth" })
setpageIdex(value - 1);
},
[setpageIdex, invScrollRef],
)
return <Box display="flex" flexDirection="column" gap={1} my={1}>
<InfoComponent
pageKey="artifactPage"
modalTitle={t`info.title`}
text={t("tipsOfTheDay", { returnObjects: true }) as string[]}
>
<InfoDisplay />
</InfoComponent>
{noArtifact && <Alert severity="info" variant="filled">Looks like you haven't added any artifacts yet. If you want, there are <Link color="warning.main" component={RouterLink} to="/scanner">automatic scanners</Link> that can speed up the import process!</Alert>}
<ArtifactFilter filterOption={filterOption} filterOptionDispatch={filterOptionDispatch} filterDispatch={stateDispatch}
numShowing={artifactIds.length} total={totalArtNum} />
{showProbability && <ProbabilityFilter probabilityFilter={probabilityFilter} setProbabilityFilter={setProbabilityFilter} />}
<CardDark ref={invScrollRef}>
<CardContent>
<Grid container sx={{ mb: 1 }}>
<Grid item flexGrow={1}><span><Trans t={t} i18nKey="efficiencyFilter.title">Substats to use in efficiency calculation</Trans></span></Grid>
<Grid item>
<Button size="small" color="error" onClick={() => stateDispatch({ effFilter: [...allSubstatKeys] })} startIcon={<Replay />}><Trans t={t} i18nKey="ui:reset" /></Button>
</Grid>
</Grid>
<EfficiencyFilter selectedKeys={effFilter} onChange={n => stateDispatch({ effFilter: n })} />
</CardContent>
</CardDark>
<CardDark ><CardContent>
<Grid container alignItems="center" sx={{ pb: 2 }}>
<Grid item flexGrow={1}>
<Pagination count={numPages} page={currentPageIndex + 1} onChange={setPage} />
</Grid>
<Grid item flexGrow={1}>
<ShowingArt numShowing={artifactIdsToShow.length} total={totalShowing} t={t} />
</Grid>
<Grid item xs={12} sm={6} md={4} lg={4} xl={3} display="flex">
<Box flexGrow={1} />
<SortByButton sortKeys={[...artifactSortKeys.filter(key => (artifactSortKeysTC as unknown as string[]).includes(key) ? tcMode : true)]}
value={sortType} onChange={sortType => stateDispatch({ sortType })}
ascending={ascending} onChangeAsc={ascending => stateDispatch({ ascending })}
/>
</Grid>
</Grid>
<ArtifactRedButtons artifactIds={artifactIds} filterOption={filterOption} />
</CardContent></CardDark>
<Suspense fallback={<Skeleton variant="rectangular" sx={{ width: "100%", height: "100%", minHeight: 5000 }} />}>
<Grid container spacing={1} columns={columns} >
<Grid item xs={1} >
<NewArtifactCard />
</Grid>
{artifactIdsToShow.map(artId =>
<Grid item key={artId} xs={1} >
<ArtifactCard
artifactId={artId}
effFilter={effFilterSet}
onDelete={deleteArtifact}
probabilityFilter={showProbability ? probabilityFilter : undefined}
editor
canExclude
canEquip
/>
</Grid>
)}
</Grid>
</Suspense>
{numPages > 1 && <CardDark ><CardContent>
<Grid container>
<Grid item flexGrow={1}>
<Pagination count={numPages} page={currentPageIndex + 1} onChange={setPage} />
</Grid>
<Grid item>
<ShowingArt numShowing={artifactIdsToShow.length} total={totalShowing} t={t} />
</Grid>
</Grid>
</CardContent></CardDark>}
</Box >
}
Example #9
Source File: random.tsx From Search-Next with GNU General Public License v3.0 | 4 votes |
Random: React.FC<RandomProps> = ({ data, onChange }) => {
const [imgList, setImgList] = React.useState([] as BingImage[]); //图片列表
const [loadings, setLoadings] = React.useState<boolean[]>([]); //图片加载数组
const [checkHsh, setCheckHsh] = React.useState<string>(''); //选中图片的hsh值
const [apiLoading, setApiLoading] = React.useState<boolean>(false);
const demoList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
const [init, setInit] = React.useState<boolean>(false);
const formatItem = (data: BingImage): AuthBackgroundRandomData => {
return {
id: data._id,
url: data.url,
hsh: data.hsh,
copyright: data.copyright,
copyrightlink: data.copyrightlink,
};
};
const getList = () => {
setApiLoading(true);
const hsh = initCheck();
bingImg({ size: 10, hsh }).then((res) => {
let list = res.data;
setImgList(list);
setLoadings(list.map(() => true));
setApiLoading(false);
const image = list[0];
if (data) {
setCheckHsh(data.hsh);
} else {
if (onChange) onChange(formatItem(image));
setCheckHsh(image.hsh);
}
});
};
// 图片加载状态
const imgLoad = (index: number) => {
let imgLoadings = loadings;
imgLoadings[index] = false;
setLoadings(imgLoadings.map((i) => i));
};
const initCheck = (): string => {
const check = data;
let hsh = '';
if (check) {
hsh = check.hsh;
}
setCheckHsh(hsh);
return hsh;
};
// 选择背景
const onCheckChange = (hsh: string) => {
setCheckHsh(hsh);
findImgToStroage(hsh);
const selected: any = imgList.find((i) => i.hsh === hsh);
if (selected && onChange) {
onChange(formatItem(selected));
}
};
const findImgToStroage = (hsh: string) => {
// 解决初始化时每个card都触发onChange事件的问题
if (!init) return setInit(true);
const userId = localStorage.getItem('account');
const image = imgList.find((i) => i.hsh === hsh);
if (image && image.hsh === data?.hsh) return;
if (image && userId) {
setBackground(userId, {
check: true,
url: image.url,
bgId: image._id,
hsh: image.hsh,
copyright: image.copyright,
copyrightlink: image.copyrightlink,
});
}
};
React.useEffect(() => {
getList();
}, []);
return (
<div className="my-0 px-4 border-t">
<ItemHeader
title="背景"
desc="修改适用于主页的背景,当前版本壁纸尺寸为标准1920x1080,在更大分辨率下会存在模糊问题。"
rightHandle={
<Tooltip title="通过刷新获取随机背景图片">
<Button
startIcon={<Replay />}
size="small"
disableElevation
onClick={() => getList()}
>
刷新
</Button>
</Tooltip>
}
/>
<div
className={classNames(
'p-4 justify-center flex flex-wrap mt-2 mb-4 gap-3',
css`
.ant-image {
display: block;
}
`,
)}
>
{apiLoading
? demoList.map((i) => (
<OutlineCard key={i} label=" " disabled loading />
))
: imgList.map((i, j) => (
<OutlineCard
key={i.hsh}
id={i.hsh}
value={checkHsh}
label={dayjs(i.enddate).format('YYYY/MM/DD')}
onChange={(val) => onCheckChange(val)}
tip={i.copyright}
>
<Spin
spinning={loadings[j]}
indicator={<CircularProgress size={18} color="inherit" />}
>
<Image
className="w-32 h-20 block"
onLoad={() => imgLoad(j)}
preview={false}
placeholder
src={i.url}
alt={i.copyright}
/>
</Spin>
</OutlineCard>
))}
</div>
</div>
);
}