@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 |
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 |
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 |
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 |
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 |
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 |
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 |
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: </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 |
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 |
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 |
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 |
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>
);
}