@chakra-ui/react#FormControl TypeScript Examples
The following examples show how to use
@chakra-ui/react#FormControl.
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: AutoInstallUpdatesField.tsx From bluebubbles-server with Apache License 2.0 | 6 votes |
AutoInstallUpdatesField = ({ helpText }: AutoInstallUpdatesFieldProps): JSX.Element => {
const autoInstall: boolean = (useAppSelector(state => state.config.auto_install_updates) ?? false);
return (
<FormControl>
<Checkbox id='auto_install_updates' isChecked={autoInstall} onChange={onCheckboxToggle}>Auto Install / Apply Updates</Checkbox>
<FormHelperText>
{helpText ?? (
<Text>
When enabled, BlueBubbles will auto-install the latest available version when an update is detected
</Text>
)}
</FormHelperText>
</FormControl>
);
}
Example #2
Source File: UrlField.tsx From calories-in with MIT License | 6 votes |
function UrlField({ canEdit, food }: Props) {
const { register } = useFormContext<FoodForm>()
return (
<Flex minHeight={canEdit ? '200px' : undefined} flexDirection="column">
{canEdit && (
<Alert status="info" mb={3}>
<AlertIcon color="teal.400" />
Add a link will open a web page when the food is clicked. This is
useful if you want to show a specific product.
</Alert>
)}
<FormControl id="email">
<Flex alignItems="center">
<FormLabel mb={0} flexShrink={0}>
Link:
</FormLabel>
{canEdit ? (
<Input
{...register('url')}
placeholder="http://example.com"
type="email"
/>
) : (
<Link
href={food?.url}
target="_blank"
noOfLines={1}
color="teal.500"
>
{food?.url}
</Link>
)}
</Flex>
</FormControl>
</Flex>
)
}
Example #3
Source File: UseHttpsField.tsx From bluebubbles-server with Apache License 2.0 | 6 votes |
UseHttpsField = ({ helpText }: UseHttpsFieldProps): JSX.Element => {
const useHttps: boolean = (useAppSelector(state => state.config.use_custom_certificate) ?? false);
return (
<FormControl>
<Checkbox id='use_custom_certificate' isChecked={useHttps} onChange={onCheckboxToggle}>Use Custom Certificate</Checkbox>
<FormHelperText>
{helpText ?? (
<Text>
This will install a self-signed certificate at: <Code>~/Library/Application Support/bluebubbles-server/Certs</Code>
<br />
Note: Only use this this option if you have your own certificate! Replace the certificates in the <Code>Certs</Code> directory
</Text>
)}
</FormHelperText>
</FormControl>
);
}
Example #4
Source File: UserForm.tsx From next-crud with MIT License | 6 votes |
UserForm = ({ initialValues, onSubmit }: IProps) => {
const { register, formState, handleSubmit } = useForm<IFormValues>({
defaultValues: initialValues,
resolver: yupResolver(schema),
mode: 'onChange',
})
return (
<VStack
as="form"
onSubmit={handleSubmit(onSubmit)}
spacing={4}
width="100%"
>
<FormControl
id="username"
isInvalid={!!formState.errors.username?.message}
>
<FormLabel>Username</FormLabel>
<Input name="username" ref={register} />
<FormErrorMessage>
{formState.errors.username?.message}
</FormErrorMessage>
</FormControl>
<Button
type="submit"
colorScheme="blue"
isLoading={formState.isSubmitting}
disabled={!formState.isValid}
>
Submit
</Button>
</VStack>
)
}
Example #5
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 #6
Source File: Content.tsx From calories-in with MIT License | 5 votes |
function Content({ title, onClose, initialRef, variantFormIndex }: Props) {
const { register } = useFormContext()
const nameRegister = register('name')
const nameInputRef = useMergeRefs(nameRegister.ref, initialRef)
const onSubmit = useSubmitVariantNameForm({
variantFormIndex,
onComplete: onClose,
})
const { errorMessage, isInvalid } = useFormError('name')
useSelectInputText(initialRef)
return (
<form onSubmit={onSubmit}>
<ModalContent>
<ModalHeader>{title}</ModalHeader>
<ModalCloseButton />
<ModalBody>
<FormControl isInvalid={isInvalid}>
<FormLabel>Name</FormLabel>
<Input
autoComplete="off"
{...nameRegister}
ref={nameInputRef}
focusBorderColor={isInvalid ? 'red.500' : undefined}
placeholder="Enter name"
/>
<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}
>
Rename
</Button>
</ModalFooter>
</ModalContent>
</form>
)
}
Example #7
Source File: index.tsx From nextjs-hasura-boilerplate with MIT License | 5 votes |
MyAccountPageComponent = ({ user }) => {
const [name, setName] = useState(user.name);
const [session] = useSession();
const [updateUser, { loading: updateUserFetching, error: updateUserError }] =
useUpdateUserMutation();
const handleSubmit = () => {
updateUser({
variables: {
userId: session.id,
name,
},
});
};
const errorNode = () => {
if (!updateUserError) {
return false;
}
return (
<Alert status="error">
<AlertIcon />
<AlertTitle>{updateUserError}</AlertTitle>
<CloseButton position="absolute" right="8px" top="8px" />
</Alert>
);
};
return (
<Stack spacing={8}>
<Heading>My Account</Heading>
{errorNode()}
<Box shadow="lg" rounded="lg" p={4}>
<Stack spacing={4}>
<FormControl isRequired>
<FormLabel htmlFor="name">Name</FormLabel>
<Input
type="text"
id="name"
value={name}
onChange={(e: FormEvent<HTMLInputElement>) =>
setName(e.currentTarget.value)
}
isDisabled={updateUserFetching}
/>
</FormControl>
<FormControl>
<Button
loadingText="Saving..."
onClick={handleSubmit}
isLoading={updateUserFetching}
isDisabled={!name.trim()}
>
Save
</Button>
</FormControl>
</Stack>
</Box>
</Stack>
);
}
Example #8
Source File: AddAppDomain.tsx From ledokku with MIT License | 5 votes |
AddAppDomain = ({ appId, appDomainsRefetch }: AddDomainProps) => {
const toast = useToast();
const [addDomainMutation] = useAddDomainMutation();
const [showAddForm, setShowAddForm] = useState(false);
const formik = useFormik<{ domainName: string }>({
initialValues: {
domainName: '',
},
validateOnChange: true,
validationSchema: addAppDomainYupSchema,
onSubmit: async (values) => {
try {
await addDomainMutation({
variables: {
input: {
appId,
domainName: values.domainName,
},
},
});
await appDomainsRefetch();
toast.success('Domain added successfully');
formik.resetForm();
} catch (error) {
toast.error(error.message);
}
},
});
return (
<>
{!showAddForm ? (
<Button variant="outline" onClick={() => setShowAddForm(true)}>
Add custom domain
</Button>
) : (
<form onSubmit={formik.handleSubmit}>
<FormControl
id="domainName"
isInvalid={Boolean(
formik.errors.domainName && formik.touched.domainName
)}
>
<Input
placeholder="www.mydomain.com"
name="domainName"
value={formik.values.domainName}
onChange={formik.handleChange}
/>
<FormErrorMessage>{formik.errors.domainName}</FormErrorMessage>
</FormControl>
<ButtonGroup mt="4" spacing="2">
<Button isLoading={formik.isSubmitting} type="submit">
Save
</Button>
<Button
variant="outline"
disabled={formik.isSubmitting}
onClick={() => setShowAddForm(false)}
>
Cancel
</Button>
</ButtonGroup>
</form>
)}
</>
);
}
Example #9
Source File: IconCustomizerDrawer.tsx From lucide with ISC License | 5 votes |
export function IconCustomizerDrawer() {
const [showCustomize, setShowCustomize] = useState(false);
const { color, setColor, size, setSize, strokeWidth, setStroke, resetStyle } = useContext(IconStyleContext);
return (
<>
<Button as="a" leftIcon={<Edit />} size="lg" onClick={() => setShowCustomize(true)}>
Customize
</Button>
<Drawer isOpen={showCustomize} placement="right" onClose={() => setShowCustomize(false)}>
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader>Customize Icons</DrawerHeader>
<DrawerBody>
<Grid gridGap={'1em'}>
<FormControl>
<ColorPicker
color={color}
value={color}
onChangeComplete={(col) => setColor(col.hex)}
/>
</FormControl>
<FormControl>
<FormLabel htmlFor="stroke">
<Flex>
<Text flexGrow={1} fontWeight={'bold'}>
Stroke
</Text>
<Text>{strokeWidth}px</Text>
</Flex>
</FormLabel>
<Slider
value={strokeWidth}
onChange={setStroke}
min={0.5}
max={3}
step={0.5}
name={'stroke'}
>
<SliderTrack>
<SliderFilledTrack bg={color} />
</SliderTrack>
<SliderThumb />
</Slider>
</FormControl>
<FormControl>
<FormLabel htmlFor="size">
<Flex>
<Text flexGrow={1} fontWeight={'bold'}>
Size
</Text>
<Text>{size}px</Text>
</Flex>
</FormLabel>
<Slider value={size} onChange={setSize} min={12} max={64} step={1} name={'size'}>
<SliderTrack>
<SliderFilledTrack bg={color} />
</SliderTrack>
<SliderThumb />
</Slider>
</FormControl>
<FormControl>
<Button onClick={resetStyle}>Reset</Button>
</FormControl>
</Grid>
</DrawerBody>
</DrawerContent>
</Drawer>
</>
);
}
Example #10
Source File: ControlAttendeeEdit.tsx From takeout-app with MIT License | 5 votes |
ControlAttendeeEdit: React.FC<Props> = () => {
const match = useRouteMatch<{ id: string }>();
const id = parseInt(match.params.id, 10);
const { data } = ControlApi.useAttendee(id);
const [errorAlert, setErrorAlert] = React.useState<JSX.Element | null>(null);
const [isRequesting, setIsRequesting] = React.useState<boolean>(false);
const { register, handleSubmit, reset } = useForm<ControlUpdateAttendeeRequestAttendee>({
defaultValues: {
name: React.useMemo(() => data?.attendee?.name, [data]),
is_staff: React.useMemo(() => data?.attendee?.is_staff, [data]),
is_speaker: React.useMemo(() => data?.attendee?.is_speaker, [data]),
is_committer: React.useMemo(() => data?.attendee?.is_committer, [data]),
presentation_slugs: React.useMemo(() => data?.attendee?.presentation_slugs || [], [data]),
},
});
const onSubmit = handleSubmit(async (data) => {
if (isRequesting) return;
setIsRequesting(true);
try {
await ControlApi.updateAttendee(id, data);
setErrorAlert(null);
} catch (e) {
setErrorAlert(
<Box my={2}>
<ErrorAlert error={e} />
</Box>,
);
}
setIsRequesting(false);
});
React.useEffect(() => {
if (data) reset(data.attendee);
}, [data]);
if (!data) return <p>Loading</p>;
// TODO: link to registration page and support email
return (
<>
{errorAlert}
<Container mt="20px">
<form onSubmit={onSubmit}>
<Text>
<Link href={data.ticket.admin_url} isExternal textDecoration="underline">
{data.ticket.reference}
</Link>
</Text>
<FormControl mt={4} id="attendee__name" isRequired>
<FormLabel>Name</FormLabel>
<Input {...register("name")} />
</FormControl>
<FormControl mt={4} id="attendee__staff" isRequired>
<FormLabel>Staff</FormLabel>
<Checkbox {...register("is_staff")} />
</FormControl>
<FormControl mt={4} id="attendee__speaker" isRequired>
<FormLabel>Speaker</FormLabel>
<Checkbox {...register("is_speaker")} />
</FormControl>
<FormControl mt={4} id="attendee__committer" isRequired>
<FormLabel>Committer</FormLabel>
<Checkbox {...register("is_committer")} />
</FormControl>
<FormControl mt={4} id="attendee__presentation_slugs">
<FormLabel>Presentation Slugs</FormLabel>
<Input {...register("presentation_slugs.0")} />
</FormControl>
<Button mt={4} size="lg" type="submit" isLoading={isRequesting}>
Save
</Button>
</form>
</Container>
</>
);
}
Example #11
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 #12
Source File: LocalPortField.tsx From bluebubbles-server with Apache License 2.0 | 5 votes |
LocalPortField = ({ helpText }: LocalPortFieldProps): JSX.Element => {
const dispatch = useAppDispatch();
const port: number = useAppSelector(state => state.config.socket_port) ?? 1234;
const [newPort, setNewPort] = useState(port);
const [portError, setPortError] = useState('');
const hasPortError: boolean = (portError?? '').length > 0;
useEffect(() => { setNewPort(port); }, [port]);
/**
* A handler & validator for saving a new port.
*
* @param theNewPort - The new port to save
*/
const savePort = (theNewPort: number): void => {
// Validate the port
if (theNewPort < 1024 || theNewPort > 65635) {
setPortError('Port must be between 1,024 and 65,635');
return;
} else if (theNewPort === port) {
setPortError('You have not changed the port since your last save!');
return;
}
dispatch(setConfig({ name: 'socket_port', value: theNewPort }));
if (hasPortError) setPortError('');
showSuccessToast({
id: 'settings',
duration: 4000,
description: 'Successfully saved new port! Restarting Proxy & HTTP services...'
});
};
return (
<FormControl isInvalid={hasPortError}>
<FormLabel htmlFor='socket_port'>Local Port</FormLabel>
<Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
<Input
id='socket_port'
type='number'
maxWidth="5em"
value={newPort}
onChange={(e) => {
if (hasPortError) setPortError('');
setNewPort(Number.parseInt(e.target.value));
}}
/>
<IconButton
ml={3}
verticalAlign='top'
aria-label='Save port'
icon={<AiOutlineSave />}
onClick={() => savePort(newPort)}
/>
</Flex>
{!hasPortError ? (
<FormHelperText>
{helpText ?? 'Enter the local port for the socket server to run on'}
</FormHelperText>
) : (
<FormErrorMessage>{portError}</FormErrorMessage>
)}
</FormControl>
);
}
Example #13
Source File: Inspector.tsx From openchakra with MIT License | 4 votes |
Inspector = () => {
const dispatch = useDispatch()
const component = useSelector(getSelectedComponent)
const { isOpen, onOpen, onClose } = useDisclosure()
const [componentName, onChangeComponentName] = useState('')
const componentsNames = useSelector(getComponentNames)
const { clearActiveProps } = useInspectorUpdate()
const saveComponent = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
dispatch.components.setComponentName({
componentId: component.id,
name: componentName,
})
onClose()
onChangeComponentName('')
}
const isValidComponentName = useMemo(() => {
return (
!!componentName.match(/^[A-Z]\w*$/g) &&
!componentsNames.includes(componentName) &&
// @ts-ignore
!componentsList.includes(componentName)
)
}, [componentName, componentsNames])
const { type, rootParentType, id, children } = component
const isRoot = id === 'root'
const parentIsRoot = component.parent === 'root'
const docType = rootParentType || type
const componentHasChildren = children.length > 0
useEffect(() => {
clearActiveProps()
}, [clearActiveProps])
return (
<>
<Box bg="white">
<Box
fontWeight="semibold"
fontSize="md"
color="yellow.900"
py={2}
px={2}
boxShadow="sm"
bg="yellow.100"
display="flex"
justifyContent="space-between"
flexDir="column"
>
{isRoot ? 'Document' : type}
{!!component.componentName && (
<Text fontSize="xs" fontWeight="light">
{component.componentName}
</Text>
)}
</Box>
{!isRoot && (
<Stack
direction="row"
py={2}
spacing={2}
align="center"
zIndex={99}
px={2}
flexWrap="wrap"
justify="flex-end"
>
<CodeActionButton />
{!component.componentName && (
<ActionButton
label="Name component"
icon={<EditIcon path="" />}
onClick={onOpen}
/>
)}
<ActionButton
label="Duplicate"
onClick={() => dispatch.components.duplicate()}
icon={<CopyIcon path="" />}
/>
<ActionButton
label="Reset props"
icon={<IoMdRefresh />}
onClick={() => dispatch.components.resetProps(component.id)}
/>
<ActionButton
label="Chakra UI Doc"
as={Link}
onClick={() => {
window.open(
`https://chakra-ui.com/${docType.toLowerCase()}`,
'_blank',
)
}}
icon={<GoRepo />}
/>
<ActionButton
bg="red.500"
label="Remove"
onClick={() => dispatch.components.deleteComponent(component.id)}
icon={<FiTrash2 />}
/>
</Stack>
)}
</Box>
<Box pb={1} bg="white" px={3}>
<Panels component={component} isRoot={isRoot} />
</Box>
<StylesPanel
isRoot={isRoot}
showChildren={componentHasChildren}
parentIsRoot={parentIsRoot}
/>
<Modal onClose={onClose} isOpen={isOpen} isCentered>
<ModalOverlay>
<ModalContent>
<form onSubmit={saveComponent}>
<ModalHeader>Save this component</ModalHeader>
<ModalCloseButton />
<ModalBody>
<FormControl isInvalid={!isValidComponentName}>
<FormLabel>Component name</FormLabel>
<Input
size="md"
autoFocus
variant="outline"
isFullWidth
focusBorderColor="blue.500"
errorBorderColor="red.500"
value={componentName}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
onChangeComponentName(e.target.value)
}
/>
{!isValidComponentName && (
<FormErrorMessage>
Component name must start with a capital character and
must not contain space or special character, and name
should not be already taken (including existing chakra-ui
components).
</FormErrorMessage>
)}
<FormHelperText>
This will name your component that you will see in the code
panel as a separated component.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="blue"
mr={3}
type="submit"
isDisabled={!isValidComponentName}
>
Save
</Button>
<Button onClick={onClose}>Cancel</Button>
</ModalFooter>
</form>
</ModalContent>
</ModalOverlay>
</Modal>
</>
)
}
Example #14
Source File: index.tsx From calories-in with MIT License | 4 votes |
function StatFormField(props: Props) {
const {
name,
label,
inputType,
isIdented = false,
nutritionValueUnit = 'g',
textInputRef,
isReadOnly = false,
isEmphasized = false,
isValueBold = false,
isCaption = false,
isRequired,
children,
labelDetail,
dividerProps = {},
hasDivider = true,
dailyValuePercent,
labelElement,
formLabelProps,
...rest
} = props
const { errorMessage, isInvalid } = useFormError(name)
const inputElement = useGetInputElement({
isInvalid,
name,
inputType,
textInputRef,
isReadOnly,
nutritionValueUnit,
isBold: isValueBold,
})
const labelDetailElement = labelDetail ? (
<Text
as={isReadOnly ? 'span' : undefined}
fontSize="sm"
fontWeight="thin"
ml={1}
>
{labelDetail}
</Text>
) : null
const isValueNextToLabel = isReadOnly && !(isCaption || isEmphasized)
return (
<FormControl
isInvalid={isInvalid}
id={name}
pl={isIdented ? 10 : 0}
isRequired={!isReadOnly && isRequired}
{...rest}
>
<VStack spacing={2} alignItems="stretch">
{hasDivider && <Divider {...dividerProps} />}
<Flex justifyContent={'space-between'} alignItems="center">
<Flex>
<FormLabel
fontWeight={
isIdented ? 'normal' : isEmphasized ? 'semibold' : 'medium'
}
flexShrink={0}
fontSize={isCaption ? 'lg' : 'md'}
m={0}
{...formLabelProps}
>
{label || labelElement}
{isReadOnly && labelDetailElement}
</FormLabel>
{!isReadOnly && labelDetailElement}
{isValueNextToLabel && <Box ml={2}>{inputElement}</Box>}
</Flex>
<Flex ml={2} justifyContent="flex-end">
{!isValueNextToLabel && inputElement}
{dailyValuePercent !== undefined && isValueNextToLabel && (
<Text fontWeight="medium">{`${dailyValuePercent}%`}</Text>
)}
{!isReadOnly && inputType === 'nutritionValue' && (
<Flex
width={9}
flexShrink={0}
justifyContent="flex-start"
alignItems="center"
ml={1}
>
<Text textColor="gray.500">{nutritionValueUnit}</Text>
</Flex>
)}
</Flex>
</Flex>
</VStack>
<Collapse animateOpacity={true} in={Boolean(errorMessage)}>
<Box minHeight="21px">
<FormErrorMessage>{errorMessage}</FormErrorMessage>
</Box>
</Collapse>
{children}
</FormControl>
)
}
Example #15
Source File: NgrokAuthTokenField.tsx From bluebubbles-server with Apache License 2.0 | 4 votes |
NgrokAuthTokenField = ({ helpText }: NgrokAuthTokenFieldProps): JSX.Element => {
const dispatch = useAppDispatch();
const ngrokToken: string = (useAppSelector(state => state.config.ngrok_key) ?? '');
const [showNgrokToken, setShowNgrokToken] = useBoolean();
const [newNgrokToken, setNewNgrokToken] = useState(ngrokToken);
const [ngrokTokenError, setNgrokTokenError] = useState('');
const hasNgrokTokenError: boolean = (ngrokTokenError ?? '').length > 0;
useEffect(() => { setNewNgrokToken(ngrokToken); }, [ngrokToken]);
/**
* A handler & validator for saving a new Ngrok auth token.
*
* @param theNewNgrokToken - The new auth token to save
*/
const saveNgrokToken = (theNewNgrokToken: string): void => {
theNewNgrokToken = theNewNgrokToken.trim();
// Validate the port
if (theNewNgrokToken === ngrokToken) {
setNgrokTokenError('You have not changed the token since your last save!');
return;
} else if (theNewNgrokToken.includes(' ')) {
setNgrokTokenError('Invalid Ngrok Auth Token! Please check that you have copied it correctly.');
return;
}
dispatch(setConfig({ name: 'ngrok_key', value: theNewNgrokToken }));
setNgrokTokenError('');
showSuccessToast({
id: 'settings',
duration: 4000,
description: 'Successfully saved new Ngrok Auth Token! Restarting Proxy service...'
});
};
return (
<FormControl isInvalid={hasNgrokTokenError}>
<FormLabel htmlFor='ngrok_key'>Ngrok Auth Token (Optional)</FormLabel>
<Input
id='password'
type={showNgrokToken ? 'text' : 'password'}
maxWidth="20em"
value={newNgrokToken}
onChange={(e) => {
if (hasNgrokTokenError) setNgrokTokenError('');
setNewNgrokToken(e.target.value);
}}
/>
<IconButton
ml={3}
verticalAlign='top'
aria-label='View Ngrok token'
icon={showNgrokToken ? <AiFillEye /> : <AiFillEyeInvisible />}
onClick={() => setShowNgrokToken.toggle()}
/>
<IconButton
ml={3}
verticalAlign='top'
aria-label='Save Ngrok token'
icon={<AiOutlineSave />}
onClick={() => saveNgrokToken(newNgrokToken)}
/>
{!hasNgrokTokenError ? (
<FormHelperText>
{helpText ?? (
<Text>
Using an Auth Token will allow you to use the benefits of the upgraded Ngrok
service. This can improve connection stability and reliability. If you do not have
an Ngrok Account, sign up for free here:
<Box as='span' color={baseTheme.colors.brand.primary}>
<Link href='https://dashboard.ngrok.com/get-started/your-authtoken' target='_blank'>ngrok.com</Link>
</Box>
</Text>
)}
</FormHelperText>
) : (
<FormErrorMessage>{ngrokTokenError}</FormErrorMessage>
)}
</FormControl>
);
}
Example #16
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>
);
}
Example #17
Source File: PaymentMethodsInput.tsx From coindrop with GNU General Public License v3.0 | 4 votes |
PaymentMethodsInput: FC<Props> = ({ fieldArrayName, fields, control, register, remove, append }) => {
const { colors } = useTheme();
const paymentMethodsDataWatch: PaymentMethod[] = useWatch({
control,
name: fieldArrayName,
});
const [openAccordionItemIndex, setOpenAccordionItemIndex] = useState(-1);
useEffect(() => {
if (
paymentMethodsDataWatch[paymentMethodsDataWatch.length - 1]?.paymentMethodId === "default-blank"
|| paymentMethodsDataWatch[paymentMethodsDataWatch.length - 1]?.address === ""
) {
setOpenAccordionItemIndex(paymentMethodsDataWatch.length - 1);
}
}, [paymentMethodsDataWatch]);
const containsInvalidAddress = paymentMethodsDataWatch.some(paymentMethod => !paymentMethod.address);
const { isAddressTouched, setIsAddressTouched } = useContext(AdditionalValidation);
// optgroup not compatible with Chakra dark mode: https://github.com/chakra-ui/chakra-ui/issues/2853
// const optionsGroup = (category: Category) => {
// const optgroupLabels: Record<Category, string> = {
// "digital-wallet": 'Digital Wallets',
// "digital-asset": "Digital Assets",
// "subscription-platform": "Subscription Platforms",
// };
// return (
// <optgroup label={optgroupLabels[category]}>
// {paymentMethods
// .filter(paymentMethod => paymentMethod.category === category)
// .sort((a, b) => (a.id < b.id ? -1 : 1))
// .map(({ id, displayName }) => (
// <option
// key={id}
// value={id}
// style={{display: paymentMethodsDataWatch.map(paymentMethodDataWatch => paymentMethodDataWatch.paymentMethodId).includes(id) ? "none" : undefined }}
// >
// {displayName}
// </option>
// ))}
// </optgroup>
// );
// };
return (
<>
{fields.length < 1
? 'No payment methods defined yet.'
: (
<Accordion
allowToggle
defaultIndex={-1}
index={openAccordionItemIndex}
>
{
fields
.map((item, index) => {
const watchedData = paymentMethodsDataWatch.find(watchedPaymentMethod => watchedPaymentMethod.id === item.id);
const PaymentMethodIcon = paymentMethodIcons[watchedData?.paymentMethodId];
return (
<AccordionItem
key={item.id}
>
<AccordionButton
onClick={() => {
setIsAddressTouched(true);
if (openAccordionItemIndex !== index && !paymentMethodsDataWatch.find(paymentMethod => paymentMethod.address === "")) {
setOpenAccordionItemIndex(index);
} else {
setOpenAccordionItemIndex(undefined);
}
}}
>
<Flex flex="1" textAlign="left" align="center">
<Flex mr={1} align="center">
{PaymentMethodIcon ? <PaymentMethodIcon mr={2} /> : <QuestionOutlineIcon mr={2} />}
{paymentMethodNames[watchedData?.paymentMethodId] ?? 'Add payment method'}
</Flex>
{watchedData?.isPreferred && (
<Flex>
<StarIcon
ml={2}
size="16px"
color={colors.yellow['400']}
/>
<Text
as="span"
fontSize="xs"
ml={1}
>
<i>Preferred</i>
</Text>
</Flex>
)}
</Flex>
<AccordionIcon />
</AccordionButton>
<AccordionPanel pb={4} id={`accordion-panel-${watchedData.paymentMethodId}`}>
<input
ref={register()}
name={`${fieldArrayName}[${index}].id`}
defaultValue={item.id}
style={{display: 'none'}}
/>
<Box
display={paymentMethodNames[watchedData?.paymentMethodId] ? "none" : "block"}
data-cy={`select-payment-method-container-${watchedData.paymentMethodId}`}
>
<Select
name={`${fieldArrayName}[${index}].paymentMethodId`}
ref={register()}
defaultValue={paymentMethodNames[item.paymentMethodId] ? item.paymentMethodId : 'default-blank'}
isInvalid={containsInvalidAddress && isAddressTouched}
onChange={() => setIsAddressTouched(false)}
>
<option hidden disabled value="default-blank">Select...</option>
{/* optgroup not compatible with Chakra dark mode: https://github.com/chakra-ui/chakra-ui/issues/2853 */}
{Object.entries(paymentMethodNames)
.sort((a, b) => {
const [aId] = a;
const [bId] = b;
return aId < bId ? -1 : 1;
})
.map(([paymentMethodId, paymentMethodDisplayName]) => (
<option
key={paymentMethodId}
value={paymentMethodId}
style={{display: paymentMethodsDataWatch.map(paymentMethod => paymentMethod.paymentMethodId).includes(paymentMethodId) ? "none" : undefined }}
>
{paymentMethodDisplayName}
</option>
))}
</Select>
</Box>
<Box
mx={3}
display={paymentMethodNames[watchedData?.paymentMethodId] ? "block" : "none"}
>
<FormControl isRequired>
<FormLabel htmlFor={`${fieldArrayName}[${index}].address`}>Address</FormLabel>
<Input
id={`address-input-${watchedData.paymentMethodId}`}
name={`${fieldArrayName}[${index}].address`}
ref={register()}
defaultValue={item.address}
isInvalid={containsInvalidAddress && isAddressTouched}
/>
<Box>
<Checkbox
name={`${fieldArrayName}[${index}].isPreferred`}
ref={register()}
defaultValue={item?.isPreferred ? 1 : 0}
defaultIsChecked={item?.isPreferred}
mt={1}
colorScheme="yellow"
>
Preferred
</Checkbox>
</Box>
</FormControl>
</Box>
<Flex
justify={watchedData?.paymentMethodId === 'default-blank' ? 'space-between' : 'flex-end'}
mt={3}
wrap="wrap"
align="center"
>
{watchedData?.paymentMethodId === 'default-blank' && (
<Text fontSize="xs" ml={1}>
<Link href="/blog/payment-method-request" isExternal>
Payment method not listed?
</Link>
</Text>
)}
<Button
onClick={() => {
remove(index);
setIsAddressTouched(false);
}}
leftIcon={<MinusIcon />}
size="sm"
>
{'Remove '}
{/* {paymentMethodNames[watchedData?.paymentMethodId]} */}
</Button>
</Flex>
</AccordionPanel>
</AccordionItem>
);
})
}
</Accordion>
)}
<Flex
justify="center"
mt={2}
>
<Button
id="add-payment-method-button"
onClick={() => {
append({});
setIsAddressTouched(false);
}}
leftIcon={<AddIcon />}
variant="ghost"
size="sm"
isDisabled={
(
fields.length > 0
&& !paymentMethodNames[paymentMethodsDataWatch[paymentMethodsDataWatch.length - 1]?.paymentMethodId]
)
|| containsInvalidAddress
}
>
Add payment method
</Button>
</Flex>
</>
);
}
Example #18
Source File: PaddingPanel.tsx From openchakra with MIT License | 4 votes |
PaddingPanel = ({ type }: PaddingPanelPropsType) => {
const { setValueFromEvent } = useForm()
const all = usePropsSelector(ATTRIBUTES[type].all)
const left = usePropsSelector(ATTRIBUTES[type].left)
const right = usePropsSelector(ATTRIBUTES[type].right)
const bottom = usePropsSelector(ATTRIBUTES[type].bottom)
const top = usePropsSelector(ATTRIBUTES[type].top)
return (
<Box mb={4}>
<FormControl>
<FormLabel fontSize="xs" htmlFor="width" textTransform="capitalize">
{type}
</FormLabel>
<InputGroup size="sm">
<Input
mb={1}
placeholder="All"
size="sm"
type="text"
name={ATTRIBUTES[type].all}
value={all || ''}
onChange={setValueFromEvent}
/>
</InputGroup>
<SimpleGrid columns={2} spacing={1}>
<InputGroup size="sm">
<InputLeftElement
children={
<ArrowBackIcon path="" fontSize="md" color="gray.300" />
}
/>
<Input
placeholder="left"
size="sm"
type="text"
name={ATTRIBUTES[type].left}
value={left || ''}
onChange={setValueFromEvent}
autoComplete="off"
/>
</InputGroup>
<InputGroup size="sm">
<InputLeftElement
children={
<ArrowForwardIcon path="" fontSize="md" color="gray.300" />
}
/>
<Input
placeholder="right"
size="sm"
type="text"
value={right || ''}
name={ATTRIBUTES[type].right}
onChange={setValueFromEvent}
autoComplete="off"
/>
</InputGroup>
<InputGroup size="sm">
<InputLeftElement
children={<ArrowUpIcon path="" fontSize="md" color="gray.300" />}
/>
<Input
placeholder="top"
size="sm"
type="text"
value={top || ''}
name={ATTRIBUTES[type].top}
onChange={setValueFromEvent}
autoComplete="off"
/>
</InputGroup>
<InputGroup size="sm">
<InputLeftElement
children={
<ChevronDownIcon path="" fontSize="md" color="gray.300" />
}
/>
<Input
placeholder="bottom"
size="sm"
type="text"
value={bottom || ''}
name={ATTRIBUTES[type].bottom}
onChange={setValueFromEvent}
autoComplete="off"
/>
</InputGroup>
</SimpleGrid>
</FormControl>
</Box>
)
}
Example #19
Source File: NameControls.tsx From dope-monorepo with GNU General Public License v3.0 | 4 votes |
NameControls = ({
config,
setHustlerConfig,
enableNameVisible = false,
}: ConfigureHustlerProps & { enableNameVisible?: boolean }) => {
const [errorName, setErrorName] = useState<string | undefined>(undefined);
const [hustlerName, setHustlerName] = useState(config.name ?? '');
const [nameFieldDirty, setNameFieldDirty] = useState(false);
const debouncedHustlerName = useDebounce<string>(hustlerName, 250);
const validateName = (value: string) => {
if (!value) {
setErrorName('Name is required');
} else if (value.length > NAME_MAX_LENGTH) {
setErrorName('Name too long');
}
};
useEffect(() => {
// Set from typing
if (nameFieldDirty && config.name !== debouncedHustlerName) {
setHustlerConfig({ ...config, name: debouncedHustlerName });
setNameFieldDirty(false);
// Set from randomize or external change
} else if (!nameFieldDirty && config.name !== debouncedHustlerName) {
setHustlerName(config.name ?? '');
}
}, [debouncedHustlerName, config, nameFieldDirty, setHustlerConfig]);
return (
<div>
<Stack spacing={FIELD_SPACING} borderBottom="1px solid #EFEFEF" paddingBottom="16px">
<FormLabel htmlFor="name" color="#000" padding="0" fontSize="14px">
Name
</FormLabel>
<HStack
display="flex"
alignItems="center"
css={css`
margin-top: unset !important;
`}
>
<FormControl width="70%" mr="27px">
<Input
id="name"
placeholder="name"
maxLength={NAME_MAX_LENGTH}
value={hustlerName}
border="2px"
fontSize="14px"
paddingX="14px"
paddingY="10px"
rounded="unset"
onChange={e => {
setNameFieldDirty(true);
validateName(e.target.value);
setHustlerName(e.currentTarget.value);
}}
/>
{errorName && <FormErrorMessage>{errorName}</FormErrorMessage>}
</FormControl>
<FormControl display="flex" alignItems="center" width="30%">
{enableNameVisible && (
<>
<Checkbox
id="render-name"
isChecked={config.renderName ?? false}
onChange={e => setHustlerConfig({ ...config, renderName: e.target.checked })}
colorScheme="whiteAlpha"
iconColor="black"
borderRadius="10px"
sx={{
borderColor: '#000',
'[data-checked][data-hover]': {
borderColor: '#000',
},
'[data-checked]': {
borderColor: '#000 !important',
},
}}
/>
<FormLabel htmlFor="render-name" ml="2" mt="2" color="#000">
Visible
</FormLabel>
</>
)}
</FormControl>
</HStack>
</Stack>
</div>
);
}
Example #20
Source File: Header.tsx From openchakra with MIT License | 4 votes |
Header = () => {
const showLayout = useSelector(getShowLayout)
const showCode = useSelector(getShowCode)
const dispatch = useDispatch()
return (
<DarkMode>
<Flex
justifyContent="space-between"
bg="#1a202c"
as="header"
height="3rem"
px="1rem"
>
<Flex
width="14rem"
height="100%"
backgroundColor="#1a202c"
color="white"
as="a"
fontSize="xl"
flexDirection="row"
alignItems="center"
aria-label="Chakra UI, Back to homepage"
>
<Box fontSize="2xl" as={AiFillThunderbolt} mr={1} color="teal.100" />{' '}
<Box fontWeight="bold">open</Box>chakra
</Flex>
<Flex flexGrow={1} justifyContent="space-between" alignItems="center">
<HStack spacing={4} justify="center" align="center">
<Box>
<HeaderMenu />
</Box>
<FormControl flexDirection="row" display="flex" alignItems="center">
<Tooltip
zIndex={100}
hasArrow
bg="yellow.100"
aria-label="Builder mode help"
label="Builder mode adds extra padding/borders"
>
<FormLabel
cursor="help"
color="gray.200"
fontSize="xs"
htmlFor="preview"
pb={0}
mb={0}
mr={2}
whiteSpace="nowrap"
>
Builder mode
</FormLabel>
</Tooltip>
<LightMode>
<Switch
isChecked={showLayout}
colorScheme="teal"
size="sm"
onChange={() => dispatch.app.toggleBuilderMode()}
id="preview"
/>
</LightMode>
</FormControl>
<FormControl display="flex" flexDirection="row" alignItems="center">
<FormLabel
color="gray.200"
fontSize="xs"
mr={2}
mb={0}
htmlFor="code"
pb={0}
whiteSpace="nowrap"
>
Code panel
</FormLabel>
<LightMode>
<Switch
isChecked={showCode}
id="code"
colorScheme="teal"
onChange={() => dispatch.app.toggleCodePanel()}
size="sm"
/>
</LightMode>
</FormControl>
</HStack>
<Stack direction="row">
<CodeSandboxButton />
<Popover>
{({ onClose }) => (
<>
<PopoverTrigger>
<Button
ml={4}
rightIcon={<SmallCloseIcon path="" />}
size="xs"
variant="ghost"
>
Clear
</Button>
</PopoverTrigger>
<LightMode>
<PopoverContent zIndex={100} bg="white">
<PopoverArrow />
<PopoverCloseButton />
<PopoverHeader>Are you sure?</PopoverHeader>
<PopoverBody fontSize="sm">
Do you really want to remove all components on the
editor?
</PopoverBody>
<PopoverFooter display="flex" justifyContent="flex-end">
<Button
size="sm"
variant="ghost"
colorScheme="red"
rightIcon={<CheckIcon path="" />}
onClick={() => {
dispatch.components.reset()
if (onClose) {
onClose()
}
}}
>
Yes, clear
</Button>
</PopoverFooter>
</PopoverContent>
</LightMode>
</>
)}
</Popover>
</Stack>
</Flex>
<Stack
justifyContent="flex-end"
width="13rem"
align="center"
direction="row"
spacing="2"
>
<Link isExternal href="https://github.com/premieroctet/openchakra">
<Box as={DiGithubBadge} size={32} color="gray.200" />
</Link>
<Box lineHeight="shorter" color="white" fontSize="xs">
by{' '}
<Link isExternal href="https://premieroctet.com" color="teal.100">
Premier Octet
</Link>
</Box>
</Stack>
</Flex>
</DarkMode>
)
}
Example #21
Source File: DynamicDnsDialog.tsx From bluebubbles-server with Apache License 2.0 | 4 votes |
DynamicDnsDialog = ({
onCancel,
onConfirm,
isOpen,
modalRef,
onClose,
port = 1234
}: DynamicDnsDialogProps): JSX.Element => {
const [address, setAddress] = useState('');
const [error, setError] = useState('');
const isInvalid = (error ?? '').length > 0;
return (
<AlertDialog
isOpen={isOpen}
leastDestructiveRef={modalRef}
onClose={() => onClose()}
>
<AlertDialogOverlay>
<AlertDialogContent>
<AlertDialogHeader fontSize='lg' fontWeight='bold'>
Set Dynamic DNS
</AlertDialogHeader>
<AlertDialogBody>
<Text>Enter your Dynamic DNS URL, including the schema and port. Here are some examples:</Text>
<br />
<UnorderedList>
<ListItem>http://thequickbrownfox.ddns.net:{port}</ListItem>
<ListItem>http://bluebubbles.no-ip.org:{port}</ListItem>
</UnorderedList>
<br />
<Text>If you plan to use your own custom certificate, please remember to use <strong>"https://"</strong> as your URL scheme</Text>
<br />
<FormControl isInvalid={isInvalid}>
<FormLabel htmlFor='address'>Dynamic DNS</FormLabel>
<Input
id='address'
type='text'
maxWidth="20em"
value={address}
placeholder={`http://<your DNS>:${port}`}
onChange={(e) => {
setError('');
setAddress(e.target.value);
}}
/>
{isInvalid ? (
<FormErrorMessage>{error}</FormErrorMessage>
) : null}
</FormControl>
</AlertDialogBody>
<AlertDialogFooter>
<Button
ref={modalRef as React.LegacyRef<HTMLButtonElement> | undefined}
onClick={() => {
if (onCancel) onCancel();
onClose();
}}
>
Cancel
</Button>
<Button
ml={3}
bg='brand.primary'
ref={modalRef as React.LegacyRef<HTMLButtonElement> | undefined}
onClick={() => {
if (address.length === 0) {
setError('Please enter a Dynamic DNS address!');
return;
}
if (onConfirm) onConfirm(address);
onClose();
}}
>
Save
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialogOverlay>
</AlertDialog>
);
}
Example #22
Source File: index.tsx From ksana.in with Apache License 2.0 | 4 votes |
export function UrlForm({ user, onSuccess }: IUrlFormProps) {
const { showAlert, hideAlert } = useAlertContext()
const [url, setUrl] = useState<string>('')
const [slug, setSlug] = useState<string>('')
const [isCheckPass, setIsCheckPass] = useState<boolean>(false)
const [isDynamic, setIsDynamic] = useState<boolean>(false)
const [errorUrl, setErrorUrl] = useState<boolean | string>(false)
const [errorSlug, setErrorSlug] = useState<boolean | string>(false)
const [loading, setLoading] = useState<boolean>(false)
const handleChangeUrl = (e: ChangeEvent<HTMLInputElement>) => {
const value = e.target.value
setUrl(value)
setErrorUrl('')
}
const handleChangeSlug = (e: ChangeEvent<HTMLInputElement>) => {
const value = e.target.value
setSlug(value)
setErrorSlug('')
}
const handleChangeIsDynamic = (e: ChangeEvent<HTMLInputElement>) => {
const value = e.target.checked
setIsDynamic(value)
}
const resetErrorMessage = () => {
setErrorUrl('')
setErrorSlug('')
}
const checkIsEmpty = () => {
if (url === '') {
setErrorUrl('URL dan slug tidak bisa dikosongkan')
return true
}
if (url.indexOf('http://') === -1 && url.indexOf('https://') === -1) {
setErrorUrl('Pastikan URL dimulai dengan http:// atau https://')
return true
}
if (slug === '') {
setErrorSlug('URL dan slug tidak bisa dikosongkan')
return true
}
return false
}
const checkParamRequired = () => {
const params = url.match(/{param}/g) || []
if (isDynamic && !params.length) {
setErrorUrl('Tautan dinamis membutuhkan teks {param} di dalamnya')
return false
}
if (isDynamic && params.length > 1) {
setErrorUrl('Teks {param} cukup satu saja')
return false
}
return true
}
const handleCheckAvailability = async () => {
setLoading(true)
resetErrorMessage()
const isEmpty = checkIsEmpty()
const hasParam = checkParamRequired()
if (!isEmpty && hasParam) {
const response = await checkSlug({ slug: sanitizeSlug(slug) })
if (response.error) {
setIsCheckPass(true)
resetErrorMessage()
} else {
setErrorSlug(`Slug ${slug} telah digunakan, coba slug lain`)
}
}
setLoading(false)
}
const handleSaveNew = async () => {
setLoading(true)
const isEmpty = checkIsEmpty()
if (!isEmpty) {
const { error: errorInsert } = await saveUrl({
url: url,
slug: sanitizeSlug(slug),
is_dynamic: isDynamic,
userId: user?.id
})
if (!errorInsert) {
showAlert({
title: 'Sukses menyimpan tautan baru',
message: 'Tautan telah disimpan dalam basis data kami, silahkan mulai bagikan',
onClose: () => {
hideAlert()
mutate(apiUrlsGet(user?.id))
setUrl('')
setSlug('')
setIsCheckPass(false)
resetErrorMessage()
onSuccess()
}
})
} else {
showAlert({
title: 'Terjadi galat pada saat berusaha menyimpan data',
message: `Pesan: ${errorInsert.message}`,
onClose: () => {
hideAlert()
setIsCheckPass(false)
resetErrorMessage()
}
})
}
}
setLoading(false)
}
return (
<Box width={{ base: '100%' }}>
<Stack spacing={4} direction={{ base: 'column' }}>
<FormControl id="url" isRequired>
<Input
isRequired
isInvalid={Boolean(errorUrl)}
size="lg"
name="url"
placeholder={'Tautan yang akan dipercantik'}
variant="filled"
value={url}
onChange={handleChangeUrl}
/>
{errorUrl && <FormHelperText color="red.500">Error: {errorUrl}</FormHelperText>}
<FormHelperText>
Membutuhkan tautan dalam bentuk utuh, termasuk awalan https://
</FormHelperText>
{isDynamic && (
<FormHelperText>
Sisipkan teks <code>{'{param}'}</code> pada tautan
</FormHelperText>
)}
</FormControl>
<FormControl display="flex" alignItems="center">
<FormLabel htmlFor="is-dynamic" mb="0" display="flex">
Tautan dinamis{' '}
<Tooltip
label="Kamu bisa membuat tautan dinamis macam: https://mazipan.space/{param}"
placement="bottom"
>
<i>
<HiQuestionMarkCircle />
</i>
</Tooltip>
</FormLabel>
<Switch id="is-dynamic" onChange={handleChangeIsDynamic} />
</FormControl>
<FormControl id="slug" isRequired>
<InputGroup size="lg">
<InputLeftAddon
color={'orange.400'}
fontWeight="bold"
px={2}
children={HOME?.replace('https://', '').replace('http://', '')}
fontSize="xs"
/>
<Input
isRequired
isInvalid={Boolean(errorSlug)}
size="lg"
name="slug"
placeholder={'Slug cantik dambaanmu'}
variant="filled"
value={slug}
onChange={handleChangeSlug}
/>
</InputGroup>
{errorSlug && <FormHelperText color="red.500">Error: {errorSlug}</FormHelperText>}
<FormHelperText>
Hanya diperbolehkan menggunakan huruf, angka, karakter titik dan strip saja
</FormHelperText>
</FormControl>
{isCheckPass ? (
<Button
isLoading={loading}
loadingText="Processing"
size="lg"
px={6}
mt="4"
color={'white'}
bg={'green.400'}
_hover={{
bg: 'green.500'
}}
_focus={{
bg: 'green.500'
}}
onClick={handleSaveNew}
>
Simpan sekarang
</Button>
) : (
<Button
isLoading={loading}
loadingText="Processing"
size="lg"
px={6}
my="4"
color={'white'}
bg={'orange.400'}
_hover={{
bg: 'orange.500'
}}
_focus={{
bg: 'orange.500'
}}
onClick={handleCheckAvailability}
>
Cek ketersediaan
</Button>
)}
</Stack>
</Box>
)
}
Example #23
Source File: Navigation.tsx From bluebubbles-server with Apache License 2.0 | 4 votes |
MobileNav = ({ onOpen, onNotificationOpen, unreadCount, ...rest }: MobileProps) => {
const { colorMode, toggleColorMode } = useColorMode();
return (
<Flex
ml={{ base: 0, md: 60 }}
px={{ base: 4, md: 4 }}
height="20"
alignItems="center"
borderBottomWidth="1px"
borderBottomColor={useColorModeValue('gray.200', 'gray.700')}
justifyContent={{ base: 'space-between', md: 'flex-end' }}
{...rest}
>
<IconButton
display={{ base: 'flex', md: 'none' }}
onClick={onOpen}
variant="outline"
aria-label="open menu"
icon={<FiMenu />}
/>
<Text display={{ base: 'flex', md: 'none' }} fontSize="2xl" fontFamily="monospace" fontWeight="bold">
<img src={logo} className="logo-small" alt="logo" />
</Text>
<HStack spacing={{ base: '0', md: '1' }}>
<Tooltip label="Website Home" aria-label="website-tip">
<Link href="https://bluebubbles.app" style={{ textDecoration: 'none' }} target="_blank">
<IconButton size="lg" variant="ghost" aria-label="website" icon={<AiOutlineHome />} />
</Link>
</Tooltip>
<Tooltip label="BlueBubbles Web" aria-label="website-tip">
<Link href="https://bluebubbles.app/web" style={{ textDecoration: 'none' }} target="_blank">
<IconButton size="lg" variant="ghost" aria-label="bluebubbles web" icon={<FiMessageCircle />} />
</Link>
</Tooltip>
<Tooltip label="Sponsor Us" aria-label="sponsor-tip">
<Link href="https://github.com/sponsors/BlueBubblesApp" style={{ textDecoration: 'none' }} target="_blank">
<IconButton size="lg" variant="ghost" aria-label="donate" icon={<AiOutlineHeart />} />
</Link>
</Tooltip>
<Tooltip label="Support Us" aria-label="donate-tip">
<Link href="https://bluebubbles.app/donate" style={{ textDecoration: 'none' }} target="_blank">
<IconButton size="lg" variant="ghost" aria-label="donate" icon={<MdOutlineAttachMoney />} />
</Link>
</Tooltip>
<Tooltip label="Join our Discord" aria-label="discord-tip">
<Link href="https://discord.gg/yC4wr38" style={{ textDecoration: 'none' }} target="_blank">
<IconButton size="lg" variant="ghost" aria-label="discord" icon={<FaDiscord />} />
</Link>
</Tooltip>
<Tooltip label="Read our Source Code" aria-label="github-tip">
<Link href="https://github.com/BlueBubblesApp" style={{ textDecoration: 'none' }} target="_blank">
<IconButton size="lg" variant="ghost" aria-label="github" icon={<FiGithub />} />
</Link>
</Tooltip>
<Box position='relative' float='left'>
<IconButton
size="lg"
verticalAlign='middle'
zIndex={1}
variant="ghost"
aria-label="notifications"
icon={<FiBell />}
onClick={() => onNotificationOpen()}
/>
{(unreadCount > 0) ? (
<Badge
borderRadius='lg'
variant='solid'
colorScheme='red'
position='absolute'
margin={0}
top={1}
right={1}
zIndex={2}
>{unreadCount}</Badge>
) : null}
</Box>
<Spacer />
<Divider orientation="vertical" width={1} height={15} borderColor='gray' />
<Spacer />
<Spacer />
<Spacer />
<FormControl display='flex' alignItems='center'>
<Box mr={2}><MdOutlineDarkMode size={20} /></Box>
<Switch id='theme-mode-toggle' onChange={toggleColorMode} isChecked={colorMode === 'light'} />
<Box ml={2}><MdOutlineLightMode size={20} /></Box>
</FormControl>
</HStack>
</Flex>
);
}
Example #24
Source File: create-database.tsx From ledokku with MIT License | 4 votes |
CreateDatabase = () => {
const location = useLocation();
const history = useHistory();
const toast = useToast();
const { data: dataDb } = useDatabaseQuery();
const [arrayOfCreateDbLogs, setArrayofCreateDbLogs] = useState<RealTimeLog[]>(
[]
);
const [isTerminalVisible, setIsTerminalVisible] = useState(false);
const [createDatabaseMutation] = useCreateDatabaseMutation();
const [
isDbCreationSuccess,
setIsDbCreationSuccess,
] = useState<DbCreationStatus>();
useCreateDatabaseLogsSubscription({
onSubscriptionData: (data) => {
const logsExist = data.subscriptionData.data?.createDatabaseLogs;
if (logsExist) {
setArrayofCreateDbLogs((currentLogs) => {
return [...currentLogs, logsExist];
});
if (logsExist.type === 'end:success') {
setIsDbCreationSuccess(DbCreationStatus.SUCCESS);
} else if (logsExist.type === 'end:failure') {
setIsDbCreationSuccess(DbCreationStatus.FAILURE);
}
}
},
});
const createDatabaseSchema = yup.object({
type: yup
.string()
.oneOf(['POSTGRESQL', 'MYSQL', 'MONGODB', 'REDIS'])
.required(),
name: yup.string().when('type', (type: DatabaseTypes) => {
return yup
.string()
.required('Database name is required')
.matches(/^[a-z0-9-]+$/)
.test(
'Name already exists',
`You already have created ${type} database with this name`,
(name) =>
!dataDb?.databases.find(
(db) => db.name === name && type === db.type
)
);
}),
});
const [
isDokkuPluginInstalled,
{ data, loading, error: isDokkuPluginInstalledError },
] = useIsPluginInstalledLazyQuery({
// we poll every 5 sec
pollInterval: 5000,
});
const formik = useFormik<{ name: string; type: DatabaseTypes }>({
initialValues: {
name: location.state ? (location.state as string) : '',
type: 'POSTGRESQL',
},
validateOnChange: true,
validationSchema: createDatabaseSchema,
onSubmit: async (values) => {
try {
await createDatabaseMutation({
variables: {
input: { name: values.name, type: values.type },
},
});
setIsTerminalVisible(true);
trackGoal(trackingGoals.createDatabase, 0);
} catch (error) {
toast.error(error.message);
}
},
});
const isPluginInstalled = data?.isPluginInstalled.isPluginInstalled;
const handleNext = () => {
setIsTerminalVisible(false);
const dbId = arrayOfCreateDbLogs[arrayOfCreateDbLogs.length - 1].message;
history.push(`database/${dbId}`);
};
// Effect for checking whether plugin is installed
useEffect(() => {
isDokkuPluginInstalled({
variables: {
pluginName: dbTypeToDokkuPlugin(formik.values.type),
},
});
}, [formik.values.type, isPluginInstalled, isDokkuPluginInstalled]);
// Effect for db creation
useEffect(() => {
isDbCreationSuccess === DbCreationStatus.FAILURE
? toast.error('Failed to create database')
: isDbCreationSuccess === DbCreationStatus.SUCCESS &&
toast.success('Database created successfully');
}, [isDbCreationSuccess, toast]);
return (
<>
<HeaderContainer>
<Header />
</HeaderContainer>
<Container maxW="5xl" my="4">
<Heading as="h2" size="md">
Create a new database
</Heading>
<Box mt="12">
{isTerminalVisible ? (
<>
<Text mb="2">
Creating <b>{formik.values.type}</b> database{' '}
<b>{formik.values.name}</b>
</Text>
<Text mb="2" color="gray.500">
Creating database usually takes a couple of minutes. Breathe in,
breathe out, logs are about to appear below:
</Text>
<Terminal>
{arrayOfCreateDbLogs.map((log) => (
<Text key={arrayOfCreateDbLogs.indexOf(log)} size="small">
{log.message}
</Text>
))}
</Terminal>
{!!isDbCreationSuccess &&
isDbCreationSuccess === DbCreationStatus.SUCCESS ? (
<Box mt="12" display="flex" justifyContent="flex-end">
<Button
onClick={() => handleNext()}
rightIcon={<FiArrowRight size={20} />}
>
Next
</Button>
</Box>
) : !!isDbCreationSuccess &&
isDbCreationSuccess === DbCreationStatus.FAILURE ? (
<Box mt="12" display="flex" justifyContent="flex-end">
<Button
onClick={() => {
setIsTerminalVisible(false);
formik.resetForm();
}}
rightIcon={<FiArrowLeft size={20} />}
>
Back
</Button>
</Box>
) : null}
</>
) : (
<Box mt="8">
<form onSubmit={formik.handleSubmit}>
<Box mt="12">
{loading && (
<Center>
<Spinner />
</Center>
)}
{isDokkuPluginInstalledError ? (
<Alert
status="error"
variant="top-accent"
flexDirection="column"
alignItems="flex-start"
borderBottomRadius="base"
boxShadow="md"
>
<AlertTitle mr={2}>Request failed</AlertTitle>
<AlertDescription>
{isDokkuPluginInstalledError.message}
</AlertDescription>
</Alert>
) : null}
{data?.isPluginInstalled.isPluginInstalled === false &&
!loading && (
<>
<Text mt="3">
Before creating a{' '}
<b>{formik.values.type.toLowerCase()}</b> database,
you will need to run this command on your dokku
server.
</Text>
<Terminal>{`sudo dokku plugin:install https://github.com/dokku/dokku-${dbTypeToDokkuPlugin(
formik.values.type
)}.git ${dbTypeToDokkuPlugin(
formik.values.type
)}`}</Terminal>
<Text mt="3">
Couple of seconds later you will be able to proceed
further.
</Text>
</>
)}
{data?.isPluginInstalled.isPluginInstalled === true &&
!loading && (
<SimpleGrid columns={{ sm: 1, md: 3 }}>
<FormControl
id="name"
isInvalid={Boolean(
formik.errors.name && formik.touched.name
)}
>
<FormLabel>Database name</FormLabel>
<Input
autoComplete="off"
id="name"
name="name"
value={formik.values.name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
<FormErrorMessage>
{formik.errors.name}
</FormErrorMessage>
</FormControl>
</SimpleGrid>
)}
</Box>
<Box mt="12">
<Text mb="2">Choose your database</Text>
<Grid
templateColumns={{
base: 'repeat(2, minmax(0, 1fr))',
md: 'repeat(4, minmax(0, 1fr))',
}}
gap="4"
>
<DatabaseBox
selected={formik.values.type === 'POSTGRESQL'}
label="PostgreSQL"
icon={<PostgreSQLIcon size={40} />}
onClick={() => formik.setFieldValue('type', 'POSTGRESQL')}
/>
<DatabaseBox
selected={formik.values.type === 'MYSQL'}
label="MySQL"
icon={<MySQLIcon size={40} />}
onClick={() => formik.setFieldValue('type', 'MYSQL')}
/>
<DatabaseBox
selected={formik.values.type === 'MONGODB'}
label="Mongo"
icon={<MongoIcon size={40} />}
onClick={() => formik.setFieldValue('type', 'MONGODB')}
/>
<DatabaseBox
selected={formik.values.type === 'REDIS'}
label="Redis"
icon={<RedisIcon size={40} />}
onClick={() => formik.setFieldValue('type', 'REDIS')}
/>
</Grid>
</Box>
<Box mt="12" display="flex" justifyContent="flex-end">
<Button
isLoading={formik.isSubmitting}
disabled={
data?.isPluginInstalled.isPluginInstalled === false ||
!formik.values.name ||
!!formik.errors.name ||
!dataDb?.databases
}
rightIcon={<FiArrowRight size={20} />}
type="submit"
>
Create
</Button>
</Box>
</form>
</Box>
)}
</Box>
</Container>
</>
);
}
Example #25
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>
);
}