@mui/material#Switch TypeScript Examples

The following examples show how to use @mui/material#Switch. 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: TableSwitch.tsx    From firecms with MIT License 6 votes vote down vote up
export function TableSwitch(props: {
    error: Error | undefined;
    internalValue?: boolean;
    focused: boolean;
    disabled: boolean;
    updateValue: (newValue: (boolean | null)) => void;
}) {
    const { disabled, internalValue, updateValue, focused } = props;

    const ref = React.createRef<HTMLTextAreaElement>();
    useEffect(() => {
        if (ref.current && focused) {
            ref.current.focus({ preventScroll: true });
        }
    }, [focused, ref]);

    return (
        <Switch
            inputRef={ref}
            color={"secondary"}
            checked={Boolean(internalValue)}
            disabled={disabled}
            onChange={(evt) => {
                const value = evt.target.checked as boolean;
                updateValue(value);
            }}
        />
    );
}
Example #2
Source File: index.tsx    From Search-Next with GNU General Public License v3.0 6 votes vote down vote up
Beta: React.FC = () => {
  const [checked, setChecked] = useState<boolean>(false);

  const checkChange = (v: React.ChangeEvent<HTMLInputElement>) => {
    const val = v.target.checked;
    setBeta(val);
    setChecked(val);
  };

  useEffect(() => {
    setChecked(isBeta());
  }, []);

  return (
    <div>
      <ContentList>
        <ContentTitle title="加入用户体验计划" />
        <ItemCard
          icon={<BugReportOutlined />}
          title="用户体验计划"
          desc="获取正在开发中功能的预览权限"
          action={<Switch checked={checked} onChange={checkChange} />}
        />
        <ContentTitle title="用户体验计划说明" />
        <div className="p-2">
          <Markdown source={Docs} />
        </div>
      </ContentList>
    </div>
  );
}
Example #3
Source File: SwitchElement.tsx    From react-hook-form-mui with MIT License 6 votes vote down vote up
export default function SwitchElement({ name, control, ...other }: SwitchElementProps) {
  return (
    <FormControlLabel
      control={
        <Controller
          name={name}
          control={control}
          render={({ field }) => <Switch {...field} checked={field.value} />}
        />
      }
      {...other}
    />
  )
}
Example #4
Source File: Toggle.tsx    From amplication with Apache License 2.0 6 votes vote down vote up
Toggle = (props: Props) => {
  const { label, onChange, onValueChange, ...rest } = props;

  const handleChange = useCallback(
    (event) => {
      if (onChange) {
        onChange(event, event.currentTarget.checked);
      }
      if (onValueChange) {
        onValueChange(event.currentTarget.checked);
      }
    },
    [onChange, onValueChange]
  );

  const switchNode = <Switch {...rest} onChange={handleChange} />;

  const componentNode = !isEmpty(label) ? (
    <div className={classNames(CLASS_NAME, `${CLASS_NAME}--with-label`)}>
      <label className={LABEL_CLASS}>
        <span className={LABEL_VALUE_CLASS}>{label}</span>
        {switchNode}
      </label>
    </div>
  ) : (
    <div className={CLASS_NAME}>{switchNode}</div>
  );

  return componentNode;
}
Example #5
Source File: SettingsModal.tsx    From rewind with MIT License 6 votes vote down vote up
function BeatmapRenderSettings() {
  const { beatmapRenderSettingsStore } = useCommonManagers();
  const settings = useObservable(() => beatmapRenderSettingsStore.settings$, DEFAULT_BEATMAP_RENDER_SETTINGS);

  return (
    <Stack>
      <FormGroup>
        <FormControlLabel
          control={
            <Switch
              checked={settings.sliderDevMode}
              onChange={(event) => beatmapRenderSettingsStore.setSliderDevMode(event.target.checked)}
            />
          }
          label={"Slider Dev Mode"}
        />
      </FormGroup>
      {/*  draw slider ends*/}
    </Stack>
  );
}
Example #6
Source File: SettingsModal.tsx    From rewind with MIT License 6 votes vote down vote up
function AnalysisCursorSettingsSection() {
  const { analysisCursorSettingsStore } = useCommonManagers();
  const settings = useObservable(() => analysisCursorSettingsStore.settings$, DEFAULT_ANALYSIS_CURSOR_SETTINGS);

  return (
    <Paper sx={{ boxShadow: "none", p: 2 }}>
      <Stack gap={1}>
        <Typography variant={"h6"}>Analysis Cursor</Typography>
        <FormGroup>
          <FormControlLabel
            control={
              <Switch
                checked={settings.enabled}
                onChange={(event) => analysisCursorSettingsStore.setEnabled(event.target.checked)}
              />
            }
            label={"Enabled"}
          />
        </FormGroup>
      </Stack>
    </Paper>
  );
}
Example #7
Source File: SettingsModal.tsx    From rewind with MIT License 6 votes vote down vote up
function PlaybarSettingsSection() {
  const { playbarSettingsStore } = useCommonManagers();
  const settings = useObservable(() => playbarSettingsStore.settings$, DEFAULT_PLAY_BAR_SETTINGS);

  return (
    <Paper elevation={1} sx={{ boxShadow: "none", p: 2 }}>
      <Stack gap={1}>
        <Typography variant={"h6"}>Playbar</Typography>
        <FormGroup>
          <FormControlLabel
            control={
              <Switch
                checked={settings.difficultyGraphEnabled}
                onChange={(event) =>
                  playbarSettingsStore.changeSettings((s) => (s.difficultyGraphEnabled = event.target.checked))
                }
              />
            }
            label={"Show difficulty graph"}
          />
        </FormGroup>
      </Stack>
    </Paper>
  );
}
Example #8
Source File: SwitchField.tsx    From firecms with MIT License 5 votes vote down vote up
SwitchFieldComponent = React.forwardRef(function({
                                                           name,
                                                           value,
                                                           setValue,
                                                           error,
                                                           showError,
                                                           autoFocus,
                                                           disabled,
                                                           touched,
                                                           property,
                                                           includeDescription,
                                                           shouldAlwaysRerender
                                                       }: SwitchFieldProps, ref) {

    const classes = useStyles();

    useClearRestoreValue({
        property,
        value,
        setValue
    });

    const [focus, setFocus] = useState<boolean>(autoFocus);

    return (
        <>
            <FormControl fullWidth>

                <FormControlLabel
                    className={clsx(classes.formControl,
                        {
                            [classes.focus]: focus
                        })}
                    onClick={(e) => setFocus(true)}
                    labelPlacement={"start"}
                    checked={Boolean(value)}
                    inputRef={ref}
                    control={
                        <Switch
                            type={"checkbox"}
                            color={"secondary"}
                            autoFocus={autoFocus}
                            disabled={disabled}
                            onFocus={(e) => setFocus(true)}
                            onBlur={(e) => setFocus(false)}
                            onChange={(evt) => {
                                setFocus(true);
                                setValue(
                                    evt.target.checked
                                );
                            }}/>
                    }
                    disabled={disabled}
                    label={
                        <Typography color={"textSecondary"}>
                            <LabelWithIcon

                                property={property}/>
                        </Typography>}
                />

                {includeDescription &&
                <FieldDescription property={property}/>}

                {showError && <FormHelperText>{error}</FormHelperText>}

            </FormControl>


        </>

    );
})
Example #9
Source File: FormSwitchWrapper.tsx    From console with GNU Affero General Public License v3.0 5 votes vote down vote up
StyledSwitch = withStyles((theme) => ({
  root: {
    width: 50,
    height: 24,
    padding: 0,
    margin: 0,
  },
  switchBase: {
    padding: 1,
    "&$checked": {
      transform: "translateX(24px)",
      color: theme.palette.common.white,
      "& + $track": {
        backgroundColor: "#4CCB92",
        boxShadow: "inset 0px 1px 4px rgba(0,0,0,0.1)",
        opacity: 1,
        border: "none",
      },
    },
    "&$focusVisible $thumb": {
      color: "#4CCB92",
      border: "6px solid #fff",
    },
  },
  thumb: {
    width: 22,
    height: 22,
    backgroundColor: "#FAFAFA",
    border: "2px solid #FFFFFF",
    marginLeft: 1,
  },
  track: {
    borderRadius: 24 / 2,
    backgroundColor: "#E2E2E2",
    boxShadow: "inset 0px 1px 4px rgba(0,0,0,0.1)",
    opacity: 1,
    transition: theme.transitions.create(["background-color", "border"]),
  },
  checked: {},
  focusVisible: {},
  switchContainer: {
    display: "flex",
    alignItems: "center",
    justifyContent: "flex-end",
  },
}))(Switch)
Example #10
Source File: SettingsModal.tsx    From rewind with MIT License 5 votes vote down vote up
function ReplayCursorSettingsSection() {
  const { replayCursorSettingsStore } = useCommonManagers();
  const settings = useObservable(() => replayCursorSettingsStore.settings$, DEFAULT_REPLAY_CURSOR_SETTINGS);

  return (
    <Paper sx={{ boxShadow: "none", p: 2 }}>
      <Stack gap={1}>
        <Typography variant={"h6"}>Replay Cursor</Typography>
        <FormGroup>
          <FormControlLabel
            control={
              <Switch
                checked={settings.enabled}
                onChange={(event) =>
                  replayCursorSettingsStore.changeSettings((s) => (s.enabled = event.target.checked))
                }
              />
            }
            label={"Enabled"}
          />
        </FormGroup>
        <FormGroup>
          <FormControlLabel
            disabled={!settings.enabled}
            control={
              <Switch
                checked={settings.smoothCursorTrail}
                onChange={(event) =>
                  replayCursorSettingsStore.changeSettings((s) => (s.smoothCursorTrail = event.target.checked))
                }
              />
            }
            label={"Smooth Cursor Trail"}
          />
        </FormGroup>
        <Typography>Scale</Typography>
        <Slider
          value={Math.round(settings.scale * 100)}
          valueLabelFormat={formatToPercent}
          disabled={!settings.enabled}
          min={10}
          max={200}
          onChange={(_, v) => replayCursorSettingsStore.changeSettings((s) => (s.scale = (v as number) / 100))}
          valueLabelDisplay={"auto"}
        />
      </Stack>
    </Paper>
  );
}
Example #11
Source File: ModeSwitch.tsx    From Cromwell with MIT License 5 votes vote down vote up
ModeSwitch = withStyles((theme: Theme | undefined) =>
    createStyles({
        track: {
            backgroundImage: "url('/admin/static/sun.svg')",
            transition: theme?.transitions?.create(['background-color', 'border']),
        },
        switchBase: {
            '&$checked': {
                '& + $track': {
                    backgroundImage: 'url("/admin/static/night-mode.svg")',
                },
            }
        },
        checked: {},
    }),
)(({ classes, ...props }: Props & {
    onToggle: () => void;
    value: 'light' | 'dark';
}) => {
    return (
        <div className={props.value === 'dark' ? styles.darkMode : ''}>
            <Tooltip title={props.value === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'}>
                <Switch
                    size="medium"
                    checked={props.value === 'dark'}
                    onChange={props.onToggle}
                    focusVisibleClassName={classes.focusVisible}
                    classes={{
                        root: styles.root,
                        switchBase: clsx(styles.switchBase, classes.switchBase),
                        thumb: styles.thumb,
                        track: clsx(styles.track, classes.track),
                        checked: clsx(styles.checked, classes.checked),
                    }}
                    {...props}
                />
            </Tooltip>
        </div>
    );
})
Example #12
Source File: ReaderNavBar.tsx    From Tachidesk-WebUI with Mozilla Public License 2.0 4 votes vote down vote up
export default function ReaderNavBar(props: IProps) {
    const history = useHistory();

    const {
        settings, setSettings, manga, chapter, curPage,
    } = props;

    const [drawerOpen, setDrawerOpen] = useState(false || settings.staticNav);
    const [hideOpenButton, setHideOpenButton] = useState(false);
    const [prevScrollPos, setPrevScrollPos] = useState(0);
    const [settingsCollapseOpen, setSettingsCollapseOpen] = useState(true);

    const setSettingValue = (key: string, value: any) => setSettings({ ...settings, [key]: value });

    const handleScroll = () => {
        const currentScrollPos = window.pageYOffset;

        if (Math.abs(currentScrollPos - prevScrollPos) > 20) {
            setHideOpenButton(currentScrollPos > prevScrollPos);
            setPrevScrollPos(currentScrollPos);
        }
    };

    useEffect(() => {
        window.addEventListener('scroll', handleScroll);

        const rootEl:HTMLDivElement = document.querySelector('#root')!;
        const mainContainer:HTMLDivElement = document.querySelector('#appMainContainer')!;

        // main container and root div need to change styles...
        rootEl.style.display = 'flex';
        mainContainer.style.display = 'none';

        return () => {
            rootEl.style.display = 'block';
            mainContainer.style.display = 'block';
            window.removeEventListener('scroll', handleScroll);
        };
    }, [handleScroll]);// handleScroll changes on every render

    return (
        <>
            <Slide
                direction="right"
                in={drawerOpen}
                timeout={200}
                appear={false}
                mountOnEnter
                unmountOnExit
            >
                <Root sx={{
                    position: settings.staticNav ? 'sticky' : 'fixed',
                }}
                >
                    <header>
                        {!settings.staticNav
                        && (
                            <IconButton
                                edge="start"
                                color="inherit"
                                aria-label="menu"
                                disableRipple
                                onClick={() => setDrawerOpen(false)}
                                size="large"
                            >
                                <KeyboardArrowLeftIcon />
                            </IconButton>
                        )}
                        <Typography variant="h1">
                            {chapter.name}
                        </Typography>
                        <IconButton
                            edge="start"
                            color="inherit"
                            aria-label="menu"
                            disableRipple
                            onClick={() => history.push('..')}
                            size="large"
                            sx={{ mr: -1 }}
                        >
                            <CloseIcon />
                        </IconButton>
                    </header>
                    <ListItem
                        ContainerComponent="div"
                        sx={{
                            '& span': {
                                fontWeight: 'bold',
                            },
                        }}
                    >
                        <ListItemText primary="Reader Settings" />
                        <ListItemSecondaryAction>
                            <IconButton
                                edge="start"
                                color="inherit"
                                aria-label="menu"
                                disableRipple
                                disableFocusRipple
                                onClick={() => setSettingsCollapseOpen(!settingsCollapseOpen)}
                                size="large"
                            >
                                {settingsCollapseOpen && <KeyboardArrowUpIcon />}
                                {!settingsCollapseOpen && <KeyboardArrowDownIcon />}
                            </IconButton>
                        </ListItemSecondaryAction>
                    </ListItem>
                    <Collapse in={settingsCollapseOpen} timeout="auto" unmountOnExit>
                        <List>
                            <ListItem>
                                <ListItemText primary="Static Navigation" />
                                <ListItemSecondaryAction>
                                    <Switch
                                        edge="end"
                                        checked={settings.staticNav}
                                        onChange={(e) => setSettingValue('staticNav', e.target.checked)}
                                    />
                                </ListItemSecondaryAction>
                            </ListItem>
                            <ListItem>
                                <ListItemText primary="Show page number" />
                                <ListItemSecondaryAction>
                                    <Switch
                                        edge="end"
                                        checked={settings.showPageNumber}
                                        onChange={(e) => setSettingValue('showPageNumber', e.target.checked)}
                                    />
                                </ListItemSecondaryAction>
                            </ListItem>
                            <ListItem>
                                <ListItemText primary="Load next chapter at ending" />
                                <ListItemSecondaryAction>
                                    <Switch
                                        edge="end"
                                        checked={settings.loadNextonEnding}
                                        onChange={(e) => setSettingValue('loadNextonEnding', e.target.checked)}
                                    />
                                </ListItemSecondaryAction>
                            </ListItem>
                            <ListItem>
                                <ListItemText primary="Reader Type" />
                                <Select
                                    variant="standard"
                                    value={settings.readerType}
                                    onChange={(e) => setSettingValue('readerType', e.target.value)}
                                    sx={{ p: 0 }}
                                >
                                    <MenuItem value="SingleLTR">
                                        Single Page (LTR)
                                    </MenuItem>
                                    <MenuItem value="SingleRTL">
                                        Single Page (RTL)
                                    </MenuItem>
                                    {/* <MenuItem value="SingleVertical">
                                       Vertical(WIP)
                                    </MenuItem> */}
                                    <MenuItem value="DoubleLTR">
                                        Double Page (LTR)
                                    </MenuItem>
                                    <MenuItem value="DoubleRTL">
                                        Double Page (RTL)
                                    </MenuItem>
                                    <MenuItem value="Webtoon">
                                        Webtoon
                                    </MenuItem>
                                    <MenuItem value="ContinuesVertical">
                                        Continues Vertical
                                    </MenuItem>
                                    <MenuItem value="ContinuesHorizontalLTR">
                                        Horizontal (LTR)
                                    </MenuItem>
                                    <MenuItem value="ContinuesHorizontalRTL">
                                        Horizontal (RTL)
                                    </MenuItem>
                                </Select>
                            </ListItem>
                        </List>
                    </Collapse>
                    <hr />
                    <Navigation>
                        <span>
                            {`Currently on page ${curPage + 1} of ${chapter.pageCount}`}
                        </span>
                        <ChapterNavigation>
                            {chapter.index > 1
                        && (
                            <Link
                                replace
                                to={`/manga/${manga.id}/chapter/${chapter.index - 1}`}
                            >
                                <Button
                                    variant="outlined"
                                    sx={{ gridArea: 'prev' }}
                                    startIcon={<KeyboardArrowLeftIcon />}
                                >
                                    Prev. Chapter
                                </Button>
                            </Link>
                        )}
                            {chapter.index < chapter.chapterCount
                        && (
                            <Link
                                replace
                                style={{ gridArea: 'next' }}
                                to={`/manga/${manga.id}/chapter/${chapter.index + 1}`}
                            >
                                <Button
                                    variant="outlined"
                                    endIcon={<KeyboardArrowRightIcon />}
                                >
                                    Next Chapter
                                </Button>
                            </Link>
                        )}
                        </ChapterNavigation>
                    </Navigation>
                </Root>
            </Slide>
            <Zoom in={!drawerOpen}>
                <Fade in={!hideOpenButton}>
                    <OpenDrawerButton
                        edge="start"
                        color="inherit"
                        aria-label="menu"
                        disableRipple
                        disableFocusRipple
                        onClick={() => setDrawerOpen(true)}
                        size="large"
                    >
                        <KeyboardArrowRightIcon />
                    </OpenDrawerButton>
                </Fade>
            </Zoom>
        </>
    );
}
Example #13
Source File: GroupLog.tsx    From abrechnung with GNU Affero General Public License v3.0 4 votes vote down vote up
export default function GroupLog({ group }) {
    const [message, setMessage] = useState("");
    const [showAllLogs, setShowAllLogs] = useState(false);
    const logEntries = useRecoilValue(groupLog(group.id));
    const members = useRecoilValue(groupMembers(group.id));

    useTitle(`${group.name} - Log`);

    const sendMessage = () => {
        sendGroupMessage({
            groupID: group.id,
            message: message,
        })
            .then((result) => {
                setMessage("");
            })
            .catch((err) => {
                toast.error(err);
            });
    };

    const getMemberUsername = (member_id) => {
        const member = members.find((member) => member.user_id === member_id);
        if (member === undefined) {
            return "unknown";
        }
        return member.username;
    };

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

    const log = showAllLogs ? logEntries : logEntries.filter((entry) => entry.type === "text-message");

    return (
        <MobilePaper>
            <Typography component="h3" variant="h5">
                Group Log
            </Typography>
            <FormControlLabel
                control={
                    <Switch
                        name="showAllLogs"
                        checked={showAllLogs}
                        color="primary"
                        onChange={(e) => setShowAllLogs(e.target.checked)}
                    />
                }
                label="Show all Logs"
            />
            <TextField
                required
                fullWidth
                name="newMessage"
                placeholder="Write a message to the group ..."
                value={message}
                variant="outlined"
                onKeyUp={onKeyUp}
                multiline
                onChange={(e) => setMessage(e.target.value)}
            />
            <Button type="submit" color="primary" onClick={sendMessage}>
                Send
            </Button>
            <Divider variant="middle" />
            <List>
                {log.map((logEntry) => (
                    <ListItem key={logEntry.id}>
                        <ListItemText
                            primary={`${logEntry.type} - ${logEntry.message}`}
                            secondary={`by ${getMemberUsername(logEntry.user_id)} 
                            on ${DateTime.fromISO(logEntry.logged_at).toLocaleString(DateTime.DATETIME_FULL)}`}
                        />
                    </ListItem>
                ))}
            </List>
        </MobilePaper>
    );
}
Example #14
Source File: Config.tsx    From NekoMaid with MIT License 4 votes vote down vote up
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: EntityEditor.tsx    From NekoMaid with MIT License 4 votes vote down vote up
EntityEditor: React.FC = () => {
  const theme = useTheme()
  const plugin = usePlugin()
  const his = useHistory()
  const loc = useLocation()
  const globalData = useGlobalData()
  const drawerWidth = useDrawerWidth()
  const [customName, setCustomName] = useState('')
  const [entity, setEntity] = useState<Entity>()
  let id: string | null = null
  if (loc.pathname.startsWith('/NekoMaid/entity/')) {
    const arr = loc.pathname.split('/')
    if (arr.length > 3) id = arr[3]
  }
  useEffect(() => {
    const off = plugin.on('entity:select', id => his.push('/NekoMaid/entity/' + id))
    return () => void off()
  }, [])
  const update = () => {
    if (id) {
      plugin.emit('entity:fetch', (entity: Entity) => {
        if (!entity) {
          failed()
          his.push('/NekoMaid/entity')
          return
        }
        if (globalData.hasNBTAPI && entity.nbt) entity.nbt = stringify(parse(entity.nbt), { pretty: true })
        setCustomName(entity.customName || '')
        setEntity(entity)
      }, id)
    }
  }
  const updateWithAction = (res: boolean) => {
    action(res)
    update()
  }
  useEffect(update, [id])
  return <Box sx={{ minHeight: '100%', py: 3 }}>
    <Toolbar />
    <Container maxWidth={false}>
      <Grid container spacing={3} sx={{ width: { sm: `calc(100vw - ${drawerWidth}px - ${theme.spacing(3)})` } }}>
        <Grid item lg={6} md={12} xl={6} xs={12}>
          <Card>
            <CardHeader
              title={(entity && minecraft['entity.minecraft.' + entity.type.toLowerCase()]) || lang.entityEditor.title}
              sx={{ position: 'relative' }}
              action={<Box sx={cardActionStyles}>
                <IconButton
                  size='small'
                  disabled={!entity}
                  onClick={() => entity && plugin.emit('entity:save', (res: boolean) => {
                    action(res)
                    update()
                  }, id, entity.nbt || null, customName || null)}
                ><Save /></IconButton>
                <IconButton
                  size='small'
                  disabled={!entity}
                  onClick={() => {
                    update()
                    success()
                  }}
                ><Refresh /></IconButton>
              </Box>}
            />
            <Divider />
            {entity
              ? <>
                <CardContent>
                  <Grid container>
                    <Grid item lg={6} md={6} xl={6} xs={12}>
                      <TextField
                        size='small'
                        label={lang.entityEditor.customName}
                        value={customName}
                        sx={{ width: '90%' }}
                        onChange={e => setCustomName(e.target.value)}
                      />
                    </Grid>
                    {values.map(it => <Grid item lg={6} md={6} xl={6} xs={12} key={it}>
                      <FormControlLabel
                        control={<Switch checked={(entity as any)[it]} />}
                        label={(lang.entityEditor as any)[it]}
                        onChange={(e: any) => plugin.emit('entity:set', (res: boolean) => {
                          action(res)
                          update()
                        }, id, it, e.target.checked)}
                      />
                    </Grid>)}
                  </Grid>
                </CardContent>
                {entity.nbt != null && <Accordion sx={{ '&::before': { opacity: '1!important' } }} disableGutters>
                  <AccordionSummary expandIcon={<ExpandMore />}><Typography>NBT</Typography></AccordionSummary>
                  <AccordionDetails sx={{
                    padding: 0,
                    '& .CodeMirror': { width: '100%', height: 350 },
                    '& .CodeMirror-dialog, .CodeMirror-scrollbar-filler': { backgroundColor: theme.palette.background.paper + '!important' }
                  }}>
                    <UnControlled
                      value={entity.nbt}
                      options={{
                        mode: 'javascript',
                        phrases: lang.codeMirrorPhrases,
                        theme: theme.palette.mode === 'dark' ? 'material' : 'one-light'
                      }}
                      onChange={(_: any, __: any, data: string) => (entity.nbt = data)}
                    />
                  </AccordionDetails>
                </Accordion>}
              </>
              : <CardContent><EntitySelector /></CardContent>}
          </Card>
        </Grid>
        {entity?.inventory?.length
          ? <Grid item lg={6} md={12} xl={6} xs={12}>
            <Card>
              <CardHeader
                title={lang.entityEditor.container}
                sx={{ position: 'relative' }}
              />
              <Divider />
              <CardContent sx={{ whiteSpace: 'nowrap', overflowX: 'auto', textAlign: 'center' }}>
                {entity.inventory.map((it, i) => <React.Fragment key={i}><ItemViewer
                  item={it}
                  data={{ type: InvType.ENTITY, solt: i, id }}
                  onDrag={() => plugin.emit('entity:setItem', update, id, i, null, -1)}
                  onDrop={(item, obj) => plugin.emit('entity:setItem', update, id, i, JSON.stringify(item),
                    obj?.type === InvType.ENTITY && obj.id === id ? obj.solt : -1)}
                  onEdit={item => item !== false && plugin.emit('entity:setItem', updateWithAction, id, i, item && JSON.stringify(item), -1)}
                />{!((i + 1) % 9) && <br />}</React.Fragment>)}
              </CardContent>
            </Card>
          </Grid>
          : undefined}
      </Grid>
    </Container>
  </Box>
}
Example #16
Source File: Profiler.tsx    From NekoMaid with MIT License 4 votes vote down vote up
Timings: React.FC = React.memo(() => {
  const plugin = usePlugin()
  const theme = useTheme()
  const { isTimingsV1 } = useGlobalData()
  const [status, setStatus] = useState(false)
  const [data, setData] = useState<TimingsData | null>(null)
  useEffect(() => {
    const off = plugin.emit('profiler:timingsStatus', setStatus).on('profiler:timings', setData)
    return () => { off() }
  }, [])

  const [tree, entitiesTick, tilesTick] = useMemo(() => {
    if (!data) return []
    const entitiesTickMap: Record<string, { value: number, name: string, count: number }> = {}
    const tilesTickMap: Record<string, { value: number, name: string, count: number }> = {}
    const map: Record<number, [number, number, number, [number, number, number][] | undefined] | undefined> = { }
    data.data.forEach(it => (map[it[0]] = it))
    const createNode = (id: number, percent: number) => {
      const cur = map[id]
      if (!cur) return
      map[id] = undefined
      const [, count, time] = cur
      const handler = data.handlers[id] || [0, lang.unknown]
      const handlerName = data.groups[handler[0]] || lang.unknown
      const name = handler[1]
      const children = cur[cur.length - 1]

      if (isTimingsV1) {
        if (name.startsWith('tickEntity - ')) {
          const came = name.slice(13).replace(/^Entity(Mob)?/, '')
          const entity = decamelize(came)
          const node = entitiesTickMap[entity]
          if (node) {
            node.count += count
            node.value += time
          } else entitiesTickMap[entity] = { count, value: time, name: minecraft['entity.minecraft.' + entity] || came }
        } else if (name.startsWith('tickTileEntity - ')) {
          const came = name.slice(17).replace(/^TileEntity(Mob)?/, '')
          const entity = decamelize(came)
          const node = tilesTickMap[entity]
          if (node) {
            node.count += count
            node.value += time
          } else tilesTickMap[entity] = { count, value: time, name: minecraft['block.minecraft.' + entity] || came }
        }
      } else {
        if (name.startsWith('tickEntity - ') && name.endsWith('ick')) {
          const res = ENTITY_TYPE.exec(name)
          if (res) {
            const node = entitiesTickMap[res[1]]
            if (node) {
              node.count += count
              node.value += time
            } else entitiesTickMap[res[1]] = { count, value: time, name: minecraft['entity.minecraft.' + res[1]] || res[1] }
          }
        } else if (name.startsWith('tickTileEntity - ')) {
          const arr = name.split('.')
          const came = arr[arr.length - 1].replace(/^TileEntity(Mob)?/, '')
          const tile = decamelize(came)
          const node = tilesTickMap[tile]
          if (node) {
            node.count += count
            node.value += time
          } else tilesTickMap[tile] = { count, value: time, name: minecraft['block.minecraft.' + tile] || came }
        }
      }

      return <TreeItem
        key={id}
        nodeId={id.toString()}
        label={<Box sx={{
          '& .info, .count': { color: 'transparent' },
          '&:hover .count': { color: 'inherit' },
          '&:hover .info': {
            color: theme.palette.primary.contrastText,
            textShadow: theme.palette.mode === 'light'
              ? '#000 1px 0 0, #000 0 1px 0, #000 -1px 0 0, #000 0 -1px 0'
              : '#fff 1px 0 0, #fff 0 1px 0, #fff -1px 0 0, #fff 0 -1px 0'
          }
        }}>
          <Box sx={{
            position: 'relative',
            zIndex: 2,
            display: 'flex',
            alignItems: 'center'
          }}>
            {handlerName !== 'Minecraft' && <><Typography color='primary' component='span'>
              {isTimingsV1 ? 'Bukkit' : lang.plugin + ':' + handlerName}</Typography>::</>}
            {name}&nbsp;
            <Typography variant='caption' className='count'>({lang.profiler.timingsCount}: {count})</Typography>
          </Box>
          <Box className='info' sx={{
            position: 'absolute',
            height: 10,
            right: 0,
            top: '50%',
            marginTop: '-5px',
            minWidth: 40,
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center'
          }}>
            <Typography variant='caption' sx={{ position: 'absolute' }}>({Math.round(100 * percent)}%)</Typography>
            <div style={{ width: 100 * percent + 'px' }} className='bar' />
          </Box>
        </Box>}
      >{Array.isArray(children) && children.sort((a, b) => b[2] - a[2]).map(it => createNode(it[0], percent * (it[2] / time)))}</TreeItem>
    }
    // eslint-disable-next-line react/jsx-key
    return [<TreeView defaultCollapseIcon={<ExpandMore />} defaultExpandIcon={<ChevronRight />} defaultExpanded={['1']}>
      {createNode(1, 1)}
    </TreeView>, Object.values(entitiesTickMap), Object.values(tilesTickMap)]
  }, [data])

  return <Container maxWidth={false} sx={{ py: 3 }}>
    <Grid container spacing={3}>
      <Grid item xs={12}>
        <Card>
          <CardHeader title='Timings' sx={{ position: 'relative' }} action={<FormControlLabel
            control={<Switch checked={status} onChange={e => plugin.emit('profiler:timingsStatus', setStatus, e.target.checked)} />}
            label={minecraft['addServer.resourcePack.enabled']}
            sx={cardActionStyles}
          />} />
          <Divider />
          {status
            ? <Box sx={{
              position: 'relative',
              minHeight: data ? undefined : 300,
              '& .bar': { backgroundColor: theme.palette.primary.main, height: 10, marginLeft: 'auto', borderRadius: 2 }
            }}>
              <CircularLoading loading={!data} />
              {tree}
            </Box>
            : <CardContent><Empty title={lang.profiler.timingsNotStarted} /></CardContent>}
        </Card>
      </Grid>
      {data && <Pie title={lang.profiler.entitiesTick} data={entitiesTick!} formatter={countFormatter} />}
      {data && <Pie title={lang.profiler.tilesTick} data={tilesTick!} formatter={countFormatter} />}
    </Grid>
  </Container>
})
Example #17
Source File: Scheduler.tsx    From NekoMaid with MIT License 4 votes vote down vote up
Scheduler: React.FC = () => {
  const plugin = usePlugin()
  const [id, setId] = useState(-1)
  let [tasks, setTasks] = useState<Task[]>([])
  const [name, setName] = useState('')
  const [cron, setCron] = useState('')
  const [values, setValues] = useState('')
  const [whenIdle, setWhenIdle] = useState(false)
  const [cronError, setCronError] = useState('')
  const save = () => plugin.emit('scheduler:update', (res: boolean) => {
    action(res)
    plugin.emit('scheduler:fetch', setTasks)
  }, JSON.stringify(tasks))
  useEffect(() => { plugin.emit('scheduler:fetch', setTasks) }, [])

  return <Box sx={{ minHeight: '100%', py: 3 }}>
    <Toolbar />
    <Container maxWidth={false}>
      <Grid container spacing={3}>
        <Grid item lg={4} md={12} xl={4} xs={12}>
          <Card>
            <CardHeader
              title={lang.scheduler.title}
              sx={{ position: 'relative' }}
              action={<IconButton
                size='small'
                onClick={() => {
                  const task = {
                    name: lang.scheduler.newTask,
                    cron: '*/1 * * * *',
                    enabled: true,
                    whenIdle: false,
                    values: ['/say Hello, %server_tps% (PlaceholderAPI)', 'This is a chat message']
                  }
                  setTasks([...tasks, task])
                  setId(tasks.length)
                  setCronError('')
                  setCron(task.cron)
                  setName(task.name)
                  setValues(task.values.join('\n'))
                  setWhenIdle(false)
                }}
                sx={cardActionStyles}
              ><Add /></IconButton>}
            />
            <Divider />
            {tasks.length
              ? <List
                sx={{ width: '100%' }}
                component='nav'
              >
                {tasks.map((it, i) => <ListItem
                  key={i}
                  disablePadding
                  secondaryAction={<IconButton
                    edge='end'
                    onClick={() => dialog(lang.scheduler.confirmDelete)
                      .then(it => {
                        if (it == null) return
                        setTasks((tasks = tasks.filter((_, id) => i !== id)))
                        save()
                      })}
                  ><Delete /></IconButton>}
                  sx={{ position: 'relative' }}
                >
                  <ListItemIcon sx={{ paddingLeft: 2, position: 'absolute' }}>
                    <Checkbox
                      edge='start'
                      checked={it.enabled}
                      tabIndex={-1}
                    />
                  </ListItemIcon>
                  <ListItemButton onClick={() => {
                    setId(i)
                    setCronError('')
                    setCron(tasks[i].cron)
                    setName(tasks[i].name)
                    setValues(tasks[i].values.join('\n'))
                    setWhenIdle(!!tasks[i].whenIdle)
                  }}><ListItemText inset primary={it.name} /></ListItemButton >
                </ListItem>)}
              </List>
              : <CardContent><Empty /></CardContent>}
          </Card>
        </Grid>
        <Grid item lg={8} md={12} xl={8} xs={12}>
          <Card>
            <CardHeader
              title={lang.scheduler.editor}
              sx={{ position: 'relative' }}
              action={<IconButton
                size='small'
                onClick={() => {
                  tasks[id].values = values.split('\n')
                  tasks[id].cron = cron
                  tasks[id].name = name
                  tasks[id].whenIdle = whenIdle
                  save()
                }}
                sx={cardActionStyles}
                disabled={!tasks[id] || !!cronError}
              ><Save /></IconButton>}
            />
            <Divider />
            <CardContent>
              {tasks[id]
                ? <>
                  <TextField
                    required
                    fullWidth
                    variant='standard'
                    label={lang.scheduler.name}
                    value={name}
                    onChange={e => setName(e.target.value)}
                  />
                  <TextField
                    fullWidth
                    multiline
                    rows={4}
                    value={values}
                    sx={{ marginTop: 3 }}
                    label={lang.scheduler.content}
                    onChange={e => setValues(e.target.value)}
                  />
                  <FormControlLabel
                    control={<Switch checked={whenIdle} />}
                    label={lang.scheduler.whenIdle}
                    onChange={(e: any) => setWhenIdle(e.target.checked)}
                  />
                </>
                : <Empty title={lang.scheduler.notSelected} />}
            </CardContent>
            {tasks[id] && <>
              <Divider textAlign='left'>{lang.scheduler.timer}</Divider>
              <CardContent>
                <Box sx={{
                  '& .MuiTextField-root': { backgroundColor: 'inherit!important' },
                  '& .MuiOutlinedInput-input': { color: 'inherit!important' },
                  '& .MuiTypography-h6': { color: theme => theme.palette.primary.main + '!important' }
                }}>
                  <Cron cron={cron} setCron={setCron} setCronError={setCronError} locale={currentLanguage as any} isAdmin />
                </Box>
              </CardContent>
            </>}
          </Card>
        </Grid>
      </Grid>
    </Container>
  </Box>
}
Example #18
Source File: Worlds.tsx    From NekoMaid with MIT License 4 votes vote down vote up
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>
}
Example #19
Source File: index.tsx    From wallet-adapter with Apache License 2.0 4 votes vote down vote up
Index: NextPage = () => {
    const { autoConnect, setAutoConnect } = useAutoConnect();

    return (
        <Table>
            <TableHead>
                <TableRow>
                    <TableCell width={240}>Component</TableCell>
                    <TableCell width={240}>Material UI</TableCell>
                    <TableCell width={240}>Ant Design</TableCell>
                    <TableCell width={240}>React UI</TableCell>
                    <TableCell>Example v{pkg.version}</TableCell>
                </TableRow>
            </TableHead>
            <TableBody>
                <TableRow>
                    <TableCell>Connect Button</TableCell>
                    <TableCell>
                        <MaterialUIWalletConnectButton />
                    </TableCell>
                    <TableCell>
                        <AntDesignWalletConnectButton />
                    </TableCell>
                    <TableCell>
                        <ReactUIWalletConnectButton />
                    </TableCell>
                    <TableCell></TableCell>
                </TableRow>
                <TableRow>
                    <TableCell>Disconnect Button</TableCell>
                    <TableCell>
                        <MaterialUIWalletDisconnectButton />
                    </TableCell>
                    <TableCell>
                        <AntDesignWalletDisconnectButton />
                    </TableCell>
                    <TableCell>
                        <ReactUIWalletDisconnectButton />
                    </TableCell>
                    <TableCell></TableCell>
                </TableRow>
                <TableRow>
                    <TableCell>Dialog/Modal Button</TableCell>
                    <TableCell>
                        <MaterialUIWalletDialogButton />
                    </TableCell>
                    <TableCell>
                        <AntDesignWalletModalButton />
                    </TableCell>
                    <TableCell>
                        <ReactUIWalletModalButton />
                    </TableCell>
                    <TableCell></TableCell>
                </TableRow>
                <TableRow>
                    <TableCell>Multi Button</TableCell>
                    <TableCell>
                        <MaterialUIWalletMultiButton />
                    </TableCell>
                    <TableCell>
                        <AntDesignWalletMultiButton />
                    </TableCell>
                    <TableCell>
                        <ReactUIWalletMultiButton />
                    </TableCell>
                    <TableCell></TableCell>
                </TableRow>
                <TableRow>
                    <TableCell></TableCell>
                    <TableCell>
                        <Tooltip title="Only runs if the wallet is ready to connect" placement="left">
                            <FormControlLabel
                                control={
                                    <Switch
                                        name="autoConnect"
                                        color="secondary"
                                        checked={autoConnect}
                                        onChange={(event, checked) => setAutoConnect(checked)}
                                    />
                                }
                                label="AutoConnect"
                            />
                        </Tooltip>
                    </TableCell>
                    <TableCell>
                        <RequestAirdrop />
                    </TableCell>
                    <TableCell>
                        <SendTransaction />
                    </TableCell>
                    <TableCell>
                        <SignMessage />
                    </TableCell>
                </TableRow>
            </TableBody>
        </Table>
    );
}
Example #20
Source File: TextField.tsx    From firecms with MIT License 4 votes vote down vote up
/**
 * Generic text field.
 * This is one of the internal components that get mapped natively inside forms
 * and tables to the specified properties.
 * @category Form fields
 */
