@material-ui/core#Input TypeScript Examples

The following examples show how to use @material-ui/core#Input. 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: SelectAndTextInputContainer.stories.tsx    From anchor-web-app with Apache License 2.0 6 votes vote down vote up
Basic = () => {
  const [selectedItem, setSelectedItem] = useState<Item>(() => items[0]);

  return (
    <SelectAndTextInputContainer gridColumns={[120, '1fr']}>
      <MuiNativeSelect
        value={selectedItem.value}
        onChange={({ target }) =>
          setSelectedItem(
            items.find(({ value }) => target.value === value) ?? items[0],
          )
        }
      >
        {items.map(({ label, value }) => (
          <option key={value} value={value}>
            {label}
          </option>
        ))}
      </MuiNativeSelect>
      <Input placeholder="PLACEHOLDER" />
    </SelectAndTextInputContainer>
  );
}
Example #2
Source File: SaveAsMissionForm.tsx    From project-tauntaun with GNU Lesser General Public License v3.0 6 votes vote down vote up
export function SaveAsMissionForm() {
  const { setShowSaveAsMissionForm } = AppStateContainer.useContainer();
  const [filename, setFilename] = useState('');

  const onSaveClick = () => {
    gameService.sendSaveMission(filename);
    console.log(`Saving mission ${filename}`);
    setShowSaveAsMissionForm(false);
  };

  return (
    <div className="PopupBig">
      <span>Filename:</span>
      <Input onChange={e => setFilename(e.target.value)}></Input>
      <button onClick={onSaveClick}>Save</button>
      <button onClick={() => setShowSaveAsMissionForm(false)}>Close</button>
    </div>
  );
}
Example #3
Source File: NumberMuiInput.tsx    From anchor-web-app with Apache License 2.0 6 votes vote down vote up
export function NumberMuiInput({
  type = 'decimal',
  maxDecimalPoints,
  maxIntegerPoinsts,
  onChange,
  inputMode = type === 'decimal' ? 'decimal' : 'numeric',
  pattern = '[0-9.]*',
  ...props
}: NumberMuiInputProps) {
  const handlers = useRestrictedNumberInput({
    type,
    maxIntegerPoinsts,
    maxDecimalPoints,
    onChange,
  });
  return (
    <Input
      {...props}
      type="text"
      inputProps={{
        inputMode,
        pattern,
      }}
      {...handlers}
    />
  );
}
Example #4
Source File: SelectAndTextInputContainer.stories.tsx    From anchor-web-app with Apache License 2.0 6 votes vote down vote up
Disabled = () => {
  const [selectedItem, setSelectedItem] = useState<Item>(() => items[0]);

  return (
    <SelectAndTextInputContainer gridColumns={[120, '1fr']} aria-disabled>
      <MuiNativeSelect
        value={selectedItem.value}
        onChange={({ target }) =>
          setSelectedItem(
            items.find(({ value }) => target.value === value) ?? items[0],
          )
        }
      >
        {items.map(({ label, value }) => (
          <option key={value} value={value}>
            {label}
          </option>
        ))}
      </MuiNativeSelect>
      <Input placeholder="PLACEHOLDER" />
    </SelectAndTextInputContainer>
  );
}
Example #5
Source File: SelectAndTextInputContainer.stories.tsx    From anchor-web-app with Apache License 2.0 6 votes vote down vote up
Readonly = () => {
  const [selectedItem, setSelectedItem] = useState<Item>(() => items[0]);

  return (
    <SelectAndTextInputContainer gridColumns={[120, '1fr']} aria-readonly>
      <MuiNativeSelect
        value={selectedItem.value}
        onChange={({ target }) =>
          setSelectedItem(
            items.find(({ value }) => target.value === value) ?? items[0],
          )
        }
      >
        {items.map(({ label, value }) => (
          <option key={value} value={value}>
            {label}
          </option>
        ))}
      </MuiNativeSelect>
      <Input placeholder="PLACEHOLDER" />
    </SelectAndTextInputContainer>
  );
}
Example #6
Source File: SelectAndTextInputContainer.stories.tsx    From anchor-web-app with Apache License 2.0 6 votes vote down vote up
HelperText = () => {
  const [selectedItem, setSelectedItem] = useState<Item>(() => items[0]);

  return (
    <SelectAndTextInputContainer
      gridColumns={[120, '1fr']}
      leftHelperText="LEFT"
      rightHelperText="RIGHT"
    >
      <MuiNativeSelect
        value={selectedItem.value}
        onChange={({ target }) =>
          setSelectedItem(
            items.find(({ value }) => target.value === value) ?? items[0],
          )
        }
      >
        {items.map(({ label, value }) => (
          <option key={value} value={value}>
            {label}
          </option>
        ))}
      </MuiNativeSelect>
      <Input placeholder="PLACEHOLDER" />
    </SelectAndTextInputContainer>
  );
}
Example #7
Source File: PasswordForm.tsx    From project-tauntaun with GNU Lesser General Public License v3.0 6 votes vote down vote up
export function PasswordForm(props: PasswordFormProps) {
  const { onOk, onCancel } = props;
  const [password, setPassword] = useState('');

  return (
    <div className="Popup">
      <p>Enter password</p>
      <Input
        type="password"
        onChange={(event: React.ChangeEvent<HTMLInputElement>) => setPassword(event.target.value)}
      />
      <div>
        <button onClick={() => onOk(password)}>OK</button>
        <button onClick={() => onCancel()}>Cancel</button>
      </div>
    </div>
  );
}
Example #8
Source File: StationSelectInput.tsx    From metro-fare with MIT License 6 votes vote down vote up
StationSelectInput = ({
  title,
  value,
  onFocus,
}: StationSelectInputProps) => {
  const { i18n } = useTranslation();
  const stationElementId = `${title}-native-required`;
  const station = STATIONS.find((station) => station.id === value);
  const getStationLabel = (station: Station | undefined) => {
    if (!station) return "";

    const lineType = getLineTypeLabel(station.lineType);
    const stationName = getStationName(station, i18n.language);
    return station ? `${lineType} [${station.id}] ${stationName}` : "";
  };
  const label = getStationLabel(station);
  return (
    <div className="station-select-input" onFocus={onFocus}>
      <InputLabel htmlFor={stationElementId}>{title}</InputLabel>
      <Input className="station-input" value={label} />
    </div>
  );
}
Example #9
Source File: CalendarSelect.tsx    From backstage with Apache License 2.0 5 votes vote down vote up
CalendarSelect = ({
  disabled,
  selectedCalendars = [],
  setSelectedCalendars,
  calendars,
}: CalendarSelectProps) => {
  const classes = useStyles();

  return (
    <FormControl className={classes.formControl}>
      <Select
        labelId="calendars-label"
        disabled={disabled || calendars.length === 0}
        multiple
        value={selectedCalendars}
        onChange={async e => setSelectedCalendars(e.target.value as string[])}
        input={<Input />}
        renderValue={selected => (
          <Typography className={classes.selectedCalendars} variant="body2">
            {calendars
              .filter(c => c.id && (selected as string[]).includes(c.id))
              .map(c => c.summary)
              .join(', ')}
          </Typography>
        )}
        MenuProps={{
          PaperProps: {
            style: {
              width: 350,
            },
          },
        }}
      >
        {sortBy(calendars, 'summary').map(c => (
          <MenuItem key={c.id} value={c.id}>
            <Checkbox checked={selectedCalendars.includes(c.id!)} />
            <ListItemText primary={c.summary} />
          </MenuItem>
        ))}
      </Select>
    </FormControl>
  );
}
Example #10
Source File: RadarPage.tsx    From backstage with Apache License 2.0 5 votes vote down vote up
/**
 * Main Page of Tech Radar
 */
export function RadarPage(props: TechRadarPageProps) {
  const {
    title = 'Tech Radar',
    subtitle = 'Pick the recommended technologies for your projects',
    pageTitle = 'Company Radar',
    ...componentProps
  } = props;
  const classes = useStyles();
  const [searchText, setSearchText] = React.useState('');

  return (
    <Page themeId="tool">
      <Header title={title} subtitle={subtitle} />
      <Content className={classes.overflowXScroll}>
        <ContentHeader title={pageTitle}>
          <Input
            id="tech-radar-filter"
            type="search"
            placeholder="Filter"
            onChange={e => setSearchText(e.target.value)}
          />
          <SupportButton>
            <p>
              This is used for visualizing the official guidelines of different
              areas of software development such as languages, frameworks,
              infrastructure and processes. You can find an explanation for the
              radar at{' '}
              <Link to="https://opensource.zalando.com/tech-radar/">
                Zalando Tech Radar
              </Link>
              .
            </p>
          </SupportButton>
        </ContentHeader>
        <Grid container spacing={3} direction="row">
          <Grid item xs={12} sm={6} md={4}>
            <RadarComponent searchText={searchText} {...componentProps} />
          </Grid>
        </Grid>
      </Content>
    </Page>
  );
}
Example #11
Source File: MapSearch.tsx    From covid19testing-map with GNU General Public License v3.0 5 votes vote down vote up
MapSearch = ({ map }: SearchProps) => {
  const classes = useStyles();

  const [searchBox, setSearchBox] = useState<any>();

  const onLoad = (ref: any) => {
    setSearchBox(ref);
  };

  const onPlacesChanged = () => {
    if (searchBox.getPlaces()[0] !== undefined) {
      map.fitBounds(searchBox.getPlaces()[0].geometry.viewport);
    }
  };

  const { isLoaded, loadError } = useLoadScript({
    googleMapsApiKey: REACT_APP_GCP_MAPS_API_KEY,
    libraries,
  });

  const handleSearchClick = React.useCallback(() => {
    trackUiClick('Search');
  }, []);

  const renderSearchBox = () => {
    return (
      <div id="search-input">
        <StandaloneSearchBox onLoad={onLoad} onPlacesChanged={onPlacesChanged}>
          <Input
            placeholder='Search places like "Albany", "NYU", or "94025"'
            className={classes.input}
            inputProps={{ 'aria-label': 'search google maps' }}
            onClick={handleSearchClick}
            startAdornment={
              <InputAdornment position="start">
                <SearchIcon />
              </InputAdornment>
            }
          />
        </StandaloneSearchBox>
      </div>
    );
  };
  if (loadError) {
    return <div>Search bar cannot be loaded right now, sorry.</div>;
  }

  return isLoaded ? renderSearchBox() : <div />;
}
Example #12
Source File: ColorPicker.tsx    From glific-frontend with GNU Affero General Public License v3.0 5 votes vote down vote up
ColorPicker: React.SFC<Props> = ({ ...props }) => {
  const [displayPicker, setDisplayPicker] = useState(false);
  const [colorHex, setColorHex] = useState('');
  const { t } = useTranslation();

  const { colorCode, name, helperText } = props;

  useEffect(() => {
    setColorHex(colorCode);
  }, [colorCode]);

  const handleChangeComplete = (color: any) => {
    setColorHex(color.hex);
    props.form.setFieldValue(props.field.name, color.hex);
  };

  const onClickAway = () => {
    setDisplayPicker(false);
  };

  return (
    <div className={styles.Container}>
      <div className={styles.ColorPicker} data-testid="ColorPicker">
        <div className={styles.ColorInput}>
          <Input type="text" placeholder={t('Select color')} name={name} value={colorHex} />
        </div>
        <ClickAwayListener onClickAway={onClickAway}>
          <div className={styles.ContainListener}>
            <div
              role="button"
              tabIndex={0}
              data-testid="ChooseColor"
              className={styles.ChooseColor}
              style={{
                backgroundColor: colorHex,
              }}
              onClick={() => setDisplayPicker(!displayPicker)}
              onKeyDown={() => setDisplayPicker(!displayPicker)}
            >
              &nbsp;
            </div>
            {helperText ? (
              <FormHelperText className={styles.HelperText}>{helperText}</FormHelperText>
            ) : null}
            {displayPicker ? (
              <TwitterPicker
                className={styles.PickerPanel}
                triangle="hide"
                onChangeComplete={handleChangeComplete}
              />
            ) : null}
          </div>
        </ClickAwayListener>
      </div>
    </div>
  );
}
Example #13
Source File: SelectAndTextInputContainer.stories.tsx    From anchor-web-app with Apache License 2.0 5 votes vote down vote up
Multiline = () => {
  const [selectedItem, setSelectedItem] = useState<Item>(() => items[0]);

  return (
    <SelectAndTextInputContainer
      gridColumns={[120, '1fr', '1fr']}
      gridRows={[60, 60, 60, 60]}
    >
      <MuiNativeSelect
        value={selectedItem.value}
        onChange={({ target }) =>
          setSelectedItem(
            items.find(({ value }) => target.value === value) ?? items[0],
          )
        }
      >
        {items.map(({ label, value }) => (
          <option key={value} value={value}>
            {label}
          </option>
        ))}
      </MuiNativeSelect>
      <Input placeholder="PLACEHOLDER" />
      <Input placeholder="PLACEHOLDER" />

      <MuiNativeSelect
        value={selectedItem.value}
        onChange={({ target }) =>
          setSelectedItem(
            items.find(({ value }) => target.value === value) ?? items[0],
          )
        }
      >
        {items.map(({ label, value }) => (
          <option key={value} value={value}>
            {label}
          </option>
        ))}
      </MuiNativeSelect>
      <Input placeholder="PLACEHOLDER" />
      <Input placeholder="PLACEHOLDER" />

      <MuiNativeSelect
        value={selectedItem.value}
        onChange={({ target }) =>
          setSelectedItem(
            items.find(({ value }) => target.value === value) ?? items[0],
          )
        }
      >
        {items.map(({ label, value }) => (
          <option key={value} value={value}>
            {label}
          </option>
        ))}
      </MuiNativeSelect>
      <Input placeholder="PLACEHOLDER" />
      <Input placeholder="PLACEHOLDER" />

      <MuiNativeSelect
        value={selectedItem.value}
        onChange={({ target }) =>
          setSelectedItem(
            items.find(({ value }) => target.value === value) ?? items[0],
          )
        }
      >
        {items.map(({ label, value }) => (
          <option key={value} value={value}>
            {label}
          </option>
        ))}
      </MuiNativeSelect>
      <Input placeholder="PLACEHOLDER" />
      <Input placeholder="PLACEHOLDER" />
    </SelectAndTextInputContainer>
  );
}
Example #14
Source File: EntitySearchBar.tsx    From backstage with Apache License 2.0 5 votes vote down vote up
EntitySearchBar = () => {
  const classes = useStyles();

  const { filters, updateFilters } = useEntityList();
  const [search, setSearch] = useState(filters.text?.value ?? '');

  useDebounce(
    () => {
      updateFilters({
        text: search.length ? new EntityTextFilter(search) : undefined,
      });
    },
    250,
    [search, updateFilters],
  );

  return (
    <Toolbar className={classes.searchToolbar}>
      <FormControl>
        <Input
          aria-label="search"
          id="input-with-icon-adornment"
          className={classes.input}
          placeholder="Search"
          autoComplete="off"
          onChange={event => setSearch(event.target.value)}
          value={search}
          startAdornment={
            <InputAdornment position="start">
              <Search />
            </InputAdornment>
          }
          endAdornment={
            <InputAdornment position="end">
              <IconButton
                aria-label="clear search"
                onClick={() => setSearch('')}
                edge="end"
                disabled={search.length === 0}
              >
                <Clear />
              </IconButton>
            </InputAdornment>
          }
        />
      </FormControl>
    </Toolbar>
  );
}
Example #15
Source File: settings_button.tsx    From jupyter-extensions with Apache License 2.0 5 votes vote down vote up
render() {
    return (
      <React.Fragment>
        <IconButton
          title="Settings"
          color="inherit"
          onClick={() => this._onClick()}
        >
          <SettingsIcon fontSize="small" />
        </IconButton>

        <Dialog
          open={this.state.open}
          onClose={() => this._onClose()}
          fullWidth
        >
          <DialogTitle>Settings</DialogTitle>
          <DialogContent>
            <Grid container alignItems="center" spacing={2}>
              <Grid item xs={11}>
                <Typography id="input-slider">Sync Frequency</Typography>
              </Grid>
              <Grid item xs={1}>
                <Tooltip title="Time between each sync with remote repository. After each sync, the extension will wait before syncing again.">
                  <HelpOutlineIcon color="disabled" />
                </Tooltip>
              </Grid>
              <Grid item xs={1}>
                <TimerIcon />
              </Grid>
              <Grid item xs={7}>
                <Slider
                  value={
                    typeof this.state.value === 'number'
                      ? this.state.value / (this.maxInterval / 100)
                      : 0
                  }
                  onChange={(event, value) =>
                    this._onSliderChange(event, value)
                  }
                  aria-labelledby="sync-interval-slider"
                />
              </Grid>
              <Grid item xs={4}>
                <Input
                  value={this.state.value}
                  onChange={event => this._onInputChange(event)}
                  onBlur={() => this._onBlur()}
                  margin="dense"
                  inputProps={{
                    step: 1,
                    min: 0,
                    max: this.maxInterval,
                    type: 'number',
                    'aria-labelledby': 'sync-interval-slider',
                  }}
                  endAdornment={
                    <InputAdornment position="end">seconds</InputAdornment>
                  }
                />
              </Grid>
            </Grid>
          </DialogContent>
          <DialogActions>
            <Button onClick={() => this._onClose()} color="primary">
              Cancel
            </Button>
            <Button
              onClick={() => this._onSave()}
              color="primary"
              variant="contained"
            >
              Save
            </Button>
          </DialogActions>
        </Dialog>
      </React.Fragment>
    );
  }
