@mui/icons-material#Close TypeScript Examples

The following examples show how to use @mui/icons-material#Close. 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: Message.tsx    From your_spotify with GNU General Public License v3.0 6 votes vote down vote up
Alert = React.forwardRef<HTMLDivElement, AlertProps>(
  ({ message, level, onClose }: AlertProps, ref) => {
    return (
      <div
        ref={ref}
        className={clsx({
          [s.alert]: true,
          [s[level]]: true,
        })}>
        <Text onDark>{message}</Text>
        <IconButton size="small" onClick={onClose}>
          <Close className={s.icon} fontSize="small" />
        </IconButton>
      </div>
    );
  },
)
Example #2
Source File: HelpModal.tsx    From rewind with MIT License 6 votes vote down vote up
export function HelpBox(props: Pick<HelpModalProps, "onClose">) {
  const { onClose } = props;
  return (
    <Paper sx={{ px: 2, py: 2, display: "flex", flexDirection: "column" }}>
      {/*MenuBar could be reused*/}
      <Stack sx={{ alignItems: "center" }} direction={"row"} gap={1}>
        <Help />
        <Typography fontWeight={"bolder"}>Help</Typography>
        <Box flexGrow={1} />
        <IconButton onClick={onClose}>
          <Close />
        </IconButton>
      </Stack>
      <Divider />

      <PlaybarNavigationShortcuts />
      {/*<OtherResources />*/}
      {/*Footer*/}
      <Divider />
      <Stack sx={{ paddingTop: 1 }}>
        <PromotionFooter />
      </Stack>
    </Paper>
  );
}
Example #3
Source File: CompareBuildButton.tsx    From genshin-optimizer with MIT License 6 votes vote down vote up
export default function CompareBuildButton({ artId, weaponId }: { artId?: string, weaponId?: string }) {
  const { t } = useTranslation("page_character")
  const [show, onShow, onHide] = useBoolState(false)
  const { database } = useContext(DatabaseContext)
  const { character, character: { key: characterKey, equippedArtifacts }, characterSheet, data: oldData, mainStatAssumptionLevel, characterDispatch } = useContext(DataContext)
  const build = useMemo(() => {
    const newArt = database._getArt(artId ?? "")
    const artmap = objectMap(equippedArtifacts, (id, slot) => slot === newArt?.slotKey ? newArt : database._getArt(id))
    return Object.values(artmap)
  }, [database, equippedArtifacts, artId])
  const teamData = useTeamData(characterKey, mainStatAssumptionLevel, build, weaponId ? database._getWeapon(weaponId) : undefined)
  if (!teamData) return null
  return <>
    <ModalWrapper open={show} onClose={onHide} containerProps={{ maxWidth: "xl" }}>
      <Suspense fallback={<Skeleton variant="rectangular" width="100%" height={600} />}>
        <DataContext.Provider value={{ characterSheet, character, characterDispatch, mainStatAssumptionLevel, data: teamData[characterKey]!.target, teamData, oldData }}>
          <BuildDisplayItem compareBuild={true} extraButtons={<><HitModeToggle size="small" /><ReactionToggle size="small" /><Button size='small' color="error" onClick={onHide} ><Close /></Button></>} />
        </DataContext.Provider>
      </Suspense>
    </ModalWrapper>
    <Tooltip title={<Typography>{t`tabEquip.compare`}</Typography>} placement="top" arrow>
      <Button color="info" size="small" onClick={onShow} ><Difference /></Button>
    </Tooltip>
  </>
}
Example #4
Source File: BaseSettingsModal.tsx    From rewind with MIT License 5 votes vote down vote up
export function BaseSettingsModal(props: SettingsProps) {
  const { onClose, tabs, opacity, onOpacityChange, tabIndex, onTabIndexChange } = props;

  const handleTabChange = (event: any, newValue: any) => {
    onTabIndexChange(newValue);
  };

  const displayedTab = tabs[tabIndex].component;

  return (
    <Paper
      sx={{
        filter: `opacity(${opacity}%)`,
        height: "100%",
        display: "flex",
        flexDirection: "column",
        position: "relative",
      }}
      elevation={2}
    >
      <Stack sx={{ py: 1, px: 2, alignItems: "center" }} direction={"row"} gap={1}>
        <SettingsIcon />
        <Typography fontWeight={"bolder"}>Settings</Typography>
        <Box flexGrow={1} />
        <IconButton onClick={onClose}>
          <Close />
        </IconButton>
      </Stack>
      <Divider />
      <Stack direction={"row"} sx={{ flexGrow: 1, overflow: "auto" }}>
        {/*TODO: Holy moly, the CSS here needs to be changed a bit*/}
        <Tabs
          orientation="vertical"
          variant="scrollable"
          value={tabIndex}
          onChange={handleTabChange}
          sx={{ borderRight: 1, borderColor: "divider", position: "absolute" }}
        >
          {tabs.map(({ label }, index) => (
            <Tab label={label} key={index} tabIndex={index} sx={{ textTransform: "none" }} />
          ))}
        </Tabs>
        <Box sx={{ marginLeft: "90px" }}>{displayedTab}</Box>
      </Stack>
      <Divider />
      <Stack sx={{ px: 2, py: 1, flexDirection: "row", alignItems: "center" }}>
        <PromotionFooter />
        <Box flexGrow={1} />
        <Stack direction={"row"} alignItems={"center"} gap={2}>
          <IconButton onClick={() => onOpacityChange(MIN_OPACITY)}>
            <VisibilityOff />
          </IconButton>
          <Slider
            value={opacity}
            onChange={(_, v) => onOpacityChange(v as number)}
            step={5}
            min={MIN_OPACITY}
            max={MAX_OPACITY}
            valueLabelFormat={(value: number) => `${value}%`}
            sx={{ width: "12em" }}
            valueLabelDisplay={"auto"}
          />
          <IconButton onClick={() => onOpacityChange(MAX_OPACITY)}>
            <Visibility />
          </IconButton>
        </Stack>
      </Stack>
    </Paper>
  );
}
Example #5
Source File: dialog.tsx    From Search-Next with GNU General Public License v3.0 5 votes vote down vote up
Dialog: React.FC<DialogProps> = ({
  open,
  title,
  onOk,
  onCancel,
  okText,
  cancelText,
  children,
  container,
  width = 520,
  showFooter = true,
}) => {
  const { t } = useTranslation();

  return (
    <MModal
      aria-labelledby="transition-modal-title"
      aria-describedby="transition-modal-description"
      open={open}
      onClose={onCancel}
      closeAfterTransition
      BackdropComponent={Backdrop}
      BackdropProps={{
        timeout: 500,
      }}
      container={container}
      disableAutoFocus
    >
      <Fade in={open}>
        <div
          className={classnames(
            'rounded transform mx-auto relative top-28',
            css`
              width: ${typeof width === 'number' ? width + 'px' : width};
              max-width: calc(100vw - 32px);
              background-color: rgba(255, 255, 255, 0.9);
              backdrop-filter: blur(8px);
            `,
          )}
        >
          <div className="p-3 py-2 font-bold text-base flex justify-between items-center">
            {title}
            <IconButton size="small" onClick={onCancel}>
              <Close />
            </IconButton>
          </div>
          <div className="p-4">{children}</div>
          {showFooter && (
            <div className="p-2 flex justify-end gap-2">
              <Button variant="text" onClick={onCancel}>
                {cancelText ? cancelText : t('cancel')}
              </Button>
              <Button variant="text" onClick={onOk}>
                {okText ? okText : t('submit')}
              </Button>
            </div>
          )}
        </div>
      </Fade>
    </MModal>
  );
}
Example #6
Source File: CloseButton.tsx    From genshin-optimizer with MIT License 5 votes vote down vote up
export default function CloseButton({ large = false, ...props }: CloseButtonProps) {
  const { t } = useTranslation("ui")
  if (large)
    return <Button color="error" startIcon={<Close />} {...props} >{t`close`}</Button>
  return <Button color="error" sx={{ p: 1, minWidth: 0 }} {...props} >
    <Close />
  </Button>
}
Example #7
Source File: UseEquipped.tsx    From genshin-optimizer with MIT License 5 votes vote down vote up
function SelectItem({ characterKey, rank, maxRank, setRank, onRemove, numAbove }: {
  characterKey: CharacterKey,
  rank: number,
  maxRank: number,
  setRank: (r: number | undefined) => void,
  onRemove: () => void,
  numAbove: number,
}) {
  const { t } = useTranslation("page_character")
  const { database } = useContext(DatabaseContext)
  const character = useCharacter(characterKey)
  if (!character) return null
  const { equippedWeapon, equippedArtifacts } = character
  return <CardLight sx={{ p: 1 }}  >
    <Box sx={{ pb: 1, display: "flex", justifyContent: "space-between", gap: 1 }}>
      <SqBadge color="info">
        <Typography>#{rank}</Typography>
      </SqBadge>
      <SqBadge sx={{ flexGrow: 1 }} color={numAbove === (rank - 1) ? "warning" : (rank - 1) < numAbove ? "error" : "success"}>
        <Typography>{numAbove === (rank - 1) ? <Trans t={t} i18nKey="tabOptimize.useEquipped.modal.status.curr">Current character</Trans>
          : (rank - 1) < numAbove ? <Trans t={t} i18nKey="tabOptimize.useEquipped.modal.status.dont">Don't Use artifacts</Trans> :
            <Trans t={t} i18nKey="tabOptimize.useEquipped.modal.status.use">Use artifacts</Trans>}</Typography>
      </SqBadge>
      <Box>
        <ButtonGroup sx={{ flexGrow: 1 }} size="small">
          <CustomNumberInputButtonGroupWrapper >
            <CustomNumberInput onChange={setRank} value={rank}
              // startAdornment="Rank:"
              inputProps={{ min: 1, max: maxRank, sx: { textAlign: "center" } }}
              sx={{ width: "100%", height: "100%", pl: 2 }} />
          </CustomNumberInputButtonGroupWrapper>
          <Button disabled={rank === 1} onClick={() => setRank(1)} >
            <KeyboardDoubleArrowUp />
          </Button>
          <Button disabled={rank === 1} onClick={() => setRank(rank - 1)}  >
            <KeyboardArrowUp />
          </Button>
          <Button disabled={rank === maxRank} onClick={() => setRank(rank + 1)}  >
            <KeyboardArrowDown />
          </Button>
          <Button disabled={rank === maxRank} onClick={() => setRank(maxRank)}  >
            <KeyboardDoubleArrowDown />
          </Button>
          <Button color="error" onClick={onRemove}>
            <Close />
          </Button>
        </ButtonGroup>
      </Box>
    </Box>
    <Grid container columns={7} spacing={1}>
      <Grid item xs={1} >
        <CharacterCardPico characterKey={characterKey} />
      </Grid>
      <Grid item xs={1}><WeaponCardPico weaponId={equippedWeapon} /></Grid>
      {Object.entries(equippedArtifacts).map(([slotKey, aId]) => <Grid item xs={1} key={slotKey} ><ArtifactCardPico slotKey={slotKey} artifactObj={database._getArt(aId)} /></Grid>)}
    </Grid>

  </CardLight>
}
Example #8
Source File: PersonDialog.tsx    From frontend with MIT License 5 votes vote down vote up
export default function PersonDialog({ label, type, onSubmit }: Props) {
  const { t } = useTranslation()
  const [open, setOpen] = useState(false)

  const handleClickOpen = () => setOpen(true)
  const handleClose = () => setOpen(false)

  return (
    <>
      <Button fullWidth variant="contained" color="info" onClick={handleClickOpen}>
        {label}
      </Button>
      <Dialog
        open={open}
        onClose={(e, reason) => {
          if (reason === 'backdropClick') return
          handleClose()
        }}
        onBackdropClick={() => false}>
        <DialogTitle>
          {label}
          <IconButton
            aria-label="close"
            onClick={handleClose}
            sx={{
              position: 'absolute',
              right: 8,
              top: 8,
              color: (theme) => theme.palette.grey[500],
            }}>
            <Close />
          </IconButton>
        </DialogTitle>
        <DialogContent>
          <Box sx={{ mb: 2 }}>
            {type === 'beneficiary' ? (
              <Alert severity="info">
                <AlertTitle>{t('campaigns:campaign.beneficiary.name')}</AlertTitle>
                Лице, в чиято полза се организира кампанията. От юридическа гледна точка,
                бенефициентът <strong>НЕ влиза</strong> във взаимоотношения с оператора при набиране
                на средства в негова полза. Всички договори, изисквания, банкова сметка на
                кампанията са на името на организатора. Възможно е бенефициентът по една кампания да
                е и неговият организатор.
              </Alert>
            ) : (
              <Alert severity="warning">
                <AlertTitle>{t('campaigns:campaign.coordinator.name')}</AlertTitle>
                Организаторът е физическото или юридическо лице, с което се сключва договор за
                набиране на средства, след като негова заявка за кампания е одобрена. Набраните
                средства се прехвърлят в неговата банкова сметка, от него се изискват отчети за
                разходените средства. Когато дадено лице иска да стане организатор на кампании,
                преминава през процес на верификация, за да се избегнат измамите. Организаторът също
                може да е и бенефициент по дадена кампания.
              </Alert>
            )}
          </Box>
          <PersonForm
            {...type}
            onSubmit={(...args) => {
              onSubmit(...args)
              handleClose()
            }}
          />
        </DialogContent>
      </Dialog>
    </>
  )
}
Example #9
Source File: CloseModalButton.tsx    From frontend with MIT License 5 votes vote down vote up
export default function CloseModalButton({
  edge = 'end',
  fontSize = 'small',
  href = routes.index,
  Icon = Close,
  onClose,
  ...buttonProps
}: CloseModalButtonProps) {
  if (typeof onClose === 'function') {
    return (
      <IconButton
        onClick={onClose}
        sx={(theme) => ({
          position: 'absolute',
          top: theme.spacing(1),
          left: edge === 'start' ? theme.spacing(1) : undefined,
          right: edge === 'end' ? theme.spacing(1) : undefined,
          zIndex: theme.zIndex.drawer,
        })}
        {...buttonProps}
        size="large">
        <Icon fontSize={fontSize} />
      </IconButton>
    )
  }
  return (
    <LinkIconButton
      href={href}
      sx={(theme) => ({
        position: 'absolute',
        top: theme.spacing(1),
        left: edge === 'start' ? theme.spacing(1) : undefined,
        right: edge === 'end' ? theme.spacing(1) : undefined,
        zIndex: theme.zIndex.drawer,
      })}
      {...buttonProps}>
      <Icon fontSize={fontSize} />
    </LinkIconButton>
  )
}
Example #10
Source File: index.tsx    From Search-Next with GNU General Public License v3.0 4 votes vote down vote up
OtherApis: React.FC<PageProps> = (props) => {
  const { route, children } = props;
  const [iconApi, setIconApi] = React.useState('');
  const [apiStatus, setApiStatus] = React.useState<ApiStatus>({});

  const init = () => {
    const account = localStorage.getItem('account');
    const data = getOtherIconApi({
      userId: account ?? '',
      type: 'icon',
    });
    setIconApi(data.apiId);
    let map = {} as ApiStatus;
    websiteIconApis.forEach((i) => {
      map[i.id] = 'warning';
    });
    setApiStatus(map);
  };

  const onChange = (event: SelectChangeEvent<any>) => {
    const select = event.target.value;
    setIconApi(select);
    const account = localStorage.getItem('account');
    setOtherIconApi({
      userId: account ?? '',
      apiId: select,
      type: 'icon',
    });
  };

  const StatusChip = (status: string) => {
    const statusMap = {
      warning: (
        <>
          <PendingOutlined /> 等待响应
        </>
      ),
      success: (
        <>
          <Done /> 成功
        </>
      ),
      error: (
        <>
          <Close /> 失败
        </>
      ),
    };
    return (
      <Chip
        size="small"
        color={status as any}
        label={
          <div className="text-sm flex items-center gap-1">
            {(statusMap as any)[status as any]}
          </div>
        }
      />
    );
  };

  React.useEffect(() => {
    init();
  }, []);

  return (
    <div>
      <ContentList>
        <Alert severity="info">
          <AlertTitle>提示</AlertTitle>
          不同地区,不同网络下各API的表现可能不同,请选择最适合的API以提高使用体验。
        </Alert>
        <ItemAccordion
          title="Website Icon API"
          desc="设置获取网站图标的api"
          action={
            <Select
              label="API"
              value={iconApi}
              size="small"
              onChange={onChange}
              options={websiteIconApis.map((i) => ({
                label: i.name,
                value: i.id,
              }))}
            />
          }
        >
          <div className="flex items-center text-sm gap-1 pb-2">
            <PendingOutlined /> <span>等待响应</span>
            <Done /> <span>成功</span>
            <Close /> <span>失败</span> 状态仅作参考,具体以实际使用为准
          </div>
          {websiteIconApis.map((i) => {
            return (
              <AccordionDetailItem
                key={i.id}
                disabledRightPadding
                title={i.name}
                action={
                  <>
                    {StatusChip(apiStatus[i.id])}
                    <img
                      className={css`
                        display: none;
                      `}
                      src={`${i.url}google.com`}
                      alt={i.name}
                      onLoad={(v) => {
                        setApiStatus({ ...apiStatus, [i.id]: 'success' });
                      }}
                      onError={(err) => {
                        setApiStatus({ ...apiStatus, [i.id]: 'error' });
                      }}
                    />
                  </>
                }
              />
            );
          })}
        </ItemAccordion>
      </ContentList>
    </div>
  );
}
Example #11
Source File: EditableField.tsx    From abrechnung with GNU Affero General Public License v3.0 4 votes vote down vote up
export default function EditableField({
    value,
    onChange,
    validate = undefined,
    helperText = undefined,
    onStopEdit = undefined,
    canEdit = true,
    ...props
}) {
    const [currentValue, setValue] = useState("");
    const [editing, setEditing] = useState(false);
    const [error, setError] = useState(false);
    const classes = useStyles();

    useEffect(() => {
        setValue(value);
    }, [value]);

    const onSave = () => {
        if (!error) {
            onChange(currentValue);
            setValue("");
            setEditing(false);
        }
    };

    const startEditing = () => {
        setValue(value);
        setEditing(true);
    };

    const stopEditing = () => {
        setValue(value);
        setEditing(false);
        if (onStopEdit) {
            onStopEdit();
        }
    };

    const onValueChange = (event) => {
        setValue(event.target.value);
        if (validate) {
            setError(!validate(event.target.value));
        }
    };

    const onKeyUp = (key) => {
        if (key.keyCode === 13) {
            onSave();
        }
    };

    return (
        <div className={classes.root}>
            <DisabledTextField
                error={error}
                value={currentValue}
                disabled={!editing}
                onChange={onValueChange}
                className={classes.input}
                helperText={error ? helperText : null}
                onKeyUp={onKeyUp}
                {...props}
            />
            {canEdit &&
                (editing ? (
                    <>
                        <IconButton color="primary" onClick={onSave}>
                            <Check />
                        </IconButton>
                        <IconButton color="secondary" onClick={stopEditing}>
                            <Close />
                        </IconButton>
                    </>
                ) : (
                    <IconButton color="primary" onClick={startEditing}>
                        <Edit />
                    </IconButton>
                ))}
        </div>
    );
}
Example #12
Source File: engineSelectPopper.tsx    From Search-Next with GNU General Public License v3.0 4 votes vote down vote up
EngineSelectPopper: FC<EngineSelectPopperProps> = (props) => {
  const {
    width = 300,
    anchorEl,
    open = false,
    onBtnClick,
    onEngineSelect,
    engine,
  } = props;
  const [classifyEngineList, setClassifyEngineList] = useState<
    SearchEngineClassifyWithChildren[]
  >([]);
  const [selected, setSelected] = useState<string>('');
  const [engineList, setEngineList] = React.useState([] as SearchEngine[]);

  const getClassifyEngine = () => {
    getClassifyEngineListApi().then((res) => {
      const current = {
        ...engine.engine,
        classifyId: engine.engine?.classify?._id,
      } as SearchEngine;
      let filterEngines = res
        .map((i) => i.children)
        .flat()
        .filter((u) => u._id !== engine.engine?._id)
        .slice(0, engine.indexCount - 1);
      if (engine.sortType === 'count') {
        filterEngines = filterEngines.sort((r, t) => t.count - r.count);
      }
      setEngineList([current, ...filterEngines]);
      setClassifyEngineList(res);
      res.length > 0 && setSelected(res[0]._id);
    });
  };

  useEffect(() => {
    if (engine?.engine?.classify?._id) {
      setSelected(engine?.engine?.classify?._id);
    }
    getClassifyEngine();
  }, [engine]);

  return (
    <div className="mb-1">
      <Popper open={open} anchorEl={anchorEl} placement="top">
        {({ TransitionProps }) => (
          <Card
            {...TransitionProps}
            style={{ width: `${anchorEl?.clientWidth}px` }}
            className="mb-1"
          >
            <div className="p-2 flex gap-2 items-start">
              <div className="max-h-20 overflow-y-auto pr-1">
                {classifyEngineList.map((item) => (
                  <div
                    key={item._id}
                    onClick={() => {
                      setSelected(item._id);
                    }}
                    className={classnames(
                      'px-1.5 py-0.5 cursor-pointer rounded text-sm',
                      selected === item._id
                        ? 'bg-primary text-white'
                        : 'bg-white',
                    )}
                  >
                    {item.name}
                  </div>
                ))}
              </div>
              <div className="flex gap-1 items-start justify-start flex-grow">
                {classifyEngineList
                  .filter((i) => i._id === selected)?.[0]
                  ?.children.map((i) => (
                    <div
                      className={classnames(
                        'px-1.5 py-0.5 cursor-pointer rounded text-sm',
                        engine?.engine?._id === i._id
                          ? 'bg-primary text-white'
                          : 'bg-white border',
                      )}
                      onClick={() => {
                        onEngineSelect(i);
                      }}
                    >
                      {i.name}
                    </div>
                  ))}
              </div>
              <IconButton
                onClick={() => {
                  onBtnClick(false);
                }}
                size="small"
              >
                <Close />
              </IconButton>
            </div>
          </Card>
        )}
      </Popper>
      <div className="w-full text-left mb-1 flex justify-start items-center overflow-x-auto">
        {engineList.map((i, j) => (
          <Chip
            key={j}
            className={classnames(
              'mx-1',
              i._id === engine?.engine?._id
                ? 'bg-primary text-white'
                : 'bg-gray-100',
            )}
            size="small"
            label={i.name}
            onClick={(e) => onEngineSelect(i)}
          ></Chip>
        ))}
        {engine.mode === 'custom' && (
          <Chip
            onClick={(e: any) => {
              onBtnClick(!open);
            }}
            className={classnames('mx-1', 'bg-gray-100')}
            size="small"
            label={
              <div className="text-sm flex gap-1 items-center">
                <Settings className="text-base" />
              </div>
            }
          />
        )}
      </div>
    </div>
  );
}
Example #13
Source File: index.tsx    From genshin-optimizer with MIT License 4 votes vote down vote up
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 #14
Source File: CodeBlock.tsx    From fluttertemplates.dev with MIT License 4 votes vote down vote up
function CodeBlock(params: CodeBlockParams) {
  const [code, setCode] = useState("");
  const isDarkTheme = useTheme().palette.mode === "dark";

  const [open, setOpen] = React.useState(false);

  const handleClose = (
    event: React.SyntheticEvent | Event,
    reason?: string
  ) => {
    if (reason === "clickaway") {
      return;
    }

    setOpen(false);
  };

  useEffect(() => {
    fetch(params.url)
      .then((response) => response.text())
      .then((textString) => {
        setCode(textString);
      });
  }, [params.url]);

  const _snackBarAction = (
    <React.Fragment>
      <IconButton
        size="small"
        aria-label="close"
        color="inherit"
        onClick={handleClose}
      >
        <Close fontSize="small" />
      </IconButton>
    </React.Fragment>
  );

  return (
    <div>
      <div
        style={{
          position: "relative",
        }}
      >
        <SyntaxHighlighter
          language="dart"
          style={!isDarkTheme ? github : dracula}
          showLineNumbers={false}
          customStyle={{
            maxHeight: `${params.height}`,
            fontSize: "0.95rem",
          }}
        >
          {code}
        </SyntaxHighlighter>

        <Button
          aria-label="Copy"
          size="medium"
          variant="contained"
          color="secondary"
          disableElevation
          style={{
            position: "absolute",
            top: "16px",
            right: "20px",
            borderRadius: "10rem",
          }}
          startIcon={<FileCopyRounded />}
          onClick={() => {
            copy(code);
            setOpen(true);
          }}
        >
          Copy
        </Button>
      </div>

      {!code && (
        <Grid
          container
          direction="column"
          justifyContent="center"
          alignItems="center"
          style={{
            minHeight: "40vh",
          }}
        >
          <Grid item>
            <CircularProgress size="1.5rem" thickness={8} color="secondary" />
          </Grid>
        </Grid>
      )}

      <Snackbar
        open={open}
        autoHideDuration={4000}
        onClose={handleClose}
        message="Code copied successfully!"
        action={_snackBarAction}
        TransitionComponent={SlideTransition}
      />
    </div>
  );
}
Example #15
Source File: AuthButton.tsx    From multi-downloader-nx with MIT License 4 votes vote down vote up
AuthButton: React.FC = () => {
  const snackbar = useSnackbar();

  const [open, setOpen] = React.useState(false);

  const [username, setUsername] = React.useState('');
  const [password, setPassword] = React.useState('');

  const [usernameError, setUsernameError] = React.useState(false);
  const [passwordError, setPasswordError] = React.useState(false);

  const messageChannel = React.useContext(messageChannelContext);

  const [loading, setLoading] = React.useState(false);
  const [error, setError] = React.useState<Error|undefined>(undefined);
  const [authed, setAuthed] = React.useState(false);

  const checkAuth = async () => {
    console.log(await messageChannel?.checkToken());
    setAuthed((await messageChannel?.checkToken())?.isOk ?? false);
  }

  React.useEffect(() => { checkAuth() }, []);

  const handleSubmit = async () => {
    if (!messageChannel)
      throw new Error('Invalid state'); //The components to confirm only render if the messageChannel is not undefinded
    if (username.trim().length === 0)
      return setUsernameError(true);
    if (password.trim().length === 0)
      return setPasswordError(true);
    setUsernameError(false);
    setPasswordError(false);
    setLoading(true);

    const res = await messageChannel.auth({ username, password });
    if (res.isOk) {
      setOpen(false);
      snackbar.enqueueSnackbar('Logged in', {
        variant: 'success'
      });
      setUsername('');
      setPassword('');
    } else {
      setError(res.reason);
    }
    await checkAuth();
    setLoading(false);
  }

  return <Require value={messageChannel}>
    <Dialog open={open}>
      <Dialog open={!!error}>
        <DialogTitle>Error during Authentication</DialogTitle>
        <DialogContentText>
          {error?.name}
          {error?.message}
        </DialogContentText>
        <DialogActions>
          <Button onClick={() => setError(undefined)}>Close</Button>
        </DialogActions>
      </Dialog>
      <DialogTitle sx={{ flexGrow: 1 }}>Authentication</DialogTitle>
      <DialogContent>
        <DialogContentText>
          Here, you need to enter your username (most likely your Email) and your password.<br />
          These information are not stored anywhere and are only used to authenticate with the service once.
        </DialogContentText>
        <TextField
          error={usernameError}
          helperText={usernameError ? 'Please enter something before submiting' : undefined}
          margin="dense"
          id="username"
          label="Username"
          type="text"
          fullWidth
          variant="standard"
          value={username}
          onChange={(e) => setUsername(e.target.value)}
          disabled={loading}
        />
        <TextField
          error={passwordError}
          helperText={passwordError ? 'Please enter something before submiting' : undefined}
          margin="dense"
          id="password"
          label="Password"
          type="password"
          fullWidth
          variant="standard"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          disabled={loading}
        />
      </DialogContent>
      <DialogActions>
        {loading && <CircularProgress size={30}/>}
        <Button disabled={loading} onClick={() => setOpen(false)}>Close</Button>
        <Button disabled={loading} onClick={() => handleSubmit()}>Authenticate</Button>
      </DialogActions>
    </Dialog>
    <Button startIcon={authed ? <Check />: <Close />} variant="contained" onClick={() => setOpen(true)}>Authenticate</Button>
  </Require>
}
Example #16
Source File: SessionList.tsx    From abrechnung with GNU Affero General Public License v3.0 4 votes vote down vote up
export default function SessionList() {
    // TODO: fix editing functions
    const [editedSessions, setEditedSessions] = useState({});
    const [sessionToDelete, setSessionToDelete] = useState({
        show: false,
        toDelete: null,
    });
    const user = useRecoilValue(userData);
    const sessions = user.sessions;

    useTitle("Abrechnung - Sessions");

    const editSession = (id) => {
        if (!editedSessions.hasOwnProperty(id)) {
            const newSessions = {
                ...editedSessions,
                [id]: sessions.find((session) => session.id === id)?.name,
            };
            setEditedSessions(newSessions);
        }
    };

    const stopEditSession = (id) => {
        if (editedSessions.hasOwnProperty(id)) {
            let newEditedSessions = { ...editedSessions };
            delete newEditedSessions[id];
            setEditedSessions(newEditedSessions);
        }
    };

    const closeDeleteSessionModal = () => {
        setSessionToDelete({ show: false, toDelete: null });
    };

    const performRename = (id) => {
        if (editedSessions.hasOwnProperty(id)) {
            renameSession({
                sessionID: id,
                name: editedSessions[id],
            }).catch((err) => {
                toast.error(err);
            });
            stopEditSession(id);
        }
    };

    const openDeleteSessionModal = (id) => {
        setSessionToDelete({ show: true, toDelete: id });
    };

    const confirmDeleteSession = () => {
        if (sessionToDelete.toDelete !== null) {
            deleteSession({ sessionID: sessionToDelete.toDelete }).catch((err) => {
                toast.error(err);
            });
            setSessionToDelete({ show: false, toDelete: null });
        }
    };

    const handleEditChange = (id, value) => {
        const newEditedSessions = { ...editedSessions, [id]: value };
        setEditedSessions(newEditedSessions);
    };

    const onKeyUp = (id) => (key) => {
        if (key.keyCode === 13) {
            performRename(id);
        }
    };

    return (
        <MobilePaper>
            <Typography component="h3" variant="h5">
                Login Sessions
            </Typography>
            <List>
                {sessions.map((session) => {
                    if (editedSessions.hasOwnProperty(session.id)) {
                        return (
                            <ListItem key={session.id}>
                                <TextField
                                    margin="normal"
                                    variant="standard"
                                    fullWidth
                                    onKeyUp={onKeyUp(session.id)}
                                    value={editedSessions[session.id]}
                                    onChange={(event) => handleEditChange(session.id, event.target.value)}
                                />
                                <ListItemSecondaryAction>
                                    <Button onClick={() => performRename(session.id)}>
                                        <Check />
                                    </Button>
                                    <Button onClick={() => stopEditSession(session.id)}>
                                        <Close />
                                    </Button>
                                </ListItemSecondaryAction>
                            </ListItem>
                        );
                    } else {
                        return (
                            <ListItem key={session.id}>
                                <ListItemText
                                    primary={session.name}
                                    secondary={
                                        <>
                                            <span>
                                                Valid until{" "}
                                                {DateTime.fromISO(session.valid_until).toLocaleString(
                                                    DateTime.DATETIME_FULL
                                                ) && "indefinitely"}
                                                ,{" "}
                                            </span>
                                            <span>
                                                Last seen on{" "}
                                                {DateTime.fromISO(session.last_seen).toLocaleString(
                                                    DateTime.DATETIME_FULL
                                                )}
                                            </span>
                                        </>
                                    }
                                />
                                <ListItemSecondaryAction>
                                    <IconButton onClick={() => editSession(session.id)}>
                                        <Edit />
                                    </IconButton>
                                    <IconButton onClick={() => openDeleteSessionModal(session.id)}>
                                        <Delete />
                                    </IconButton>
                                </ListItemSecondaryAction>
                            </ListItem>
                        );
                    }
                })}
            </List>
            <Dialog open={sessionToDelete.show} onClose={closeDeleteSessionModal}>
                <DialogTitle>Delete Session?</DialogTitle>

                <DialogContent>
                    <DialogContentText>
                        {sessionToDelete.toDelete !== null
                            ? `Are you sure you want to delete session ${
                                  sessions.find((session) => session.id === sessionToDelete.toDelete)?.name
                              }`
                            : null}
                    </DialogContentText>
                </DialogContent>

                <DialogActions>
                    <Button color="secondary" onClick={confirmDeleteSession}>
                        Yes pls
                    </Button>
                    <Button color="primary" onClick={closeDeleteSessionModal}>
                        No
                    </Button>
                </DialogActions>
            </Dialog>
        </MobilePaper>
    );
}
Example #17
Source File: Vault.tsx    From NekoMaid with MIT License 1 votes vote down vote up
PermissionDialog: React.FC<{ plugin: Plugin, id: string | undefined, isGroup: boolean, onClose: () => void }> = ({ plugin, id, onClose, isGroup }) => {
  const [value, setValue] = useState('')
  const [status, setStatus] = useState<boolean | undefined>(false)
  const [options, setOptions] = useState<string[]>([])
  useEffect(() => {
    if (!id) return
    setValue('')
    setStatus(false)
    plugin.emit('vault:getAllPermissions', (it: any) => setOptions(it.sort()))
  }, [id])
  const queryStatus = useMemo(() => throttle((value: string) => plugin.emit('vault:permission', setStatus, id, value, 0, isGroup), 500), [id, isGroup])
  return <Dialog open={!!id} onClose={onClose}>
    <DialogTitle>{lang.vault.editorTitle}</DialogTitle>
    <DialogContent sx={{ overflow: 'hidden' }}>
      <DialogContentText>{lang.vault.permissionInput}: <span className='bold' style={{ }}>
        ({isGroup ? lang.vault.permissionGroup : minecraft['entity.minecraft.player']}: {id})</span></DialogContentText>
      <Autocomplete
        freeSolo
        options={options}
        sx={{ marginTop: 1 }}
        inputValue={value}
        renderInput={params => <TextField {...params as any} label={lang.vault.permission} />}
        onInputChange={(_, it) => {
          setValue(it)
          setStatus(undefined)
          queryStatus(it)
        }}
      />
      <Box sx={{ display: 'flex', alignItems: 'center', marginTop: 1 }}>
        {lang.status}:{status == null
          ? <CircularProgress size={20} sx={{ margin: '5px' }}/>
          : status ? <Check color='success' /> : <Close color='error' />}
        &nbsp;{status != null && <Button
          disabled={!value}
          variant='outlined'
          size='small'
          onClick={() => plugin.emit('vault:permission', (res: boolean) => {
            action(res)
            setStatus(undefined)
            queryStatus(value)
          }, id, value, status ? 2 : 1, isGroup)}
        >{lang.vault[status ? 'removePermission' : 'addPermission']}</Button>}
      </Box>
    </DialogContent>
    <DialogActions><Button onClick={onClose}>{minecraft['gui.back']}</Button></DialogActions>
  </Dialog>
}