@chakra-ui/react#Textarea TypeScript Examples

The following examples show how to use @chakra-ui/react#Textarea. 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 formik-chakra-ui with MIT License 6 votes vote down vote up
TextareaControl: FC<TextareaControlProps> = React.forwardRef(
  (
    props: TextareaControlProps,
    ref: React.ForwardedRef<HTMLTextAreaElement>
  ) => {
    const { name, label, textareaProps, ...rest } = props;
    const [field] = useField(name);

    return (
      <FormControl name={name} label={label} {...rest}>
        <Textarea {...field} id={name} ref={ref} {...textareaProps} />
      </FormControl>
    );
  }
)
Example #2
Source File: InputWidget.tsx    From ke with MIT License 5 votes vote down vote up
InputWidget = forwardRef<HTMLInputElement, InputWidgetProps>((props: InputWidgetProps, ref): JSX.Element => {
  const {
    name,
    helpText,
    targetPayload,
    style,
    submitChange,
    setInitialValue,
    containerStore,
    isTextarea = true,
    height,
    debounce = 1000,
    notifier,
    copyValue,
    useClipboard,
    isDisabled,
    mainDetailObject,
    textareaResize = 'none',
    containerProps,
    labelContainerProps,
    inputProps,
  } = props
  const context = containerStore.getState()

  const { targetUrl, content, isRequired, widgetDescription } = useWidgetInitialization({ ...props, context })

  setInitialValue({ [name]: content })

  const handleChange = (value: string): void => {
    pushAnalytics({
      eventName: EventNameEnum.INPUT_CHANGE,
      widgetType: WidgetTypeEnum.INPUT,
      value,
      objectForAnalytics: props.mainDetailObject,
      ...props,
    })

    const inputPayload = getPayload(value, name, targetPayload)
    submitChange({ url: targetUrl, payload: inputPayload })
  }

  const handleCopyValue = getCopyHandler(content, copyValue)

  const { getDataTestId } = useCreateTestId()

  return (
    <WidgetWrapper
      name={name}
      style={style}
      helpText={helpText}
      description={widgetDescription}
      required={isRequired}
      notifier={notifier}
      useClipboard={useClipboard}
      copyValue={handleCopyValue}
      containerProps={containerProps}
      labelContainerProps={labelContainerProps}
      {...getDataTestId(props)}
    >
      <DebounceInput
        name={name}
        value={content as string}
        height={height || (isTextarea ? 263 : undefined)}
        debounceTimeout={debounce}
        element={isTextarea ? (Textarea as React.FC) : (Input as React.FC)}
        onChange={handleChange}
        inputRef={ref}
        disabled={getAccessor(isDisabled, mainDetailObject, context)}
        resize={isTextarea ? textareaResize : undefined}
        {...inputProps}
      />
    </WidgetWrapper>
  )
})
Example #3
Source File: index.tsx    From engine with MIT License 5 votes vote down vote up
Details = () => (
  <AccordionItem>
    <h2>
      <AccordionButton _expanded={{ bg: "gray.300", color: "white" }}>
        <Box flex="1" textAlign="left">
          Details
        </Box>
        <AccordionIcon />
      </AccordionButton>
    </h2>
    <AccordionPanel pb={4}>
      <InputGroup size="sm">
        <InputLeftAddon children="Name" w="24" />
        <Input defaultValue="DoThingsFoo" />
      </InputGroup>
      <InputGroup size="sm">
        <InputLeftAddon children="File path" w="24" />
        <Input defaultValue="src/producers/domain/computation.ts" />
      </InputGroup>
      <InputGroup size="sm">
        <InputLeftAddon children="Author" w="24" />
        <Input defaultValue="John Doe" isReadOnly />
      </InputGroup>
      <InputGroup size="sm">
        <InputLeftAddon children="Created on" w="24" />
        <Input defaultValue="19/02/2022" isReadOnly />
      </InputGroup>
      <InputGroup size="sm">
        <InputLeftAddon children="Version" w="24" />
        <Select placeholder="Select a version">
          <option value="option1" selected>
            V2 22/02/2022 14:23 - John a2a2b227a7 (latest)
          </option>
          <option value="option2">V1 20/02/2022 13:23 Jane 9c32e516af</option>
        </Select>
      </InputGroup>
      <InputGroup size="sm">
        <InputLeftAddon children="Description" w="24" />
        <Textarea defaultValue="Does what it needs to do in terms of computation"></Textarea>
      </InputGroup>
    </AccordionPanel>
  </AccordionItem>
)
Example #4
Source File: AddNewFeedForm.tsx    From nextjs-hasura-boilerplate with MIT License 5 votes vote down vote up
AddNewFeedForm = () => {
  const [body, setBody] = useState("");
  const [session] = useSession();
  const [
    insertFeed,
    { loading: insertFeedFetching, error: insertFeedError },
  ] = useInsertFeedMutation();

  if (!session) {
    return (
      <AccessDeniedIndicator message="You need to be signed in to add a new feed!" />
    );
  }

  const handleSubmit = async () => {
    await insertFeed({
      variables: {
        author_id: session.id,
        body,
      },
    });

    setBody("");
  };

  const errorNode = () => {
    if (!insertFeedError) {
      return false;
    }

    return (
      <Alert status="error">
        <AlertIcon />
        <AlertTitle>{insertFeedError}</AlertTitle>
        <CloseButton position="absolute" right="8px" top="8px" />
      </Alert>
    );
  };

  return (
    <Stack spacing={4}>
      {errorNode()}
      <Box p={4} shadow="lg" rounded="lg">
        <Stack spacing={4}>
          <FormControl isRequired>
            <FormLabel htmlFor="body">What's on your mind?</FormLabel>
            <Textarea
              id="body"
              value={body}
              onChange={(e: ChangeEvent<HTMLTextAreaElement>) =>
                setBody(e.currentTarget.value)
              }
              isDisabled={insertFeedFetching}
            />
          </FormControl>
          <FormControl>
            <Button
              loadingText="Posting..."
              onClick={handleSubmit}
              isLoading={insertFeedFetching}
              isDisabled={!body.trim()}
            >
              Post
            </Button>
          </FormControl>
        </Stack>
      </Box>
    </Stack>
  );
}
Example #5
Source File: index.tsx    From calories-in with MIT License 5 votes vote down vote up
function Form({
  ownerName,
  notes,
  onClose,
  initialRef,
  onEditNotes,
  fieldId,
  textAreaHeight,
}: Props) {
  const { register, handleSubmit } = useFormContext()
  const notesRegister = register('notes')
  const notesInputRef = useMergeRefs(notesRegister.ref, initialRef)
  const oneTimeCheckActions = useOneTimeCheckActions()

  const onSubmit = handleSubmit((form: NotesForm) => {
    oneTimeCheckActions.set(`notes-${fieldId}`)
    onEditNotes(form.notes || undefined)
    onClose()
  })

  const { errorMessage, isInvalid } = useFormError('name')

  return (
    <form onSubmit={onSubmit}>
      <ModalContent>
        <Header ownerName={ownerName} notes={notes} />
        <ModalCloseButton />

        <ModalBody>
          <FormControl isInvalid={isInvalid}>
            <FormLabel>Notes</FormLabel>
            <Textarea
              autoComplete="off"
              {...notesRegister}
              ref={notesInputRef}
              focusBorderColor={isInvalid ? 'red.500' : undefined}
              placeholder="Enter notes"
              height={textAreaHeight}
            />
            <Collapse animateOpacity={true} in={Boolean(errorMessage)}>
              <Box minHeight="21px">
                <FormErrorMessage>{errorMessage}</FormErrorMessage>
              </Box>
            </Collapse>
          </FormControl>
        </ModalBody>

        <ModalFooter>
          <Button mr={3} onClick={onClose}>
            Close
          </Button>
          <Button
            type="submit"
            colorScheme="teal"
            variant="solid"
            onClick={onSubmit}
          >
            Save
          </Button>
        </ModalFooter>
      </ModalContent>
    </form>
  )
}
Example #6
Source File: NewTunnel.tsx    From wiregui with MIT License 4 votes vote down vote up
export default function NewTunnel() {
  const history = useHistory();
  const dispatch = useDispatch();
  const [hiddenInput, setHiddenInput] = useState<HTMLInputElement | null>();
  const [fileName, setFileName] = useState<string>();
  const [interfaceText, setInterfaceText] = useState<string>();
  const { userDataPath } = useSelector<StoreState, AppState>(
    (state) => state.app
  );
  const { files } = useSelector<StoreState, WgConfigState>(
    (state) => state.wgConfig
  );

  function handleImport(event: React.ChangeEvent<HTMLInputElement>) {
    if (event.currentTarget.files && event.currentTarget.files.length > 0) {
      const currentFile = event.currentTarget.files[0];

      try {
        const reader = new FileReader();
        reader.readAsText(currentFile, "utf-8");
        reader.onload = function (evt: ProgressEvent<FileReader>) {
          if (!evt.target) return;

          const data = evt.target.result as string;
          const name = currentFile.name.split(".conf");

          setFileName(name[0]);
          setInterfaceText(data);
        };

        reader.onerror = function () {
          toast(`Could not read file ${currentFile.name}`, { type: "error" });
        };
      } catch (e) {
        toast(e.message, { type: "error" });
      }
    }
  }

  function handleCancel() {
    history.push("/");
  }

  async function handleSave() {
    if (!fileName || fileName.length === 0) {
      toast("Name cannot be empty", { type: "error" });
      return;
    }

    if (fileName.length > 15) {
      toast("Filename is too long, maximum 15 characters", { type: "error" });
      return;
    }

    if (!interfaceText || interfaceText.length === 0) {
      toast("Interface cannot be empty", { type: "error" });
      return;
    }

    if (files.some(f => f.name === fileName)) {
      toast(`A tunnel named ${fileName} already exists`, { type: "error" });
      return;
    }

    try {
      dispatch(addFile(`${fileName}.conf`, interfaceText, userDataPath));
      history.push(`/tunnel/${fileName}`);
    } catch (e) {
      toast(e.message, { type: "error" });
    }
  }

  function onNameChange(event: React.ChangeEvent<HTMLInputElement>) {
    setFileName(event.target.value);
  }

  function onInterfaceTextChange(event: React.ChangeEvent<HTMLTextAreaElement>) {
    setInterfaceText(event.target.value);
  }

  return (
    <Content>
      <Flex
        bg="gray.200"
        borderRadius="4"
        color="whiteAlpha.700"
        direction="column"
        p="4"
        w="575px"
        h="auto"
        mx="auto"
        my="10"
      >
        <Flex justify="space-between" w="100%">
          <Text color="whiteAlpha.800" fontSize="lg" fontWeight="bold">
            New Tunnel
          </Text>
          <Button size="xs" onClick={() => hiddenInput?.click()}>
            Import
          </Button>
          <input
            hidden
            type="file"
            accept=".conf"
            onChange={handleImport}
            ref={(el) => setHiddenInput(el)}
          ></input>
        </Flex>
        <Flex align="center" mt="4" w="100%">
          <Text>Name:</Text>
          <Input
            bg="gray.300"
            borderColor="transparent"
            size="xs"
            w="50%"
            ml="2"
            value={fileName || ""}
            onChange={onNameChange}
          />
        </Flex>
        <Flex direction="column" mt="4" w="100%" h="100%">
          <Text>Interface:</Text>
          <Textarea
            bg="gray.300"
            borderColor="transparent"
            size="xs"
            resize="none"
            mt="2"
            w="100%"
            h="100%"
            value={interfaceText || ""}
            onChange={onInterfaceTextChange}
          />
        </Flex>
        <Flex justify="flex-end" mt="4">
          <Button size="sm" onClick={handleCancel}>
            Cancel
          </Button>
          <Button
            colorScheme="orange"
            color="whiteAlpha.800"
            size="sm"
            ml="4"
            disabled={!fileName || !interfaceText}
            onClick={handleSave}
          >
            Save
          </Button>
        </Flex>
      </Flex>
    </Content>
  );
}
Example #7
Source File: TunnelInfo.tsx    From wiregui with MIT License 4 votes vote down vote up
export default function TunnelInfo() {
  const history = useHistory();
  const dispatch = useDispatch();
  const [wgConfigFile, setWgConfigFile] = useState<WgConfigFile>();
  const [fileName, setFileName] = useState<string>("");
  const [interfaceText, setInterfaceText] = useState<string>("");
  const [originalInterfaceText, setOriginalInterfaceText] = useState<string>(
    ""
  );
  const { name } = useParams<TunnelParam>();
  const { files } = useSelector<StoreState, WgConfigState>(
    (state) => state.wgConfig
  );
  const { userDataPath } = useSelector<StoreState, AppState>(
    (state) => state.app
  );

  useEffect(() => {
    const filePath = path.join(userDataPath, "configurations", `${name}.conf`);

    const data = fs.readFileSync(filePath, "utf-8");
    const config = new WgConfig({});
    config.parse(data);

    setFileName(name);
    setInterfaceText(config.toString());
    setOriginalInterfaceText(config.toString());
    setWgConfigFile(files.find((f) => f.name === name));
  }, [name]);

  useEffect(() => {
    setWgConfigFile(files.find((f) => f.name === name));
  }, [files]);

  async function toggleActive() {
    if (!wgConfigFile) {
      toast("Could not load config file", { type: "error" });
      return;
    }

    try {
      const started = await WireGuard.toggle(wgConfigFile.path);
      const message = started ? "Activated" : "Deactivated";
      toast(`${message} ${wgConfigFile.name}`, { type: "success" });
      dispatch(updateStatus(started ? wgConfigFile.name : ""));
    } catch (e) {
      try {
        await checkWgIsInstalled();
      } catch (e) {
        toast("Wireguard was not detected on the system. If you just installed it, try restarting wiregui.", { type: "error" });
        return;
      }
      toast(e.message, { type: "error" });
    }
  }

  async function handleDelete() {
    if (!wgConfigFile) {
      toast(`Could not find config for ${name}`, { type: "error" });
      return;
    }

    if (wgConfigFile.active) {
      toast("Stop the tunnel before deleting", { type: "error" });
      return;
    }

    try {
      dispatch(deleteFile(wgConfigFile, userDataPath));
      history.push("/");
    } catch (e) {
      toast(e.message, { type: "error" });
    }
  }

  async function handleSave(): Promise<void> {
    if (files.some((f) => f.name === fileName && fileName !== name)) {
      toast(`A tunnel named ${fileName} already exists`, { type: "error" });
      return;
    }

    if (fileName.length > 15) {
      toast("Filename is too long, maximum 15 characters", { type: "error" });
      return;
    }

    try {
      if (wgConfigFile) {
        if (wgConfigFile.active) {
          toast("Stop the tunnel before updating", { type: "error" });
          return;
        }
        dispatch(deleteFile(wgConfigFile, userDataPath));
      }
      dispatch(addFile(`${fileName}.conf`, interfaceText, userDataPath));

      if (fileName !== name) {
        const lastConnectAt = localStorage.getItem(name);
        if (lastConnectAt) {
          localStorage.setItem(fileName, lastConnectAt);
          localStorage.removeItem(name);
        }
        history.push(`/tunnel/${fileName}`);
      }

      setOriginalInterfaceText(interfaceText);
      dispatch(fetchFiles(userDataPath));
    } catch (e) {
      toast(e.message, { type: "error" });
    }
  }

  function isAllowedToSave(): boolean {
    return (
      (fileName !== name || interfaceText !== originalInterfaceText) &&
      fileName.length > 0 &&
      interfaceText.length > 0
    );
  }

  function onNameChange(event: React.ChangeEvent<HTMLInputElement>): void {
    setFileName(event.target.value);
  }

  function onInterfaceTextChange(
    event: React.ChangeEvent<HTMLTextAreaElement>
  ): void {
    setInterfaceText(event.target.value);
  }

  function handleCancel() {
    history.push("/");
  }

  return (
    <Content>
      <Flex
        bg="gray.200"
        borderRadius="4"
        color="whiteAlpha.700"
        direction="column"
        p="4"
        w="575px"
        h="auto"
        mx="auto"
        my="10"
      >
        <Flex justify="space-between" w="100%">
          <Text color="whiteAlpha.800" fontSize="lg" fontWeight="bold">
            {name}
          </Text>
          <DialogButton
            title="Delete"
            color="whiteAlpha.800"
            colorScheme="red"
            header="Are you sure?"
            body="You cannot recover this file after deleting."
            onConfirm={handleDelete}
            launchButtonText={<DeleteIcon />}
          />
        </Flex>
        <Flex align="center" mt="4" w="100%">
          <Text>Name:</Text>
          <Input
            bg="gray.300"
            borderColor="transparent"
            size="xs"
            w="50%"
            ml="2"
            value={fileName}
            onChange={onNameChange}
          />
        </Flex>
        <Flex direction="column" mt="4" w="100%" h="100%">
          <Text fontWeight="medium">Interface:&nbsp;</Text>
          <Textarea
            bg="gray.300"
            borderColor="transparent"
            size="xs"
            resize="none"
            mt="2"
            w="100%"
            h="100%"
            value={interfaceText}
            onChange={onInterfaceTextChange}
          />
        </Flex>
        <Flex justify="flex-end" mt="4">
          <Button size="sm" onClick={handleCancel}>
            Cancel
          </Button>
          <Button colorScheme="orange" color="whiteAlpha.800" size="sm" ml="4" onClick={toggleActive}>
            {wgConfigFile && wgConfigFile.active ? "Deactivate" : "Activate"}
          </Button>
          {isAllowedToSave() && (
            <Button colorScheme="green" color="blackAlpha.800" size="sm" ml="4" onClick={handleSave}>
              Save
            </Button>
          )}
        </Flex>
      </Flex>
    </Content>
  );
}
Example #8
Source File: note-form.tsx    From notebook with MIT License 4 votes vote down vote up
NoteForm: React.SFC<NoteFormProps> = ({
  isOpen,
  onClose,
  selectedNote,
  handleNoteCreate,
  handleNoteUpdate
}) => {
  const { register, handleSubmit, formState, errors } = useForm<FormInputs>({
    mode: "onChange"
  });

  const onSubmit: SubmitHandler<FormInputs> = data => {
    let newNote: note = {
      id: "",
      title: data.title,
      body: data.body
    };
    if (handleNoteCreate) {
      newNote.id = nanoid();
      if (handleNoteCreate) handleNoteCreate(newNote);
    } else {
      newNote.id = selectedNote ? selectedNote.id : "";
      if (handleNoteUpdate) handleNoteUpdate(newNote);
    }
    onClose();
  };

  const validateTitle = (value: string) => {
    if (!value) {
      return "Title is required";
    } else return true;
  };

  const validateBody = (value: string) => {
    if (!value) {
      return "Body is required";
    } else return true;
  };

  return (
    <Modal
      isOpen={isOpen}
      onClose={onClose}
      size="lg"
      isCentered
      motionPreset="slideInBottom"
    >
      <ModalOverlay />
      <ModalContent>
        <form onSubmit={handleSubmit(onSubmit)}>
          <ModalHeader>{selectedNote ? "Edit" : "Create"} a Note</ModalHeader>
          <ModalCloseButton />
          <ModalBody pb={6}>
            <FormControl isInvalid={!!errors?.title} isRequired>
              <FormLabel>Title</FormLabel>
              <Input
                name="title"
                placeholder="Title"
                defaultValue={selectedNote?.title}
                ref={register({ validate: validateTitle })}
              />
              <FormErrorMessage>
                {!!errors?.title && errors?.title?.message}
              </FormErrorMessage>
            </FormControl>
            <FormControl size="lg" mt={4} isInvalid={!!errors?.body} isRequired>
              <FormLabel>Body</FormLabel>
              <Textarea
                name="body"
                placeholder="Body"
                size="md"
                borderRadius="5px"
                defaultValue={selectedNote?.body}
                ref={register({ validate: validateBody })}
              />
              <FormErrorMessage>
                {!!errors?.body && errors?.body?.message}
              </FormErrorMessage>
            </FormControl>
          </ModalBody>
          <ModalFooter>
            <Button
              type="submit"
              colorScheme="blue"
              isLoading={formState.isSubmitting}
              mr={3}
            >
              Save
            </Button>
            <Button onClick={onClose}>Cancel</Button>
          </ModalFooter>
        </form>
      </ModalContent>
    </Modal>
  );
}
Example #9
Source File: index.tsx    From jsonschema-editor-react with Apache License 2.0 4 votes vote down vote up
AdvancedNumber: React.FunctionComponent<AdvancedItemStateProps> = (
	props: React.PropsWithChildren<AdvancedItemStateProps>
) => {
	const { itemStateProp } = props;

	const changeEnumOtherValue = (value: string): string[] | null => {
		const array = value.split("\n");
		if (array.length === 0 || (array.length === 1 && !array[0])) {
			return null;
		}

		return array;
	};

	const itemState = useState(itemStateProp);

	const isEnumChecked = (itemState.value as JSONSchema7).enum !== undefined;
	const enumData = (itemState.value as JSONSchema7).enum
		? (itemState.enum.value as string[])
		: [];
	const enumValue = enumData?.join("\n");

	return (
		<Flex direction="column" wrap="nowrap">
			<Stack
				isInline
				alignItems="center"
				justifyContent="center"
				alignContent="center"
				m={1}
			>
				<FormLabel mr={2}>Default: </FormLabel>

				<NumberInput
					size="sm"
					defaultValue={Number(itemState.default.value)}
					placeholder="Default value"
					onChange={(value: number | string) => {
						itemState.default.set(Number(value));
					}}
				>
					<NumberInputField value={Number(itemState.default.value)} />
					<NumberInputStepper>
						<NumberIncrementStepper />
						<NumberDecrementStepper />
					</NumberInputStepper>
				</NumberInput>
			</Stack>

			<Stack
				isInline
				alignItems="center"
				justifyContent="center"
				alignContent="center"
				m={1}
			>
				<FormLabel mr={2}>Min Value: </FormLabel>
				<NumberInput
					size="sm"
					defaultValue={Number(itemState.minimum.value)}
					onChange={(value: number | string) => {
						itemState.minimum.set(Number(value));
					}}
				>
					<NumberInputField value={Number(itemState.minimum.value)} />
					<NumberInputStepper>
						<NumberIncrementStepper />
						<NumberDecrementStepper />
					</NumberInputStepper>
				</NumberInput>
				<FormLabel mr={2}>Max Value: </FormLabel>
				<NumberInput
					size="sm"
					defaultValue={Number(itemState.maximum.value)}
					onChange={(value: number | string) => {
						itemState.maximum.set(Number(value));
					}}
				>
					<NumberInputField value={Number(itemState.maximum.value)} />
					<NumberInputStepper>
						<NumberIncrementStepper />
						<NumberDecrementStepper />
					</NumberInputStepper>
				</NumberInput>
			</Stack>
			<Stack
				isInline
				alignItems="center"
				justifyContent="center"
				alignContent="center"
				m={1}
			>
				<FormLabel mr={2}>Enum: </FormLabel>
				<Checkbox
					isChecked={isEnumChecked}
					onChange={(evt: React.ChangeEvent<HTMLInputElement>) => {
						if (!evt.target.checked) {
							itemState.enum.set(none);
						} else {
							itemState.enum.set(Array<string>());
						}
					}}
				/>
				<Textarea
					value={enumValue}
					isDisabled={!isEnumChecked}
					placeholder="ENUM Values - One Entry Per Line"
					type={"number"}
					onChange={(evt: React.ChangeEvent<HTMLTextAreaElement>) => {
						const re = /^[0-9\n]+$/;
						if (evt.target.value === "" || re.test(evt.target.value)) {
							const update = changeEnumOtherValue(evt.target.value);
							if (update === null) {
								itemState.enum.set(none);
							} else {
								itemState.enum.set(update as string[]);
							}
						}
					}}
				/>
			</Stack>
		</Flex>
	);
}
Example #10
Source File: index.tsx    From jsonschema-editor-react with Apache License 2.0 4 votes vote down vote up
AdvancedString: React.FunctionComponent<AdvancedItemStateProps> = (
	props: React.PropsWithChildren<AdvancedItemStateProps>
) => {
	const { itemStateProp } = props;

	const changeEnumOtherValue = (value: string): string[] | null => {
		const array = value.split("\n");
		if (array.length === 0 || (array.length === 1 && !array[0])) {
			return null;
		}

		return array;
	};

	const itemState = useState(itemStateProp);

	const isEnumChecked = (itemState.value as JSONSchema7).enum !== undefined;
	const enumData = (itemState.value as JSONSchema7).enum
		? (itemState.enum.value as string[])
		: [];
	const enumValue = enumData?.join("\n");

	return (
		<Flex direction="column" wrap="nowrap">
			<Stack
				isInline
				alignItems="center"
				justifyContent="center"
				alignContent="center"
				m={1}
			>
				<FormLabel mr={2}>Default: </FormLabel>
				<Input
					id="default"
					placeholder="Default value"
					value={(itemState.default.value as string) ?? ""}
					onChange={(evt: React.ChangeEvent<HTMLInputElement>) => {
						itemState.default.set(evt.target.value);
					}}
				/>
			</Stack>

			<Stack
				isInline
				alignItems="center"
				justifyContent="center"
				alignContent="center"
				m={1}
			>
				<FormLabel mr={2}>Min Length: </FormLabel>
				<NumberInput
					size="sm"
					defaultValue={Number(itemState.minLength.value)}
					onChange={(value: number | string) => {
						itemState.minLength.set(Number(value));
					}}
				>
					<NumberInputField value={Number(itemState.minLength.value)} />
					<NumberInputStepper>
						<NumberIncrementStepper />
						<NumberDecrementStepper />
					</NumberInputStepper>
				</NumberInput>
				<FormLabel mr={2}>Max Length: </FormLabel>
				<NumberInput
					size="sm"
					defaultValue={Number(itemState.maxLength.value)}
					onChange={(value: number | string) => {
						itemState.maxLength.set(Number(value));
					}}
				>
					<NumberInputField value={Number(itemState.maxLength.value)} />
					<NumberInputStepper>
						<NumberIncrementStepper />
						<NumberDecrementStepper />
					</NumberInputStepper>
				</NumberInput>
			</Stack>
			<Stack
				isInline
				alignItems="center"
				justifyContent="center"
				alignContent="center"
				m={1}
			>
				<FormLabel mr={2} htmlFor="pattern">
					Pattern:{" "}
				</FormLabel>
				<Input
					id="pattern"
					placeholder="MUST be a valid regular expression."
					value={itemState.pattern.value ?? ""}
					onChange={(evt: React.ChangeEvent<HTMLInputElement>) => {
						itemState.pattern.set(evt.target.value);
					}}
				/>
			</Stack>

			<Stack
				isInline
				alignItems="center"
				justifyContent="center"
				alignContent="center"
				m={1}
			>
				<FormLabel mr={2}>Enum: </FormLabel>
				<Checkbox
					isChecked={isEnumChecked}
					onChange={(evt: React.ChangeEvent<HTMLInputElement>) => {
						if (!evt.target.checked) {
							itemState.enum.set(none);
						} else {
							itemState.enum.set(Array<string>());
						}
					}}
				/>
				<Textarea
					value={enumValue || ""}
					isDisabled={!isEnumChecked}
					placeholder="ENUM Values - One Entry Per Line"
					onChange={(evt: React.ChangeEvent<HTMLTextAreaElement>) => {
						const update = changeEnumOtherValue(evt.target.value);
						if (update === null) {
							itemState.enum.set(none);
						} else {
							itemState.enum.set(update as string[]);
						}
					}}
				/>
			</Stack>
			<Stack
				isInline
				alignItems="center"
				justifyContent="center"
				alignContent="center"
				m={1}
			>
				<FormLabel mr={2} htmlFor="format">
					Format:{" "}
				</FormLabel>
				<Select
					variant="outline"
					value={itemState.format.value ?? ""}
					size="sm"
					margin={2}
					placeholder="Choose data type"
					onChange={(evt: React.ChangeEvent<HTMLSelectElement>) => {
						if (evt.target.value === "") {
							itemState.format.set(none);
						} else {
							itemState.format.set(evt.target.value);
						}
					}}
				>
					{StringFormat.map((item, index) => {
						return (
							<option key={String(index)} value={item.name}>
								{item.name}
							</option>
						);
					})}
				</Select>
			</Stack>
		</Flex>
	);
}
Example #11
Source File: ChatForm.tsx    From takeout-app with MIT License 4 votes vote down vote up
ChatForm: React.FC<Props> = ({ track, channel }) => {
  const chat = useChat();

  const { data: session } = Api.useSession();
  const isStaff = session?.attendee?.is_staff;

  const [errorAlert, setErrorAlert] = React.useState<JSX.Element | null>(null);
  const [isRequesting, setIsRequesting] = React.useState<boolean>(false);

  const { register, handleSubmit, reset, setFocus, watch } = useForm<{
    message: string;
    asAdmin: boolean;
  }>({
    defaultValues: {
      message: "",
      asAdmin: false,
    },
  });

  const asAdmin = watch("asAdmin");

  const onSubmit = handleSubmit(async (data) => {
    if (!chat.session || !channel) return;
    if (isRequesting) return;
    setIsRequesting(true);
    setErrorAlert(null);

    try {
      if (data.asAdmin && isStaff) {
        await Api.sendChatMessage(track.slug, data.message, true);
      } else {
        // Workaround: aws-sdk-v3 sigv4 fails to generate correct signature for payload containing emoji...
        if (/\p{Extended_Pictographic}/u.test(data.message)) {
          await Api.sendChatMessage(track.slug, data.message, false);
        } else {
          await chat.session.postMessage(channel, data.message);
        }
      }
      reset({ message: "", asAdmin: false });
    } catch (e) {
      setErrorAlert(
        <Box my={2}>
          <ErrorAlert error={e} />
        </Box>,
      );
    }
    setFocus("message");
    setIsRequesting(false);
  });

  const shouldDisable = !session?.attendee || !chat.session || !channel;

  // TODO: errorAlert to toast

  return (
    <Box p="16px" bgColor="#ffffff" borderTop="1px solid" borderColor={Colors.chatBorder}>
      {errorAlert}
      <form onSubmit={onSubmit}>
        <VStack w="100%">
          {session && !session.attendee?.is_ready ? (
            <Box w="100%">
              <Alert status="warning">
                <AlertIcon />
                <Text as="span">
                  Set your name at{" "}
                  <Link as={RouterLink} to="/attendee" textDecoration="underline">
                    Settings
                  </Link>{" "}
                  page
                </Text>
              </Alert>
            </Box>
          ) : null}
          <Box w="100%">
            <Textarea
              as={TextareaAutoSize}
              {...register("message")}
              size="sm"
              placeholder={asAdmin ? "SAY SOMETHING AS ADMIN..." : "Send a message"}
              isRequired
              isDisabled={shouldDisable}
              autoComplete="off"
              rows={1}
              minRows={1}
              maxRows={4}
              onKeyPress={(e) => {
                if (e.key == "Enter") {
                  e.preventDefault();
                  onSubmit();
                }
              }}
              css={{ resize: "none" }}
            />
          </Box>
          <Flex w="100%" alignItems="flex-end" direction="row-reverse" justifyContent="space-between">
            <IconButton
              icon={<SendIcon boxSize="14px" />}
              minW="30px"
              w="30px"
              h="30px"
              aria-label="Send"
              type="submit"
              isLoading={isRequesting}
              isDisabled={shouldDisable}
            />
            {isStaff ? (
              <Tooltip label="Send as an official announcement" aria-label="">
                <FormControl display="flex" alignSelf="center" h="30px">
                  <FormLabel htmlFor="ChatForm__asAdmin" aria-hidden="true" m={0} mr={1}>
                    <CampaignIcon w="24px" h="24px" />
                  </FormLabel>
                  <Switch
                    aria-label="Send as an official announcement"
                    id="ChatForm__asAdmin"
                    size="sm"
                    isChecked={asAdmin}
                    isDisabled={shouldDisable}
                    {...register("asAdmin")}
                  />
                </FormControl>
              </Tooltip>
            ) : null}
          </Flex>
        </VStack>
      </form>
    </Box>
  );
}