react#KeyboardEventHandler TypeScript Examples

The following examples show how to use react#KeyboardEventHandler. 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: index.tsx    From skin-react with MIT License 6 votes vote down vote up
Button = ({
  onClick = () => {},
  onEscape = () => {},
  ...props
}: ButtonProps<HTMLButtonElement | HTMLAnchorElement> & {
  onEscape?: KeyboardEventHandler<HTMLButtonElement | HTMLAnchorElement>;
}) => {
  const handleClick = (event) => !props.disabled && onClick(event);
  const handleKeyDown = (event) => {
    !props.disabled && event.key === 'Escape' && onEscape(event);
  };
  return <BasicButton {...props} onClick={handleClick} onKeyDown={handleKeyDown} />;
}
Example #2
Source File: AmountInput.tsx    From mStable-apps with GNU Lesser General Public License v3.0 6 votes vote down vote up
AmountInput: FC<Props> = ({
  className,
  error,
  disabled = false,
  placeholder = '0.0',
  onChange,
  value,
  min = '0',
  max,
  step = '0.01',
  decimals,
}) => {
  const trimmedValue = useMemo(() => trimInput(value, decimals), [value, decimals])

  const handleKeyPress = useCallback<KeyboardEventHandler<HTMLInputElement>>(event => {
    // Prevent 'minus' key
    if ((event.which || event.keyCode) === 45) {
      event.preventDefault()
      event.stopPropagation()
    }
  }, [])

  const handleChange = useCallback<ChangeEventHandler<HTMLInputElement>>(event => onChange?.(event.target.value ?? undefined), [onChange])

  return (
    <Input
      className={className}
      error={error}
      min={min}
      max={max}
      placeholder={placeholder}
      step={step}
      value={trimmedValue}
      onKeyPress={handleKeyPress}
      onChange={handleChange}
      disabled={disabled}
    />
  )
}
Example #3
Source File: AmountInputButton.tsx    From mStable-apps with GNU Lesser General Public License v3.0 6 votes vote down vote up
AmountInputButton: FC<Props> = ({ className, disabled = false, placeholder = '0.00', onChange, value, min = '0', max }) => {
  const handleKeyPress = useCallback<KeyboardEventHandler<HTMLInputElement>>(event => {
    // Prevent 'minus' key
    if ((event.which || event.keyCode) === 45) {
      event.preventDefault()
      event.stopPropagation()
    }
  }, [])

  const handleChange = useCallback<ChangeEventHandler<HTMLInputElement>>(
    event => {
      onChange?.(event.target.value ?? undefined)
    },
    [onChange],
  )

  return (
    <StyledButton className={className}>
      <input
        type="number"
        min={min}
        max={max}
        placeholder={placeholder}
        step="0.01"
        value={value || ''}
        onKeyPress={handleKeyPress}
        onChange={handleChange}
        disabled={disabled}
      />
    </StyledButton>
  )
}
Example #4
Source File: CheckboxField.tsx    From typescript-fun with MIT License 6 votes vote down vote up
CheckboxField: FC<{
  field: Field<boolean, FormErrorID>;
  label: string;
}> = ({ field, label, ...rest }) => {
  const handleChange = useCallback<FormEventHandler<HTMLInputElement>>(
    event => {
      field.onChange(event.currentTarget.checked);
    },
    [field],
  );

  const handleKeyPress = useCallback<KeyboardEventHandler<HTMLInputElement>>(
    event => {
      if (event.key === 'Enter') field.submit();
    },
    [field],
  );

  return (
    <FormControl {...rest}>
      <Checkbox
        isChecked={field.value}
        onChange={handleChange}
        onKeyPress={handleKeyPress}
        ref={field.ref}
      >
        {label}
      </Checkbox>
    </FormControl>
  );
}
Example #5
Source File: Items.tsx    From backstage with Apache License 2.0 5 votes vote down vote up
export function SidebarSearchField(props: SidebarSearchFieldProps) {
  const { sidebarConfig } = useContext(SidebarConfigContext);
  const [input, setInput] = useState('');
  const classes = useMemoStyles(sidebarConfig);
  const Icon = props.icon ? props.icon : SearchIcon;

  const search = () => {
    props.onSearch(input);
    setInput('');
  };

  const handleEnter: KeyboardEventHandler = ev => {
    if (ev.key === 'Enter') {
      ev.preventDefault();
      search();
    }
  };

  const handleInput = (ev: React.ChangeEvent<HTMLInputElement>) => {
    setInput(ev.target.value);
  };

  const handleInputClick = (ev: React.MouseEvent<HTMLInputElement>) => {
    // Clicking into the search fields shouldn't navigate to the search page
    ev.preventDefault();
    ev.stopPropagation();
  };

  const handleItemClick = (ev: React.MouseEvent) => {
    // Clicking on the search icon while should execute a query with the current field content
    search();
    ev.preventDefault();
  };

  return (
    <div className={classes.searchRoot}>
      <SidebarItem
        icon={Icon}
        to={props.to}
        onClick={handleItemClick}
        disableHighlight
      >
        <TextField
          placeholder="Search"
          value={input}
          onClick={handleInputClick}
          onChange={handleInput}
          onKeyDown={handleEnter}
          className={classes.searchContainer}
          InputProps={{
            disableUnderline: true,
            className: classes.searchField,
          }}
          inputProps={{
            className: classes.searchFieldHTMLInput,
          }}
        />
      </SidebarItem>
    </div>
  );
}
Example #6
Source File: index.tsx    From xiaobi with MIT License 5 votes vote down vote up
handleKeydown: KeyboardEventHandler<HTMLInputElement> = (e) => {
	if (e.key === '-') {
		e.preventDefault();
	}
}
Example #7
Source File: aria.ts    From pybricks-code with MIT License 5 votes vote down vote up
/**
 * React hook for creating toolbar item element props for focus management.
 *
 * Using this hook requires the current element to be inside of an
 * {@link ToolbarStateContext} and to be inside of a {@link FocusScope}.
 */
