diff --git a/client/src/components/Filters/Check/index.tsx b/client/src/components/Filters/Check/index.tsx index 32024397..996d6b75 100644 --- a/client/src/components/Filters/Check/index.tsx +++ b/client/src/components/Filters/Check/index.tsx @@ -48,6 +48,7 @@ const Check = ({ onSubmit }: Props) => { { {t('check')} diff --git a/client/src/components/Filters/FiltersList.tsx b/client/src/components/Filters/FiltersList.tsx index fb9bedb7..ca0d1fbb 100644 --- a/client/src/components/Filters/FiltersList.tsx +++ b/client/src/components/Filters/FiltersList.tsx @@ -74,7 +74,12 @@ export const FiltersList = ({ categories, filters, selectedSources }: Props) => name={id} control={control} render={({ field }) => ( - + )} /> {renderIcons(iconsData)} diff --git a/client/src/components/Filters/Form.tsx b/client/src/components/Filters/Form.tsx index 82a61df9..9641cfd4 100644 --- a/client/src/components/Filters/Form.tsx +++ b/client/src/components/Filters/Form.tsx @@ -15,13 +15,13 @@ type FormValues = { }; type Props = { - closeModal: (...args: unknown[]) => void; + closeModal: () => void; onSubmit: (values: FormValues) => void; processingAddFilter: boolean; processingConfigFilter: boolean; whitelist?: boolean; modalType: string; - toggleFilteringModal: (...args: unknown[]) => void; + toggleFilteringModal: ({ type }: { type?: keyof typeof MODAL_TYPE }) => void; selectedSources?: Record; initialValues?: FormValues; }; @@ -42,14 +42,14 @@ export const Form = ({ const methods = useForm({ defaultValues: initialValues }); const { handleSubmit, control } = methods; - const openModal = (modalType: any, timeout = MODAL_OPEN_TIMEOUT) => { - toggleFilteringModal(); + const openModal = (modalType: keyof typeof MODAL_TYPE, timeout = MODAL_OPEN_TIMEOUT) => { + toggleFilteringModal(undefined); setTimeout(() => toggleFilteringModal({ type: modalType }), timeout); }; - const openFilteringListModal = () => openModal(MODAL_TYPE.CHOOSE_FILTERING_LIST); + const openFilteringListModal = () => openModal('CHOOSE_FILTERING_LIST'); - const openAddFiltersModal = () => openModal(MODAL_TYPE.ADD_FILTERS); + const openAddFiltersModal = () => openModal('ADD_FILTERS'); return ( @@ -81,8 +81,15 @@ export const Form = ({ ( - + render={({ field, fieldState }) => ( + )} /> @@ -92,11 +99,13 @@ export const Form = ({ name="url" control={control} rules={{ validate: { validateRequiredValue, validatePath } }} - render={({ field }) => ( + render={({ field, fieldState }) => ( )} @@ -118,6 +127,7 @@ export const Form = ({ {modalType !== MODAL_TYPE.SELECT_MODAL_TYPE && ( {t('save_btn')} diff --git a/client/src/components/Filters/Rewrites/Form.tsx b/client/src/components/Filters/Rewrites/Form.tsx index b8b30602..2fd06dcb 100644 --- a/client/src/components/Filters/Rewrites/Form.tsx +++ b/client/src/components/Filters/Rewrites/Form.tsx @@ -5,16 +5,16 @@ import { Trans, useTranslation } from 'react-i18next'; import { validateAnswer, validateDomain, validateRequiredValue } from '../../../helpers/validators'; import { Input } from '../../ui/Controls/Input'; -interface FormValues { +interface RewriteFormValues { domain: string; answer: string; } type Props = { processingAdd: boolean; - currentRewrite?: { answer: string; domain: string }; + currentRewrite?: RewriteFormValues; toggleRewritesModal: () => void; - onSubmit?: (data: FormValues) => Promise | void; + onSubmit?: (data: RewriteFormValues) => Promise | void; }; const Form = ({ processingAdd, currentRewrite, toggleRewritesModal, onSubmit }: Props) => { @@ -25,7 +25,7 @@ const Form = ({ processingAdd, currentRewrite, toggleRewritesModal, onSubmit }: reset, control, formState: { isDirty, isSubmitting }, - } = useForm({ + } = useForm({ mode: 'onBlur', defaultValues: { domain: currentRewrite?.domain || '', @@ -33,7 +33,7 @@ const Form = ({ processingAdd, currentRewrite, toggleRewritesModal, onSubmit }: }, }); - const handleFormSubmit = async (data: FormValues) => { + const handleFormSubmit = async (data: RewriteFormValues) => { if (onSubmit) { await onSubmit(data); } @@ -59,6 +59,7 @@ const Form = ({ processingAdd, currentRewrite, toggleRewritesModal, onSubmit }: @@ -91,6 +92,7 @@ const Form = ({ processingAdd, currentRewrite, toggleRewritesModal, onSubmit }: @@ -111,6 +113,7 @@ const Form = ({ processingAdd, currentRewrite, toggleRewritesModal, onSubmit }: { @@ -122,6 +125,7 @@ const Form = ({ processingAdd, currentRewrite, toggleRewritesModal, onSubmit }: save_btn diff --git a/client/src/components/Filters/Services/Form.tsx b/client/src/components/Filters/Services/Form.tsx index c8938343..b49183a4 100644 --- a/client/src/components/Filters/Services/Form.tsx +++ b/client/src/components/Filters/Services/Form.tsx @@ -46,6 +46,7 @@ export const Form = ({ initialValues, blockedServices, processing, processingSet handleToggleAllServices(true)}> @@ -56,6 +57,7 @@ export const Form = ({ initialValues, blockedServices, processing, processingSet handleToggleAllServices(false)}> @@ -65,7 +67,7 @@ export const Form = ({ initialValues, blockedServices, processing, processingSet - {blockedServices.map((service: any) => ( + {blockedServices.map((service: BlockedService) => ( ( save_btn diff --git a/client/src/components/Filters/Services/ServiceField.tsx b/client/src/components/Filters/Services/ServiceField.tsx index 1c09f5fe..7e6c7aee 100644 --- a/client/src/components/Filters/Services/ServiceField.tsx +++ b/client/src/components/Filters/Services/ServiceField.tsx @@ -11,7 +11,7 @@ type Props = ControllerRenderProps & { }; export const ServiceField = React.forwardRef( - ({ name, value, onChange, onBlur, placeholder, disabled, className, icon, error }, ref) => ( + ({ name, value, onChange, onBlur, placeholder, disabled, className, icon, error, ...rest }, ref) => ( <> ( onBlur={onBlur} ref={ref} disabled={disabled} + {...rest} /> diff --git a/client/src/components/Settings/Clients/Form/components/BlockedServices.tsx b/client/src/components/Settings/Clients/Form/components/BlockedServices.tsx index 945f7c4e..001ce0fa 100644 --- a/client/src/components/Settings/Clients/Form/components/BlockedServices.tsx +++ b/client/src/components/Settings/Clients/Form/components/BlockedServices.tsx @@ -28,6 +28,7 @@ export const BlockedServices = ({ services }: Props) => { render={({ field }) => ( @@ -38,6 +39,7 @@ export const BlockedServices = ({ services }: Props) => { handleToggleAllServices(true)}> @@ -48,6 +50,7 @@ export const BlockedServices = ({ services }: Props) => { handleToggleAllServices(false)}> @@ -65,6 +68,7 @@ export const BlockedServices = ({ services }: Props) => { render={({ field }) => ( { { @@ -43,6 +44,7 @@ export const ClientIds = () => { remove(index)}> @@ -59,6 +61,7 @@ export const ClientIds = () => { ))} append({ name: '' })} title={t('form_add_id')}> diff --git a/client/src/components/Settings/Clients/Form/components/MainSettings.tsx b/client/src/components/Settings/Clients/Form/components/MainSettings.tsx index c8c03cf4..258ff677 100644 --- a/client/src/components/Settings/Clients/Form/components/MainSettings.tsx +++ b/client/src/components/Settings/Clients/Form/components/MainSettings.tsx @@ -64,6 +64,7 @@ export const MainSettings = ({ safeSearchServices }: Props) => { render={({ field }) => ( @@ -77,7 +78,12 @@ export const MainSettings = ({ safeSearchServices }: Props) => { name="safe_search.enabled" control={control} render={({ field }) => ( - + )} /> @@ -89,7 +95,12 @@ export const MainSettings = ({ safeSearchServices }: Props) => { name={`safe_search.${searchKey}`} control={control} render={({ field }) => ( - + )} /> @@ -104,7 +115,9 @@ export const MainSettings = ({ safeSearchServices }: Props) => { } + render={({ field }) => ( + + )} /> ))} diff --git a/client/src/components/Settings/Clients/Form/components/UpstreamDns.tsx b/client/src/components/Settings/Clients/Form/components/UpstreamDns.tsx index feb26bfc..2a22da2d 100644 --- a/client/src/components/Settings/Clients/Form/components/UpstreamDns.tsx +++ b/client/src/components/Settings/Clients/Form/components/UpstreamDns.tsx @@ -8,7 +8,7 @@ import { Textarea } from '../../../../ui/Controls/Textarea'; import { ClientForm } from '../types'; import { Checkbox } from '../../../../ui/Controls/Checkbox'; import { Input } from '../../../../ui/Controls/Input'; -import { trimLinesAndRemoveEmpty } from '../../../../../helpers/helpers'; +import { toNumber } from '../../../../../helpers/form'; export const UpstreamDns = () => { const { t } = useTranslation(); @@ -18,14 +18,7 @@ export const UpstreamDns = () => { return ( - - link - , - ]}> - upstream_dns_client_desc - + ]}>upstream_dns_client_desc { render={({ field }) => ( { - const normalizedValue = trimLinesAndRemoveEmpty(event.target.value); - field.onBlur(); - field.onChange(normalizedValue); - }} + trimOnBlur /> )} /> @@ -53,7 +43,13 @@ export const UpstreamDns = () => { } + render={({ field }) => ( + + )} /> @@ -69,10 +65,15 @@ export const UpstreamDns = () => { { + const { value } = e.target; + field.onChange(toNumber(value)); + }} /> )} /> diff --git a/client/src/components/Settings/Clients/Form/index.tsx b/client/src/components/Settings/Clients/Form/index.tsx index e5195cd2..de5cc7b3 100644 --- a/client/src/components/Settings/Clients/Form/index.tsx +++ b/client/src/components/Settings/Clients/Form/index.tsx @@ -70,7 +70,6 @@ export const Form = ({ handleSubmit, reset, control, - setValue, formState: { isSubmitting, isValid }, } = methods; @@ -116,6 +115,7 @@ export const Form = ({ { @@ -155,13 +155,11 @@ export const Form = ({ render={({ field }) => ( { - setValue('tags', selectedOptions); - }} /> )} /> diff --git a/client/src/components/Settings/Encryption/Form.tsx b/client/src/components/Settings/Encryption/Form.tsx index 5121430a..5b76858c 100644 --- a/client/src/components/Settings/Encryption/Form.tsx +++ b/client/src/components/Settings/Encryption/Form.tsx @@ -27,6 +27,7 @@ import { Radio } from '../../ui/Controls/Radio'; import { Input } from '../../ui/Controls/Input'; import { Textarea } from '../../ui/Controls/Textarea'; import { EncryptionData } from '../../../initialState'; +import { toNumber } from '../../../helpers/form'; const certificateSourceOptions = [ { @@ -335,6 +336,10 @@ export const Form = ({ placeholder={t('encryption_https')} error={fieldState.error?.message} disabled={!isEnabled} + onChange={(e) => { + const { value } = e.target; + field.onChange(toNumber(value)); + }} /> )} /> @@ -362,6 +367,10 @@ export const Form = ({ placeholder={t('encryption_dot')} error={fieldState.error?.message} disabled={!isEnabled} + onChange={(e) => { + const { value } = e.target; + field.onChange(toNumber(value)); + }} /> )} /> @@ -389,6 +398,10 @@ export const Form = ({ placeholder={t('encryption_doq')} error={fieldState.error?.message} disabled={!isEnabled} + onChange={(e) => { + const { value } = e.target; + field.onChange(toNumber(value)); + }} /> )} /> diff --git a/client/src/components/Settings/StatsConfig/Form.tsx b/client/src/components/Settings/StatsConfig/Form.tsx index 312fbc65..b7c8b690 100644 --- a/client/src/components/Settings/StatsConfig/Form.tsx +++ b/client/src/components/Settings/StatsConfig/Form.tsx @@ -5,7 +5,6 @@ import i18next from 'i18next'; import { Controller, useForm } from 'react-hook-form'; import { STATS_INTERVALS_DAYS, DAY, RETENTION_CUSTOM, RETENTION_RANGE } from '../../../helpers/constants'; -import { trimLinesAndRemoveEmpty } from '../../../helpers/helpers'; import '../FormButton.css'; import { Checkbox } from '../../ui/Controls/Checkbox'; import { Input } from '../../ui/Controls/Input'; @@ -75,11 +74,6 @@ export const Form = ({ initialValues, processing, processingReset, onSubmit, onR onSubmit(data); }; - const handleIgnoredBlur = (e: React.FocusEvent) => { - const trimmed = trimLinesAndRemoveEmpty(e.target.value); - setValue('ignored', trimmed); - }; - const disableSubmit = isSubmitting || processing || (intervalValue === RETENTION_CUSTOM && !customIntervalValue); return ( @@ -179,7 +173,7 @@ export const Form = ({ initialValues, processing, processingReset, onSubmit, onR className="text-input" disabled={processing} error={fieldState.error?.message} - onBlur={handleIgnoredBlur} + trimOnBlur /> )} /> diff --git a/client/src/components/ui/Controls/Checkbox/index.tsx b/client/src/components/ui/Controls/Checkbox/index.tsx index dc496ca3..32a97fba 100644 --- a/client/src/components/ui/Controls/Checkbox/index.tsx +++ b/client/src/components/ui/Controls/Checkbox/index.tsx @@ -15,7 +15,7 @@ type Props = { }; export const Checkbox = forwardRef( - ({ title, subtitle, value, name, disabled, error, className = 'checkbox--form', onChange }, ref) => ( + ({ title, subtitle, value, name, disabled, error, className = 'checkbox--form', onChange, ...rest }, ref) => ( <> @@ -27,6 +27,7 @@ export const Checkbox = forwardRef( checked={value} onChange={(e) => onChange(e.target.checked)} ref={ref} + {...rest} /> diff --git a/client/src/components/ui/Controls/Textarea.tsx b/client/src/components/ui/Controls/Textarea.tsx index 4ceb1390..9a1c0d4d 100644 --- a/client/src/components/ui/Controls/Textarea.tsx +++ b/client/src/components/ui/Controls/Textarea.tsx @@ -1,29 +1,42 @@ import React, { ComponentProps, forwardRef } from 'react'; import clsx from 'clsx'; +import { trimLinesAndRemoveEmpty } from '../../../helpers/helpers'; type Props = ComponentProps<'textarea'> & { className?: string; label?: string; error?: string; + trimOnBlur?: boolean; }; -export const Textarea = forwardRef(({ name, label, className, error, ...rest }, ref) => ( - - {label && ( - - {label} - - )} - ( + ({ name, label, className, error, trimOnBlur, onBlur, ...rest }, ref) => ( + + {label && ( + + {label} + )} - ref={ref} - {...rest} - /> - {error && {error}} - -)); + { + if (trimOnBlur) { + const normalizedValue = trimLinesAndRemoveEmpty(e.target.value); + rest.onChange(normalizedValue); + } + if (onBlur) { + onBlur(e); + } + }} + {...rest} + /> + {error && {error}} + + ), +); Textarea.displayName = 'Textarea'; diff --git a/client/src/components/ui/Guide/MobileConfigForm.tsx b/client/src/components/ui/Guide/MobileConfigForm.tsx index 07e75aa8..b9610f5e 100644 --- a/client/src/components/ui/Guide/MobileConfigForm.tsx +++ b/client/src/components/ui/Guide/MobileConfigForm.tsx @@ -14,6 +14,7 @@ import { validateIsSafePort, } from '../../../helpers/validators'; import { Input } from '../Controls/Input'; +import { Select } from '../Controls/Select'; const getDownloadLink = (host: string, clientId: string, protocol: string, invalid: boolean) => { if (!host || invalid) { @@ -62,7 +63,6 @@ export const MobileConfigForm = ({ initialValues }: Props) => { const { t } = useTranslation(); const { - register, watch, control, formState: { isValid }, @@ -101,6 +101,7 @@ export const MobileConfigForm = ({ initialValues }: Props) => { { { @@ -168,14 +171,16 @@ export const MobileConfigForm = ({ initialValues }: Props) => { - - {i18next.t('protocol')} - - - - {i18next.t('dns_over_tls')} - {i18next.t('dns_over_https')} - + ( + + {i18next.t('dns_over_tls')} + {i18next.t('dns_over_https')} + + )} + />