export function TextField<T extends string | number>({
                                                         name,
                                                         value,
                                                         setValue,
                                                         error,
                                                         showError,
                                                         disabled,
                                                         autoFocus,
                                                         property,
                                                         includeDescription,
                                                         allowInfinity,
                                                         shouldAlwaysRerender
                                                     }: TextFieldProps<T>) {

    const classes = formStyles();

    let mediaType: MediaType | undefined;
    let multiline: boolean | undefined;
    if (property.dataType === "string") {
        const url = (property as StringProperty).config?.url;
        mediaType = typeof url === "string" ? url : undefined;
        multiline = (property as StringProperty).config?.multiline;
    }

    useClearRestoreValue({
        property,
        value,
        setValue
    });

    const isMultiline = !!multiline;

    const internalValue = value ?? (property.dataType === "string" ? "" : value === 0 ? 0 : "");

    const valueIsInfinity = internalValue === Infinity;
    const inputType = !valueIsInfinity && property.dataType === "number" ? "number" : undefined;

    const updateValue = (newValue: typeof internalValue | undefined) => {

        if (!newValue) {
            setValue(
                null
            );
        } else if (inputType === "number") {
            const numValue = parseFloat(newValue as string);
            setValue(
                numValue as T
            );
        } else {
            setValue(
                newValue
            );
        }
    };

    const filledInput = (
        <FilledInput
            sx={{
                minHeight: "64px"
            }}
            autoFocus={autoFocus}
            type={inputType}
            multiline={isMultiline}
            inputProps={{
                rows: 4
            }}
            value={valueIsInfinity ? "Infinity" : (value ?? "")}
            disabled={disabled}
            onChange={(evt) => {
                updateValue(evt.target.value as T);
            }}
        />
    );

    return (
        <>

            <FormControl
                variant="filled"
                required={property.validation?.required}
                error={showError}
                disabled={valueIsInfinity}
                fullWidth>

                <InputLabel
                    classes={{
                        root: classes.inputLabel,
                        shrink: classes.shrinkInputLabel
                    }}>
                    <LabelWithIcon property={property}/>
                </InputLabel>

                {filledInput}

                <Box display={"flex"}>

                    <Box flexGrow={1}>
                        {showError && <FormHelperText>{error}</FormHelperText>}

                        {includeDescription &&
                        <FieldDescription property={property}/>}
                    </Box>

                    {allowInfinity &&
                    <FormControlLabel
                        checked={valueIsInfinity}
                        style={{ marginRight: 0 }}
                        labelPlacement={"start"}
                        control={
                            <Switch
                                size={"small"}
                                type={"checkbox"}
                                onChange={(evt) => {
                                    updateValue(
                                        evt.target.checked ? Infinity as T : undefined);
                                }}/>
                        }
                        disabled={disabled}
                        label={
                            <Typography variant={"caption"}>
                                Set value to Infinity
                            </Typography>
                        }
                    />
                    }
                </Box>

            </FormControl>

            {mediaType && internalValue &&
            <ErrorBoundary>
                <Box m={1}>
                    <PreviewComponent name={name}
                                      value={internalValue}
                                      property={property}
                                      size={"regular"}/>
                </Box>
            </ErrorBoundary>
            }
        </>
    );

}
Example #21
Source File: index.tsx    From Search-Next with GNU General Public License v3.0 4 votes vote down vote up
Release: FC = () => {
  const [update, setUpdate] = React.useState(false);
  const [remind, setRemind] =
    React.useState<AccountUpdateMessageRemind>('popup');
  const [interval, setInterval] = React.useState(0);
  const [messageData, setMessageData] = React.useState({} as AuthMessage);

  const init = () => {
    const account = localStorage.getItem('account');
    const result = getAuthDataByKey(account ?? '', 'message');
    if (isBoolean(result?.update)) {
      setUpdate(result.update);
      setRemind('popup');
      setInterval(0);
    } else {
      const { update = {} } = result || {};
      const {
        update: privUpdate = true,
        remind = 'popup',
        interval = 0,
      } = update;
      setUpdate(privUpdate);
      setRemind(remind);
      setInterval(interval);
    }
    setMessageData(result);
  };

  const handleUpdate = (key: any, val: any) => {
    const account = localStorage.getItem('account');
    const updateData: any = {
      update,
      interval,
      remind,
      lastTime: dayjs(),
    };
    updateData[key] = val;
    const newMessageData = {
      ...messageData,
      update: updateData,
    };
    setMessageData(newMessageData);
    updateAuthDataByKey(account ?? '', 'message', newMessageData);
    init();
  };

  const onUpdateSwichChange = (
    _: React.ChangeEvent<HTMLInputElement>,
    checked: boolean,
  ) => {
    setUpdate(checked);
    handleUpdate('update', checked);
  };

  const onRemindChange = (e: SelectChangeEvent<any>) => {
    const value = e.target.value as AccountUpdateMessageRemind;
    setRemind(value);
    handleUpdate('remind', value);
  };

  const onIntervalChange = (e: SelectChangeEvent<any>) => {
    const value = e.target.value as number;
    setInterval(value);
    handleUpdate('interval', value);
  };

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

  return (
    <div>
      <ContentList>
        <Alert severity="info">
          <AlertTitle>提示</AlertTitle>
          修改任意配置都会重置版本更新时间间隔依赖的时间
        </Alert>
        <ItemCard
          title="版本更新提醒"
          desc="设置版本更新时是否提醒"
          action={<Switch checked={update} onChange={onUpdateSwichChange} />}
        />
        <ItemCard
          title="提醒方式"
          desc="设置版本更新提醒方式"
          action={
            <Select
              size="small"
              label="提醒方式"
              value={remind}
              options={[
                { label: '消息', value: 'message' },
                // { label: '通知', value: 'notification' },
                { label: '弹窗', value: 'popup' },
              ]}
              onChange={onRemindChange}
            />
          }
        />
        <ItemCard
          title="提醒间隔"
          desc="设置版本更新提醒时间间隔"
          action={
            <Select
              size="small"
              label="提醒间隔"
              value={interval}
              options={[
                { label: '随时', value: 0 },
                { label: '7天', value: 7 },
                { label: '30天', value: 30 },
                { label: '60天', value: 60 },
                { label: '90天', value: 90 },
              ]}
              onChange={onIntervalChange}
            />
          }
        />
      </ContentList>
    </div>
  );
}
Example #22
Source File: index.tsx    From Search-Next with GNU General Public License v3.0 4 votes vote down vote up
Weather: FC = () => {
  let timer: number | undefined; // 定时器(点击授权时检查是否授权)
  let lastState: string | undefined; // 上一次的状态(记录单次点击授权最后一次状态,undefined时表示第一次点击)
  const userId = localStorage.getItem('account') ?? '';

  const [weather, setWeather] = React.useState<QWeatherNow>({} as QWeatherNow);
  const [location, setLocation] = React.useState<QWeatherCity>(
    {} as QWeatherCity,
  );
  const [permission, setPermission] = React.useState<boolean>(false);
  const [status, setStatus] = React.useState<string>('');
  const [geolocationStatus, setGeolocationStatusStatus] =
    React.useState<boolean>(false);
  const [key, setKey] = useState('');
  const [pluginKey, setPluginKey] = useState('');
  const [loading, setLoading] = useState(false);
  const [latlng, setLatlng] = useState<number[]>([]);
  const [weatherInterval, setWeatherInterval] = useState(15);
  const [show, setShow] = useState(true);

  const refreshOptions = [
    { label: '10分钟', value: 10 },
    { label: '15分钟', value: 15 },
    { label: '30分钟', value: 30 },
  ];

  // 获取当前位置并获取天气
  const getCurrentPosition = () => {
    geolocation.getCurrentPosition().then((res) => {
      const localData = getWeather(userId);
      const time = dayjs(localData?.updatedTime ?? localData?.createdTime);
      const diff = localData ? dayjs().diff(time, 'minute') > 10 : true;
      setKey(localData?.key ?? '');
      setPluginKey(localData?.pluginKey ?? '');
      if (diff) {
        setLatlng([res.longitude, res.latitude]);
        getLocationInfo({
          key: key ?? localData?.key,
          location: res.longitude + ',' + res.latitude,
        });
        getWeatherInfo({
          key: key ?? localData?.key,
          location: res.longitude + ',' + res.latitude,
        });
      } else if (localData) {
        localData.weather && setWeather(localData.weather);
        localData.city && setLocation(localData.city);
      }
    });
  };

  const applyPermission = () => {
    if (geolocation.checkGeolocation) {
      /* 地理位置服务可用 */
      setPermission(true);
      geolocation.getPermissionStatus().then((res) => {
        if (res === 'granted') {
          setGeolocationStatusStatus(true);
          getCurrentPosition();
        } else {
          setGeolocationStatusStatus(false);
        }
      });
    } else {
      /* 地理位置服务不可用 */
      setPermission(false);
    }
  };

  // 检查授权状态
  const checkPermission = () => {
    getCurrentPosition();
    timer = setInterval(async () => {
      geolocation.getPermissionStatus().then((res) => {
        setGeolocationStatusStatus(res === 'granted');
        setStatus(res);
        if (res !== 'prompt') {
          clearTimeout(timer);
          !lastState && toast.info('已选择位置信息权限,请检查浏览器设置');
          return;
        }
        lastState = res;
      });
    }, 100);
  };

  // 获取位置城市信息
  const getLocationInfo = (params: QweatherCityParams) => {
    setLoading(true);
    const { key } = params;
    locationInfo(params).then((res) => {
      setLocation(key ? res : res.data);
      setLoading(false);
    });
  };

  // 获取天气信息
  const getWeatherInfo = (params: QweatherNowParams) => {
    setLoading(true);
    const { key } = params;
    qweatherNow(params).then((res) => {
      setWeather(key ? res : res.data);
      setLoading(false);
    });
  };

  // 获取主页 天气设置
  const getWeatherSetting = () => {
    const res = getIndexWeatherSetting(userId);
    const setting = res?.navBar?.left?.weather;
    if (setting) {
      setWeatherInterval(setting.interval);
      setShow(setting.show);
    }
  };

  useEffect(() => {
    applyPermission();
    getWeatherSetting();
  }, []);

  useEffect(() => {
    // 保存天气信息前校验是否超过十分钟,填写key时不校验
    const localData = getWeather(userId);
    const time = dayjs(localData?.updatedTime ?? localData?.createdTime);
    const diff = localData ? dayjs().diff(time, 'minute') > 10 : true;
    if (
      Object.keys(weather).length > 0 &&
      Object.keys(location).length > 0 &&
      (diff || !!localData?.key)
    ) {
      saveWeather({
        userId,
        weather: weather,
        city: location,
        key: key,
        latlng,
      });
    }
  }, [weather, location]);

  useEffect(() => {
    saveIndexWeatherSetting({
      userId,
      interval: weatherInterval,
      show,
    });
  }, [weatherInterval, show]);

  return (
    <div>
      {geolocationStatus && (
        <WeatherCard
          apiKey={key}
          onRefresh={() => getCurrentPosition()}
          weather={weather}
          city={location}
          loading={loading}
        />
      )}
      <ContentList>
        <ContentTitle title="权限"></ContentTitle>
        <ItemAccordion
          title="位置访问"
          desc="获取用户地理位置信息,用于天气查询"
          action={
            <Switch
              disabled={!permission}
              onClick={(e) => e.stopPropagation()}
              checked={geolocationStatus}
              onChange={(e) => {
                checkPermission();
              }}
            />
          }
        >
          {!permission && (
            <Alert severity="warning">当前浏览器位置访问权限不可用</Alert>
          )}
          {status === 'granted' && (
            <Alert severity="success">已授权位置访问权限</Alert>
          )}
          {status === 'denied' && (
            <Alert severity="error">位置访问权限被拒绝,请检查浏览器设置</Alert>
          )}
          {status === 'prompt' && (
            <Alert severity="info">等待授权位置访问权限</Alert>
          )}
        </ItemAccordion>
        <ContentTitle title="KEY"></ContentTitle>
        <Alert severity="info">
          <AlertTitle>为什么需要填写KEY?</AlertTitle>
          虽然和风天气提供了免费方案,但考虑到使用次数限制,最好的方式是自己申请KEY,然后填写到下方。
          当然不填写KEY也可以使用天气功能,但是查询次数会有限制,如果超过限制,则无法使用天气功能。
        </Alert>
        <ItemAccordion title="和风天气KEY" desc="设置和风天气使用时必须的KEY">
          <Alert
            severity="warning"
            className={css`
              margin-bottom: 8px;
            `}
          >
            该KEY仅用作和风天气API使用,不会保存到服务器,请勿将KEY泄露给他人。
          </Alert>
          <TextField
            fullWidth
            variant="standard"
            label="和风天气API KEY"
            placeholder="请输入和风天气API KEY"
            value={key}
            disabled={!permission}
            onChange={(e) => {
              setKey(e.target.value);
            }}
            onBlur={() => {
              saveWeather({
                userId,
                weather: weather,
                city: location,
                key,
              });
            }}
            error={key.length > 32}
            helperText={key.length > 32 ? 'KEY长度不能超过32位' : ''}
          ></TextField>
          <div className="h-3"></div>
          <Alert
            severity="warning"
            className={css`
              margin-bottom: 8px;
            `}
          >
            该KEY仅用作和风天气插件使用,不会保存到服务器,请勿将KEY泄露给他人。
          </Alert>
          <TextField
            fullWidth
            variant="standard"
            label="和风天气插件 KEY"
            placeholder="请输入和风天气天气插件 KEY"
            value={pluginKey}
            disabled={!permission}
            onChange={(e) => {
              setPluginKey(e.target.value);
            }}
            onBlur={() => {
              saveWeather({
                userId,
                weather: weather,
                city: location,
                pluginKey,
              });
            }}
            error={pluginKey.length > 32}
            helperText={pluginKey.length > 32 ? 'KEY长度不能超过32位' : ''}
          ></TextField>
        </ItemAccordion>
        <ContentTitle title="高级设置" />
        <ItemCard
          title="刷新时间"
          desc="设置天气自动更新时间间隔"
          action={
            <Select
              disabled={!key || !permission}
              value={weatherInterval}
              onChange={(e) => setWeatherInterval(e.target.value)}
              options={refreshOptions}
            />
          }
        />
        <ItemCard
          title="首页展示"
          desc="设置首页是否展示天气"
          action={
            <Switch
              disabled={!permission}
              checked={show}
              onChange={(e) => setShow(e.target.checked)}
            />
          }
        />
      </ContentList>
      <ContentLinkList>
        <ContentTitle title="相关链接" />
        <Link text="和风天气开发平台" href="https://dev.qweather.com/" />
      </ContentLinkList>
    </div>
  );
}
Example #23
Source File: index.tsx    From Search-Next with GNU General Public License v3.0 4 votes vote down vote up
Logo: React.FC = () => {
  const [logoData, setLogoData] = React.useState<AuthLogo>({
    type: 'clock',
    show: true,
    zoom: true,
  } as AuthLogo);

  const [clockLogoData, setClockLogoData] = React.useState<ClockLogo>(
    {} as ClockLogo,
  );

  const initData = () => {
    const id = localStorage.getItem('account');
    if (!id) return;
    const logoData = getAuthDataByKey(id, 'logo');
    setClockLogoData(logoData.config.clock);
    setLogoData(logoData);
  };

  const updateLogoData = (data: any) => {
    const id = localStorage.getItem('account');
    if (!id) return;
    const newData = {
      ...logoData,
      ...data,
    };
    const update = updateLogoSetting(id, newData);
    if (!update) toast.error('设置logo出现错误');
    setClockLogoData(newData.config.clock);
    setLogoData(newData);
  };

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

  return (
    <div>
      <ContentList>
        <ItemCard
          title="Logo显示"
          desc="设置首页是否显示Logo"
          action={
            <Switch
              checked={logoData?.show}
              onChange={(e) => updateLogoData({ show: e.target.checked })}
            />
          }
        />
        <Alert title="提示" severity="warning">
          由于需求调整,Logo缩放功能将在未来删除
        </Alert>
        <ItemCard
          title="Logo缩放"
          desc="设置首页Logo在点击搜索框时是否缩放"
          action={
            <Switch
              disabled
              checked={logoData?.zoom}
              onChange={(e) => updateLogoData({ zoom: e.target.checked })}
            />
          }
        />
        <ItemCard
          title="Logo类型"
          desc="设置首页显示Logo的类型"
          action={
            <Select
              label="Logo类型"
              size="small"
              value={logoData?.type}
              onChange={(e) => updateLogoData({ type: e.target.value })}
              options={LogoData.filter((i) => i.show).map((i) => ({
                value: i.value,
                label: i.name,
              }))}
            ></Select>
          }
        />
        <ItemAccordion title="Logo样式" desc="设置首页显示Logo的样式">
          {logoData.type === 'clock' && (
            <div className="flex flex-col gap-2">
              {ClockData.map((i) => (
                <OutlineCard
                  fullWidth
                  key={i.id}
                  id={i.value}
                  value={clockLogoData.type}
                  tip={i.tooltip}
                  label={i.title}
                  onChange={(val) =>
                    updateLogoData({
                      config: {
                        ...logoData.config,
                        clock: { type: val },
                      },
                    })
                  }
                >
                  <div className="flex justify-center items-center p-2">
                    {React.createElement(i.component)}
                  </div>
                </OutlineCard>
              ))}
            </div>
          )}
          {logoData.type === 'image' && (
            <AccordionDetailItem
              title="在线图片地址"
              action={
                <TextField
                  size="small"
                  label="链接"
                  value={''}
                  placeholder="请输入图片链接"
                  onFocus={() => {}}
                  onChange={(e) => {}}
                  onBlur={(e) => {}}
                />
              }
            />
          )}
          {logoData.type !== 'clock' && <Alert severity="info">敬请期待</Alert>}
        </ItemAccordion>
      </ContentList>
    </div>
  );
}
Example #24
Source File: SettingsView.tsx    From react-flight-tracker with MIT License 4 votes vote down vote up
SettingsView: React.FC<Props> = (props) => {

  // Fields
  const contextName: string = ViewKeys.SettingsView;

  // Contexts
  const systemContext = useContext(SystemContext);
  const appContext = useContext(AppContext);

  const getSetting = (key: string, type: string) => {

    const value = systemContext.getSetting(key)
    if (typeof (value) === type)
      return value;

    return false;
  };

  const handleChange = (e: SelectChangeEvent) => {
    appContext.changeTheme(e.target.value);
  };

  const handleSettingsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    systemContext.storeSetting(e.target.name, e.target.checked);
  };

  const renderAppSettings = () => {

    return (
      <Card>

        <CardContent>

          <Typography
            variant={'h6'}
            gutterBottom={true}>
            {'App settings'}
          </Typography>

          <FormGroup>
            <FormControl
              color='secondary'
              variant="filled"
              sx={{ m: 1, minWidth: 120 }}>

              <InputLabel
                id="demo-simple-select-filled-label">
                Theme change
              </InputLabel>
              <Select
                labelId="demo-simple-select-filled-label"
                id="demo-simple-select-filled"
                value={appContext.activeThemeName}
                onChange={handleChange}>

                <MenuItem
                  value={ThemeKeys.DarkTheme}>
                  {ThemeKeys.DarkTheme}
                </MenuItem>
                <MenuItem
                  value={ThemeKeys.LightTheme}>
                  {ThemeKeys.LightTheme}
                </MenuItem>
                <MenuItem
                  value={ThemeKeys.PineappleTheme}>
                  {ThemeKeys.PineappleTheme}
                </MenuItem>
              </Select>
            </FormControl>

          </FormGroup>
        </CardContent>
      </Card>
    );
  };

  const renderMapSettings = () => {

    return (
      <Card>

        <CardContent>

          <Typography
            variant={'h6'}
            gutterBottom={true}>
            {'Map settings'}
          </Typography>

          <FormGroup>
            <FormControlLabel
              control={
                <Switch
                  color='secondary'
                  name={SettingKeys.ShowDataOverlayOnMap}
                  checked={getSetting(SettingKeys.ShowDataOverlayOnMap, 'boolean')}
                  onChange={handleSettingsChange} />
              }
              label="Show data overlay on map"
            />
            <FormControlLabel
              control={
                <Switch
                  color='secondary'
                  name={SettingKeys.ShowLogOverlayOnMap}
                  checked={getSetting(SettingKeys.ShowLogOverlayOnMap, 'boolean')}
                  onChange={handleSettingsChange} />
              }
              label="Show log overlay on map"
            />
          </FormGroup>
        </CardContent>
      </Card>
    );
  };

  return (

    <ViewContainer
      isScrollLocked={true}>

      {renderAppSettings()}

      <Box sx={{ height: (theme) => theme.spacing(1) }} />

      {renderMapSettings()}
    </ViewContainer>
  );
}
Example #25
Source File: index.tsx    From yearn-watch-legacy with GNU Affero General Public License v3.0 4 votes vote down vote up
SearchInput = (props: SearchInputProps) => {
    const {
        onFilter,
        debounceWait,
        totalItems,
        foundItems,
        totalSubItems,
        foundSubItems,
    } = props;
    const [searchText, setSearchText] = useState('');
    const [isSearching, setIsSearching] = useState(false);
    const [filterVaultsWithWarnings, setFilterVaultsWithWarnings] =
        useState(false);
    const [healthCheckFilter, setHealthCheckFilter] = useState('');

    const debounceFilter = useCallback(
        debounce((newSearchText, flags) => {
            const newSearchTextLowerCase = newSearchText.toLowerCase();
            onFilter(newSearchTextLowerCase, flags, healthCheckFilter);
            setIsSearching(false);
        }, debounceWait),
        [debounceWait, isSearching]
    );

    // Event listener called on every change
    const onChange = useCallback(
        (event: ChangeEvent) => {
            const value = (event.target as HTMLInputElement).value;
            setIsSearching(true);
            setSearchText(value);
            debounceFilter(value, getCurrentFlags(filterVaultsWithWarnings));
        },
        [filterVaultsWithWarnings, searchText, isSearching, healthCheckFilter]
    );

    const onFilterVaultsWithWarnings = useCallback(
        (e: ChangeEvent<HTMLInputElement>) => {
            setFilterVaultsWithWarnings(e.target.checked);
            setIsSearching(true);
            const newSearchTextLowerCase = searchText.toLowerCase();
            onFilter(
                newSearchTextLowerCase,
                getCurrentFlags(e.target.checked),
                healthCheckFilter
            );
            setIsSearching(false);
        },
        [searchText, isSearching, healthCheckFilter]
    );
    const handleClickClearSearch = useCallback(() => {
        setSearchText('');
        setFilterVaultsWithWarnings(false);
        onFilter('', getCurrentFlags(false), '');
    }, [onFilter]);
    const healthCheckFilterChange = useCallback(
        (e: SelectChangeEvent<unknown>) => {
            setHealthCheckFilter((e.target as HTMLInputElement).value);
            setIsSearching(true);
            const newSearchTextLowerCase = searchText.toLowerCase();
            onFilter(
                newSearchTextLowerCase,
                getCurrentFlags(filterVaultsWithWarnings),
                (e.target as HTMLInputElement).value
            );
            setIsSearching(false);
        },
        [searchText, healthCheckFilter, isSearching]
    );

    const renderSearchingLabel = useCallback(() => {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        let render: any;
        if (isSearching) {
            render = (
                <div>
                    <ProgressSpinnerBar label="results" />
                </div>
            );
        } else {
            render = (
                <>
                    {healthCheckFilter !== '' && (
                        <WarningLabel
                            warningText={'HealthCheck Filter is ON!'}
                        />
                    )}
                    <ResultsLabel
                        title="Vaults"
                        totalItems={totalItems}
                        foundItems={foundItems}
                        displayFound={true}
                        isSearching={isSearching}
                    />
                    <ResultsLabel
                        title="Strategies"
                        totalItems={totalSubItems}
                        foundItems={foundSubItems}
                        displayFound={true}
                        isSearching={isSearching}
                    />
                </>
            );
        }

        return render;
    }, [isSearching, totalItems, foundItems, totalSubItems, foundSubItems]);

    return (
        <div>
            <StyledForm>
                <Grid container direction="row" alignItems="center" spacing={3}>
                    <Grid item xs={12} sm={6}>
                        <StyledContainer maxWidth="lg">
                            <StyledTextField
                                variant="outlined"
                                onChange={onChange}
                                type="search"
                                value={searchText}
                                placeholder="Search by vault/strategy address/name, strategist address, token name/symbol, share token symbol/name or API version."
                                InputProps={
                                    searchText == ''
                                        ? {
                                              startAdornment: (
                                                  <InputAdornment position="end">
                                                      <Search />
                                                  </InputAdornment>
                                              ),
                                          }
                                        : {
                                              endAdornment: (
                                                  <InputAdornment position="end">
                                                      <IconButton
                                                          aria-label="delete"
                                                          onClick={
                                                              handleClickClearSearch
                                                          }
                                                          size="large"
                                                      >
                                                          <Delete />
                                                      </IconButton>
                                                  </InputAdornment>
                                              ),
                                          }
                                }
                            />
                        </StyledContainer>
                    </Grid>
                    <Grid item xs={12} sm={3}>
                        <StyledContainer maxWidth="lg">
                            <StyledFormControlLabel
                                control={
                                    <Switch
                                        checked={filterVaultsWithWarnings}
                                        onChange={onFilterVaultsWithWarnings}
                                        color="primary"
                                    />
                                }
                                labelPlacement="start"
                                label="Vaults with warnings"
                            />
                        </StyledContainer>
                    </Grid>
                    <Grid item xs={12} sm={3}>
                        <StyledContainer maxWidth="lg">
                            <StyledFormControlLabel
                                control={
                                    <StyledSelect
                                        displayEmpty
                                        variant="standard"
                                        defaultValue=""
                                        value={healthCheckFilter}
                                        onChange={healthCheckFilterChange}
                                    >
                                        <MenuItem value="">All</MenuItem>
                                        <MenuItem value="Enabled">
                                            Enabled
                                        </MenuItem>
                                        <MenuItem value="Disabled">
                                            Disabled
                                        </MenuItem>
                                        <MenuItem value="None">
                                            Not Set
                                        </MenuItem>
                                    </StyledSelect>
                                }
                                labelPlacement="start"
                                label="HealthCheck"
                            />
                        </StyledContainer>
                    </Grid>
                </Grid>
            </StyledForm>
            <StyledContainerResult maxWidth="lg">
                {renderSearchingLabel()}
            </StyledContainerResult>
        </div>
    );
}