export function useToolbarItemFocus(props: { id: string }): ToolbarItemFocusAria {
    const { id } = props;
    const state = useContext(ToolbarStateContext);
    const focusManager = useFocusManager();

    const onKeyDown = useCallback<KeyboardEventHandler<HTMLElement>>(
        (e) => {
            switch (e.key) {
                case 'ArrowLeft':
                    focusManager.focusPrevious({ wrap: true });
                    break;
                case 'ArrowRight':
                    focusManager.focusNext({ wrap: true });
                    break;
                case 'Home':
                    focusManager.focusFirst();
                    break;
                case 'End':
                    focusManager.focusLast();
                    break;
                default:
                    return;
            }
        },
        [focusManager],
    );

    const onFocus = useCallback<FocusEventHandler<HTMLElement>>(
        (e) => {
            state.setLastFocusedItem(e.target.id);
        },
        [state.setLastFocusedItem],
    );
    const excludeFromTabOrder = id !== state.lastFocusedItem;

    return { toolbarItemFocusProps: { onKeyDown, onFocus }, excludeFromTabOrder };
}
Example #8
Source File: TextInputField.tsx    From typescript-fun with MIT License 5 votes vote down vote up
TextInputField: FC<{
  field: FieldMaybeOptional<string, FormErrorID>;
  icon?: ReactNode;
  label: string;
  type: 'text' | 'email' | 'password' | 'tel';
}> = ({ field, icon, label, type, ...rest }) => {
  const handleChange = useCallback<FormEventHandler<HTMLInputElement>>(
    ({ currentTarget: { value } }) => {
      if (isOptionalField(field)) {
        field.onChange(value.length === 0 ? O.none : O.some(value));
      } else {
        field.onChange(value);
      }
    },
    [field],
  );

  const handleKeyPress = useCallback<KeyboardEventHandler<HTMLInputElement>>(
    event => {
      if (event.key === 'Enter') field.submit();
    },
    [field],
  );

  const value = isOptionalField(field)
    ? pipe(
        field.value,
        O.getOrElse(() => ''),
      )
    : field.value;

  const input = (
    <Input
      id={field.key}
      type={type}
      value={value}
      onChange={handleChange}
      onKeyPress={handleKeyPress}
      ref={field.ref}
    />
  );

  return (
    <FormControl
      isRequired={!isOptionalField(field)}
      isInvalid={field.isInvalid}
      {...rest}
    >
      <FormLabel htmlFor={field.key}>{label}</FormLabel>
      {icon ? (
        <InputGroup>
          <InputLeftElement>{icon}</InputLeftElement>
          {input}
        </InputGroup>
      ) : (
        input
      )}
      <FormErrorMessage error={field.firstError} />
    </FormControl>
  );
}
Example #9
Source File: NewCommentModal.tsx    From apps with GNU Affero General Public License v3.0 4 votes vote down vote up
export default function NewCommentModal({
  onRequestClose,
  editId,
  onComment,
  onInputChange,
  ...props
}: NewCommentModalProps): ReactElement {
  const [input, setInput] = useState<string>(props.editContent || '');
  const [showDiscardModal, setShowDiscardModal] = useState<boolean>(false);
  const [activeTab, setActiveTab] = useState('Write');
  const [sendingComment, setSendingComment] = useState<boolean>(false);
  const [errorMessage, setErrorMessage] = useState<string>(null);
  const isPreview = activeTab === 'Preview';
  const { requestMethod } = useRequestProtocol();
  const previewQueryKey = ['comment_preview', input];
  const { data: previewContent } = useQuery<{ preview: string }>(
    previewQueryKey,
    () =>
      requestMethod(
        `${apiUrl}/graphql`,
        PREVIEW_COMMENT_MUTATION,
        { content: input },
        { requestKey: JSON.stringify(previewQueryKey) },
      ),
    { enabled: isPreview && input?.length > 0 },
  );

  const confirmClose = (event: MouseEvent): void => {
    if (
      (!props.editContent && input?.length) ||
      (props.editContent && props.editContent !== input)
    ) {
      setShowDiscardModal(true);
    } else {
      onRequestClose(event);
    }
  };

  const key = ['post_comments_mutations', props.post.id];
  const { mutateAsync: comment } = useMutation<
    CommentOnData,
    unknown,
    CommentVariables
  >(
    (variables) =>
      requestMethod(
        `${apiUrl}/graphql`,
        props.commentId
          ? COMMENT_ON_COMMENT_MUTATION
          : COMMENT_ON_POST_MUTATION,
        variables,
        { requestKey: JSON.stringify(key) },
      ),
    { onSuccess: (data) => data && onComment(data.comment, true) },
  );

  const { mutateAsync: editComment } = useMutation<
    CommentOnData,
    unknown,
    CommentVariables
  >(
    (variables) =>
      requestMethod(`${apiUrl}/graphql`, EDIT_COMMENT_MUTATION, variables, {
        requestKey: JSON.stringify(key),
      }),
    { onSuccess: (data) => data && onComment(data.comment, false) },
  );

  const modalRef = (element: HTMLDivElement): void => {
    if (element) {
      // eslint-disable-next-line no-param-reassign
      element.scrollTop = element.scrollHeight - element.clientHeight;
    }
  };

  const sendComment = async (): Promise<void> => {
    if (sendingComment || !input?.trim().length) {
      return;
    }
    setErrorMessage(null);
    setSendingComment(true);
    try {
      if (editId) {
        await editComment({
          id: editId,
          content: input,
        });
      } else {
        await comment({
          id: props.commentId || props.post.id,
          content: input,
        });
      }
    } catch (err) {
      setErrorMessage('Something went wrong, try again');
      setSendingComment(false);
    }
  };

  const onKeyDown = async (
    event: KeyboardEvent<HTMLDivElement>,
    defaultCallback?: KeyboardEventHandler<HTMLDivElement>,
  ): Promise<void> => {
    // Ctrl / Command + Enter
    if (
      (event.ctrlKey || event.metaKey) &&
      event.keyCode === 13 &&
      input?.length
    ) {
      await sendComment();
    } else {
      defaultCallback?.(event);
    }
  };

  useEffect(() => {
    onInputChange?.(input);
  }, [input]);

  return (
    <ResponsiveModal
      contentRef={modalRef}
      onRequestClose={confirmClose}
      overlayClassName="fixed-position"
      padding={false}
      {...props}
    >
      <ModalCloseButton onClick={confirmClose} className="top-2" />
      <TabContainer
        onActiveChange={(active: string) => setActiveTab(active)}
        shouldMountInactive
        className="tablet:max-h-[40rem] grow tablet:grow-0"
      >
        <Tab label="Write" className="flex flex-col flex-1">
          <CommentBox
            {...props}
            onInput={setInput}
            input={input}
            editId={editId}
            errorMessage={errorMessage}
            sendingComment={sendingComment}
            sendComment={sendComment}
            onKeyDown={onKeyDown}
          />
        </Tab>
        <Tab label="Preview" className="flex overflow-y-auto flex-col flex-1">
          {isPreview && previewContent?.preview && (
            <Markdown
              content={previewContent.preview}
              appendTooltipTo={props.parentSelector}
            />
          )}
          {isPreview && (
            <Button
              disabled={!input?.trim().length || input === props.editContent}
              loading={sendingComment}
              onClick={sendComment}
              className="mt-auto ml-auto btn-primary-avocado"
            >
              {editId ? 'Update' : 'Comment'}
            </Button>
          )}
        </Tab>
      </TabContainer>
      <DiscardCommentModal
        isOpen={showDiscardModal}
        onRequestClose={() => setShowDiscardModal(false)}
        onDeleteComment={onRequestClose}
        shouldCloseOnOverlayClick={false}
        parentSelector={props.parentSelector}
      />
    </ResponsiveModal>
  );
}
Example #10
Source File: index.tsx    From xiaobi with MIT License 4 votes vote down vote up
Index: React.FC<Props> = ({ match }) => {
	const id = match.params.id.split('?')[0];
	const pair = getLocationQuery(window.location.href, 'pair');
	const history = useHistory();
	const [keywords, setKeyWords] = useState<string>();
	const [enable, setEnable] = useState(false);
	const [isFocus, setFocus] = useState(false);
	const [times, setTimes] = useState(0);
	const inputEl = useRef<HTMLInputElement>(null);
	const { data } = useLoop({
		fn: () => sendMessage({ command: CMDS.CMD_NOTICEINFO, data: id }),
		updated: [times],
		delay: 3000,
	});

	const handleCreate = useMemo(
		() =>
			debounce(() => {
				if (!enable || !keywords) return;

				const time = Date.now();
				const params: NoticeType = {
					id,
					uid: `${id}_${time}`,
					name: data.pair,
					type: 'price',
					rule: Number(keywords).toString(),
					market: data.market,
					create: time,
					compare: Number(keywords) > data.usd,
				};

				sendMessage({ command: CMDS.CMD_ADDNOTICE, data: params })
					.then((isSend) => {
						setTimes(times + 1);
						setEnable(false);
						if (inputEl.current) {
							inputEl.current.value = '';
						}

						message.info('通知创建成功');
					})
					.catch((error) => error);
			}, 500),
		[enable, keywords],
	);

	const goBack = () => history.replace({ pathname: StaticRoutes.Notify });
	const handleFocus = () => setFocus(true);
	const handleBlur = () => setFocus(false);
	const handleKeyup: KeyboardEventHandler<HTMLInputElement> = (e) => {
		if (e.key == 'Enter') {
			handleCreate();
		}
	};
	const handleChange: ChangeEventHandler<HTMLInputElement> = (e) => {
		setEnable(!!e.target.value);
		setKeyWords(e.target.value);
	};

	const handleDel = (uid: string) => {
		sendMessage({ command: CMDS.CMD_DELNOTICE, data: uid });
		setTimes(times + 1);
	};

	const renderDataJSX = () => {
		if (data?.pair) {
			const { usd, cny, percent } = data;
			const className = percent > 0 ? 'increase' : 'decrease';
			const p = percent > 0 ? `+${percent}%` : `${percent}%`;

			return (
				<DataUI>
					<p className='usd'>{usd}</p>
					<div className='desc'>
						<p>¥{cny}</p>
						<p className={className}>{p}</p>
					</div>
				</DataUI>
			);
		}

		return (
			<DataUI>
				<DataLoading />
			</DataUI>
		);
	};
	const renderListJSX = () => {
		if (data?.list.length) {
			return (data.list as NoticeType[]).map((v) => (
				<ListBlock {...v} key={v.uid} current={data.usd} delEvent={() => handleDel(v.uid)} />
			));
		}
		return <Empty />;
	};

	return (
		<WrapperUI>
			<ContentUI>
				<HeadUI>
					<p className='name'>{pair}</p>
					<p className='back' onClick={goBack}>
						返回
					</p>
				</HeadUI>
				{renderDataJSX()}
				<InputBoxUI className={`${isFocus ? 'active' : ''}`}>
					<input
						ref={inputEl}
						type='number'
						placeholder='请输入价格'
						onFocus={handleFocus}
						onBlur={handleBlur}
						onKeyDown={handleKeydown}
						onKeyUp={handleKeyup}
						onChange={handleChange}
					/>
				</InputBoxUI>
				<BtnUI className={`${enable ? 'active' : ''}`} onClick={handleCreate}>
					创建提醒
				</BtnUI>
				<DescUI>提示:插件通知需开启浏览器通知权限,插件会定时更新数据根据填入规则决定是否通知</DescUI>
			</ContentUI>
			<ListUI>
				<TabUI>
					<p>类型</p>
					<p>提醒规则</p>
					<p>操作</p>
				</TabUI>
				<ScrollUI>{renderListJSX()}</ScrollUI>
			</ListUI>
		</WrapperUI>
	);
}
Example #11
Source File: NewConfigButton.tsx    From xcloud-keyboard-mouse with GNU General Public License v3.0 4 votes vote down vote up
export default function NewConfigButton({ disabled, allConfigs, onCreate, onImport }: NewConfigButtonProps) {
  const buttonId = 'new-config-btn';
  const [isOpen, setIsOpen] = useState(false);
  const [name, setName] = useState('');
  const isMounted = useIsMounted();
  const isTaken = useMemo(() => {
    return (
      Object.keys(allConfigs)
        .map((existing) => existing.toLowerCase())
        .indexOf(name.toLowerCase()) !== -1
    );
  }, [name, allConfigs]);
  const triggerRef = useRef<null | HTMLButtonElement>(null);
  const handleToggleClick = useCallback(() => {
    setIsOpen(!isOpen);
  }, [isOpen]);
  const handleClose = useCallback(() => {
    setIsOpen(false);
  }, []);
  const handleImport = useCallback(() => {
    importConfig()
      .then((config) => {
        onImport(name, config);
        if (isMounted()) setName('');
        alert('Preset imported from file successfully');
      })
      .catch((errorMsg) => {
        console.error('Import failed', errorMsg);
        alert(errorMsg);
      });
  }, [isMounted, name, onImport]);
  const handleSubmit = useCallback(() => {
    onCreate(name);
  }, [onCreate, name]);
  const handleKeyPress: KeyboardEventHandler<HTMLInputElement | HTMLTextAreaElement> = useCallback(
    (e) => {
      if (e.code === 'Enter') {
        handleSubmit();
      }
    },
    [handleSubmit],
  );
  return (
    <>
      <IconButton
        id={buttonId}
        elementRef={triggerRef}
        onClick={handleToggleClick}
        title="Add new preset"
        ariaLabel="Add new preset"
        disabled={disabled}
      >
        <PlusCircleIcon />
      </IconButton>
      {isOpen ? (
        <Callout
          setInitialFocus
          gapSpace={0}
          directionalHint={DirectionalHint.bottomRightEdge}
          target={`#${buttonId}`}
          onDismiss={handleClose}
          // Needed to fix issue in Safari
          preventDismissOnEvent={(e) => e.type === 'resize'}
        >
          <div style={{ width: 250 }} className="padding-full">
            <TextField
              placeholder="New preset name"
              autoFocus={isOpen}
              value={name}
              maxLength={18}
              onKeyPress={handleKeyPress}
              onChange={(e) => setName(e.currentTarget.value)}
            />
            {isTaken ? <div className="error margin-top-s">Config with that name already exists!</div> : null}
            <div className="horizontal space-between margin-top-s">
              <DefaultButton disabled={!name || isTaken} onClick={handleImport}>
                Import File
              </DefaultButton>
              <PrimaryButton disabled={!name || isTaken} onClick={handleSubmit}>
                Create New
              </PrimaryButton>
            </div>
          </div>
        </Callout>
      ) : null}
    </>
  );
}
Example #12
Source File: loginSimple.tsx    From typescript-fun with MIT License 4 votes vote down vote up
LoginSimple = () => {
  const [isPending, setIsPending] = useState(false);

  const handleSubmit = (form: Form<typeof LoginForm['props']>) => {
    const asyncLogin = (
      data: LoginForm,
    ): TE.TaskEither<LoginError, LoginPayload> => {
      if (data.email === '[email protected]' && data.password === 'aaaaaa')
        return T.delay(500)(TE.right({ token: '123' }));
      if (data.email === '[email protected]') return TE.left('suspendedEmail');
      return TE.left({ password: ['VerifiedPassword'] });
    };

    const handleError = (error: LoginError) => {
      setIsPending(false);
      form.enable();
      if (LoginFormAsyncErrors.is(error)) form.setAsyncErrors(error);
      else {
        switch (error) {
          case 'suspendedEmail':
            alert(error);
            break;
          case 'somethingElse':
            alert(error);
            break;
          default:
            // Absurd ensures all cases are handled.
            absurd(error);
        }
      }
    };

    const handleSuccess = (payload: LoginPayload) => {
      setIsPending(false);
      form.reset();
      alert(`payload: ${JSON.stringify(payload, null, 2)}`);
      Router.push('/');
    };

    pipe(
      form.validated,
      E.fold(constVoid, data => {
        setIsPending(true);
        form.disable();
        pipe(
          asyncLogin(data),
          TE.mapLeft(handleError),
          TE.map(handleSuccess),
        )();
      }),
    );
  };

  const form = useForm(
    LoginForm,
    {
      email: '',
      password: '',
    },
    { handleSubmit },
  );

  const submitOnEnter: KeyboardEventHandler<HTMLInputElement> = event => {
    if (event.key === 'Enter') form.submit();
  };

  return (
    <>
      <Head>
        <title>Login</title>
      </Head>
      <Box m={8}>
        <Text fontSize="xl">Login</Text>
        <Box maxW="sm" borderWidth="1px" rounded="lg" p={4} my={4}>
          <Stack spacing={4}>
            <FormControl isRequired isInvalid={form.fields.email.isInvalid}>
              <FormLabel htmlFor="email">Email</FormLabel>
              <Input
                id="email"
                type="email"
                value={form.fields.email.value}
                onChange={form.fields.email.onHTMLInputValue}
                onKeyPress={submitOnEnter}
                ref={form.fields.email.ref}
              />
              <FormErrorMessage error={form.fields.email.firstError} />
            </FormControl>
            <FormControl isRequired isInvalid={form.fields.password.isInvalid}>
              <FormLabel htmlFor="password">Password</FormLabel>
              <Input
                id="password"
                type="password"
                value={form.fields.password.value}
                onChange={form.fields.password.onHTMLInputValue}
                onKeyPress={submitOnEnter}
                ref={form.fields.password.ref}
              />
              <FormErrorMessage error={form.fields.password.firstError} />
            </FormControl>
            <Button
              isDisabled={isPending}
              onClick={form.submit}
              mt={4}
              variantColor="green"
            >
              Login
            </Button>
          </Stack>
        </Box>
        <Stack>
          <Text>
            Try email <b>[email protected]</b> with passwords <b>aaaaaa</b>, <b>bbbbbb</b>
            , or email <b>[email protected]</b>.
          </Text>
        </Stack>
      </Box>
    </>
  );
}