@mui/material#ToggleButton TypeScript Examples
The following examples show how to use
@mui/material#ToggleButton.
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: HitModeEditor.tsx From genshin-optimizer with MIT License | 6 votes |
export function HitModeToggle(props: HitModeToggleProps) {
const { character: { hitMode }, characterDispatch } = useContext(DataContext)
return <SolidToggleButtonGroup exclusive baseColor="secondary"
value={hitMode} onChange={(_, hitMode) => characterDispatch({ hitMode })} {...props} >
<ToggleButton value="avgHit" disabled={hitMode === "avgHit"}>Avg. DMG</ToggleButton>
<ToggleButton value="hit" disabled={hitMode === "hit"}>Non Crit DMG</ToggleButton>
<ToggleButton value="critHit" disabled={hitMode === "critHit"}>Crit Hit DMG</ToggleButton>
</SolidToggleButtonGroup>
}
Example #2
Source File: SolidColoredToggleButton.tsx From genshin-optimizer with MIT License | 6 votes |
SolidColoredToggleButton = styled(ToggleButton, {
shouldForwardProp: (prop) => prop !== "baseColor" && prop !== "selectedColor"
})<SolidColoredToggleButtonPartial>(({ theme, baseColor = "secondary", selectedColor = "success" }) => ({
'&': {
backgroundColor: theme.palette[baseColor].main,
color: theme.palette[baseColor].contrastText,
},
'&:hover': {
backgroundColor: theme.palette[baseColor].dark,
},
'&.Mui-selected': {
backgroundColor: theme.palette[selectedColor].main,
color: theme.palette[selectedColor].contrastText,
},
'&.Mui-selected:hover': {
backgroundColor: theme.palette[selectedColor].dark,
},
'&.Mui-disabled': {
backgroundColor: theme.palette[baseColor].dark,
},
'&.Mui-selected.Mui-disabled': {
backgroundColor: theme.palette[selectedColor].dark,
},
}))
Example #3
Source File: index.tsx From genshin-optimizer with MIT License | 6 votes |
function EfficiencyFilter({ selectedKeys, onChange }) {
const keys1 = allSubstatKeys.slice(0, 6)
const keys2 = allSubstatKeys.slice(6)
const selKeys1 = selectedKeys.filter(k => keys1.includes(k))
const selKeys2 = selectedKeys.filter(k => keys2.includes(k))
return <Grid container spacing={1}>
<Grid item xs={12} md={6}>
<SolidToggleButtonGroup fullWidth value={selKeys1} onChange={(e, arr) => onChange([...selKeys2, ...arr])} sx={{ height: "100%" }}>
{keys1.map(key => <ToggleButton size="small" key={key} value={key}>
<Box display="flex" gap={1} alignItems="center">
{StatIcon[key]}
{KeyMap.getArtStr(key)}
</Box>
</ToggleButton>)}
</SolidToggleButtonGroup>
</Grid>
<Grid item xs={12} md={6}>
<SolidToggleButtonGroup fullWidth value={selKeys2} onChange={(e, arr) => onChange([...selKeys1, ...arr])} sx={{ height: "100%" }}>
{keys2.map(key => <ToggleButton size="small" key={key} value={key}>
<Box display="flex" gap={1} alignItems="center">
{StatIcon[key]}
{KeyMap.getArtStr(key)}
</Box>
</ToggleButton>)}
</SolidToggleButtonGroup>
</Grid>
</Grid>
}
Example #4
Source File: HorizontalAlign.tsx From mui-toolpad with MIT License | 6 votes |
function HorizontalAlignPropEditor({
label,
value = 'start',
onChange,
disabled,
}: EditorProps<string>) {
const handleHorizontalAlign = (
event: React.MouseEvent<HTMLElement>,
newHorizontalAlign: string | null,
) => {
if (newHorizontalAlign) {
onChange(newHorizontalAlign);
}
};
return (
<Box>
<Typography>{label}:</Typography>
<ToggleButtonGroup
exclusive
disabled={disabled}
value={value}
onChange={handleHorizontalAlign}
aria-label="HorizontalAlign"
>
<ToggleButton value="start" aria-label="start">
<AlignHorizontalLeftIcon />
</ToggleButton>
<ToggleButton value="center" aria-label="center">
<AlignHorizontalCenterIcon />
</ToggleButton>
<ToggleButton value="end" aria-label="end">
<AlignHorizontalRightIcon />
</ToggleButton>
</ToggleButtonGroup>
</Box>
);
}
Example #5
Source File: VerticalAlign.tsx From mui-toolpad with MIT License | 6 votes |
function VerticalAlignPropEditor({
label,
value = 'start',
onChange,
disabled,
}: EditorProps<string>) {
const VerticalAlign = (event: React.MouseEvent<HTMLElement>, newVerticalAlign: string | null) => {
if (newVerticalAlign) {
onChange(newVerticalAlign);
}
};
return (
<Box>
<Typography>{label}:</Typography>
<ToggleButtonGroup
exclusive
disabled={disabled}
value={value}
onChange={VerticalAlign}
aria-label="VerticalAlign"
>
<ToggleButton value="start" aria-label="start">
<AlignverticalTopIcon />
</ToggleButton>
<ToggleButton value="center" aria-label="center">
<AlignVerticalCenterIcon />
</ToggleButton>
<ToggleButton value="end" aria-label="end">
<AlignVerticalBottomIcon />
</ToggleButton>
</ToggleButtonGroup>
</Box>
);
}
Example #6
Source File: ArtifactFilterDisplay.tsx From genshin-optimizer with MIT License | 5 votes |
export default function ArtifactFilterDisplay({ filterOption, filterOptionDispatch, }: { filterOption: FilterOption, filterOptionDispatch: (any) => void }) {
const { t } = useTranslation(["artifact", "ui"]);
const { artSetKeys = [], mainStatKeys = [], rarity = [], slotKeys = [], levelLow, levelHigh, substats = [],
location = "", exclusion = ["excluded", "included"], locked = ["locked", "unlocked"] } = filterOption
return <Grid container spacing={1}>
{/* left */}
<Grid item xs={12} md={6} display="flex" flexDirection="column" gap={1}>
{/* Artifact stars filter */}
<SolidToggleButtonGroup fullWidth onChange={(e, newVal) => filterOptionDispatch({ rarity: newVal })} value={rarity} size="small">
{allArtifactRarities.map(star => <ToggleButton key={star} value={star}><Stars stars={star} /></ToggleButton>)}
</SolidToggleButtonGroup>
{/* Artifact Slot */}
<SolidToggleButtonGroup fullWidth onChange={(e, newVal) => filterOptionDispatch({ slotKeys: newVal })} value={slotKeys} size="small">
{allSlotKeys.map(slotKey => <ToggleButton key={slotKey} value={slotKey}>{artifactSlotIcon(slotKey)}</ToggleButton>)}
</SolidToggleButtonGroup>
{/* exclusion + locked */}
<Box display="flex" gap={1}>
<SolidToggleButtonGroup fullWidth value={exclusion} onChange={(e, newVal) => filterOptionDispatch({ exclusion: newVal })} size="small">
<ToggleButton value="excluded" sx={{ display: "flex", gap: 1 }}>
<FontAwesomeIcon icon={faBan} /><Trans i18nKey={"exclusion.excluded"} t={t} />
</ToggleButton>
<ToggleButton value="included" sx={{ display: "flex", gap: 1 }}>
<FontAwesomeIcon icon={faChartLine} /><Trans i18nKey={"exclusion.included"} t={t} />
</ToggleButton>
</SolidToggleButtonGroup>
<SolidToggleButtonGroup fullWidth value={locked} onChange={(e, newVal) => filterOptionDispatch({ locked: newVal })} size="small">
<ToggleButton value="locked" sx={{ display: "flex", gap: 1 }}>
<Lock /><Trans i18nKey={"ui:locked"} t={t} />
</ToggleButton>
<ToggleButton value="unlocked" sx={{ display: "flex", gap: 1 }}>
<LockOpen /><Trans i18nKey={"ui:unlocked"} t={t} />
</ToggleButton>
</SolidToggleButtonGroup>
</Box>
{/* Artiface level filter */}
<ArtifactLevelSlider showLevelText levelLow={levelLow} levelHigh={levelHigh}
setLow={levelLow => filterOptionDispatch({ levelLow })}
setHigh={levelHigh => filterOptionDispatch({ levelHigh })}
setBoth={(levelLow, levelHigh) => filterOptionDispatch({ levelLow, levelHigh })} />
<Grid container display="flex" gap={1}>
<Grid item flexGrow={1}>
{/* location */}
<CharacterAutocomplete
value={location}
onChange={location => filterOptionDispatch({ location })}
placeholderText={t("artifact:filterLocation.any")}
defaultText={t("artifact:filterLocation.any")}
labelText={t("artifact:filterLocation.location")}
showDefault
showInventory
showEquipped
/>
</Grid>
</Grid>
</Grid>
{/* right */}
<Grid item xs={12} md={6} display="flex" flexDirection="column" gap={1}>
{/* Artifact Set */}
<ArtifactSetMultiAutocomplete artSetKeys={artSetKeys} setArtSetKeys={artSetKeys => filterOptionDispatch({ artSetKeys })} />
<ArtifactMainStatMultiAutocomplete mainStatKeys={mainStatKeys} setMainStatKeys={mainStatKeys => filterOptionDispatch({ mainStatKeys })} />
<ArtifactSubstatMultiAutocomplete substatKeys={substats} setSubstatKeys={substats => filterOptionDispatch({ substats })} />
</Grid>
</Grid>
}
Example #7
Source File: HitModeEditor.tsx From genshin-optimizer with MIT License | 5 votes |
export function ReactionToggle(props: ReactionToggleProps) {
const { data, character: { reactionMode }, characterDispatch } = useContext(DataContext)
const charEleKey = data.get(input.charEle).value as ElementKey
const infusion = data.get(infusionNode).value as ElementKey
if (!["pyro", "hydro", "cryo"].includes(charEleKey) && !["pyro", "hydro", "cryo"].includes(infusion)) return null
return <SolidToggleButtonGroup exclusive baseColor="secondary"
value={reactionMode} onChange={(_, reactionMode) => characterDispatch({ reactionMode })} {...props}>
<ToggleButton value="" disabled={reactionMode === ""} >No Reactions</ToggleButton >
{(charEleKey === "pyro" || infusion === "pyro") && <ToggleButton value="pyro_vaporize" disabled={reactionMode === "pyro_vaporize"}>
<ColorText color="vaporize">
Vaporize(Pyro)
</ColorText>
<Box display="flex" alignItems="center">
<SqBadge sx={sqBadgeStyle} color="hydro">{uncoloredEleIcons.hydro}</SqBadge>
+
<SqBadge sx={sqBadgeStyle} color="pyro">{uncoloredEleIcons.pyro}</SqBadge>
</Box>
</ToggleButton >}
{(charEleKey === "pyro" || infusion === "pyro") && <ToggleButton value={"pyro_melt"} disabled={reactionMode === "pyro_melt"}>
<ColorText color="melt">
Melt(Pyro)
</ColorText>
<Box display="flex" alignItems="center">
<SqBadge sx={sqBadgeStyle} color="cryo">{uncoloredEleIcons.cryo}</SqBadge>
+
<SqBadge sx={sqBadgeStyle} color="pyro">{uncoloredEleIcons.pyro}</SqBadge>
</Box>
</ToggleButton >}
{(charEleKey === "hydro" || infusion === "hydro") && <ToggleButton value={"hydro_vaporize"} disabled={reactionMode === "hydro_vaporize"}>
<ColorText color="vaporize">
Vaporize(Hydro)
</ColorText>
<Box display="flex" alignItems="center">
<SqBadge sx={sqBadgeStyle} color="pyro">{uncoloredEleIcons.pyro}</SqBadge>
+
<SqBadge sx={sqBadgeStyle} color="hydro">{uncoloredEleIcons.hydro}</SqBadge>
</Box>
</ToggleButton >}
{(charEleKey === "cryo" || infusion === "cryo") && <ToggleButton value={"cryo_melt"} disabled={reactionMode === "cryo_melt"}>
<ColorText color="melt">
Melt(Cryo)
</ColorText>
<Box display="flex" alignItems="center">
<SqBadge sx={sqBadgeStyle} color="pyro">{uncoloredEleIcons.pyro}</SqBadge>
+
<SqBadge sx={sqBadgeStyle} color="cryo">{uncoloredEleIcons.cryo}</SqBadge>
</Box>
</ToggleButton >}
</SolidToggleButtonGroup>
}
Example #8
Source File: WeaponToggle.tsx From genshin-optimizer with MIT License | 5 votes |
export default function WeaponToggle({ value, onChange, ...props }: WeaponToggleProps) {
const cb = useCallback((e, newVal) => onChange(newVal || ""), [onChange])
return <SolidToggleButtonGroup exclusive onChange={cb} value={value || allWeaponTypeKeys} {...props}>
{allWeaponTypeKeys.map(wt => <ToggleButton key={wt} value={wt}>
<ImgIcon src={Assets.weaponTypes?.[wt]} size={2} />
</ToggleButton>)}
</SolidToggleButtonGroup>
}
Example #9
Source File: WeaponSwapModal.tsx From genshin-optimizer with MIT License | 5 votes |
export default function WeaponSwapModal({ onChangeId, weaponTypeKey, show, onClose }: { onChangeId: (id: string) => void, weaponTypeKey: WeaponTypeKey, show: boolean, onClose: () => void }) {
const { t } = useTranslation("page_character")
const { database } = useContext(DatabaseContext)
const clickHandler = useCallback((id) => {
onChangeId(id)
onClose()
}, [onChangeId, onClose])
const [dbDirty, forceUpdate] = useForceUpdate()
useEffect(() => database.followAnyWeapon(forceUpdate), [forceUpdate, database])
const weaponSheets = usePromise(WeaponSheet.getAll, [])
const filterConfigs = useMemo(() => weaponSheets && weaponFilterConfigs(weaponSheets), [weaponSheets])
const sortConfigs = useMemo(() => weaponSheets && weaponSortConfigs(weaponSheets), [weaponSheets])
const [rarity, setRarity] = useState([5, 4, 3])
const weaponIdList = useMemo(() => (filterConfigs && sortConfigs && dbDirty && database._getWeapons()
.filter(filterFunction({ weaponType: weaponTypeKey, rarity }, filterConfigs))
.sort(sortFunction("level", false, sortConfigs))
.map(weapon => weapon.id)) ?? []
, [dbDirty, database, filterConfigs, sortConfigs, rarity, weaponTypeKey])
return <ModalWrapper open={show} onClose={onClose} >
<CardDark>
<CardContent sx={{ py: 1 }}>
<Grid container>
<Grid item flexGrow={1}>
<Typography variant="h6">{weaponTypeKey ? <ImgIcon src={Assets.weaponTypes[weaponTypeKey]} /> : null} {t`tabEquip.swapWeapon`}</Typography>
</Grid>
<Grid item>
<CloseButton onClick={onClose} />
</Grid>
</Grid>
</CardContent>
<Divider />
<CardContent>
<Box mb={1}>
<SolidToggleButtonGroup sx={{ height: "100%" }} onChange={(e, newVal) => setRarity(newVal)} value={rarity} size="small">
{allRarities.map(star => <ToggleButton key={star} value={star}><Box display="flex" gap={1}><strong>{star}</strong><Stars stars={1} /></Box></ToggleButton>)}
</SolidToggleButtonGroup>
</Box>
<Grid container spacing={1}>
{weaponIdList.map(weaponId =>
<Grid item key={weaponId} xs={6} sm={6} md={4} lg={3} >
<WeaponCard
weaponId={weaponId}
onClick={clickHandler}
extraButtons={<CompareBuildButton weaponId={weaponId} />}
/>
</Grid>)}
</Grid>
</CardContent>
</CardDark>
</ModalWrapper>
}
Example #10
Source File: ThemeEditor.tsx From mui-toolpad with MIT License | 5 votes |
IconToggleButton = styled(ToggleButton)({
display: 'flex',
justifyContent: 'center',
width: '100%',
'& > *': {
marginRight: '8px',
},
})
Example #11
Source File: SettingsDrawer.tsx From GTAV-NativeDB with MIT License | 4 votes |
export default function SettingsDrawer({ open, onClose }: SettingsDrawerProps) {
const smallDisplay = useIsSmallDisplay()
const settings = useSettings()
const dispatch = useDispatch()
const handleThemeChanged = useCallback((e: ReactMouseEvent<HTMLElement, MouseEvent>, value: any) => {
if (value !== null) {
dispatch(setTheme(value))
}
}, [dispatch])
const handleSourcesChanged = useCallback((e: ReactMouseEvent<HTMLElement, MouseEvent>, value: any) => {
dispatch(setSources(value))
}, [dispatch])
return (
<Drawer
anchor={smallDisplay ? 'bottom' : 'right' }
open={open}
onClose={onClose}
>
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
width: smallDisplay ? undefined : 400,
p: 2
}}
>
<Typography
variant="h5"
>
Settings
</Typography>
<IconButton aria-label="close settings" onClick={onClose}>
<CloseIcon fontSize="medium" />
</IconButton>
</Box>
<Divider variant="fullWidth" />
<Box sx={{ p: 2 }}>
<Stack spacing={2}>
<div>
<Typography variant="body1" gutterBottom>
Theme
</Typography>
<ToggleButtonGroup
color="primary"
value={settings.theme}
onChange={handleThemeChanged}
exclusive
fullWidth
>
<ToggleButton value="light">
<LightIcon sx={{ mr: 1 }} />
Light
</ToggleButton>
<ToggleButton value="system">
<SystemIcon sx={{ mr: 1 }} />
System
</ToggleButton>
<ToggleButton value="dark">
<DarkIcon sx={{ mr: 1 }} />
Dark
</ToggleButton>
</ToggleButtonGroup>
</div>
<div>
<Typography variant="body1" gutterBottom>
Sources
</Typography>
<ToggleButtonGroup
color="primary"
value={settings.sources}
onChange={handleSourcesChanged}
fullWidth
>
<ToggleButton value="alloc8or" disabled>
Alloc8or
</ToggleButton>
<ToggleButton value="dottiedot">
DottieDot
</ToggleButton>
<ToggleButton value="fivem">
FiveM
</ToggleButton>
</ToggleButtonGroup>
</div>
</Stack>
</Box>
</Drawer>
)
}
Example #12
Source File: index.tsx From genshin-optimizer with MIT License | 4 votes |
export default function TabBuild() {
const { character, character: { key: characterKey } } = useContext(DataContext)
const [{ tcMode }] = useDBState("GlobalSettings", initGlobalSettings)
const { database } = useContext(DatabaseContext)
const [generatingBuilds, setgeneratingBuilds] = useState(false)
const [generationProgress, setgenerationProgress] = useState(0)
const [generationDuration, setgenerationDuration] = useState(0)//in ms
const [generationSkipped, setgenerationSkipped] = useState(0)
const [chartData, setchartData] = useState(undefined as ChartData | undefined)
const [artsDirty, setArtsDirty] = useForceUpdate()
const [{ equipmentPriority, threads = defThreads }, setOptimizeDBState] = useOptimizeDBState()
const maxWorkers = threads > defThreads ? defThreads : threads
const setMaxWorkers = useCallback(threads => setOptimizeDBState({ threads }), [setOptimizeDBState],)
const characterDispatch = useCharacterReducer(characterKey)
const buildSettings = character?.buildSettings ?? initialBuildSettings()
const { plotBase, setFilters, statFilters, mainStatKeys, optimizationTarget, mainStatAssumptionLevel, useExcludedArts, useEquippedArts, builds, buildDate, maxBuildsToShow, levelLow, levelHigh } = buildSettings
const buildsArts = useMemo(() => builds.map(build => build.map(i => database._getArt(i)!)), [builds, database])
const teamData = useTeamData(characterKey, mainStatAssumptionLevel)
const { characterSheet, target: data } = teamData?.[characterKey as CharacterKey] ?? {}
const compareData = character?.compareData ?? false
const noArtifact = useMemo(() => !database._getArts().length, [database])
const buildSettingsDispatch = useCallback((action) =>
characterDispatch && characterDispatch({ buildSettings: buildSettingsReducer(buildSettings, action) })
, [characterDispatch, buildSettings])
const onChangeMainStatKey = useCallback((slotKey: SlotKey, mainStatKey?: MainStatKey) => {
if (mainStatKey === undefined) buildSettingsDispatch({ type: "mainStatKeyReset", slotKey })
else buildSettingsDispatch({ type: "mainStatKey", slotKey, mainStatKey })
}, [buildSettingsDispatch])
//register changes in artifact database
useEffect(() =>
database.followAnyArt(setArtsDirty),
[setArtsDirty, database])
const { split, setPerms, totBuildNumber } = useMemo(() => {
if (!characterKey) // Make sure we have all slotKeys
return { totBuildNumber: 0 }
let cantTakeList: CharacterKey[] = []
if (useEquippedArts) {
const index = equipmentPriority.indexOf(characterKey)
if (index < 0) cantTakeList = [...equipmentPriority]
else cantTakeList = equipmentPriority.slice(0, index)
}
const arts = database._getArts().filter(art => {
if (art.level < levelLow) return false
if (art.level > levelHigh) return false
const mainStats = mainStatKeys[art.slotKey]
if (mainStats?.length && !mainStats.includes(art.mainStatKey)) return false
// If its equipped on the selected character, bypass the check
if (art.location === characterKey) return true
if (art.exclude && !useExcludedArts) return false
if (art.location && !useEquippedArts) return false
if (art.location && useEquippedArts && cantTakeList.includes(art.location)) return false
return true
})
const split = compactArtifacts(arts, mainStatAssumptionLevel)
const setPerms = [...artSetPerm([setFilters.map(({ key, num }) => ({ key, min: num }))])]
const totBuildNumber = [...setPerms].map(perm => countBuilds(filterArts(split, perm))).reduce((a, b) => a + b, 0)
return artsDirty && { split, setPerms, totBuildNumber }
}, [characterKey, useExcludedArts, useEquippedArts, equipmentPriority, mainStatKeys, setFilters, levelLow, levelHigh, artsDirty, database, mainStatAssumptionLevel])
// Reset the Alert by setting progress to zero.
useEffect(() => {
setgenerationProgress(0)
}, [totBuildNumber])
// Provides a function to cancel the work
const cancelToken = useRef(() => { })
//terminate worker when component unmounts
useEffect(() => () => cancelToken.current(), [])
const generateBuilds = useCallback(async () => {
if (!characterKey || !optimizationTarget || !split || !setPerms) return
const teamData = await getTeamData(database, characterKey, mainStatAssumptionLevel, [])
if (!teamData) return
const workerData = uiDataForTeam(teamData.teamData, characterKey)[characterKey as CharacterKey]?.target.data![0]
if (!workerData) return
Object.assign(workerData, mergeData([workerData, dynamicData])) // Mark art fields as dynamic
let optimizationTargetNode = objPathValue(workerData.display ?? {}, optimizationTarget) as NumNode | undefined
if (!optimizationTargetNode) return
const targetNode = optimizationTargetNode
const valueFilter: { value: NumNode, minimum: number }[] = Object.entries(statFilters).map(([key, value]) => {
if (key.endsWith("_")) value = value / 100 // TODO: Conversion
return { value: input.total[key], minimum: value }
}).filter(x => x.value && x.minimum > -Infinity)
const t1 = performance.now()
setgeneratingBuilds(true)
setchartData(undefined)
setgenerationDuration(0)
setgenerationProgress(0)
setgenerationSkipped(0)
const cancelled = new Promise<void>(r => cancelToken.current = r)
let nodes = [...valueFilter.map(x => x.value), optimizationTargetNode], arts = split!
const origCount = totBuildNumber, minimum = [...valueFilter.map(x => x.minimum), -Infinity]
if (plotBase) {
nodes.push(input.total[plotBase])
minimum.push(-Infinity)
}
nodes = optimize(nodes, workerData, ({ path: [p] }) => p !== "dyn");
({ nodes, arts } = pruneAll(nodes, minimum, arts, maxBuildsToShow,
new Set(setFilters.map(x => x.key as ArtifactSetKey)), {
reaffine: true, pruneArtRange: true, pruneNodeRange: true, pruneOrder: true
}))
const plotBaseNode = plotBase ? nodes.pop() : undefined
optimizationTargetNode = nodes.pop()!
let wrap = {
buildCount: 0, failedCount: 0, skippedCount: origCount,
buildValues: Array(maxBuildsToShow).fill(0).map(_ => -Infinity)
}
setPerms.forEach(filter => wrap.skippedCount -= countBuilds(filterArts(arts, filter)))
const setPerm = splitFiltersBySet(arts, setPerms,
maxWorkers === 1
// Don't split for single worker
? Infinity
// 8 perms / worker, up to 1M builds / perm
: Math.min(origCount / maxWorkers / 4, 1_000_000))[Symbol.iterator]()
function fetchWork(): Request | undefined {
const { done, value } = setPerm.next()
return done ? undefined : {
command: "request",
threshold: wrap.buildValues[maxBuildsToShow - 1], filter: value,
}
}
const filters = nodes
.map((value, i) => ({ value, min: minimum[i] }))
.filter(x => x.min > -Infinity)
const finalizedList: Promise<FinalizeResult>[] = []
for (let i = 0; i < maxWorkers; i++) {
const worker = new Worker()
const setup: Setup = {
command: "setup",
id: `${i}`,
arts,
optimizationTarget: optimizationTargetNode,
plotBase: plotBaseNode,
maxBuilds: maxBuildsToShow,
filters
}
worker.postMessage(setup, undefined)
let finalize: (_: FinalizeResult) => void
const finalized = new Promise<FinalizeResult>(r => finalize = r)
worker.onmessage = async ({ data }: { data: WorkerResult }) => {
switch (data.command) {
case "interim":
wrap.buildCount += data.buildCount
wrap.failedCount += data.failedCount
wrap.skippedCount += data.skippedCount
if (data.buildValues) {
wrap.buildValues.push(...data.buildValues)
wrap.buildValues.sort((a, b) => b - a).splice(maxBuildsToShow)
}
break
case "request":
const work = fetchWork()
if (work) {
worker.postMessage(work)
} else {
const finalizeCommand: Finalize = { command: "finalize" }
worker.postMessage(finalizeCommand)
}
break
case "finalize":
worker.terminate()
finalize(data);
break
default: console.log("DEBUG", data)
}
}
cancelled.then(() => worker.terminate())
finalizedList.push(finalized)
}
const buildTimer = setInterval(() => {
setgenerationProgress(wrap.buildCount)
setgenerationSkipped(wrap.skippedCount)
setgenerationDuration(performance.now() - t1)
}, 100)
const results = await Promise.any([Promise.all(finalizedList), cancelled])
clearInterval(buildTimer)
cancelToken.current = () => { }
if (!results) {
setgenerationDuration(0)
setgenerationProgress(0)
setgenerationSkipped(0)
} else {
if (plotBase) {
const plotData = mergePlot(results.map(x => x.plotData!))
const plotBaseNode = input.total[plotBase] as NumNode
let data = Object.values(plotData)
if (KeyMap.unit(targetNode.info?.key) === "%")
data = data.map(({ value, plot }) => ({ value: value * 100, plot })) as Build[]
if (KeyMap.unit(plotBaseNode!.info?.key) === "%")
data = data.map(({ value, plot }) => ({ value, plot: (plot ?? 0) * 100 })) as Build[]
setchartData({
valueNode: targetNode,
plotNode: plotBaseNode,
data
})
}
const builds = mergeBuilds(results.map(x => x.builds), maxBuildsToShow)
if (process.env.NODE_ENV === "development") console.log("Build Result", builds)
buildSettingsDispatch({ builds: builds.map(build => build.artifactIds), buildDate: Date.now() })
const totalDuration = performance.now() - t1
setgenerationProgress(wrap.buildCount)
setgenerationSkipped(wrap.skippedCount)
setgenerationDuration(totalDuration)
}
setgeneratingBuilds(false)
}, [characterKey, database, totBuildNumber, mainStatAssumptionLevel, maxBuildsToShow, optimizationTarget, plotBase, setPerms, split, buildSettingsDispatch, setFilters, statFilters, maxWorkers])
const characterName = characterSheet?.name ?? "Character Name"
const setPlotBase = useCallback(plotBase => {
buildSettingsDispatch({ plotBase })
setchartData(undefined)
}, [buildSettingsDispatch])
const dataContext: dataContextObj | undefined = useMemo(() => {
return data && characterSheet && character && teamData && {
data,
characterSheet,
character,
mainStatAssumptionLevel,
teamData,
characterDispatch
}
}, [data, characterSheet, character, teamData, characterDispatch, mainStatAssumptionLevel])
return <Box display="flex" flexDirection="column" gap={1}>
{noArtifact && <Alert severity="warning" variant="filled"> Opps! It looks like you haven't added any artifacts to GO yet! You should go to the <Link component={RouterLink} to="/artifact">Artifacts</Link> page and add some!</Alert>}
{/* Build Generator Editor */}
{dataContext && <DataContext.Provider value={dataContext}>
<Grid container spacing={1} >
{/* 1*/}
<Grid item xs={12} sm={6} lg={3} display="flex" flexDirection="column" gap={1}>
{/* character card */}
<Box><CharacterCard characterKey={characterKey} /></Box>
</Grid>
{/* 2 */}
<Grid item xs={12} sm={6} lg={3}>
<CardLight>
<CardContent >
<Typography gutterBottom>Main Stat</Typography>
<BootstrapTooltip placement="top" title={<Typography><strong>Level Assumption</strong> changes mainstat value to be at least a specific level. Does not change substats.</Typography>}>
<Box>
<AssumeFullLevelToggle mainStatAssumptionLevel={mainStatAssumptionLevel} setmainStatAssumptionLevel={mainStatAssumptionLevel => buildSettingsDispatch({ mainStatAssumptionLevel })} disabled={generatingBuilds} />
</Box>
</BootstrapTooltip>
</CardContent>
{/* main stat selector */}
<MainStatSelectionCard
mainStatKeys={mainStatKeys}
onChangeMainStatKey={onChangeMainStatKey}
disabled={generatingBuilds}
/>
</CardLight>
</Grid>
{/* 3 */}
<Grid item xs={12} sm={6} lg={3} display="flex" flexDirection="column" gap={1}>
{/*Minimum Final Stat Filter */}
<StatFilterCard statFilters={statFilters} setStatFilters={sFs => buildSettingsDispatch({ statFilters: sFs })} disabled={generatingBuilds} />
<BonusStatsCard />
{/* use excluded */}
<UseExcluded disabled={generatingBuilds} useExcludedArts={useExcludedArts} buildSettingsDispatch={buildSettingsDispatch} artsDirty={artsDirty} />
{/* use equipped */}
<UseEquipped disabled={generatingBuilds} useEquippedArts={useEquippedArts} buildSettingsDispatch={buildSettingsDispatch} />
{ /* Level Filter */}
<CardLight>
<CardContent sx={{ py: 1 }}>
Artifact Level Filter
</CardContent>
<ArtifactLevelSlider levelLow={levelLow} levelHigh={levelHigh}
setLow={levelLow => buildSettingsDispatch({ levelLow })}
setHigh={levelHigh => buildSettingsDispatch({ levelHigh })}
setBoth={(levelLow, levelHigh) => buildSettingsDispatch({ levelLow, levelHigh })}
disabled={generatingBuilds}
/>
</CardLight>
</Grid>
{/* 4 */}
<Grid item xs={12} sm={6} lg={3} display="flex" flexDirection="column" gap={1}>
<ArtifactSetConditional disabled={generatingBuilds} />
{/* Artifact set pickers */}
{setFilters.map((setFilter, index) => (index <= setFilters.filter(s => s.key).length) && <ArtifactSetPicker key={index} index={index} setFilters={setFilters}
disabled={generatingBuilds} onChange={(index, key, num) => buildSettingsDispatch({ type: 'setFilter', index, key, num })} />)}
</Grid>
</Grid>
{/* Footer */}
<Grid container spacing={1}>
<Grid item flexGrow={1} >
<ButtonGroup>
<Button
disabled={!characterKey || generatingBuilds || !optimizationTarget || !totBuildNumber || !objPathValue(data?.getDisplay(), optimizationTarget)}
color={(characterKey && totBuildNumber <= warningBuildNumber) ? "success" : "warning"}
onClick={generateBuilds}
startIcon={<FontAwesomeIcon icon={faCalculator} />}
>Generate Builds</Button>
<DropdownButton disabled={generatingBuilds || !characterKey}
title={<span><b>{maxBuildsToShow}</b> {maxBuildsToShow === 1 ? "Build" : "Builds"}</span>}>
<MenuItem>
<Typography variant="caption" color="info.main">
Decreasing the number of generated build will decrease build calculation time for large number of builds.
</Typography>
</MenuItem>
<Divider />
{maxBuildsToShowList.map(v => <MenuItem key={v}
onClick={() => buildSettingsDispatch({ maxBuildsToShow: v })}>{v} {v === 1 ? "Build" : "Builds"}</MenuItem>)}
</DropdownButton>
<DropdownButton disabled={generatingBuilds || !characterKey}
title={<span><b>{maxWorkers}</b> {maxWorkers === 1 ? "Thread" : "Threads"}</span>}>
<MenuItem>
<Typography variant="caption" color="info.main">
Increasing the number of threads will speed up build time, but will use more CPU power.
</Typography>
</MenuItem>
<Divider />
{range(1, defThreads).reverse().map(v => <MenuItem key={v}
onClick={() => setMaxWorkers(v)}>{v} {v === 1 ? "Thread" : "Threads"}</MenuItem>)}
</DropdownButton>
<Button
disabled={!generatingBuilds}
color="error"
onClick={() => cancelToken.current()}
startIcon={<Close />}
>Cancel</Button>
</ButtonGroup>
</Grid>
<Grid item>
<span>Optimization Target: </span>
{<OptimizationTargetSelector
optimizationTarget={optimizationTarget}
setTarget={target => buildSettingsDispatch({ optimizationTarget: target })}
disabled={!!generatingBuilds}
/>}
</Grid>
</Grid>
{!!characterKey && <Box >
<BuildAlert {...{ totBuildNumber, generatingBuilds, generationSkipped, generationProgress, generationDuration, characterName, maxBuildsToShow }} />
</Box>}
{tcMode && <Box >
<ChartCard disabled={generatingBuilds} chartData={chartData} plotBase={plotBase} setPlotBase={setPlotBase} />
</Box>}
<CardLight>
<CardContent>
<Box display="flex" alignItems="center" gap={1} mb={1} >
<Typography sx={{ flexGrow: 1 }}>
{builds ? <span>Showing <strong>{builds.length}</strong> Builds generated for {characterName}. {!!buildDate && <span>Build generated on: <strong>{(new Date(buildDate)).toLocaleString()}</strong></span>}</span>
: <span>Select a character to generate builds.</span>}
</Typography>
<Button disabled={!builds.length} color="error" onClick={() => buildSettingsDispatch({ builds: [], buildDate: 0 })} >Clear Builds</Button>
</Box>
<Grid container display="flex" spacing={1}>
<Grid item><HitModeToggle size="small" /></Grid>
<Grid item><ReactionToggle size="small" /></Grid>
<Grid item flexGrow={1} />
<Grid item><SolidToggleButtonGroup exclusive value={compareData} onChange={(e, v) => characterDispatch({ compareData: v })} size="small">
<ToggleButton value={false} disabled={!compareData}>
<small>Show New artifact Stats</small>
</ToggleButton>
<ToggleButton value={true} disabled={compareData}>
<small>Compare against equipped artifacts</small>
</ToggleButton>
</SolidToggleButtonGroup></Grid>
</Grid>
</CardContent>
</CardLight>
<BuildList {...{ buildsArts, character, characterKey, characterSheet, data, compareData, mainStatAssumptionLevel, characterDispatch, disabled: !!generatingBuilds }} />
</DataContext.Provider>}
</Box>
}
Example #13
Source File: index.tsx From genshin-optimizer with MIT License | 4 votes |
export default function PageWeapon() {
const { t } = useTranslation(["page_weapon", "ui"]);
const { database } = useContext(DatabaseContext)
const [state, stateDisplatch] = useDBState("WeaponDisplay", initialState)
const [newWeaponModalShow, setnewWeaponModalShow] = useState(false)
const [dbDirty, forceUpdate] = useForceUpdate()
const invScrollRef = useRef<HTMLDivElement>(null)
const [pageIdex, setpageIdex] = useState(0)
//set follow, should run only once
useEffect(() => {
ReactGA.send({ hitType: "pageview", page: '/weapon' })
return database.followAnyWeapon(forceUpdate)
}, [forceUpdate, database])
const brPt = useMediaQueryUp()
const maxNumToDisplay = numToShowMap[brPt]
const weaponSheets = usePromise(WeaponSheet.getAll, [])
const deleteWeapon = useCallback(async (key) => {
const weapon = database._getWeapon(key)
if (!weapon) return
const name = i18next.t(`weapon_${weapon.key}_gen:name`)
if (!window.confirm(`Are you sure you want to remove ${name}?`)) return
database.removeWeapon(key)
if (state.editWeaponId === key)
stateDisplatch({ editWeaponId: "" })
}, [state.editWeaponId, stateDisplatch, database])
const editWeapon = useCallback(key => {
stateDisplatch({ editWeaponId: key })
}, [stateDisplatch])
const newWeapon = useCallback(
(weaponKey: WeaponKey) => {
editWeapon(database.createWeapon(initialWeapon(weaponKey)))
},
[database, editWeapon])
const { sortType, ascending, weaponType, rarity } = state
const sortConfigs = useMemo(() => weaponSheets && weaponSortConfigs(weaponSheets), [weaponSheets])
const filterConfigs = useMemo(() => weaponSheets && weaponFilterConfigs(weaponSheets), [weaponSheets])
const { weaponIdList, totalWeaponNum } = useMemo(() => {
const weapons = database._getWeapons()
const totalWeaponNum = weapons.length
if (!sortConfigs || !filterConfigs) return { weaponIdList: [], totalWeaponNum }
const weaponIdList = weapons.filter(filterFunction({ weaponType, rarity }, filterConfigs))
.sort(sortFunction(sortType, ascending, sortConfigs)).map(weapon => weapon.id);
return dbDirty && { weaponIdList, totalWeaponNum }
}, [dbDirty, database, sortConfigs, filterConfigs, sortType, ascending, rarity, weaponType])
const { weaponIdsToShow, numPages, currentPageIndex } = useMemo(() => {
const numPages = Math.ceil(weaponIdList.length / maxNumToDisplay)
const currentPageIndex = clamp(pageIdex, 0, numPages - 1)
return { weaponIdsToShow: weaponIdList.slice(currentPageIndex * maxNumToDisplay, (currentPageIndex + 1) * maxNumToDisplay), numPages, currentPageIndex }
}, [weaponIdList, pageIdex, maxNumToDisplay])
//for pagination
const totalShowing = weaponIdList.length !== totalWeaponNum ? `${weaponIdList.length}/${totalWeaponNum}` : `${totalWeaponNum}`
const setPage = useCallback(
(e, value) => {
invScrollRef.current?.scrollIntoView({ behavior: "smooth" })
setpageIdex(value - 1);
},
[setpageIdex, invScrollRef],
)
const resetEditWeapon = useCallback(() => stateDisplatch({ editWeaponId: "" }), [stateDisplatch])
const { editWeaponId } = state
// Validate weaponId to be an actual weapon
useEffect(() => {
if (!editWeaponId) return
if (!database._getWeapon(editWeaponId))
resetEditWeapon()
}, [database, editWeaponId, resetEditWeapon])
return <Box my={1} display="flex" flexDirection="column" gap={1}>
{/* editor/character detail display */}
<Suspense fallback={false}>
<WeaponEditor
weaponId={editWeaponId}
footer
onClose={resetEditWeapon}
/>
</Suspense>
<CardDark ref={invScrollRef} sx={{ p: 2, pb: 1 }}>
<Grid container spacing={1} sx={{ mb: 1 }}>
<Grid item>
<WeaponToggle sx={{ height: "100%" }} onChange={weaponType => stateDisplatch({ weaponType })} value={weaponType} size="small" />
</Grid>
<Grid item flexGrow={1}>
<SolidToggleButtonGroup sx={{ height: "100%" }} onChange={(e, newVal) => stateDisplatch({ rarity: newVal })} value={rarity} size="small">
{allRarities.map(star => <ToggleButton key={star} value={star}><Box display="flex" gap={1}><strong>{star}</strong><Stars stars={1} /></Box></ToggleButton>)}
</SolidToggleButtonGroup>
</Grid>
<Grid item >
<SortByButton sx={{ height: "100%" }} sortKeys={weaponSortKeys}
value={sortType} onChange={sortType => stateDisplatch({ sortType })}
ascending={ascending} onChangeAsc={ascending => stateDisplatch({ ascending })}
/>
</Grid>
</Grid>
<Grid container alignItems="flex-end">
<Grid item flexGrow={1}>
<Pagination count={numPages} page={currentPageIndex + 1} onChange={setPage} />
</Grid>
<Grid item>
<ShowingWeapon numShowing={weaponIdsToShow.length} total={totalShowing} t={t} />
</Grid>
</Grid>
</CardDark>
<Suspense fallback={<Skeleton variant="rectangular" sx={{ width: "100%", height: "100%", minHeight: 500 }} />}>
<Grid container spacing={1} columns={columns}>
<Grid item xs={1}>
<CardDark sx={{ height: "100%", width: "100%", minHeight: 300, display: "flex", flexDirection: "column" }}>
<CardContent>
<Typography sx={{ textAlign: "center" }}>Add New Weapon</Typography>
</CardContent>
<WeaponSelectionModal show={newWeaponModalShow} onHide={() => setnewWeaponModalShow(false)} onSelect={newWeapon} />
<Box sx={{
flexGrow: 1,
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
>
<Button onClick={() => setnewWeaponModalShow(true)} color="info" sx={{ borderRadius: "1em" }}>
<Typography variant="h1"><FontAwesomeIcon icon={faPlus} className="fa-fw" /></Typography>
</Button>
</Box>
</CardDark>
</Grid>
{weaponIdsToShow.map(weaponId =>
<Grid item key={weaponId} xs={1} >
<WeaponCard
weaponId={weaponId}
onDelete={deleteWeapon}
onEdit={editWeapon}
canEquip
/>
</Grid>)}
</Grid>
</Suspense>
{numPages > 1 && <CardDark><CardContent>
<Grid container alignItems="flex-end">
<Grid item flexGrow={1}>
<Pagination count={numPages} page={currentPageIndex + 1} onChange={setPage} />
</Grid>
<Grid item>
<ShowingWeapon numShowing={weaponIdsToShow.length} total={totalShowing} t={t} />
</Grid>
</Grid>
</CardContent></CardDark>}
</Box>
}
Example #14
Source File: Config.tsx From NekoMaid with MIT License | 4 votes |
configs.push({
title: lang.config.serverConfig,
component () {
const plugin = usePlugin()
const globalData = useGlobalData()
const [flag, update] = useState(0)
const [info, setInfo] = useState<Record<string, string>>({ })
const [open, setOpen] = useState(false)
const [canGetData, setCanGetData] = useState(true)
const [loading, setLoading] = useState(false)
const setValue = (field: string, value: any, isGlobal = true) => {
plugin.emit('server:set', field, value)
success()
if (isGlobal) {
(globalData as any)[field] = value
update(flag + 1)
location.reload()
}
}
const createEditButtom = (field: string, isGlobal?: boolean, isInt = true) => <IconButton
onClick={() => dialog(
{
content: lang.inputValue,
input: isInt
? {
error: true,
type: 'number',
helperText: lang.invalidValue,
validator: (it: string) => /^\d+$/.test(it) && +it >= 0
}
: { }
}).then(res => res != null && setValue(field, isInt ? parseInt(res as any) : (res || null), isGlobal))}
><Edit /></IconButton>
const infoElm: JSX.Element[] = []
for (const key in info) {
const name = (lang.config as any)[key]
infoElm.push(<ListItem key={key} sx={{ pl: 4 }}>
<ListItemText
primary={key === 'isAikarFlags' ? <Link href='https://mcflags.emc.gs' target='_blank' rel='noopener'>{name}</Link> : name}
secondary={info[key].toString()}
/>
</ListItem>)
}
return <List>
<CircularLoading loading={loading} />
<ListItem secondaryAction={globalData.canSetMaxPlayers
? createEditButtom('maxPlayers')
: undefined}>
<ListItemText primary={lang.config.maxPlayers + ': ' + globalData.maxPlayers} />
</ListItem>
<ListItem secondaryAction={createEditButtom('spawnRadius')}>
<ListItemText primary={lang.config.spawnRadius + ': ' + globalData.spawnRadius} />
</ListItem>
<ListItem secondaryAction={createEditButtom('motd', false, false)}>
<ListItemText primary={lang.config.motd} />
</ListItem>
<ListItem secondaryAction={<Switch checked={globalData.hasWhitelist} onChange={e => setValue('hasWhitelist', e.target.checked)} />}>
<ListItemText primary={lang.config.whitelist} />
</ListItem>
{canGetData && <>
<ListItemButton onClick={() => {
if (infoElm.length) setOpen(!open)
else {
setLoading(true)
plugin.emit('server:fetchInfo', (data: any) => {
setLoading(false)
if (!data) {
failed(lang.unsupported)
setCanGetData(false)
return
}
setInfo(data)
setOpen(true)
})
}
}}>
<ListItemIcon><Equalizer /></ListItemIcon>
<ListItemText primary={lang.info} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={open} timeout='auto' unmountOnExit>
<List component='div' dense disablePadding>{infoElm}</List>
</Collapse>
</>}
</List>
}
},
{
title: lang.history,
component () {
const [cur, update] = useState(0)
const list: ServerRecord[] = JSON.parse(localStorage.getItem('NekoMaid:servers') || '[]')
return <List>
{list.sort((a, b) => b.time - a.time).map(it => {
const i = it.address.indexOf('?')
return <ListItem
disablePadding
key={it.address}
secondaryAction={<IconButton edge='end' size='small' onClick={() => {
localStorage.setItem('NekoMaid:servers', JSON.stringify(list.filter(s => s.address !== it.address)))
success()
update(cur + 1)
}}><Delete /></IconButton>}
>
<ListItemButton onClick={() => {
location.hash = ''
location.search = it.address
}} dense>
<ListItemAvatar><Avatar src={it.icon} variant='rounded'><HelpOutline /></Avatar></ListItemAvatar>
<ListItemText primary={<Tooltip title={it.address.slice(i + 1)}>
<span>{it.address.slice(0, i)}</span></Tooltip>} secondary={dayjs(it.time).fromNow()} />
</ListItemButton>
</ListItem>
})}
</List>
}
},
{
title: lang.config.theme,
component () {
const color = localStorage.getItem('NekoMaid:color') || 'blue'
return <CardContent sx={{ textAlign: 'center' }}>
<Box>
<ToggleButtonGroup exclusive value={localStorage.getItem('NekoMaid:colorMode') || ''} onChange={(_, it) => {
localStorage.setItem('NekoMaid:colorMode', it)
location.reload()
}}>
<ToggleButton value='light'><Brightness7 /> {lang.config.light}</ToggleButton>
<ToggleButton value=''><SettingsBrightness /> {lang.config.system}</ToggleButton>
<ToggleButton value='dark'><Brightness4 /> {lang.config.dark}</ToggleButton>
</ToggleButtonGroup>
</Box>
<Paper sx={{ marginTop: 2, width: '176px', overflow: 'hidden', display: 'inline-block' }}>
{Object.keys(colors).slice(1, 17).map((key, i) => {
const checked = color === key
const elm = <Box
key={key}
onClick={() => {
localStorage.setItem('NekoMaid:color', key)
location.reload()
}}
sx={{
backgroundColor: (colors as any)[key][600],
width: '44px',
height: '44px',
display: 'inline-block',
cursor: 'pointer'
}}
><Check htmlColor='white' sx={{ top: '10px', position: 'relative', opacity: checked ? 1 : 0 }} /></Box>
return (i + 1) % 4 === 0 ? <React.Fragment key={key}>{elm}<br /></React.Fragment> : elm
})}
</Paper>
</CardContent>
}
})
Example #15
Source File: PlayerList.tsx From NekoMaid with MIT License | 4 votes |
Players: React.FC = () => {
const his = useHistory()
const plugin = usePlugin()
const [page, setPage] = useState(0)
const [loading, setLoading] = useState(true)
const [state, setState] = useState<number | null>(null)
const [activedPlayer, setActivedPlayer] = useState<PlayerData | null>(null)
const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null)
const [data, setData] = useState<{ count: number, players: PlayerData[] }>(() => ({ count: 0, players: [] }))
const globalData = useGlobalData()
const { hasWhitelist } = globalData
const refresh = () => {
setLoading(true)
plugin.emit('playerList:fetchPage', (it: any) => {
if (it.players == null) it.players = []
setData(it)
setLoading(false)
}, page, state === 1 || state === 2 ? state : 0, null)
}
useMemo(refresh, [page, state])
const close = () => {
setAnchorEl(null)
setActivedPlayer(null)
}
return <Card>
<CardHeader
title={lang.playerList.title}
action={
<ToggleButtonGroup
size='small'
color={(state === 1 ? 'warning' : state === 2 ? 'error' : undefined) as any}
value={state}
exclusive
onChange={(_, it) => {
if (it === 3) return
setState(it)
if (state === 3) refresh()
}}
>
<ToggleButton disabled={loading} value={1}><Star /></ToggleButton>
<ToggleButton disabled={loading} value={2}><Block /></ToggleButton>
<ToggleButton disabled={loading} value={3} onClick={() => state !== 3 && dialog(lang.playerList.nameToSearch, lang.username)
.then(filter => {
if (filter == null) return
his.push('/NekoMaid/playerList/' + filter)
setState(3)
setLoading(true)
plugin.emit('playerList:fetchPage', (it: any) => {
if (it.players == null) it.players = []
setPage(0)
setData(it)
setLoading(false)
}, page, 0, filter.toLowerCase())
})}><Search /></ToggleButton>
</ToggleButtonGroup>
}
/>
<Divider />
<Box sx={{ position: 'relative' }}>
<CircularLoading loading={loading} />
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell padding='checkbox' />
<TableCell>{lang.username}</TableCell>
<TableCell align='right'>{minecraft['stat.minecraft.play_time']}</TableCell>
<TableCell align='right'>{lang.playerList.lastPlay}</TableCell>
<TableCell align='right'>{lang.operations}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{data.players.map(it => <TableRow key={it.name}>
<TableCell sx={{ cursor: 'pointer', padding: theme => theme.spacing(1, 1, 1, 2) }} onClick={() => his.push('/NekoMaid/playerList/' + it.name)}>
<Avatar src={getSkin(globalData, it.name, true)} imgProps={{ crossOrigin: 'anonymous', style: { width: 40, height: 40 } }} variant='rounded' />
</TableCell>
<TableCell>{it.name}</TableCell>
<TableCell align='right'>{dayjs.duration(it.playTime / 20, 'seconds').humanize()}</TableCell>
<TableCell align='right'>{dayjs(it.lastOnline).fromNow()}</TableCell>
<TableCell align='right'>
{(state === 1 || hasWhitelist) && <Tooltip title={lang.playerList[it.whitelisted ? 'clickToRemoveWhitelist' : 'clickToAddWhitelist']}>
<IconButton onClick={() => whitelist(it.name, plugin, refresh, !it.whitelisted)}>
{it.whitelisted ? <Star color='warning' /> : <StarBorder />}
</IconButton>
</Tooltip>}
<Tooltip title={it.ban == null ? lang.playerList.clickToBan : lang.playerList.banned + ': ' + it.ban}>
<IconButton onClick={() => banPlayer(it.name, plugin, refresh, it.ban == null)}>
<Block color={it.ban == null ? undefined : 'error'} />
</IconButton>
</Tooltip>
{actions.length
? <IconButton onClick={e => {
setActivedPlayer(anchorEl ? null : it)
setAnchorEl(anchorEl ? null : e.currentTarget)
}}><MoreHoriz /></IconButton>
: null}
</TableCell>
</TableRow>)}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={[]}
component='div'
count={data.count}
rowsPerPage={10}
page={page}
onPageChange={(_, it) => !loading && setPage(it)}
/>
</Box>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={() => setAnchorEl(null)}
>{actions.map((It, i) => <It key={i} onClose={close} player={activedPlayer} />)}</Menu>
</Card>
}
Example #16
Source File: Worlds.tsx From NekoMaid with MIT License | 4 votes |
Worlds: React.FC = () => {
const plugin = usePlugin()
const globalData = useGlobalData()
const [worlds, setWorlds] = useState<World[]>([])
const [selected, setSelected] = useState('')
const [open, setOpen] = useState(false)
const update = () => plugin.emit('worlds:fetch', (data: World[]) => {
setWorlds(data)
if (data.length) setSelected(old => data.some(it => it.id === old) ? old : '')
})
useEffect(() => {
const offUpdate = plugin.on('worlds:update', update)
update()
return () => { offUpdate() }
}, [])
const sw = worlds.find(it => it.id === selected)
const getSwitch = (name: string, configId = name) => sw
? <ListItem
secondaryAction={<Switch disabled={!globalData.hasMultiverse} checked={(sw as any)[name]}
onChange={e => {
plugin.emit('worlds:set', sw.id, configId, e.target.checked.toString())
success()
}}
/>}><ListItemText primary={(lang.worlds as any)[name]} /></ListItem>
: null
return <Box sx={{ minHeight: '100%', py: 3 }}>
<Toolbar />
<Container maxWidth={false}>
<Grid container spacing={3}>
<Grid item lg={8} md={12} xl={9} xs={12}>
<Card>
<CardHeader title={lang.worlds.title} />
<Divider />
<Box sx={{ position: 'relative' }}>
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell padding='checkbox' />
<TableCell>{lang.worlds.name}</TableCell>
{globalData.hasMultiverse && <TableCell>{lang.worlds.alias}</TableCell>}
<TableCell>{lang.worlds.players}</TableCell>
<TableCell>{lang.worlds.chunks}</TableCell>
<TableCell>{lang.worlds.entities}</TableCell>
<TableCell>{lang.worlds.tiles}</TableCell>
<TableCell>{lang.worlds.time}</TableCell>
<TableCell>{lang.worlds.weather}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{worlds.map(it => <TableRow key={it.id}>
<TableCell padding='checkbox'><Checkbox checked={selected === it.id} onClick={() => setSelected(it.id)} /></TableCell>
<TableCell><Tooltip title={it.id}><span>{it.name}</span></Tooltip></TableCell>
{globalData.hasMultiverse && <TableCell>{it.alias}
<IconButton size='small' onClick={() => dialog(lang.inputValue, lang.worlds.alias).then(res => {
if (res == null) return
plugin.emit('worlds:set', it.id, 'alias', res)
success()
})}><Edit fontSize='small' /></IconButton>
</TableCell>}
<TableCell>{it.players}</TableCell>
<TableCell>{it.chunks}</TableCell>
<TableCell>{it.entities}</TableCell>
<TableCell>{it.tiles}</TableCell>
<TableCell><Countdown time={it.time} max={24000} interval={50} /></TableCell>
<TableCell><IconButton size='small' onClick={() => {
plugin.emit('worlds:weather', it.id)
success()
}}>
{React.createElement((it.weather === 1 ? WeatherRainy : it.weather === 2 ? WeatherLightningRainy : WbSunny) as any)}
</IconButton></TableCell>
</TableRow>)}
</TableBody>
</Table>
</TableContainer>
</Box>
</Card>
</Grid>
<Grid item lg={4} md={6} xl={3} xs={12}>
<Card>
<CardHeader
title={lang.operations}
sx={{ position: 'relative' }}
action={<Tooltip title={lang.worlds.save} placement='left'>
<IconButton
size='small'
onClick={() => {
if (!sw) return
plugin.emit('worlds:save', sw.id)
success()
}}
sx={cardActionStyles}
><Save /></IconButton>
</Tooltip>}
/>
<Divider />
<Box sx={{ position: 'relative' }}>
{sw
? <List sx={{ width: '100%' }} component='nav'>
<ListItem secondaryAction={<ToggleButtonGroup
exclusive
color='primary'
size='small'
value={sw.difficulty}
onChange={(_, value) => {
plugin.emit('worlds:difficulty', sw.id, value)
success()
}}
>
{difficulties.map(it => <ToggleButton value={it.toUpperCase()} key={it}>{minecraft['options.difficulty.' + it]}</ToggleButton>)}
</ToggleButtonGroup>}><ListItemText primary={minecraft['options.difficulty']} /></ListItem>
<ListItem secondaryAction={<Switch checked={sw.pvp} onChange={e => {
plugin.emit('worlds:pvp', sw.id, e.target.checked)
success()
}} />}><ListItemText primary='PVP' /></ListItem>
{getSwitch('allowAnimals', 'spawning.animals.spawn')}
{getSwitch('allowMonsters', 'spawning.monsters.spawn')}
{globalData.hasMultiverse && <>
{getSwitch('allowFlight')}
{getSwitch('autoHeal')}
{getSwitch('hunger')}
</>}
<ListItem secondaryAction={globalData.canSetViewDistance
? <IconButton
onClick={() => dialog({
content: lang.inputValue,
input: {
error: true,
type: 'number',
helperText: lang.invalidValue,
validator: (it: string) => /^\d+$/.test(it) && +it > 1 && +it < 33
}
}).then(res => {
if (!res) return
plugin.emit('worlds:viewDistance', sw.id, parseInt(res as any))
success()
})}
><Edit /></IconButton>
: undefined}>
<ListItemText primary={lang.worlds.viewDistance + ': ' + sw.viewDistance} />
</ListItem>
<ListItem><ListItemText primary={minecraft['selectWorld.enterSeed']} secondary={sw.seed} /></ListItem>
<ListItemButton onClick={() => setOpen(!open)}>
<ListItemText primary={minecraft['selectWorld.gameRules']} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={open} timeout="auto" unmountOnExit>
<List component='div' dense disablePadding>
{sw.rules.map(([key, value]) => {
const isTrue = value === 'true'
const isBoolean = isTrue || value === 'false'
const isNumber = /^\d+$/.test(value)
return <ListItem
key={key}
sx={{ pl: 4 }}
secondaryAction={isBoolean
? <Switch
checked={isTrue}
onChange={e => {
plugin.emit('worlds:rule', sw.id, key, e.target.checked.toString())
success()
}}
/>
: <IconButton
onClick={() => dialog({
content: lang.inputValue,
input: isNumber
? {
error: true,
type: 'number',
helperText: lang.invalidValue,
validator: (it: string) => /^\d+$/.test(it)
}
: { }
}).then(res => {
if (res == null) return
plugin.emit('worlds:rule', sw.id, key, res)
success()
})}
><Edit /></IconButton>}
>
<ListItemText primary={(minecraft['gamerule.' + key] || key) + (isBoolean ? '' : ': ' + value)} />
</ListItem>
})}
</List>
</Collapse>
</List>
: <CardContent><Empty /></CardContent>
}
</Box>
</Card>
</Grid>
</Grid>
</Container>
</Box>
}