Example #16
Source File: SQFormMultiSelect.tsx    From SQForm with MIT License 4 votes vote down vote up
function SQFormMultiSelect({
  children,
  isDisabled = false,
  label,
  name,
  onChange,
  size = 'auto',
  useSelectAll = true,
  toolTipPlacement = 'bottom',
  muiFieldProps = {},
  showTooltip = true,
  tooltipText,
}: SQFormMultiSelectProps): React.ReactElement {
  const classes = useStyles();

  const {setFieldValue} = useFormikContext();
  const [toolTipEnabled, setToolTipEnabled] = React.useState(true);
  const {
    formikField: {field},
    fieldState: {isFieldError, isFieldRequired},
    fieldHelpers: {handleBlur, HelperTextComponent},
  } = useForm<SQFormOption['value'][], unknown>({name});

  React.useEffect(() => {
    if (!children) {
      console.warn(getUndefinedChildrenWarning('SQFormMultiSelect', name));
    }

    if (field.value === undefined || field.value === null) {
      console.warn(getUndefinedValueWarning('SQFormMultiSelect', name));
    }
  }, [children, field.value, name]);

  const labelID = label.toLowerCase();
  const toolTipTitle = getToolTipTitle(field.value, children);

  const getIsSelectAllChecked = (value: SQFormOption['value'][]) =>
    value.includes('ALL');
  const getIsSelectNoneChecked = (value: SQFormOption['value'][]) =>
    value.includes('NONE');

  const getValues = (
    children: SQFormMultiSelectProps['children'],
    isSelectAllChecked: boolean,
    isSelectNoneChecked: boolean,
    value: SQFormOption['value'][]
  ) => {
    if (isSelectAllChecked) {
      return children?.map((option) => option.value);
    }

    if (isSelectNoneChecked) {
      return [];
    }

    return value;
  };

  const handleMultiSelectChange = (
    event: React.ChangeEvent<{name?: string; value: unknown}>,
    _child: ReactNode
  ) => {
    const value = event.target.value as unknown as SQFormOption['value'][];
    const isSelectAllChecked = getIsSelectAllChecked(value);
    const isSelectNoneChecked = getIsSelectNoneChecked(value);
    const values = getValues(
      children,
      isSelectAllChecked,
      isSelectNoneChecked,
      value
    );

    setFieldValue(name, values);
    onChange && onChange(event, value);
  };

  const toggleTooltip = () => {
    setToolTipEnabled(!toolTipEnabled);
  };

  /**
   * this handles scenarios where label and value are not the same,
   * e.g., if value is an "ID"
   */
  const getRenderValue = (selected: unknown) => {
    const getValue = (selectedValues: SQFormOption['value'][]) => {
      if (!selectedValues?.length) {
        return EMPTY_LABEL;
      }

      return selectedDisplayValue(selectedValues, children, name);
    };

    return getValue(selected as SQFormOption['value'][]);
  };

  const renderTooltip = () => {
    if (!showTooltip || !toolTipEnabled) {
      return '';
    }

    return tooltipText || toolTipTitle;
  };

  return (
    <Grid item sm={size}>
      <FormControl
        error={isFieldError}
        disabled={isDisabled}
        required={isFieldRequired}
        fullWidth={true}
      >
        <InputLabel shrink={true} id={labelID}>
          {label}
        </InputLabel>
        <Tooltip
          placement={toolTipPlacement}
          arrow={true}
          enterDelay={1000}
          leaveDelay={100}
          title={renderTooltip()}
        >
          <Select
            className={classes.selectHeight}
            multiple
            displayEmpty
            input={<Input disabled={isDisabled} name={name} />}
            value={(field.value as SQFormOption['value'][]) || []}
            onBlur={handleBlur}
            onChange={handleMultiSelectChange}
            fullWidth={true}
            labelId={labelID}
            renderValue={getRenderValue}
            MenuProps={MenuProps}
            onOpen={toggleTooltip}
            onClose={toggleTooltip}
            {...muiFieldProps}
          >
            {useSelectAll && (
              <MenuItem
                value={
                  children?.length === field.value?.length ? 'NONE' : 'ALL'
                }
              >
                <Checkbox checked={children?.length === field.value?.length} />
                <ListItemText
                  primary="Select All"
                  primaryTypographyProps={{variant: 'body2'}}
                />
              </MenuItem>
            )}
            {children?.map((option) => {
              return (
                <MenuItem key={`${name}_${option.value}`} value={option.value}>
                  <Checkbox checked={field.value?.includes(option.value)} />
                  <ListItemText
                    primary={option.label}
                    primaryTypographyProps={{variant: 'body2'}}
                  />
                </MenuItem>
              );
            })}
          </Select>
        </Tooltip>
        {!isDisabled && <FormHelperText>{HelperTextComponent}</FormHelperText>}
      </FormControl>
    </Grid>
  );
}
Example #17
Source File: start_thread.tsx    From jupyter-extensions with Apache License 2.0 4 votes vote down vote up
export function CommentEditor(props) {
  const [comment, setComment] = useState('');
  const [lineNumber, setLineNumber] = useState(0);
  const [showLineInput, setShowLineInput] = useState(false);

  const handleSubmit = event => {
    event.preventDefault();
    switch (props.commentType) {
      case 'review':
        newReviewCommentThread(
          props.currFilePath,
          props.serverRoot,
          comment,
          props.reviewHash,
          lineNumber
        );
        break;
      case 'detached':
        newDetachedCommentThread(
          props.currFilePath,
          props.serverRoot,
          comment,
          lineNumber
        );
        break;
    }
    setComment(''); //clear comment editor field
    setLineNumber(0);
    setShowLineInput(false);
  };
  return (
    <form
      onSubmit={handleSubmit}
      style={style.editor}
      className="commentSubmit"
    >
      <link
        rel="stylesheet"
        href="https://fonts.googleapis.com/icon?family=Material+Icons"
      />
      <Grid container direction="column">
        <Grid item>
          <TextField
            multiline
            rows={3}
            label="Start a new comment thread"
            value={comment}
            onChange={e => setComment(e.target.value)}
            variant="outlined"
            size="medium"
            style={style.textField}
            className="newThreadTextField"
          />
        </Grid>
        <Grid container direction="row" style={style.submitOptions}>
          {showLineInput ? (
            <Input
              type="number"
              value={lineNumber}
              onChange={e => setLineNumber(parseInt(e.target.value))}
              name="line number"
              margin="none"
              inputProps={{
                style: {
                  width: '40px',
                },
                min: 0,
              }}
            />
          ) : (
            <Button size="small" onClick={() => setShowLineInput(true)}>
              {'Set line #'}
            </Button>
          )}
          <Grid item style={style.submit}>
            <SendButton type="sendThread" />
          </Grid>
        </Grid>
      </Grid>
    </form>
  );
}
Example #18
Source File: index.tsx    From prism-frontend with MIT License 4 votes vote down vote up
function Analyser({ extent, classes }: AnalyserProps) {
  const dispatch = useDispatch();
  const map = useSelector(mapSelector);
  const selectedLayers = useSelector(layersSelector);
  const {
    updateHistory,
    removeKeyFromUrl,
    resetAnalysisParams,
    updateAnalysisParams,
    getAnalysisParams,
  } = useUrlHistory();

  const availableDates = useSelector(availableDatesSelector);
  const analysisResult = useSelector(analysisResultSelector);

  const isAnalysisLoading = useSelector(isAnalysisLoadingSelector);
  const isMapLayerActive = useSelector(isAnalysisLayerActiveSelector);

  const {
    analysisHazardLayerId: hazardLayerIdFromUrl,
    analysisBaselineLayerId: baselineLayerIdFromUrl,
    analysisDate: selectedDateFromUrl,
    analysisStatistic: selectedStatisticFromUrl,
    analysisThresholdAbove: aboveThresholdFromUrl,
    analysisThresholdBelow: belowThresholdFromUrl,
    analysisAdminLevel: adminLevelFromUrl,
    analysisStartDate: selectedStartDateFromUrl,
    analysisEndDate: selectedEndDateFromUrl,
  } = getAnalysisParams();

  // form elements
  const [hazardLayerId, setHazardLayerId] = useState<LayerKey | undefined>(
    hazardLayerIdFromUrl,
  );
  const [statistic, setStatistic] = useState(
    (selectedStatisticFromUrl as AggregationOperations) ||
      AggregationOperations.Mean,
  );
  const [baselineLayerId, setBaselineLayerId] = useState<LayerKey | undefined>(
    baselineLayerIdFromUrl,
  );
  const [selectedDate, setSelectedDate] = useState<number | null>(null);
  const [belowThreshold, setBelowThreshold] = useState(
    belowThresholdFromUrl || '',
  );
  const [aboveThreshold, setAboveThreshold] = useState(
    aboveThresholdFromUrl || '',
  );
  const [thresholdError, setThresholdError] = useState<string | null>(null);

  const [isAnalyserFormOpen, setIsAnalyserFormOpen] = useState<boolean>(
    hazardLayerIdFromUrl !== undefined,
  );
  const [isTableViewOpen, setIsTableViewOpen] = useState(true);

  // for polygon intersection analysis
  const [adminLevel, setAdminLevel] = useState<AdminLevelType>(
    Number(adminLevelFromUrl || '1') as AdminLevelType,
  );
  const [startDate, setStartDate] = useState<number | null>(null);
  const [endDate, setEndDate] = useState<number | null>(null);

  // find layer for the given adminLevel
  const adminLevelLayer = getAdminLevelLayer(adminLevel);
  const adminLevelLayerData = useSelector(
    // if we couldn't find an admin layer, just return undefined
    adminLevelLayer ? layerDataSelector(adminLevelLayer.id) : () => undefined,
  ) as LayerData<BoundaryLayerProps> | undefined;

  // get variables derived from state
  const selectedHazardLayer = hazardLayerId
    ? (LayerDefinitions[hazardLayerId] as WMSLayerProps)
    : null;
  const hazardDataType: HazardDataType | null = selectedHazardLayer
    ? selectedHazardLayer.geometry || RasterType.Raster
    : null;
  const availableHazardDates = selectedHazardLayer
    ? getPossibleDatesForLayer(selectedHazardLayer, availableDates)?.map(
        d => new Date(d),
      ) || []
    : undefined;

  const BASELINE_URL_LAYER_KEY = 'baselineLayerId';
  const preSelectedBaselineLayer = selectedLayers.find(
    l => l.type === 'admin_level_data',
  );
  const [previousBaselineId, setPreviousBaselineId] = useState<
    LayerKey | undefined
  >(preSelectedBaselineLayer?.id);

  const { t } = useSafeTranslation();

  // check if there is any available date from the url, otherwise use last available date for the selected hazard layer
  const lastAvailableHazardDate = availableHazardDates
    ? getDateFromList(
        selectedDateFromUrl ? new Date(selectedDateFromUrl) : null,
        availableHazardDates,
      )?.getTime() || null
    : null;
  const lastAvailableHazardStartDate = availableHazardDates
    ? getDateFromList(
        selectedStartDateFromUrl ? new Date(selectedStartDateFromUrl) : null,
        availableHazardDates,
      )?.getTime() || null
    : null;
  const lastAvailableHazardEndDate = availableHazardDates
    ? getDateFromList(
        selectedEndDateFromUrl ? new Date(selectedEndDateFromUrl) : null,
        availableHazardDates,
      )?.getTime() || null
    : null;
  const { translatedColumns } = useAnalysisTableColumns(analysisResult);

  // set default date after dates finish loading and when hazard layer changes
  useEffect(() => {
    if (isNil(lastAvailableHazardDate)) {
      setSelectedDate(null);
    } else {
      setSelectedDate(lastAvailableHazardDate);
    }
    if (isNil(lastAvailableHazardStartDate)) {
      setStartDate(null);
    } else {
      setStartDate(lastAvailableHazardStartDate);
    }
    if (isNil(lastAvailableHazardEndDate)) {
      setEndDate(null);
    } else {
      setEndDate(lastAvailableHazardEndDate);
    }
  }, [
    availableDates,
    hazardLayerId,
    lastAvailableHazardDate,
    lastAvailableHazardStartDate,
    lastAvailableHazardEndDate,
  ]);

  const onOptionChange = <T extends string>(
    setterFunc: Dispatch<SetStateAction<T>>,
  ) => (event: React.ChangeEvent<HTMLInputElement>) => {
    const value = event.target.value as T;
    setterFunc(value);
    return value;
  };
  // specially for threshold values, also does error checking
  const onThresholdOptionChange = (thresholdType: 'above' | 'below') => (
    event: React.ChangeEvent<HTMLInputElement>,
  ) => {
    const setterFunc =
      thresholdType === 'above' ? setAboveThreshold : setBelowThreshold;
    const changedOption = onOptionChange(setterFunc)(event);
    // setting a value doesn't update the existing value until next render, therefore we must decide whether to access the old one or the newly change one here.
    const aboveThresholdValue = parseFloat(
      thresholdType === 'above' ? changedOption : aboveThreshold,
    );
    const belowThresholdValue = parseFloat(
      thresholdType === 'below' ? changedOption : belowThreshold,
    );
    if (belowThresholdValue > aboveThresholdValue) {
      setThresholdError('Below threshold is larger than above threshold!');
    } else {
      setThresholdError(null);
    }
  };

  const statisticOptions = Object.entries(AggregationOperations)
    .filter(([, value]) => value !== AggregationOperations.Sum) // sum is used only for exposure analysis.
    .map(([key, value]) => (
      <FormControlLabel
        key={key}
        value={value}
        control={
          <Radio
            className={classes.radioOptions}
            color="default"
            size="small"
          />
        }
        label={t(key)}
      />
    ));
  const activateUniqueBoundary = (forceAdminLevel?: BoundaryLayerProps) => {
    if (forceAdminLevel) {
      // remove displayed boundaries
      getDisplayBoundaryLayers().forEach(l => {
        if (l.id !== forceAdminLevel.id) {
          safeDispatchRemoveLayer(map, l, dispatch);
        }
      });

      safeDispatchAddLayer(
        map,
        { ...forceAdminLevel, isPrimary: true },
        dispatch,
      );
      return;
    }

    if (!baselineLayerId) {
      throw new Error('Layer should be selected to run analysis');
    }

    const baselineLayer = LayerDefinitions[
      baselineLayerId
    ] as AdminLevelDataLayerProps;

    if (baselineLayer.boundary) {
      const boundaryLayer = LayerDefinitions[
        baselineLayer.boundary
      ] as BoundaryLayerProps;
      // remove displayed boundaries
      getDisplayBoundaryLayers().forEach(l => {
        if (l.id !== boundaryLayer.id) {
          safeDispatchRemoveLayer(map, l, dispatch);
        }
      });

      safeDispatchAddLayer(
        map,
        { ...boundaryLayer, isPrimary: true },
        dispatch,
      );
    } else {
      getDisplayBoundaryLayers().forEach(l => {
        safeDispatchAddLayer(map, l, dispatch);
      });
    }
  };

  const deactivateUniqueBoundary = () => {
    if (!baselineLayerId) {
      throw new Error('Layer should be selected to run analysis');
    }
    const baselineLayer = LayerDefinitions[
      baselineLayerId
    ] as AdminLevelDataLayerProps;

    if (baselineLayer.boundary) {
      const boundaryLayer = LayerDefinitions[
        baselineLayer.boundary
      ] as BoundaryLayerProps;
      if (!getDisplayBoundaryLayers().includes(boundaryLayer)) {
        safeDispatchRemoveLayer(map, boundaryLayer, dispatch);
      }
    }

    getDisplayBoundaryLayers().forEach(l => {
      safeDispatchAddLayer(map, l, dispatch);
    });
  };

  const clearAnalysis = () => {
    dispatch(clearAnalysisResult());

    resetAnalysisParams();

    if (previousBaselineId) {
      const previousBaseline = LayerDefinitions[
        previousBaselineId
      ] as AdminLevelDataLayerProps;
      updateHistory(BASELINE_URL_LAYER_KEY, previousBaselineId);
      safeDispatchAddLayer(map, previousBaseline, dispatch);
      // check isMapLayerActive on analysis clear
      // to avoid miss behaviour on boundary layers
      dispatch(setIsMapLayerActive(true));
    }
  };

  const shareAnalysis = () => {
    copyTextToClipboard(window.location.href).then(() => {
      dispatch(
        addNotification({
          message: 'Link to this analysis copied to clipboard!',
          type: 'success',
        }),
      );
    });
  };

  const onMapSwitchChange = (e: ChangeEvent<HTMLInputElement>) => {
    dispatch(setIsMapLayerActive(e.target.checked));

    // hazard layer doesn't needs a display boundary
    // because it is already a vector
    if (hazardDataType === GeometryType.Polygon) {
      return;
    }

    if (isMapLayerActive) {
      deactivateUniqueBoundary();
      // check for previous baseline and bring it back
      if (previousBaselineId) {
        const previousBaseline = LayerDefinitions[
          previousBaselineId
        ] as AdminLevelDataLayerProps;
        updateHistory(BASELINE_URL_LAYER_KEY, previousBaselineId);
        safeDispatchAddLayer(map, previousBaseline, dispatch);
      }
    } else {
      // check for previous baseline and remove it before...
      if (previousBaselineId) {
        const previousBaseline = LayerDefinitions[
          previousBaselineId
        ] as AdminLevelDataLayerProps;
        removeKeyFromUrl(BASELINE_URL_LAYER_KEY);
        safeDispatchRemoveLayer(map, previousBaseline, dispatch);
      }

      // activating the unique boundary layer
      activateUniqueBoundary();
    }
  };

  const runAnalyser = async () => {
    if (preSelectedBaselineLayer) {
      setPreviousBaselineId(preSelectedBaselineLayer.id);
      removeKeyFromUrl(BASELINE_URL_LAYER_KEY);
      // no need to safely dispatch remove we are sure
      dispatch(removeLayer(preSelectedBaselineLayer));
    }

    if (analysisResult) {
      clearAnalysis();
    }

    if (!extent) {
      return;
    } // hasn't been calculated yet

    if (!selectedHazardLayer) {
      throw new Error('Hazard layer should be selected to run analysis');
    }

    if (hazardDataType === GeometryType.Polygon) {
      if (!startDate) {
        throw new Error('Date Range must be given to run analysis');
      }
      if (!endDate) {
        throw new Error('Date Range must be given to run analysis');
      }
      if (!adminLevelLayer || !adminLevelLayerData) {
        // technically we can't get here because the run analaysis button
        // is disabled while the admin level data loads
        // but we have to put this in so the typescript compiler
        // doesn't throw an error when we try to access the data
        // property of adminLevelLayerData
        throw new Error('Admin level data is still loading');
      }

      const params: PolygonAnalysisDispatchParams = {
        hazardLayer: selectedHazardLayer,
        adminLevel,
        adminLevelLayer,
        adminLevelData: adminLevelLayerData.data,
        startDate,
        endDate,
        extent,
      };
      activateUniqueBoundary(adminLevelLayer);
      // update history
      updateAnalysisParams({
        analysisHazardLayerId: hazardLayerId,
        analysisAdminLevel: adminLevel.toString(),
        analysisStartDate: moment(startDate).format(DEFAULT_DATE_FORMAT),
        analysisEndDate: moment(endDate).format(DEFAULT_DATE_FORMAT),
        analysisStatistic: statistic,
      });
      dispatch(requestAndStorePolygonAnalysis(params));
    } else {
      if (!selectedDate) {
        throw new Error('Date must be given to run analysis');
      }

      if (!baselineLayerId) {
        throw new Error('Baseline layer should be selected to run analysis');
      }

      const selectedBaselineLayer = LayerDefinitions[
        baselineLayerId
      ] as AdminLevelDataLayerProps;

      activateUniqueBoundary();

      const params: AnalysisDispatchParams = {
        hazardLayer: selectedHazardLayer,
        baselineLayer: selectedBaselineLayer,
        date: selectedDate,
        statistic,
        extent,
        threshold: {
          above: parseFloat(aboveThreshold) || undefined,
          below: parseFloat(belowThreshold) || undefined,
        },
      };

      // update history
      updateAnalysisParams({
        analysisHazardLayerId: hazardLayerId,
        analysisBaselineLayerId: baselineLayerId,
        analysisDate: moment(selectedDate).format(DEFAULT_DATE_FORMAT),
        analysisStatistic: statistic,
        analysisThresholdAbove: aboveThreshold || undefined,
        analysisThresholdBelow: belowThreshold || undefined,
      });

      dispatch(requestAndStoreAnalysis(params));
    }
  };

  return (
    <div className={classes.analyser}>
      <Button
        variant="contained"
        color="primary"
        onClick={() => {
          setIsAnalyserFormOpen(!isAnalyserFormOpen);
        }}
      >
        <BarChart fontSize="small" />
        <Typography variant="body2" className={classes.analyserLabel}>
          {t('Run Analysis')}
        </Typography>
        <ArrowDropDown fontSize="small" />
      </Button>

      <Box
        className={classes.analyserMenu}
        width={isAnalyserFormOpen ? 'min-content' : 0}
        padding={isAnalyserFormOpen ? '10px' : 0}
      >
        {isAnalyserFormOpen ? (
          <div>
            <div className={classes.newAnalyserContainer}>
              <div className={classes.analyserOptions}>
                <Typography variant="body2">{t('Hazard Layer')}</Typography>
                <LayerDropdown
                  type="wms"
                  value={hazardLayerId}
                  setValue={setHazardLayerId}
                  className={classes.selector}
                  placeholder="Choose hazard layer"
                />
              </div>

              {hazardDataType === GeometryType.Polygon && (
                <>
                  <div className={classes.analyserOptions}>
                    <Typography variant="body2">Admin Level</Typography>
                    <SimpleDropdown
                      value={adminLevel}
                      options={range(getAdminLevelCount()).map(i => [
                        (i + 1) as AdminLevelType,
                        `Admin ${i + 1}`,
                      ])}
                      onChange={setAdminLevel}
                    />
                  </div>

                  <div className={classes.analyserOptions}>
                    <Typography variant="body2">{t('Date Range')}</Typography>
                    <div className={classes.dateRangePicker}>
                      <Typography variant="body2">{t('Start')}</Typography>
                      <DatePicker
                        selected={startDate ? new Date(startDate) : null}
                        onChange={date =>
                          setStartDate(date?.getTime() || startDate)
                        }
                        maxDate={new Date()}
                        todayButton="Today"
                        peekNextMonth
                        showMonthDropdown
                        showYearDropdown
                        dropdownMode="select"
                        customInput={<Input />}
                        popperClassName={classes.calendarPopper}
                        includeDates={availableHazardDates}
                      />
                    </div>
                    <div className={classes.dateRangePicker}>
                      <Typography variant="body2">{t('End')}</Typography>
                      <DatePicker
                        selected={endDate ? new Date(endDate) : null}
                        onChange={date =>
                          setEndDate(date?.getTime() || endDate)
                        }
                        maxDate={new Date()}
                        todayButton="Today"
                        peekNextMonth
                        showMonthDropdown
                        showYearDropdown
                        dropdownMode="select"
                        customInput={<Input />}
                        popperClassName={classes.calendarPopper}
                        includeDates={availableHazardDates}
                      />
                    </div>
                  </div>
                </>
              )}

              {hazardDataType === RasterType.Raster && (
                <>
                  <div className={classes.analyserOptions}>
                    <Typography variant="body2">{t('Statistic')}</Typography>
                    <FormControl component="div">
                      <RadioGroup
                        name="statistics"
                        value={statistic}
                        onChange={onOptionChange(setStatistic)}
                        row
                      >
                        {statisticOptions}
                      </RadioGroup>
                    </FormControl>
                  </div>
                  <div className={classes.analyserOptions}>
                    <Typography variant="body2">
                      {t('Baseline Layer')}
                    </Typography>
                    <LayerDropdown
                      type="admin_level_data"
                      value={baselineLayerId || undefined}
                      setValue={setBaselineLayerId}
                      className={classes.selector}
                      placeholder="Choose baseline layer"
                    />
                  </div>
                  <div className={classes.analyserOptions}>
                    <Typography variant="body2">{t('Threshold')}</Typography>
                    <TextField
                      id="filled-number"
                      error={!!thresholdError}
                      helperText={thresholdError}
                      className={classes.numberField}
                      label={t('Below')}
                      type="number"
                      value={belowThreshold}
                      onChange={onThresholdOptionChange('below')}
                      variant="filled"
                    />
                    <TextField
                      id="filled-number"
                      label={t('Above')}
                      className={classes.numberField}
                      value={aboveThreshold}
                      onChange={onThresholdOptionChange('above')}
                      type="number"
                      variant="filled"
                    />
                  </div>
                  <div className={classes.analyserOptions}>
                    <Typography variant="body2">{t('Date')}</Typography>
                    <DatePicker
                      locale={t('date_locale')}
                      dateFormat="PP"
                      selected={selectedDate ? new Date(selectedDate) : null}
                      onChange={date =>
                        setSelectedDate(date?.getTime() || selectedDate)
                      }
                      maxDate={new Date()}
                      todayButton={t('Today')}
                      peekNextMonth
                      showMonthDropdown
                      showYearDropdown
                      dropdownMode="select"
                      customInput={<Input />}
                      popperClassName={classes.calendarPopper}
                      includeDates={availableHazardDates}
                    />
                  </div>
                </>
              )}
            </div>

            {!isAnalysisLoading &&
              analysisResult &&
              (analysisResult instanceof BaselineLayerResult ||
                analysisResult instanceof PolygonAnalysisResult) && (
                <>
                  <FormGroup>
                    <FormControlLabel
                      control={
                        <Switch
                          color="default"
                          checked={isMapLayerActive}
                          onChange={onMapSwitchChange}
                        />
                      }
                      label={t('Map View')}
                    />
                    <FormControlLabel
                      control={
                        <Switch
                          color="default"
                          checked={isTableViewOpen}
                          onChange={e => setIsTableViewOpen(e.target.checked)}
                        />
                      }
                      label={t('Table View')}
                    />
                  </FormGroup>
                  {isTableViewOpen && (
                    <AnalysisTable
                      tableData={analysisResult.tableData}
                      columns={translatedColumns}
                    />
                  )}
                  <Button
                    className={classes.innerAnalysisButton}
                    onClick={() =>
                      downloadCSVFromTableData(
                        analysisResult,
                        translatedColumns,
                        selectedDate,
                      )
                    }
                  >
                    <Typography variant="body2">{t('Download')}</Typography>
                  </Button>
                  <Button
                    className={classes.innerAnalysisButton}
                    onClick={clearAnalysis}
                  >
                    <Typography variant="body2">
                      {t('Clear Analysis')}
                    </Typography>
                  </Button>
                  <Button
                    className={classes.innerAnalysisButton}
                    onClick={shareAnalysis}
                  >
                    <Typography variant="body2">
                      {t('Share Analysis')}
                    </Typography>
                  </Button>
                </>
              )}
            {(!analysisResult ||
              analysisResult instanceof ExposedPopulationResult) && (
              <Button
                className={classes.innerAnalysisButton}
                onClick={runAnalyser}
                disabled={
                  !!thresholdError || // if there is a threshold error
                  isAnalysisLoading || // or analysis is currently loading
                  !hazardLayerId || // or hazard layer hasn't been selected
                  (hazardDataType === GeometryType.Polygon
                    ? !startDate || !endDate || !adminLevelLayerData
                    : !selectedDate || !baselineLayerId) // or date hasn't been selected // or baseline layer hasn't been selected
                }
              >
                <Typography variant="body2">{t('Run Analysis')}</Typography>
              </Button>
            )}
            {isAnalysisLoading ? <LinearProgress /> : null}
          </div>
        ) : null}
      </Box>
    </div>
  );
}
Example #19
Source File: ProjectsComponent.tsx    From backstage with Apache License 2.0 4 votes vote down vote up
ProjectsComponent = () => {
  const routeRef = useRouteRef(rootRouteRef);
  const codesceneApi = useApi(codesceneApiRef);

  const classes = useStyles();
  const [searchText, setSearchText] = React.useState('');
  const {
    value: projectsWithAnalyses,
    loading,
    error,
  } = useAsync(async (): Promise<Record<number, ProjectAndAnalysis>> => {
    const projects = (await codesceneApi.fetchProjects()).projects;

    const promises = projects.map(project =>
      codesceneApi.fetchLatestAnalysis(project.id),
    );
    // analyses associates project IDs with their latests analysis
    const analyses: Record<number, Analysis> = await Promise.allSettled(
      promises,
    ).then(results => {
      return results
        .filter(result => result.status === 'fulfilled')
        .map(result => result as PromiseFulfilledResult<Analysis>)
        .map(result => result.value)
        .reduce(
          (acc, analysis) => ({ ...acc, [analysis.project_id]: analysis }),
          {},
        );
    });
    return projects.reduce(
      (acc, project) => ({
        ...acc,
        [project.id]: {
          project: project,
          analysis: analyses[project.id],
        },
      }),
      {},
    );
  }, []);

  if (loading) {
    return <Progress />;
  } else if (error) {
    return <Alert severity="error">{error.message}</Alert>;
  } else if (
    !projectsWithAnalyses ||
    Object.keys(projectsWithAnalyses).length === 0
  ) {
    return <Alert severity="error">No projects found!</Alert>;
  }

  const projects = Object.values(projectsWithAnalyses)
    .map(p => p.project)
    .sort((a, b) => a.name.localeCompare(b.name));

  const cards = projects.filter(matchFilter(searchText)).map(project => {
    const analysis = projectsWithAnalyses[project.id].analysis;
    const subtitle = analysis
      ? `Last analysis: ${analysis.readable_analysis_time} · Score: ${analysis.high_level_metrics.current_score} · Active authors: ${analysis.high_level_metrics.active_developers}`
      : undefined;
    const chips = analysis
      ? topLanguages(analysis, 3).map(lang => (
          <Chip label={lang} key={lang} size="small" />
        ))
      : undefined;
    return (
      <Grid key={project.id} item xs={3}>
        <Card>
          <CardActionArea
            style={{
              height: '100%',
              overflow: 'hidden',
              width: '100%',
            }}
            component={RouterLink}
            to={`${routeRef()}/${project.id}`}
          >
            <ItemCardHeader title={project.name} />
            <CardContent>{subtitle}</CardContent>
            <CardActions disableSpacing>{chips}</CardActions>
          </CardActionArea>
        </Card>
      </Grid>
    );
  });

  return (
    <Content className={classes.overflowXScroll}>
      <ContentHeader title="Projects">
        <Input
          id="projects-filter"
          type="search"
          placeholder="Filter"
          onChange={e => setSearchText(e.target.value)}
        />
      </ContentHeader>
      <Grid container>{cards}</Grid>
    </Content>
  );
}
Example #20
Source File: EditServerDialog.tsx    From shadowsocks-electron with GNU General Public License v3.0 4 votes vote down vote up
EditServerDialog: React.FC<EditServerDialogProps> = props => {
  const styles = useStyles();
  const { t } = useTranslation();
  const dispatch = useDispatch();

  const { open, onClose, defaultValues, onValues } = props;


  const theme = useTheme();
  const fullScreen = useMediaQuery(theme.breakpoints.down("sm"));

  const enqueueSnackbar = (message: SnackbarMessage, options: Notification) => {
    dispatch(enqueueSnackbarAction(message, options))
  };

  const [values, setValues] = useState<Partial<Config>>(
    defaultValues ?? {
      timeout: 60,
      encryptMethod: "none",
      type: 'ss'
    }
  );

  useLayoutEffect(() => {
    setValues(
      defaultValues ?? {
        timeout: 60,
        encryptMethod: "none",
        type: 'ss'
      }
    );
  }, [defaultValues]);

  const handleValueChange = (
    key: keyof Config,
    value: boolean | string | number
  ) => {
    setValues({
      ...values,
      // [key]: e.target[attr || 'value'].trim()
      [key]: value
    });
  };

  const handleCancel = () => {
    onValues(null);
  };

  const handleAdd = () => {
    if (!values.serverHost) {
      enqueueSnackbar(t("invalid_server_address"), { variant: "warning" });
      return;
    }
    if (
      !(
        values.serverPort &&
        values.serverPort > 0 &&
        values.serverPort <= 65535
      )
    ) {
      enqueueSnackbar(t("invalid_server_port"), { variant: "warning" });
      return;
    }
    if (!values.password) {
      enqueueSnackbar(t("invalid_password"), { variant: "warning" });
      return;
    }
    if (!values.timeout) {
      enqueueSnackbar(t("invalid_timeout"), { variant: "warning" });
      return;
    }

    onValues(values as Config);
  };

  const [showPassword, setShowPassword] = useState(false);

  const handleClickShowPassword = () => {
    setShowPassword(v => !v);
  };

  const handleMouseDownPassword = (
    event: React.MouseEvent<HTMLButtonElement>
  ) => {
    event.preventDefault();
  };

  const isSSR = values.type === 'ssr';
  const isSS = values.type === 'ss';

  return (
    <StyledDialog
      fullScreen={fullScreen}
      open={open}
      onClose={onClose}
    >
      <AdaptiveAppBar className={fullScreen ? styles.appBar : styles.appBarRelative}>
        <Toolbar>
          <IconButton edge="start" color="inherit" onClick={handleCancel}>
            <CloseIcon />
          </IconButton>
          <Typography variant="h6" className={styles.title}>
            { t('edit_server') }
          </Typography>
          <Button color="inherit" onClick={handleAdd}>
            { t('save') }
          </Button>
        </Toolbar>
      </AdaptiveAppBar>
      <Container className={`${styles.container}`}>
        {fullScreen && <div className={`${styles.toolbar}`} />}
        <InputLabel required style={{ marginBottom: 0 }}>
          {t('server_type')}
        </InputLabel>
        <Select
          required
          label={t('server_type')}
          displayEmpty
          fullWidth
          value={values.type ?? "ss"}
          onChange={(e: any) => handleValueChange("type", e.target.value.trim())}
        >
          {serverTypes.map(serverType => (
            <MenuItem key={serverType} value={serverType}>
              {serverType}
            </MenuItem>
          ))}
        </Select>
        <TextField
          fullWidth
          label={t('remark')}
          value={values.remark ?? ""}
          onChange={e => handleValueChange("remark", e.target.value.trim())}
        />
        <TextField
          required
          fullWidth
          label={t('server_address')}
          value={values.serverHost ?? ""}
          onChange={e => handleValueChange("serverHost", e.target.value.trim())}
        />
        <TextField
          required
          fullWidth
          type="number"
          label={t('server_port')}
          value={values.serverPort ?? ""}
          onChange={e => handleValueChange("serverPort", e.target.value.trim())}
        />
        <FormControl required fullWidth>
          <InputLabel htmlFor="password">{t('password')}</InputLabel>
          <Input
            id="password"
            type={showPassword ? "text" : "password"}
            value={values.password ?? ""}
            onChange={e => handleValueChange("password", e.target.value.trim())}
            endAdornment={
              <InputAdornment position="end">
                <IconButton
                  onClick={handleClickShowPassword}
                  onMouseDown={handleMouseDownPassword}
                >
                  {showPassword ? <Visibility /> : <VisibilityOff />}
                </IconButton>
              </InputAdornment>
            }
          />
        </FormControl>
        <InputLabel required style={{ marginBottom: 0 }}>
          {t('encryption')}
        </InputLabel>
        <Select
          required
          label={t('encryption')}
          displayEmpty
          fullWidth
          value={values.encryptMethod ?? "none"}
          onChange={(e: any) => handleValueChange("encryptMethod", e.target.value.trim())}
        >
          {encryptMethods.map(method => (
            <MenuItem key={method} value={method}>
              {method}
            </MenuItem>
          ))}
        </Select>
        {
          isSSR && (
            <>
              <InputLabel required style={{ marginBottom: 0 }}>
                {t('protocol')}
              </InputLabel>
              <Select
                required
                label={t('protocol')}
                displayEmpty
                fullWidth
                value={values.protocol ?? "origin"}
                onChange={(e: any) => handleValueChange("protocol", e.target.value.trim())}
              >
                {protocols.map(protocol => (
                  <MenuItem key={protocol} value={protocol}>
                    {protocol}
                  </MenuItem>
                ))}
              </Select>
              <TextField
                fullWidth
                label={t('protocolParam')}
                value={values.protocolParam ?? ""}
                onChange={e => handleValueChange("protocolParam", e.target.value.trim())}
              />
            </>
          )
        }
        {
          isSSR && (
            <>
              <InputLabel required style={{ marginBottom: 0 }}>
                {t('obfs')}
              </InputLabel>
              <Select
                required
                label={t('obfs')}
                displayEmpty
                fullWidth
                value={values.obfs ?? "plain"}
                onChange={(e: any) => handleValueChange("obfs", e.target.value.trim())}
              >
                {obfs.map(value => (
                  <MenuItem key={value} value={value}>
                    {value}
                  </MenuItem>
                ))}
              </Select>
              <TextField
                fullWidth
                label={t('obfsParam')}
                value={values.obfsParam ?? ""}
                onChange={e => handleValueChange("obfsParam", e.target.value.trim())}
              />
            </>
          )
        }
        <TextField
          required
          fullWidth
          label={t('timeout')}
          value={values.timeout ?? 60}
          onChange={e => handleValueChange("timeout", e.target.value)}
        />
        <List>
          <ListItem>
            <ListItemText primary="TCP Fast Open" />
            <ListItemSecondaryAction>
              <Switch checked={!!values.fastOpen} edge="end" color="primary" onChange={(e) => handleValueChange('fastOpen', e.target.checked)} />
            </ListItemSecondaryAction>
          </ListItem>
          {
            isSS && (
              <ListItem>
                <ListItemText primary="TCP No Delay" />
                <ListItemSecondaryAction>
                  <Switch checked={!!values.noDelay} edge="end" color="primary" onChange={(e) => handleValueChange('noDelay', e.target.checked)} />
                </ListItemSecondaryAction>
              </ListItem>
            )
          }
          <ListItem>
            <ListItemText primary="UDP Relay" />
            <ListItemSecondaryAction>
              <Switch checked={!!values.udp} edge="end" color="primary" onChange={(e) => handleValueChange('udp', e.target.checked)} />
            </ListItemSecondaryAction>
          </ListItem>
        </List>
        <InputLabel style={{ marginBottom: 0 }}><TextWithTooltip text={t('plugin')} tooltip={t('readme')} /></InputLabel>
        {
          isSS && (
            <>
              <Select
                label={t('plugin')}
                displayEmpty
                fullWidth
                value={values.plugin ?? ""}
                onChange={(e: any) => handleValueChange("plugin", e.target.value.trim())}
              >
                <MenuItem key="none" value="">
                  <em>{t('none')}</em>
                </MenuItem>
                {plugins.map(plugin => (
                  <MenuItem key={plugin.name} value={plugin.name}>
                    {plugin.name} {plugin.tips ? `(${t(plugin.tips)})` : ""}
                  </MenuItem>
                ))}
              </Select>
              <TextField
                fullWidth
                multiline
                label={t('plugin_options')}
                value={values.pluginOpts ?? ""}
                onChange={e => handleValueChange("pluginOpts", e.target.value.trim())}
              />
            </>
          )
        }
      </Container>
    </StyledDialog>
  );
}
Example #21
Source File: SQFormDropdown.tsx    From SQForm with MIT License 4 votes vote down vote up
function SQFormDropdown({
  children,
  displayEmpty = false,
  isDisabled = false,
  label,
  name,
  onBlur,
  onChange,
  size = 'auto',
  muiFieldProps = {},
}: SQFormDropdownProps): React.ReactElement {
  const classes = useStyles();

  const {
    formikField: {field},
    fieldState: {isFieldError, isFieldRequired},
    fieldHelpers: {handleBlur, handleChange, HelperTextComponent},
  } = useForm({
    name,
    onBlur,
    onChange,
  });
  const labelID = label.toLowerCase();

  const options = React.useMemo(() => {
    if (!children) {
      console.warn(getUndefinedChildrenWarning('SQFormDropdown', name));
      return [];
    }

    if (!displayEmpty) return children;

    const [firstOption] = children;

    if (
      firstOption?.label === EMPTY_LABEL ||
      firstOption?.label === EMPTY_VALUE
    ) {
      return children;
    }

    return [EMPTY_OPTION, ...children];
  }, [children, displayEmpty, name]);

  const renderValue = (value: unknown) => {
    const getValue = (selectedValue: SQFormOption['value']) => {
      if (selectedValue === undefined || selectedValue === null) {
        console.warn(getUndefinedValueWarning('SQFormDropdown', name));
        return EMPTY_LABEL;
      }

      if (selectedValue === EMPTY_VALUE) {
        return EMPTY_LABEL;
      }

      const valueToRender = options.find(
        (option) => option.value === selectedValue
      )?.label;
      if (!valueToRender) {
        console.warn(
          getOutOfRangeValueWarning(
            'SQFormDropdown',
            name,
            selectedValue.toString()
          )
        );
        return undefined;
      }

      return valueToRender;
    };

    return getValue(value as SQFormOption['value']);
  };

  return (
    <Grid item sm={size}>
      <FormControl
        error={isFieldError}
        required={isFieldRequired}
        disabled={isDisabled}
        fullWidth={true}
      >
        <InputLabel shrink={true} id={labelID}>
          {label}
        </InputLabel>
        <Select
          className={classes.selectHeight}
          displayEmpty={true}
          input={<Input name={name} />}
          value={field.value}
          onBlur={handleBlur}
          onChange={handleChange}
          labelId={labelID}
          renderValue={renderValue}
          {...muiFieldProps}
        >
          {options.map((option) => {
            return (
              <MenuItem
                key={`${name}_${option.value}`}
                disabled={option.isDisabled}
                value={option.value}
              >
                {option.label}
              </MenuItem>
            );
          })}
        </Select>
        {!isDisabled && <FormHelperText>{HelperTextComponent}</FormHelperText>}
      </FormControl>
    </Grid>
  );
}
Example #22
Source File: CreateRoom.tsx    From cards-against-formality-pwa with BSD 2-Clause "Simplified" License 4 votes vote down vote up
export default function CreateRoom({ onJoin, decksData, user }: any) {
  const { openSnack } = useContext(SnackbarContext);
  const [isProtected, setIsProtected] = useState(false);
  const [passcode, setPasscode] = useState('');
  const [name, setName] = useState('');
  const [roundTime, setRoundTime] = useState(60);
  const [target, setTarget] = useState(10);
  const limitedSetTarget = useCallback((event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
    setTarget(rangeLimit(event, { min: 5, max: 100 }));
  }, []);
  const [maxPlayers, setMaxPlayers] = useState(10);
  const limitedSetMaxPlayers = useCallback((event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
    setMaxPlayers(rangeLimit(event, { min: 2, max: 50 }));
  }, []);
  const [, isLoading, , createRoom] = useFetchData(`/api/rooms`, FetchType.POST);
  const [decks, setDecks] = useState([]);
  const [errorField, setErrorField] = useState<string | null>(null);

  function rangeLimit(
    event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>,
    { min, max }: { min: number; max: number } = { min: 4, max: 10 }
  ): number {
    const value = parseInt(event.target.value, 10);

    if (value < min) {
      return min;
    }

    if (value > max) {
      return max;
    }

    return value;
  }

  function handleSubmit() {
    const room = {
      name: name?.length ? name : user?.username,
      options: {
        target,
        maxPlayers,
        maxSpectators: 10,
        decks,
        roundTime
      },
    }
    if (isProtected && passcode.length) {
      (room as any).passcode = passcode;
    }
    createRoom(room)
      .then((axiosRes) => {
        onJoin(axiosRes.data._id, passcode);
        (window as any)?.gtag('event', 'create_room', { value: passcode?.length ? 'private' : 'public' });
      })
      .catch((err) => {
        if (err?.response?.data.type === 'VALIDATION_ERROR') {
          const errObj = err?.response?.data.data[0];
          setErrorField(errObj.field as string);
        }
        openSnack({ text: 'Something went wrong...', severity: 'error' });
      });
  }

  function onKeyPress(e: any) {
    if (e.charCode === 13) {
      handleSubmit();
    }
  }

  function renderPasswordForm() {
    if (!isProtected) {
      return null;
    }

    return <FormControl error={errorField === 'passcode'}>
      <InputLabel htmlFor="passcode">Password</InputLabel>
      <Input id="passcode" aria-describedby="password-helper" required={false} value={passcode} onChange={e => setPasscode(e.target.value)} />
      <FormHelperText id="password-helper">
        {errorField !== 'passcode' ? 'People will need this password to enter your game.' : 'Password must be between 4-12 characters long with no special characters.'}
      </FormHelperText>
    </FormControl>;
  }

  function renderCardContent() {
    return <CardContent className="create-form-card-content">
      <form className="create-room-form" onKeyPress={onKeyPress}>
        <FormControl required={true} error={errorField === 'name'}>
          <Input id="name" aria-describedby="game-name-helper" value={name} onChange={e => { setName(e.target.value); }} placeholder={user?.username} />
          <FormHelperText id="score-helper">{errorField === 'name' ? "Game name must be between 2-16 characters long with no special characters!" : "Game name"}</FormHelperText>
        </FormControl>
        <FormControl>
          <InputLabel htmlFor="target">Target Score</InputLabel>
          <Input id="target" type="number" aria-describedby="score-helper" value={target} onChange={limitedSetTarget} />
          <FormHelperText id="score-helper">Number of points required to end the game.</FormHelperText>
        </FormControl>
        <FormControl>
          <InputLabel htmlFor="max-players">Max Players</InputLabel>
          <Input id="max-players" type="number" aria-describedby="max-players-helper" value={maxPlayers} onChange={limitedSetMaxPlayers} />
        </FormControl>
        <FormControl>
          <InputLabel id="roundTime-label">Round time (seconds)</InputLabel>
          <Select
            labelId="roundTime-label"
            id="roundTime"
            value={roundTime}
            onChange={(e: any) => setRoundTime(parseInt(e.target.value, 10))}
          >
            <MenuItem value={15}>15</MenuItem>
            <MenuItem value={30}>30</MenuItem>
            <MenuItem value={45}>45</MenuItem>
            <MenuItem value={60}>60</MenuItem>
          </Select>
          <FormHelperText>How you have to select your answers</FormHelperText>
        </FormControl>
        {!decksData?.rows ? null : <DeckSelector decks={decksData.rows} onChange={setDecks as any} />}
        <FormControlLabel
          control={<Switch checked={isProtected} onChange={e => setIsProtected(e.target.checked)} name="checkedA" />}
          label="Private"
        />
        {renderPasswordForm()}
      </form>
    </CardContent>
  }

  const Submit = <CardActions>
    {!isLoading ? <Button variant="contained" color="primary" size="small" onClick={handleSubmit}>Create</Button> : <CircularProgress />}
  </CardActions>;
  return <Card className="form-container" variant="elevation">
    {Submit}
    {renderCardContent()}
    {Submit}
  </Card>
}
Example #23
Source File: index.tsx    From vscode-crossnote with GNU Affero General Public License v3.0 4 votes vote down vote up
function AudioWidget(props: WidgetArgs) {
  const attributes = props.attributes;
  const classes = useStyles(props);
  const { t } = useTranslation();
  const [source, setSource] = useState<string>(attributes["source"] || "");
  const [autoplay, setAutoplay] = useState<boolean>(
    attributes["autoplay"] || false
  );
  const [controls, setControls] = useState<boolean>(
    attributes["controls"] || true
  );
  const [loop, setLoop] = useState<boolean>(attributes["loop"] || false);
  const [muted, setMuted] = useState<boolean>(attributes["muted"] || false);

  if (attributes["src"]) {
    return (
      <span style={{ cursor: "default" }}>
        <audio
          autoPlay={attributes["autoplay"] || attributes["autoPlay"]}
          controls={attributes["controls"]}
          loop={attributes["loop"]}
          muted={attributes["muted"]}
          style={attributes["style"]}
        >
          {t("widget/crossnote.audio/audio_element_fail")}
          <source src={attributes["src"]} type={attributes["type"]}></source>
        </audio>
        {!props.isPreview && !attributes["controls"] && "?"}
      </span>
    );
  }

  if (props.isPreview) {
    return <span></span>;
  }

  return (
    <Card elevation={2} className={clsx(classes.card)}>
      <Typography variant={"h5"}>{t("general/Audio")}</Typography>
      <Box className={clsx(classes.actionButtons)}>
        <Tooltip title={t("general/Delete")}>
          <IconButton onClick={() => props.removeSelf()}>
            <TrashCan></TrashCan>
          </IconButton>
        </Tooltip>
      </Box>
      <Box className={clsx(classes.section)}>
        <Typography variant={"subtitle1"} style={{ marginBottom: "8px" }}>
          {t("general/source-url")}
        </Typography>
        <Input
          margin={"dense"}
          placeholder={t("widget/crossnote.audio/source-url-placeholder")}
          value={source}
          onChange={(event) => {
            setSource(event.target.value);
          }}
          onKeyDown={(event) => {
            if (event.which === 13) {
              if (source) {
                const attrs = {
                  autoplay,
                  controls,
                  loop,
                  muted,
                  src: source,
                };
                props.setAttributes(attrs);
              }
            }
          }}
          fullWidth={true}
        ></Input>
      </Box>
      <Box className={clsx(classes.section)}>
        <FormControlLabel
          label={t("widget/autoplay")}
          control={
            <Switch
              checked={autoplay}
              onChange={() => setAutoplay(!autoplay)}
              color={"primary"}
            ></Switch>
          }
        ></FormControlLabel>
        <FormControlLabel
          label={t("widget/controls")}
          control={
            <Switch
              checked={controls}
              onChange={() => setControls(!controls)}
              color={"primary"}
            ></Switch>
          }
        ></FormControlLabel>
        <FormControlLabel
          label={t("widget/loop")}
          control={
            <Switch
              checked={loop}
              onChange={() => setLoop(!loop)}
              color={"primary"}
            ></Switch>
          }
        ></FormControlLabel>
        <FormControlLabel
          label={t("widget/muted")}
          control={
            <Switch
              checked={muted}
              onChange={() => setMuted(!muted)}
              color={"primary"}
            ></Switch>
          }
        ></FormControlLabel>
      </Box>
    </Card>
  );
}
Example #24
Source File: AccountManagementPage.tsx    From clarity with Apache License 2.0 4 votes vote down vote up
AccountManagementPage = observer((props: Props) => {
  const [openDialog, setOpenDialog] = React.useState(false);
  const [openKeyDialog, setOpenKeyDialog] = React.useState(false);
  const [
    selectedAccount,
    setSelectedAccount
  ] = React.useState<SignKeyPairWithAlias | null>(null);
  const [name, setName] = React.useState('');
  const [publicKey64, setPublicKey64] = React.useState('');
  const [publicKeyHex, setPublicKeyHex] = React.useState('');
  /* Note: 01 prefix denotes algorithm used in key generation */
  const address = '01' + publicKeyHex;
  const [copyStatus, setCopyStatus] = React.useState(false);

  const handleClickOpen = (account: SignKeyPairWithAlias) => {
    setOpenDialog(true);
    setSelectedAccount(account);
    setName(account.name);
  };

  const handleViewKey = async (accountName: string) => {
    let publicKey64 = await props.authContainer.getSelectedAccountKey(
      accountName
    );
    let publicKeyHex = await props.authContainer.getPublicKeyHex(accountName);
    setName(accountName);
    setPublicKey64(publicKey64);
    setPublicKeyHex(publicKeyHex);
    setOpenKeyDialog(true);
  };

  const handleCopyMessage = (event?: React.SyntheticEvent, reason?: string) => {
    if (reason === 'clickaway') {
      return;
    }
    setCopyStatus(false);
  };

  const handleClose = () => {
    setOpenDialog(false);
    setOpenKeyDialog(false);
    setSelectedAccount(null);
  };

  const handleUpdateName = () => {
    if (selectedAccount) {
      props.authContainer.renameUserAccount(selectedAccount.name, name);
      handleClose();
    }
  };

  const onDragEnd = (result: DropResult) => {
    // dropped outside the list
    if (!result.destination) {
      return;
    }

    props.authContainer.reorderAccount(
      result.source.index,
      result.destination.index
    );
  };

  const handleClickRemove = (name: string) => {
    confirm(
      <div className="text-danger">Remove account</div>,
      'Are you sure you want to remove this account?'
    ).then(() => props.authContainer.removeUserAccount(name));
  };

  return (
    <React.Fragment>
      <DragDropContext onDragEnd={result => onDragEnd(result)}>
        <Droppable droppableId="droppable">
          {(provided, snapshot) => (
            <Observer>
              {() => (
                <RootRef rootRef={provided.innerRef}>
                  <List>
                    {props.authContainer.userAccounts.map((item, index) => (
                      <Draggable
                        key={item.name}
                        draggableId={item.name}
                        index={index}
                      >
                        {(provided, snapshot) => (
                          <ListItem
                            innerRef={provided.innerRef}
                            ContainerProps={{
                              ...provided.draggableProps,
                              ...provided.dragHandleProps,
                              style: getItemStyle(
                                snapshot.isDragging,
                                provided.draggableProps.style
                              )
                            }}
                          >
                            <ListItemText primary={item.name} />
                            <ListItemSecondaryAction>
                              <IconButton
                                edge={'end'}
                                onClick={() => {
                                  handleClickOpen(item);
                                }}
                              >
                                <EditIcon />
                              </IconButton>
                              <IconButton
                                edge={'end'}
                                onClick={() => {
                                  handleClickRemove(item.name);
                                }}
                              >
                                <DeleteIcon />
                              </IconButton>
                              <IconButton
                                edge={'end'}
                                onClick={() => {
                                  handleViewKey(item.name);
                                }}
                              >
                                <VpnKeyIcon />
                              </IconButton>
                            </ListItemSecondaryAction>
                          </ListItem>
                        )}
                      </Draggable>
                    ))}
                    {provided.placeholder}
                  </List>
                </RootRef>
              )}
            </Observer>
          )}
        </Droppable>
      </DragDropContext>
      <Dialog
        open={openDialog}
        onClose={handleClose}
        aria-labelledby="form-dialog-title"
      >
        <DialogTitle id="form-dialog-title">Rename</DialogTitle>
        <DialogContent>
          <Input
            autoFocus
            margin="dense"
            id="name"
            type="text"
            fullWidth
            value={name}
            onChange={e => {
              setName(e.target.value);
            }}
          />
        </DialogContent>
        <DialogActions>
          <Button onClick={handleClose} color="primary">
            Cancel
          </Button>
          <Button onClick={handleUpdateName} color="primary">
            Update
          </Button>
        </DialogActions>
      </Dialog>

      <Dialog
        fullScreen
        open={openKeyDialog}
        onClose={handleClose}
        aria-labelledby="form-dialog-title"
      >
        <DialogTitle id="form-dialog-title">Account Details</DialogTitle>
        <DialogContent>
          <List>
            <ListSubheader>
              <Typography variant={'h6'}>{name}</Typography>
            </ListSubheader>
            <ListItem>
              <IconButton
                edge={'start'}
                onClick={() => {
                  copy(address);
                  setCopyStatus(true);
                }}
              >
                <FilterNoneIcon />
              </IconButton>
              <ListItemText
                primary={'Address: ' + address}
                style={{ overflowWrap: 'break-word' }}
              />
            </ListItem>
            <ListItem>
              <IconButton
                edge={'start'}
                onClick={() => {
                  copy(publicKey64);
                  setCopyStatus(true);
                }}
              >
                <FilterNoneIcon />
              </IconButton>
              <ListItemText
                primary={'Public Key: ' + publicKey64}
                style={{ overflowWrap: 'break-word' }}
              />
            </ListItem>
          </List>
          <Snackbar
            open={copyStatus}
            message="Copied!"
            autoHideDuration={1500}
            onClose={handleCopyMessage}
          />
        </DialogContent>
        <DialogActions>
          <Button onClick={handleClose} color="primary">
            Close
          </Button>
        </DialogActions>
      </Dialog>
    </React.Fragment>
  );
})
Example #25
Source File: AncUstLpWithdraw.tsx    From anchor-web-app with Apache License 2.0 4 votes vote down vote up
export function AncUstLpWithdraw() {
  // ---------------------------------------------
  // dependencies
  // ---------------------------------------------
  const { availablePost, connected } = useAccount();

  const fixedFee = useFixedFee();

  const [withdraw, withdrawResult] = useAncAncUstLpWithdrawTx();

  // ---------------------------------------------
  // states
  // ---------------------------------------------
  const [lpAmount, setLpAmount] = useState<AncUstLP>('' as AncUstLP);

  const [simulation, setSimulation] = useState<AncUstLpSimulation<Big> | null>(
    null,
  );

  // ---------------------------------------------
  // queries
  // ---------------------------------------------
  const bank = useAnchorBank();

  const { data: { ancPrice } = {} } = useAncPriceQuery();

  const { data: { userLPBalance } = {} } = useRewardsAncUstLpRewardsQuery();

  // ---------------------------------------------
  // logics
  // ---------------------------------------------
  const invalidTxFee = useMemo(
    () => connected && validateTxFee(bank.tokenBalances.uUST, fixedFee),
    [connected, bank, fixedFee],
  );

  const invalidLpAmount = useMemo(() => {
    if (lpAmount.length === 0 || !connected) return undefined;

    return big(microfy(lpAmount)).gt(bank.tokenBalances.uAncUstLP)
      ? 'Not enough assets'
      : undefined;
  }, [bank.tokenBalances.uAncUstLP, lpAmount, connected]);

  const updateLpAmount = useCallback(
    (nextLpAmount: string) => {
      if (!ancPrice || nextLpAmount.length === 0) {
        setLpAmount('' as AncUstLP);
        setSimulation(null);
        return;
      } else if (isZero(nextLpAmount)) {
        setLpAmount(nextLpAmount as AncUstLP);
        setSimulation(null);
        return;
      }

      const nextSimulation = ancUstLpLpSimulation(
        ancPrice,
        userLPBalance,
        nextLpAmount as AncUstLP,
        fixedFee,
        bank,
      );

      setLpAmount(nextLpAmount as AncUstLP);
      setSimulation(nextSimulation);
    },
    [ancPrice, bank, fixedFee, userLPBalance],
  );

  const init = useCallback(() => {
    setLpAmount('' as AncUstLP);
    setSimulation(null);
  }, []);

  const proceed = useCallback(
    (lpAmount: AncUstLP) => {
      if (!connected || !withdraw) {
        return;
      }

      withdraw({
        lpAmount,
        onTxSucceed: () => {
          init();
        },
      });
    },
    [connected, init, withdraw],
  );

  // ---------------------------------------------
  // presentation
  // ---------------------------------------------
  if (
    withdrawResult?.status === StreamStatus.IN_PROGRESS ||
    withdrawResult?.status === StreamStatus.DONE
  ) {
    return (
      <TxResultRenderer
        resultRendering={withdrawResult.value}
        onExit={() => {
          init();

          switch (withdrawResult.status) {
            case StreamStatus.IN_PROGRESS:
              withdrawResult.abort();
              break;
            case StreamStatus.DONE:
              withdrawResult.clear();
              break;
          }
        }}
      />
    );
  }

  return (
    <>
      {!!invalidTxFee && <MessageBox>{invalidTxFee}</MessageBox>}

      {/* ANC */}
      <div className="description">
        <p>Input</p>
        <p />
      </div>

      <NumberInput
        className="amount"
        value={lpAmount}
        maxIntegerPoinsts={ANC_INPUT_MAXIMUM_INTEGER_POINTS}
        maxDecimalPoints={ANC_INPUT_MAXIMUM_DECIMAL_POINTS}
        error={!!invalidLpAmount}
        placeholder="0.00"
        onChange={({ target }: ChangeEvent<HTMLInputElement>) =>
          updateLpAmount(target.value)
        }
        InputProps={{
          endAdornment: <InputAdornment position="end">LP</InputAdornment>,
        }}
      />

      <div className="wallet" aria-invalid={!!invalidLpAmount}>
        <span>{invalidLpAmount}</span>
        <span>
          ANC-UST LP Balance:{' '}
          <span
            style={{
              textDecoration: 'underline',
              cursor: 'pointer',
            }}
            onClick={() =>
              updateLpAmount(
                formatLPInput(demicrofy(bank.tokenBalances.uAncUstLP)),
              )
            }
          >
            {formatLP(demicrofy(bank.tokenBalances.uAncUstLP))} LP
          </span>
        </span>
      </div>

      <IconLineSeparator className="separator" />

      {/* UST */}
      <div className="description">
        <p>Output</p>
        <p />
      </div>

      <SelectAndTextInputContainer
        gridColumns={[120, '1fr']}
        gridRows={[60, 60]}
        aria-readonly
      >
        <Input readOnly value="ANC" />
        <Input
          readOnly
          value={simulation?.ancAmount ? formatANC(simulation.ancAmount) : ''}
        />
        <Input readOnly value="UST" />
        <Input
          readOnly
          value={simulation?.ustAmount ? formatUST(simulation.ustAmount) : ''}
        />
      </SelectAndTextInputContainer>

      <TxFeeList className="receipt">
        {simulation && (
          <>
            <SwapListItem
              label="Pool Price"
              currencyA="UST"
              currencyB="ANC"
              exchangeRateAB={demicrofy(simulation.poolPrice)}
              formatExchangeRate={(ratio, direction) =>
                direction === 'a/b'
                  ? formatANC(ratio as ANC<Big>)
                  : formatUST(ratio as UST<Big>)
              }
            />
            <TxFeeListItem label="LP after Tx">
              {formatLP(simulation.lpFromTx)} LP
            </TxFeeListItem>
            <TxFeeListItem label="Pool Share after Tx">
              {formatShareOfPool(simulation.shareOfPool)} %
            </TxFeeListItem>
            <TxFeeListItem label="Tx Fee">
              {formatUST(demicrofy(simulation.txFee))} UST
            </TxFeeListItem>
          </>
        )}
      </TxFeeList>

      {/* Submit */}
      <ViewAddressWarning>
        <ActionButton
          className="submit"
          disabled={
            !availablePost ||
            !connected ||
            !withdraw ||
            lpAmount.length === 0 ||
            big(lpAmount).lte(0) ||
            !simulation ||
            !!invalidTxFee ||
            !!invalidLpAmount
          }
          onClick={() => proceed(lpAmount)}
        >
          Remove Liquidity
        </ActionButton>
      </ViewAddressWarning>
    </>
  );
}
Example #26
Source File: Index.stories.tsx    From anchor-web-app with Apache License 2.0 4 votes vote down vote up
Component = styled(({ className }: { className?: string }) => {
  const [dialogOpen, setDialogOpen] = useState<Record<MessageColor, boolean>>(
    () => ({
      normal: false,
      warning: false,
      error: false,
      success: false,
    }),
  );

  const [selectedItem, setSelectedItem] = useState<Record<string, Item | null>>(
    () => ({}),
  );

  return (
    <div className={className}>
      <div className="styles">
        <section className="flat">FLAT</section>
        <section className="concave">CONCAVE</section>
        <section className="convex">CONVEX</section>
        <section className="pressed">PRESSED</section>
      </div>

      <Section className="components">
        <article className="buttons">
          <TextButton>BUTTON</TextButton>
          <ActionButton>BUTTON</ActionButton>
        </article>

        <HorizontalRuler />

        <article className="text-fields">
          <TextInput label="TEXT FIELD" />
          <TextInput
            label="ERROR"
            error={true}
            InputProps={textFieldInputProps}
            helperText="Error Content"
          />
        </article>

        <HorizontalRuler />

        <article className="text-fields">
          <TextInput />
          <TextInput
            error={true}
            InputProps={textFieldInputProps}
            helperText="Error Content"
          />
        </article>

        <HorizontalRuler />

        <article className="buttons">
          {messageColors.map((color) => (
            <Fragment key={color}>
              <ActionButton
                onClick={() =>
                  setDialogOpen((prev) => ({ ...prev, [color]: true }))
                }
              >
                OPEN {color.toUpperCase()} DIALOG
              </ActionButton>

              <Modal
                open={dialogOpen[color]}
                onClose={() =>
                  setDialogOpen((prev) => ({ ...prev, [color]: false }))
                }
              >
                <Dialog
                  color={color}
                  style={{ width: 600, height: 400 }}
                  onClose={() =>
                    setDialogOpen((prev) => ({ ...prev, [color]: false }))
                  }
                >
                  <h1 style={{ textAlign: 'center', fontWeight: 300 }}>
                    Title
                  </h1>
                </Dialog>
              </Modal>
            </Fragment>
          ))}
        </article>

        <HorizontalRuler />

        <article className="buttons">
          {messageColors.map((color) => (
            <Tooltip key={color} title={color} color={color} placement="top">
              <TextButton>{color.toUpperCase()} TOOLTIP</TextButton>
            </Tooltip>
          ))}
        </article>

        <HorizontalRuler />

        <article className="buttons">
          <NativeSelect
            value={
              selectedItem['nativeSelect']?.value ?? selectorItems[0].value
            }
            onChange={(evt: ChangeEvent<HTMLSelectElement>) =>
              setSelectedItem((prev) => ({
                ...prev,
                nativeSelect:
                  selectorItems.find(
                    ({ value }) => evt.target.value === value,
                  ) ?? null,
              }))
            }
          >
            {selectorItems.map(({ label, value }) => (
              <option key={value} value={value}>
                {label}
              </option>
            ))}
          </NativeSelect>

          <SelectAndTextInputContainer gridColumns={[100, '1fr']}>
            <MuiNativeSelect
              value={
                selectedItem['selectAndTextInput']?.value ??
                selectorItems[0].value
              }
              onChange={(evt) =>
                setSelectedItem((prev) => ({
                  ...prev,
                  selectAndTextInput:
                    selectorItems.find(
                      ({ value }) => evt.target.value === value,
                    ) ?? null,
                }))
              }
            >
              {selectorItems.map(({ label, value }) => (
                <option key={value} value={value}>
                  {label}
                </option>
              ))}
            </MuiNativeSelect>
            <Input placeholder="PLACEHOLDER" />
          </SelectAndTextInputContainer>

          <Selector
            items={selectorItems}
            selectedItem={selectedItem['selector']}
            onChange={(next) =>
              setSelectedItem((prev) => ({ ...prev, selector: next }))
            }
            labelFunction={(item) => item?.label ?? 'None'}
            keyFunction={(item) => item.value}
          />
        </article>

        <HorizontalRuler />

        <article>
          <Tab
            items={tabItems}
            selectedItem={selectedItem['tab'] ?? tabItems[0]}
            onChange={(next) =>
              setSelectedItem((prev) => ({ ...prev, tab: next }))
            }
            labelFunction={(item) => item.label}
            keyFunction={(item) => item.value}
          />
        </article>

        <HorizontalRuler />

        <article>
          <HorizontalGraphBar<{ value: number; color: string }>
            style={{ margin: '50px 0' }}
            min={-100}
            max={100}
            data={[
              { value: 50, color: '#4da3ee' },
              { value: 0, color: '#ffffff' },
              { value: -50, color: '#ff8a4b' },
            ]}
            colorFunction={({ color }) => color}
            valueFunction={({ value }) => value}
            labelRenderer={({ value }, rect) => {
              return (
                <span
                  style={{
                    top: -25,
                    left: rect.x + rect.width,
                    transform: 'translateX(-50%)',
                  }}
                >
                  {value}
                </span>
              );
            }}
          >
            <span style={{ top: 25, left: 0 }}>Borrow Limit</span>
            <span style={{ top: 25, right: 0 }}>$246k</span>
          </HorizontalGraphBar>
        </article>
      </Section>

      <Section className="table">
        <HorizontalScrollTable minWidth={800}>
          <colgroup>
            <col style={{ width: 300 }} />
            <col style={{ width: 300 }} />
            <col style={{ width: 300 }} />
            <col style={{ width: 300 }} />
          </colgroup>
          <thead>
            <tr>
              <th>A</th>
              <th>B</th>
              <th style={{ textAlign: 'right' }}>C</th>
              <th style={{ textAlign: 'right' }}>D</th>
            </tr>
          </thead>
          <tbody>
            {Array.from({ length: 5 }, (_, i) => (
              <tr key={`row-${i}`}>
                <td>{'A'.repeat(i * 3 + 1)}</td>
                <td>{'B'.repeat(i * 3 + 1)}</td>
                <td style={{ textAlign: 'right' }}>
                  {'C'.repeat(i * 3 + 1)}
                  <br />
                  {'C'.repeat(i * 2 + 1)}
                </td>
                <td style={{ textAlign: 'right' }}>{'D'.repeat(i * 3 + 1)}</td>
              </tr>
            ))}
          </tbody>
          <tfoot>
            <tr>
              <td>A</td>
              <td>B</td>
              <td style={{ textAlign: 'right' }}>C</td>
              <td style={{ textAlign: 'right' }}>D</td>
            </tr>
          </tfoot>
        </HorizontalScrollTable>
      </Section>
    </div>
  );
})`
  // ---------------------------------------------
  // style
  // ---------------------------------------------
  background-color: ${({ theme }) => theme.backgroundColor};
  color: ${({ theme }) => theme.textColor};

  .styles {
    section {
      border-radius: 20px;
      padding: 20px;

      text-align: center;
      color: ${({ theme }) => theme.textColor};

      &.flat {
        ${({ theme }) =>
          flat({
            color: theme.backgroundColor,
            distance: 6,
            intensity: theme.intensity,
          })};
      }

      &.concave {
        ${({ theme }) =>
          concave({
            color: theme.backgroundColor,
            distance: 6,
            intensity: theme.intensity,
          })};
      }

      &.convex {
        ${({ theme }) =>
          convex({
            color: theme.backgroundColor,
            distance: 6,
            intensity: theme.intensity,
          })};
      }

      &.pressed {
        ${({ theme }) =>
          pressed({
            color: theme.backgroundColor,
            distance: 6,
            intensity: theme.intensity,
          })};
      }
    }
  }

  margin-bottom: 1px;

  // ---------------------------------------------
  // layout
  // ---------------------------------------------
  .styles {
    display: flex;
    margin-bottom: 30px;
  }

  .components {
    hr {
      margin: 30px 0;
    }

    margin-bottom: 30px;
  }

  .table {
    margin-bottom: 30px;
  }

  // pc
  @media (min-width: ${screen.pc.min}px) {
    padding: 100px;

    .styles {
      section {
        flex: 1;

        &:not(:last-child) {
          margin-right: 30px;
        }
      }
    }

    .components {
      .buttons,
      .text-fields {
        display: grid;
        grid-template-columns: repeat(2, 1fr);
        grid-gap: 15px;
      }
    }
  }

  // tablet
  @media (min-width: ${screen.tablet.min}px) and (max-width: ${screen.tablet
      .max}px) {
    padding: 30px;

    .styles {
      section {
        flex: 1;

        &:not(:last-child) {
          margin-right: 10px;
        }
      }
    }

    .components {
      .buttons,
      .text-fields {
        display: grid;
        grid-template-columns: repeat(2, 1fr);
        grid-gap: 15px;
      }
    }

    .NeuSection-content {
      padding: 30px;
    }
  }

  // mobile
  @media (max-width: ${screen.mobile.max}px) {
    padding: 30px 20px;

    .styles {
      flex-direction: column;

      section {
        &:not(:last-child) {
          margin-bottom: 20px;
        }
      }
    }

    .components {
      .buttons,
      .text-fields {
        display: grid;
        grid-template-columns: repeat(1, 1fr);
        grid-gap: 15px;
      }

      .text-fields {
        grid-gap: 40px;
      }
    }

    .NeuSection-content {
      padding: 20px;
    }
  }
`
Example #27
Source File: index.tsx    From vscode-crossnote with GNU Affero General Public License v3.0 4 votes vote down vote up
function YoutubeWidget(props: WidgetArgs) {
  const attributes = props.attributes;
  const classes = useStyles(props);
  const { t } = useTranslation();
  const [url, setURL] = useState<string>("");
  const [error, setError] = useState<string>("");

  if (attributes["videoID"]) {
    if (!props.isPreview) {
      return (
        <span style={{ cursor: "default" }}>
          <img
            alt={"Youtube: " + attributes["videoID"]}
            src={`https://img.youtube.com/vi/${attributes["videoID"]}/0.jpg`}
            onClick={() => {
              window.open(
                `https://www.youtube.com/watch?v=${attributes["videoID"]}`,
                "_blank"
              );
            }}
            style={{
              cursor: "pointer",
              width: "100%",
            }}
          ></img>
        </span>
      );
    } else {
      return (
        <span style={{ cursor: "default" }}>
          <Box className={clsx(classes.videoWrapper)}>
            <iframe
              title={"youtube_" + attributes["videoID"]}
              className={clsx(classes.video)}
              src={`https://www.youtube.com/embed/${attributes["videoID"]}`}
              scrolling={"no"}
              frameBorder={"no"}
              allowFullScreen={true}
            ></iframe>
          </Box>
        </span>
      );
    }
  }

  if (props.isPreview) {
    return <span></span>;
  }

  return (
    <Card elevation={2} className={clsx(classes.card)}>
      <Typography variant={"h5"}>{t("Youtube")}</Typography>
      <Box className={clsx(classes.actionButtons)}>
        <Tooltip title={t("general/Delete")}>
          <IconButton onClick={() => props.removeSelf()}>
            <TrashCan></TrashCan>
          </IconButton>
        </Tooltip>
      </Box>
      <Box className={clsx(classes.section)}>
        <Typography variant={"subtitle1"} style={{ marginBottom: "8px" }}>
          {t("Youtube video URL")}
        </Typography>
        <Input
          margin={"dense"}
          placeholder={t(
            "widget/crossnote.youtube/youtube-video-url-placeholder"
          )}
          value={url}
          onChange={(event) => {
            setURL(event.target.value);
            setError("");
          }}
          onKeyDown={(event) => {
            if (event.which === 13) {
              if (url && url.match(/\?v=(.+?)(&|$)/)) {
                const videoID = url.match(/\?v=(.+?)(&|$)/)[1];
                const attrs = {
                  videoID,
                };
                props.setAttributes(attrs);
              } else if (url && url.match(/\/youtu\.be\/(.+?)(\?|$)/)) {
                const videoID = url.match(/\/youtu\.be\/(.+?)(\?|$)/)[1];
                const attrs = {
                  videoID,
                };
                props.setAttributes(attrs);
              } else {
                setError(t("widget/crossnote.youtube/error_message"));
              }
            }
          }}
          fullWidth={true}
        ></Input>
        <Typography className={clsx(classes.errorMessage)}>{error}</Typography>
      </Box>
    </Card>
  );
}
Example #28
Source File: index.tsx    From vscode-crossnote with GNU Affero General Public License v3.0 4 votes vote down vote up
function VideoWidget(props: WidgetArgs) {
  const attributes = props.attributes;
  const classes = useStyles(props);
  const { t } = useTranslation();
  const [source, setSource] = useState<string>(attributes["source"] || "");
  const [autoplay, setAutoplay] = useState<boolean>(
    attributes["autoplay"] || false
  );
  const [controls, setControls] = useState<boolean>(
    attributes["controls"] || true
  );
  const [loop, setLoop] = useState<boolean>(attributes["loop"] || false);
  const [muted, setMuted] = useState<boolean>(attributes["muted"] || false);
  const [poster, setPoster] = useState<string>(attributes["poster"] || "");

  if (attributes["src"]) {
    return (
      <span style={{ cursor: "default" }}>
        <Box className={clsx(classes.videoWrapper)}>
          <video
            className={clsx(classes.video)}
            autoPlay={attributes["autoplay"] || attributes["autoPlay"]}
            controls={attributes["controls"]}
            loop={attributes["loop"]}
            muted={attributes["muted"]}
            style={attributes["style"]}
            poster={attributes["poster"]}
          >
            {t("widget/crossnote.video/video_element_fail")}
            <source src={attributes["src"]} type={attributes["type"]}></source>
          </video>
        </Box>
      </span>
    );
  }

  if (props.isPreview) {
    return <span></span>;
  }

  return (
    <Card elevation={2} className={clsx(classes.card)}>
      <Typography variant={"h5"}>{t("general/Video")}</Typography>
      <Box className={clsx(classes.actionButtons)}>
        <Tooltip title={t("general/Delete")}>
          <IconButton onClick={() => props.removeSelf()}>
            <TrashCan></TrashCan>
          </IconButton>
        </Tooltip>
      </Box>
      <Box className={clsx(classes.section)}>
        <Typography variant={"subtitle1"} style={{ marginBottom: "8px" }}>
          {t("general/source-url")}
        </Typography>
        <Input
          margin={"dense"}
          placeholder={t("widget/crossnote.video/video-url-placeholder")}
          value={source}
          onChange={(event) => {
            setSource(event.target.value);
          }}
          onKeyDown={(event) => {
            if (event.which === 13) {
              if (source) {
                const attrs = {
                  autoplay,
                  controls,
                  loop,
                  muted,
                  src: source,
                  poster,
                };
                props.setAttributes(attrs);
              }
            }
          }}
          fullWidth={true}
        ></Input>
      </Box>
      <Box className={clsx(classes.section)}>
        <Typography variant={"subtitle1"} style={{ marginBottom: "8px" }}>
          {t("widget/crossnote.video/poster-url")}
        </Typography>
        <Input
          margin={"dense"}
          placeholder={t("widget/crossnote.video/poster-url-placeholder")}
          value={poster}
          onChange={(event) => {
            setPoster(event.target.value);
          }}
          fullWidth={true}
        ></Input>
      </Box>
      <Box className={clsx(classes.section)}>
        <FormControlLabel
          label={t("widget/autoplay")}
          control={
            <Switch
              checked={autoplay}
              onChange={() => setAutoplay(!autoplay)}
              color={"primary"}
            ></Switch>
          }
        ></FormControlLabel>
        <FormControlLabel
          label={t("widget/controls")}
          control={
            <Switch
              checked={controls}
              onChange={() => setControls(!controls)}
              color={"primary"}
            ></Switch>
          }
        ></FormControlLabel>
        <FormControlLabel
          label={t("widget/loop")}
          control={
            <Switch
              checked={loop}
              onChange={() => setLoop(!loop)}
              color={"primary"}
            ></Switch>
          }
        ></FormControlLabel>
        <FormControlLabel
          label={t("widget/muted")}
          control={
            <Switch
              checked={muted}
              onChange={() => setMuted(!muted)}
              color={"primary"}
            ></Switch>
          }
        ></FormControlLabel>
      </Box>
    </Card>
  );
}
Example #29
Source File: index.tsx    From vscode-crossnote with GNU Affero General Public License v3.0 4 votes vote down vote up
function OCRWidget(props: WidgetArgs) {
  const classes = useStyles(props);
  const { t } = useTranslation();
  const [canvas, setCanvas] = useState<HTMLCanvasElement>(null);
  // https://github.com/tesseract-ocr/tesseract/wiki/Data-Files#data-files-for-version-400-november-29-2016
  const [link, setLink] = useState<string>("");
  const [imageDataURL, setImageDataURL] = useState<string>("");
  const [ocrDataURL, setOCRDataURL] = useState<string>("");
  const [imageDropAreaElement, setImageDropAreaElement] = useState<
    HTMLInputElement
  >(null);
  const [isProcessing, setIsProcessing] = useState<boolean>(false);
  const [ocrProgresses, setOCRProgresses] = useState<OCRProgress[]>([]);
  const [selectedLanguages, setSelectedLanguages] = useState<string[]>(
    getInitialLanguages()
  );
  const [grayscaleChecked, setGrayscaleChecked] = useState<boolean>(true);

  useEffect(() => {
    if (canvas && imageDataURL) {
      const imageObject = new Image();
      const context = canvas.getContext("2d");
      imageObject.onload = function () {
        canvas.width = imageObject.width;
        canvas.height = imageObject.height;
        context.clearRect(0, 0, canvas.width, canvas.height);
        if (grayscaleChecked) {
          context.fillStyle = "#FFF";
          context.fillRect(0, 0, canvas.width, canvas.height);
          context.globalCompositeOperation = "luminosity";
        }
        context.drawImage(imageObject, 0, 0);
        setOCRDataURL(canvas.toDataURL());
      };
      imageObject.onerror = (error) => {
        throw error;
      };
      imageObject.setAttribute("crossOrigin", "anonymous");
      imageObject.src = imageDataURL;
    }
  }, [canvas, imageDataURL, grayscaleChecked]);

  function clickDropArea(e: any) {
    e.preventDefault();
    e.stopPropagation();
    if (!imageDropAreaElement || isProcessing) return;
    imageDropAreaElement.onchange = function (event) {
      const target = event.target as any;
      const files = target.files || [];
      if (files.length) {
        try {
          const file = files[0] as File;
          const reader = new FileReader();
          reader.readAsDataURL(file);
          reader.onload = () => {
            setImageDataURL(reader.result as string);
          };
          reader.onerror = (error) => {
            throw error;
          };
        } catch (error) {}
      }
    };
    imageDropAreaElement.click();
  }

  function startOCRFromLink() {
    try {
      setImageDataURL(link);
    } catch (error) {}
  }

  function ocr(input: File | string | HTMLCanvasElement) {
    const worker = createWorker({
      logger: (m: OCRProgress) => {
        setOCRProgresses((ocrProgresses) => {
          if (
            ocrProgresses.length &&
            ocrProgresses[ocrProgresses.length - 1].status === m.status
          ) {
            return [...ocrProgresses.slice(0, ocrProgresses.length - 1), m];
          } else {
            return [...ocrProgresses, m];
          }
        });
      },
    });

    (async () => {
      setIsProcessing(true);
      let languagesArr = selectedLanguages;
      if (languagesArr.length === 0) {
        languagesArr = ["eng"];
      }

      await worker.load();
      await worker.loadLanguage(languagesArr.join("+"));
      await worker.initialize(languagesArr.join("+"));
      const {
        data: { text },
      } = await worker.recognize(input);
      props.replaceSelf("\n" + text);
      await worker.terminate();
      setIsProcessing(false);
    })();
  }

  function toggleLanguage(lang: string) {
    setSelectedLanguages((selectedLanguages) => {
      const offset = selectedLanguages.indexOf(lang);
      if (offset >= 0) {
        selectedLanguages.splice(offset, 1);
        selectedLanguages = [...selectedLanguages];
      } else {
        selectedLanguages = [...selectedLanguages, lang];
      }
      return selectedLanguages;
    });
  }

  if (props.isPreview) {
    return <span></span>;
  }

  if (isProcessing) {
    return (
      <Card elevation={2} className={clsx(classes.card)}>
        <Typography variant={"h5"}>{t("general/Processing")}</Typography>
        {/*<Typography variant={"body1"}>{t("general/please-wait")}</Typography>*/}
        <List>
          {ocrProgresses.length > 0 && (
            <ListItem>
              <ListItemText>
                {t(
                  "tesseract/" + ocrProgresses[ocrProgresses.length - 1].status
                )}
              </ListItemText>
              <ListItemSecondaryAction>
                {Math.floor(
                  ocrProgresses[ocrProgresses.length - 1].progress * 100
                ).toString() + "%"}
              </ListItemSecondaryAction>
            </ListItem>
          )}
        </List>
      </Card>
    );
  }

  if (imageDataURL) {
    return (
      <Card elevation={2} className={clsx(classes.card)}>
        <Box className={clsx(classes.section)}>
          <Typography variant={"subtitle1"} style={{ marginBottom: "8px" }}>
            {t("widget/crossnote.ocr/recognize-text-in-languages")}
          </Typography>
          <FormGroup>
            <FormControlLabel
              control={
                <Checkbox
                  checked={selectedLanguages.indexOf("eng") >= 0}
                  onChange={() => toggleLanguage("eng")}
                  value="eng"
                />
              }
              label="English"
            />
            <FormControlLabel
              control={
                <Checkbox
                  checked={selectedLanguages.indexOf("chi_sim") >= 0}
                  onChange={() => toggleLanguage("chi_sim")}
                  value="chi_sim"
                />
              }
              label="简体中文"
            />
            <FormControlLabel
              control={
                <Checkbox
                  checked={selectedLanguages.indexOf("chi_tra") >= 0}
                  onChange={() => toggleLanguage("chi_tra")}
                  value="chi_tra"
                />
              }
              label="繁體中文"
            />
            <FormControlLabel
              control={
                <Checkbox
                  checked={selectedLanguages.indexOf("jpn") >= 0}
                  onChange={() => toggleLanguage("jpn")}
                  value="jpn"
                />
              }
              label="日本語"
            />
          </FormGroup>
        </Box>
        <Box className={clsx(classes.section)}>
          <Typography variant={"subtitle1"} style={{ marginBottom: "8px" }}>
            {t("widget/crossnote.ocr/extra-settings")}
          </Typography>
          <FormControlLabel
            control={
              <Switch
                checked={grayscaleChecked}
                onChange={() => {
                  setGrayscaleChecked(!grayscaleChecked);
                }}
                color={"primary"}
              ></Switch>
            }
            label={t("widget/crossnote.ocr/grayscale")}
          ></FormControlLabel>
        </Box>
        <Box className={clsx(classes.canvasWrapper)}>
          <canvas
            className={clsx(classes.canvas)}
            ref={(element) => setCanvas(element)}
          ></canvas>
        </Box>
        <ButtonGroup>
          <Button
            onClick={() => {
              setImageDataURL("");
              setOCRDataURL("");
            }}
          >
            {t("general/go-back")}
          </Button>
          <Button
            color={"primary"}
            onClick={() => ocr(ocrDataURL)}
            disabled={!ocrDataURL}
          >
            {t("widget/crossnote.ocr/start-ocr")}
          </Button>
        </ButtonGroup>
      </Card>
    );
  }

  return (
    <Card elevation={2} className={clsx(classes.card)}>
      <Typography variant={"h5"}>{t("widget/crossnote.ocr/ocr")}</Typography>
      <Box className={clsx(classes.actionButtons)}>
        <Tooltip title={t("general/Delete")}>
          <IconButton onClick={() => props.removeSelf()}>
            <TrashCan></TrashCan>
          </IconButton>
        </Tooltip>
      </Box>
      <Box className={clsx(classes.section)}>
        <Typography variant={"subtitle1"} style={{ marginBottom: "8px" }}>
          {t("general/Link")}
        </Typography>
        <Input
          margin={"dense"}
          placeholder={t("widget/crossnote.image/image-url-placeholder")}
          value={link}
          onChange={(event) => {
            setLink(event.target.value);
          }}
          onKeyDown={(event) => {
            if (event.which === 13) {
              startOCRFromLink();
            }
          }}
          fullWidth={true}
        ></Input>
      </Box>
      <Typography
        variant={"subtitle1"}
        style={{ marginTop: "16px", textAlign: "center" }}
      >
        {t("widget/crossnote.auth/Or")}
      </Typography>
      <Box className={clsx(classes.section)}>
        <Typography variant={"subtitle1"} style={{ marginBottom: "8px" }}>
          {t("widget/crossnote.ocr/local-image")}
        </Typography>
        <Box
          className={clsx(
            classes.dropArea,
            isProcessing ? classes.disabled : null
          )}
          onClick={clickDropArea}
        >
          <Typography>
            {isProcessing
              ? t("utils/uploading-image")
              : t("widget/crossnote.image/click-here-to-browse-image-file")}
          </Typography>
        </Box>
      </Box>
      <input
        type="file"
        // multiple
        style={{ display: "none" }}
        ref={(element: HTMLInputElement) => {
          setImageDropAreaElement(element);
        }}
      ></input>
    </Card>
  );
}