use common checkbox component

This commit is contained in:
Ildar Kamalov
2025-01-17 16:36:39 +03:00
parent 92c004d15d
commit bcf5fb2521
14 changed files with 365 additions and 394 deletions

14
client/package-lock.json generated vendored
View File

@@ -11,6 +11,7 @@
"@nivo/line": "^0.64.0",
"axios": "^0.19.2",
"classnames": "^2.5.1",
"clsx": "^2.1.1",
"countries-and-timezones": "^3.6.0",
"date-fns": "^1.29.0",
"i18next": "^19.6.2",
@@ -6389,6 +6390,14 @@
"node": ">=6"
}
},
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"engines": {
"node": ">=6"
}
},
"node_modules/co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -24834,6 +24843,11 @@
"shallow-clone": "^3.0.0"
}
},
"clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="
},
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",

1
client/package.json vendored
View File

@@ -24,6 +24,7 @@
"@nivo/line": "^0.64.0",
"axios": "^0.19.2",
"classnames": "^2.5.1",
"clsx": "^2.1.1",
"countries-and-timezones": "^3.6.0",
"date-fns": "^1.29.0",
"i18next": "^19.6.2",

View File

@@ -1,7 +1,8 @@
import React from 'react';
import classNames from 'classnames';
import { useFormContext } from 'react-hook-form';
import { Controller, useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { Checkbox } from '../ui/Controls/Checkbox';
const getIconsData = (homepage: string, source: string) => [
{
@@ -49,7 +50,7 @@ type Props = {
export const FiltersList = ({ categories, filters, selectedSources }: Props) => {
const { t } = useTranslation();
const { register } = useFormContext();
const { control } = useFormContext();
return (
<>
@@ -69,21 +70,19 @@ export const FiltersList = ({ categories, filters, selectedSources }: Props) =>
return (
<div key={name} className="d-flex align-items-center pb-1">
<label className="checkbox checkbox--settings">
<span className="checkbox__marker" />
<input
id={id}
type="checkbox"
className="checkbox__input"
disabled={isSelected}
{...register(id)}
/>
<span className="checkbox__label">
<span className="checkbox__label-text">
<span className="checkbox__label-title">{t(name)}</span>
</span>
</span>
</label>
<Controller
name={id}
control={control}
render={({ field: { value, onChange } }) => (
<Checkbox
name={id}
title={name}
value={value}
onChange={(value) => onChange(value)}
disabled={isSelected}
/>
)}
/>
{renderIcons(iconsData)}
</div>
);

View File

@@ -107,8 +107,6 @@ class Modal extends Component<ModalProps> {
const title = t(getTitle(modalType, whitelist));
console.log(modalType, initialValues);
return (
<ReactModal
className="Modal__Bootstrap modal-dialog modal-dialog-centered"

View File

@@ -1,27 +1,28 @@
import React from 'react';
import { useForm } from 'react-hook-form';
import { Controller, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import {
validateIp,
validateIpv4,
validateIpv6,
validateRequiredValue
} from '../../../../helpers/validators';
import i18next from 'i18next';
import { validateIp, validateIpv4, validateIpv6, validateRequiredValue } from '../../../../helpers/validators';
import { BLOCKING_MODES, UINT32_RANGE } from '../../../../helpers/constants';
import { removeEmptyLines } from '../../../../helpers/helpers';
import { Checkbox } from '../../../ui/Controls/Checkbox';
const checkboxes = [
const checkboxes: {
name: 'dnssec_enabled' | 'disable_ipv6';
placeholder: string;
subtitle: string;
}[] = [
{
name: 'dnssec_enabled',
placeholder: 'dnssec_enable',
subtitle: 'dnssec_enable_desc',
placeholder: i18next.t('dnssec_enable'),
subtitle: i18next.t('dnssec_enable_desc'),
},
{
name: 'disable_ipv6',
placeholder: 'disable_ipv6',
subtitle: 'disable_ipv6_desc',
placeholder: i18next.t('disable_ipv6'),
subtitle: i18next.t('disable_ipv6_desc'),
},
];
@@ -52,13 +53,13 @@ type FormData = {
blocking_ipv4?: string;
blocking_ipv6?: string;
blocked_response_ttl: number;
}
};
type Props = {
processing?: boolean;
initialValues?: Partial<FormData>;
onSubmit: (data: FormData) => void;
}
};
const Form = ({ processing, initialValues, onSubmit }: Props) => {
const { t } = useTranslation();
@@ -67,6 +68,7 @@ const Form = ({ processing, initialValues, onSubmit }: Props) => {
register,
handleSubmit,
watch,
control,
formState: { errors, isSubmitting, isDirty },
} = useForm<FormData>({
mode: 'onChange',
@@ -86,9 +88,7 @@ const Form = ({ processing, initialValues, onSubmit }: Props) => {
{t('rate_limit')}
</label>
<div className="form__desc form__desc--top">
{t('rate_limit_desc')}
</div>
<div className="form__desc form__desc--top">{t('rate_limit_desc')}</div>
<input
id="ratelimit"
@@ -103,9 +103,7 @@ const Form = ({ processing, initialValues, onSubmit }: Props) => {
})}
/>
{errors.ratelimit && (
<div className="form__message form__message--error">
{errors.ratelimit.message}
</div>
<div className="form__message form__message--error">{errors.ratelimit.message}</div>
)}
</div>
</div>
@@ -116,9 +114,7 @@ const Form = ({ processing, initialValues, onSubmit }: Props) => {
{t('rate_limit_subnet_len_ipv4')}
</label>
<div className="form__desc form__desc--top">
{t('rate_limit_subnet_len_ipv4_desc')}
</div>
<div className="form__desc form__desc--top">{t('rate_limit_subnet_len_ipv4_desc')}</div>
<input
id="ratelimit_subnet_len_ipv4"
@@ -146,9 +142,7 @@ const Form = ({ processing, initialValues, onSubmit }: Props) => {
{t('rate_limit_subnet_len_ipv6')}
</label>
<div className="form__desc form__desc--top">
{t('rate_limit_subnet_len_ipv6_desc')}
</div>
<div className="form__desc form__desc--top">{t('rate_limit_subnet_len_ipv6_desc')}</div>
<input
id="ratelimit_subnet_len_ipv6"
@@ -176,9 +170,7 @@ const Form = ({ processing, initialValues, onSubmit }: Props) => {
{t('rate_limit_whitelist')}
</label>
<div className="form__desc form__desc--top">
{t('rate_limit_whitelist_desc')}
</div>
<div className="form__desc form__desc--top">{t('rate_limit_whitelist_desc')}</div>
<textarea
id="ratelimit_whitelist"
@@ -198,41 +190,37 @@ const Form = ({ processing, initialValues, onSubmit }: Props) => {
<div className="col-12">
<div className="form__group form__group--settings">
<label className="checkbox checkbox--settings">
<span className="checkbox__marker" />
<input
id="edns_cs_enabled"
type="checkbox"
className="checkbox__input"
disabled={processing}
{...register('edns_cs_enabled')}
/>
<span className="checkbox__label">
<span className="checkbox__label-text">
<span className="checkbox__label-title">{t('edns_enable')}</span>
</span>
</span>
</label>
<Controller
name="edns_cs_enabled"
control={control}
render={({ field: { name, value, onChange } }) => (
<Checkbox
name={name}
title={t('edns_enable')}
value={value}
onChange={(value) => onChange(value)}
disabled={processing}
/>
)}
/>
</div>
</div>
<div className="col-12 form__group form__group--inner">
<div className="form__group">
<label className="checkbox checkbox--settings">
<span className="checkbox__marker" />
<input
id="edns_cs_use_custom"
type="checkbox"
className="checkbox__input"
disabled={processing || !edns_cs_enabled}
{...register('edns_cs_use_custom')}
/>
<span className="checkbox__label">
<span className="checkbox__label-text">
<span className="checkbox__label-subtitle">{t('edns_use_custom_ip')}</span>
</span>
</span>
</label>
<Controller
name="edns_cs_use_custom"
control={control}
render={({ field: { name, value, onChange } }) => (
<Checkbox
name={name}
title={t('edns_use_custom_ip')}
value={value}
onChange={(value) => onChange(value)}
disabled={processing || !edns_cs_enabled}
/>
)}
/>
</div>
{edns_cs_use_custom && (
@@ -252,42 +240,32 @@ const Form = ({ processing, initialValues, onSubmit }: Props) => {
{checkboxes.map(({ name, placeholder, subtitle }) => (
<div className="col-12" key={name}>
<div className="form__group form__group--settings">
<label className="checkbox checkbox--settings">
<span className="checkbox__marker" />
<input
id={name}
type="checkbox"
className="checkbox__input"
disabled={processing}
{...register(name as keyof FormData)}
/>
<span className="checkbox__label">
<span className="checkbox__label-text">
<span className="checkbox__label-title">{t(placeholder)}</span>
{subtitle && (
<span className="checkbox__label-subtitle">{t(subtitle)}</span>
)}
</span>
</span>
</label>
<Controller
name={name}
control={control}
render={({ field: { name, value, onChange } }) => (
<Checkbox
name={name}
title={placeholder}
subtitle={subtitle}
value={value}
onChange={(value) => onChange(value)}
disabled={processing}
/>
)}
/>
</div>
</div>
))}
<div className="col-12">
<div className="form__group form__group--settings mb-4">
<label className="form__label form__label--with-desc">
{t('blocking_mode')}
</label>
<label className="form__label form__label--with-desc">{t('blocking_mode')}</label>
<div className="form__desc form__desc--top">
{Object.values(BLOCKING_MODES)
.map((mode: any) => (
<li key={mode}>
{t(`blocking_mode_${mode}`)}
</li>
))}
{Object.values(BLOCKING_MODES).map((mode: any) => (
<li key={mode}>{t(`blocking_mode_${mode}`)}</li>
))}
</div>
<div className="custom-controls-stacked">
@@ -315,9 +293,7 @@ const Form = ({ processing, initialValues, onSubmit }: Props) => {
{t(name)}
</label>
<div className="form__desc form__desc--top">
{t(description)}
</div>
<div className="form__desc form__desc--top">{t(description)}</div>
<input
id={name}
@@ -346,9 +322,7 @@ const Form = ({ processing, initialValues, onSubmit }: Props) => {
{t('blocked_response_ttl')}
</label>
<div className="form__desc form__desc--top">
{t('blocked_response_ttl_desc')}
</div>
<div className="form__desc form__desc--top">{t('blocked_response_ttl_desc')}</div>
<input
id="blocked_response_ttl"

View File

@@ -11,6 +11,7 @@ import { getTextareaCommentsHighlight, syncScroll } from '../../../../helpers/hi
import { RootState } from '../../../../initialState';
import '../../../ui/texareaCommentsHighlight.css';
import Examples from './Examples';
import { Checkbox } from '../../../ui/Controls/Checkbox';
const UPSTREAM_DNS_NAME = 'upstream_dns';
const UPSTREAM_MODE_NAME = 'upstream_mode';
@@ -23,12 +24,12 @@ type FormData = {
local_ptr_upstreams: string;
use_private_ptr_resolvers: boolean;
resolve_clients: boolean;
}
};
type FormProps = {
initialValues?: Partial<FormData>;
onSubmit: (data: FormData) => void;
}
};
const INPUT_FIELDS = [
{
@@ -96,37 +97,29 @@ const Form = ({ initialValues, onSubmit }: FormProps) => {
return (
<form onSubmit={handleSubmit(onSubmit)} className="form--upstream">
<div className="row">
<label className="col form__label" htmlFor={UPSTREAM_DNS_NAME}>
<div className="row">
<label className="col form__label" htmlFor={UPSTREAM_DNS_NAME}>
<Trans
components={{
a: <a
href={UPSTREAM_CONFIGURATION_WIKI_LINK}
target="_blank"
rel="noopener noreferrer"
/>,
}}
>
a: <a href={UPSTREAM_CONFIGURATION_WIKI_LINK} target="_blank" rel="noopener noreferrer" />,
}}>
upstream_dns_help
</Trans>
{' '}
</Trans>{' '}
<Trans
components={[
<a
href="https://link.adtidy.org/forward.html?action=dns_kb_providers&from=ui&app=home"
target="_blank"
rel="noopener noreferrer"
key="0"
>
key="0">
DNS providers
</a>
]}
>
</a>,
]}>
dns_providers
</Trans>
</label>
<div className="col-12 mb-4">
</label>
<div className="col-12 mb-4">
<div className="text-edit-container">
<Controller
name="upstream_dns"
@@ -188,9 +181,7 @@ const Form = ({ initialValues, onSubmit }: FormProps) => {
{t('fallback_dns_title')}
</label>
<div className="form__desc form__desc--top">
{t('fallback_dns_desc')}
</div>
<div className="form__desc form__desc--top">{t('fallback_dns_desc')}</div>
<Controller
name="fallback_dns"
@@ -220,9 +211,7 @@ const Form = ({ initialValues, onSubmit }: FormProps) => {
{t('bootstrap_dns')}
</label>
<div className="form__desc form__desc--top">
{t('bootstrap_dns_desc')}
</div>
<div className="form__desc form__desc--top">{t('bootstrap_dns_desc')}</div>
<Controller
name="bootstrap_dns"
@@ -252,13 +241,13 @@ const Form = ({ initialValues, onSubmit }: FormProps) => {
{t('local_ptr_title')}
</label>
<div className="form__desc form__desc--top">
{t('local_ptr_desc')}
</div>
<div className="form__desc form__desc--top">{t('local_ptr_desc')}</div>
<div className="form__desc form__desc--top">
{defaultLocalPtrUpstreams?.length > 0
? t('local_ptr_default_resolver', { ip: defaultLocalPtrUpstreams.map((s: any) => `"${s}"`).join(', ') })
{defaultLocalPtrUpstreams?.length > 0
? t('local_ptr_default_resolver', {
ip: defaultLocalPtrUpstreams.map((s: any) => `"${s}"`).join(', '),
})
: t('local_ptr_no_default_resolver')}
</div>
@@ -284,28 +273,15 @@ const Form = ({ initialValues, onSubmit }: FormProps) => {
<Controller
name="use_private_ptr_resolvers"
control={control}
render={({ field: { value, onChange, ...field } }) => (
<label className="checkbox">
<span className="checkbox__marker" />
<input
{...field}
type="checkbox"
checked={value}
onChange={(e) => onChange(e.target.checked)}
className="checkbox__input"
disabled={processingSetConfig}
/>
<span className="checkbox__label">
<span className="checkbox__label-text checkbox__label-text--long">
<span className="checkbox__label-title">
{t('use_private_ptr_resolvers_title')}
</span>
<span className="checkbox__label-subtitle">
{t('use_private_ptr_resolvers_desc')}
</span>
</span>
</span>
</label>
render={({ field: { name, value, onChange } }) => (
<Checkbox
name={name}
title={t('use_private_ptr_resolvers_title')}
subtitle={t('use_private_ptr_resolvers_desc')}
value={value}
onChange={(value) => onChange(value)}
disabled={processingSetConfig}
/>
)}
/>
</div>
@@ -319,24 +295,15 @@ const Form = ({ initialValues, onSubmit }: FormProps) => {
<Controller
name="resolve_clients"
control={control}
render={({ field: { value, onChange, ...field } }) => (
<label className="checkbox">
<span className="checkbox__marker" />
<input
{...field}
type="checkbox"
checked={value}
onChange={(e) => onChange(e.target.checked)}
className="checkbox__input"
disabled={processingSetConfig}
/>
<span className="checkbox__label">
<span className="checkbox__label-text checkbox__label-text--long">
<span className="checkbox__label-title">{t('resolve_clients_title')}</span>
<span className="checkbox__label-subtitle">{t('resolve_clients_desc')}</span>
</span>
</span>
</label>
render={({ field: { name, value, onChange } }) => (
<Checkbox
name={name}
title={t('resolve_clients_title')}
subtitle={t('resolve_clients_desc')}
value={value}
onChange={(value) => onChange(value)}
disabled={processingSetConfig}
/>
)}
/>
</div>

View File

@@ -1,9 +1,10 @@
import React, { useEffect, useRef } from 'react';
import { useForm } from 'react-hook-form';
import { Controller, useForm } from 'react-hook-form';
import { Trans, useTranslation } from 'react-i18next';
import { toNumber } from '../../../helpers/form';
import { FILTERS_INTERVALS_HOURS, FILTERS_RELATIVE_LINK } from '../../../helpers/constants';
import { Checkbox } from '../../ui/Controls/Checkbox';
const getTitleForInterval = (interval: any, t: any) => {
if (interval === 0) {
@@ -31,7 +32,7 @@ export const FiltersConfig = ({ initialValues, setFiltersConfig, processing }: P
const { t } = useTranslation();
const prevFormValuesRef = useRef<FormValues>(initialValues);
const { register, watch } = useForm({
const { register, watch, control } = useForm({
mode: 'onChange',
defaultValues: initialValues,
});
@@ -56,24 +57,20 @@ export const FiltersConfig = ({ initialValues, setFiltersConfig, processing }: P
<div className="row">
<div className="col-12">
<div className="form__group form__group--settings">
<label className="checkbox">
<span className="checkbox__marker" />
<Controller
name="enabled"
control={control}
render={({ field: { name, value, onChange } }) => (
<Checkbox
name={name}
title={t('block_domain_use_filters_and_hosts')}
value={value}
onChange={(value) => onChange(value)}
disabled={processing}
/>
)}
/>
<input
type="checkbox"
className="checkbox__input"
{...register('enabled')}
disabled={processing}
/>
<span className="checkbox__label">
<span className="checkbox__label-text checkbox__label-text--long">
<span className="checkbox__label-title">
{t('block_domain_use_filters_and_hosts')}
</span>
</span>
</span>
</label>
<p>
<Trans components={components}>filters_block_toggle_hint</Trans>
</p>

View File

@@ -1,7 +1,7 @@
import React, { useEffect } from 'react';
import { Trans } from 'react-i18next';
import { Trans, useTranslation } from 'react-i18next';
import i18next from 'i18next';
import { useForm } from 'react-hook-form';
import { Controller, useForm } from 'react-hook-form';
import { trimLinesAndRemoveEmpty } from '../../../helpers/helpers';
import {
@@ -13,6 +13,7 @@ import {
CUSTOM_INTERVAL,
} from '../../../helpers/constants';
import '../FormButton.css';
import { Checkbox } from '../../ui/Controls/Checkbox';
const getIntervalTitle = (interval: number) => {
switch (interval) {
@@ -33,7 +34,7 @@ export type FormValues = {
interval: number;
customInterval?: number | null;
ignored: string;
}
};
type Props = {
initialValues: Partial<FormValues>;
@@ -41,29 +42,26 @@ type Props = {
processingReset: boolean;
onSubmit: (values: FormValues) => void;
onReset: () => void;
}
};
export const Form = ({ initialValues, processing, processingReset, onSubmit, onReset }: Props) => {
const { t } = useTranslation();
export const Form = ({
initialValues,
processing,
processingReset,
onSubmit,
onReset,
}: Props) => {
const {
register,
handleSubmit,
watch,
setValue,
control,
formState: { isSubmitting },
} = useForm<FormValues>({
} = useForm<FormValues>({
mode: 'onChange',
defaultValues: {
enabled: initialValues.enabled || false,
anonymize_client_ip: initialValues.anonymize_client_ip || false,
interval: initialValues.interval || DAY,
customInterval: initialValues.customInterval || null,
ignored: initialValues.ignored || '',
enabled: initialValues.enabled || false,
anonymize_client_ip: initialValues.anonymize_client_ip || false,
interval: initialValues.interval || DAY,
customInterval: initialValues.customInterval || null,
ignored: initialValues.ignored || '',
},
});
@@ -85,50 +83,41 @@ export const Form = ({
setValue('ignored', trimmed);
};
const disableSubmit =
isSubmitting ||
processing ||
(intervalValue === RETENTION_CUSTOM && !customIntervalValue);
const disableSubmit = isSubmitting || processing || (intervalValue === RETENTION_CUSTOM && !customIntervalValue);
return (
<form onSubmit={handleSubmit(onSubmitForm)}>
<div className="form__group form__group--settings">
<label className="checkbox">
<span className="checkbox__marker" />
<input
type="checkbox"
className="checkbox__input"
{...register('enabled')}
disabled={processing}
/>
<span className="checkbox__label">
<span className="checkbox__label-text checkbox__label-text--long">
<span className="checkbox__label-title">{i18next.t('query_log_enable')}</span>
</span>
</span>
</label>
<Controller
name="enabled"
control={control}
render={({ field: { name, value, onChange } }) => (
<Checkbox
name={name}
title={t('query_log_enable')}
value={value}
onChange={(value) => onChange(value)}
disabled={processing}
/>
)}
/>
</div>
<div className="form__group form__group--settings">
<label className="checkbox">
<span className="checkbox__marker" />
<input
type="checkbox"
className="checkbox__input"
{...register('anonymize_client_ip')}
disabled={processing}
/>
<span className="checkbox__label">
<span className="checkbox__label-text checkbox__label-text--long">
<span className="checkbox__label-title">{i18next.t('anonymize_client_ip')}</span>
<span className="checkbox__label-subtitle">{i18next.t('anonymize_client_ip_desc')}</span>
</span>
</span>
</label>
<Controller
name="anonymize_client_ip"
control={control}
render={({ field: { name, value, onChange } }) => (
<Checkbox
name={name}
title={t('anonymize_client_ip')}
subtitle={t('anonymize_client_ip_desc')}
value={value}
onChange={(value) => onChange(value)}
disabled={processing}
/>
)}
/>
</div>
<div className="form__label">
@@ -180,7 +169,7 @@ export const Form = ({
value={interval}
checked={intervalValue === interval}
onChange={(e) => {
setValue("interval", parseInt(e.target.value, 10));
setValue('interval', parseInt(e.target.value, 10));
}}
/>
@@ -209,11 +198,7 @@ export const Form = ({
</div>
<div className="mt-5">
<button
type="submit"
className="btn btn-success btn-standard btn-large"
disabled={disableSubmit}
>
<button type="submit" className="btn btn-success btn-standard btn-large" disabled={disableSubmit}>
<Trans>save_btn</Trans>
</button>
@@ -221,8 +206,7 @@ export const Form = ({
type="button"
className="btn btn-outline-secondary btn-standard form__button"
onClick={onReset}
disabled={processingReset}
>
disabled={processingReset}>
<Trans>query_log_clear</Trans>
</button>
</div>

View File

@@ -1,8 +1,8 @@
import React, { useEffect } from 'react';
import { Trans } from 'react-i18next';
import { Trans, useTranslation } from 'react-i18next';
import i18next from 'i18next';
import { useForm } from 'react-hook-form';
import { Controller, useForm } from 'react-hook-form';
import {
STATS_INTERVALS_DAYS,
DAY,
@@ -13,6 +13,7 @@ import {
import { trimLinesAndRemoveEmpty } from '../../../helpers/helpers';
import '../FormButton.css';
import { Checkbox } from '../../ui/Controls/Checkbox';
const getIntervalTitle = (interval: any) => {
switch (interval) {
@@ -30,7 +31,7 @@ export type FormValues = {
interval: number;
customInterval?: number | null;
ignored: string;
}
};
type Props = {
initialValues: Partial<FormValues>;
@@ -38,28 +39,25 @@ type Props = {
processingReset: boolean;
onSubmit: (values: FormValues) => void;
onReset: () => void;
}
};
export const Form = ({ initialValues, processing, processingReset, onSubmit, onReset }: Props) => {
const { t } = useTranslation();
export const Form = ({
initialValues,
processing,
processingReset,
onSubmit,
onReset,
}: Props) => {
const {
register,
handleSubmit,
watch,
setValue,
control,
formState: { isSubmitting },
} = useForm<FormValues>({
} = useForm<FormValues>({
mode: 'onChange',
defaultValues: {
enabled: initialValues.enabled || false,
interval: initialValues.interval || DAY,
customInterval: initialValues.customInterval || null,
ignored: initialValues.ignored || '',
enabled: initialValues.enabled || false,
interval: initialValues.interval || DAY,
customInterval: initialValues.customInterval || null,
ignored: initialValues.ignored || '',
},
});
@@ -81,30 +79,24 @@ export const Form = ({
setValue('ignored', trimmed);
};
const disableSubmit =
isSubmitting ||
processing ||
(intervalValue === RETENTION_CUSTOM && !customIntervalValue);
const disableSubmit = isSubmitting || processing || (intervalValue === RETENTION_CUSTOM && !customIntervalValue);
return (
<form onSubmit={handleSubmit(onSubmitForm)}>
<div className="form__group form__group--settings">
<label className="checkbox">
<span className="checkbox__marker" />
<input
type="checkbox"
className="checkbox__input"
{...register('enabled')}
disabled={processing}
/>
<span className="checkbox__label">
<span className="checkbox__label-text checkbox__label-text--long">
<span className="checkbox__label-title">{i18next.t('statistics_enable')}</span>
</span>
</span>
</label>
<Controller
name="enabled"
control={control}
render={({ field: { name, value, onChange } }) => (
<Checkbox
name={name}
title={t('statistics_enable')}
value={value}
onChange={(value) => onChange(value)}
disabled={processing}
/>
)}
/>
</div>
<div className="form__label form__label--with-desc">
@@ -134,9 +126,7 @@ export const Form = ({
{!STATS_INTERVALS_DAYS.includes(intervalValue) && (
<div className="form__group--input">
<div className="form__desc form__desc--top">
{i18next.t('custom_retention_input')}
</div>
<div className="form__desc form__desc--top">{i18next.t('custom_retention_input')}</div>
{/* <Field
key={RETENTION_CUSTOM_INPUT}
@@ -183,7 +173,7 @@ export const Form = ({
value={interval}
checked={intervalValue === interval}
onChange={(e) => {
setValue("interval", parseInt(e.target.value, 10));
setValue('interval', parseInt(e.target.value, 10));
}}
/>
@@ -221,11 +211,7 @@ export const Form = ({
</div>
<div className="mt-5">
<button
type="submit"
className="btn btn-success btn-standard btn-large"
disabled={disableSubmit}
>
<button type="submit" className="btn btn-success btn-standard btn-large" disabled={disableSubmit}>
<Trans>save_btn</Trans>
</button>

View File

@@ -1,13 +1,14 @@
import React, { Component, Fragment } from 'react';
import { withTranslation } from 'react-i18next';
import i18next from 'i18next';
import StatsConfig from './StatsConfig';
import LogsConfig from './LogsConfig';
import { FiltersConfig } from './FiltersConfig';
import Checkbox from '../ui/Checkbox';
import { Checkbox } from '../ui/Controls/Checkbox';
import Loading from '../ui/Loading';
@@ -24,14 +25,14 @@ const ORDER_KEY = 'order';
const SETTINGS = {
safebrowsing: {
enabled: false,
title: 'use_adguard_browsing_sec',
subtitle: 'use_adguard_browsing_sec_hint',
title: i18next.t('use_adguard_browsing_sec'),
subtitle: i18next.t('use_adguard_browsing_sec_hint'),
[ORDER_KEY]: 0,
},
parental: {
enabled: false,
title: 'use_adguard_parental',
subtitle: 'use_adguard_parental_hint',
title: i18next.t('use_adguard_parental'),
subtitle: i18next.t('use_adguard_parental_hint'),
[ORDER_KEY]: 1,
},
};
@@ -89,9 +90,18 @@ class Settings extends Component<SettingsProps> {
renderSettings = (settings: any) =>
getObjectKeysSorted(SETTINGS, ORDER_KEY).map((key: any) => {
const setting = settings[key];
const { enabled } = setting;
const { enabled, title, subtitle } = setting;
return <Checkbox {...setting} key={key} handleChange={() => this.props.toggleSetting(key, enabled)} />;
return (
<div key={key} className="form__group form__group--checkbox">
<Checkbox
value={enabled}
title={title}
subtitle={subtitle}
onChange={(checked) => this.props.toggleSetting(key, !checked)}
/>
</div>
);
});
renderSafeSearch = () => {
@@ -106,27 +116,29 @@ class Settings extends Component<SettingsProps> {
return (
<>
<Checkbox
enabled={enabled}
title="enforce_safe_search"
subtitle="enforce_save_search_hint"
handleChange={({ target: { checked: enabled } }) =>
this.props.toggleSetting('safesearch', { ...safesearch, enabled })
}
/>
<div className="form__group form__group--checkbox">
<Checkbox
value={enabled}
title={i18next.t('enforce_safe_search')}
subtitle={i18next.t('enforce_save_search_hint')}
onChange={(checked) =>
this.props.toggleSetting('safesearch', { ...safesearch, enabled: checked })
}
/>
</div>
<div className="form__group--inner">
{Object.keys(searches).map((searchKey) => (
<Checkbox
key={searchKey}
enabled={searches[searchKey]}
title={captitalizeWords(searchKey)}
subtitle=""
disabled={!safesearch.enabled}
handleChange={({ target: { checked } }: any) =>
this.props.toggleSetting('safesearch', { ...safesearch, [searchKey]: checked })
}
/>
<div key={searchKey} className="form__group form__group--checkbox">
<Checkbox
value={searches[searchKey]}
title={captitalizeWords(searchKey)}
disabled={!safesearch.enabled}
onChange={(checked) =>
this.props.toggleSetting('safesearch', { ...safesearch, [searchKey]: checked })
}
/>
</div>
))}
</div>
</>

View File

@@ -1,59 +0,0 @@
import React, { Component } from 'react';
import { withTranslation } from 'react-i18next';
import './Checkbox.css';
interface CheckboxProps {
title: string;
subtitle: string;
enabled: boolean;
handleChange: (...args: unknown[]) => unknown;
disabled?: boolean;
t?: (...args: unknown[]) => string;
}
class Checkbox extends Component<CheckboxProps> {
render() {
const {
title,
subtitle,
enabled,
handleChange,
disabled,
t,
} = this.props;
return (
<div className="form__group form__group--checkbox">
<label className="checkbox checkbox--settings">
<span className="checkbox__marker" />
<input
type="checkbox"
className="checkbox__input"
onChange={handleChange}
checked={enabled}
disabled={disabled}
/>
<span className="checkbox__label">
<span className="checkbox__label-text">
<span className="checkbox__label-title">{t(title)}</span>
<span
className="checkbox__label-subtitle"
dangerouslySetInnerHTML={{ __html: t(subtitle) }}
/>
</span>
</span>
</label>
</div>
);
}
}
export default withTranslation()(Checkbox);

View File

@@ -0,0 +1,35 @@
import React, { ReactNode } from 'react';
import clsx from 'clsx';
import './checkbox.css';
type Props = {
title: string;
subtitle?: ReactNode;
value: boolean;
name?: string;
disabled?: boolean;
className?: string;
onChange: (value: boolean) => void;
};
export const Checkbox = ({ title, subtitle, value, name, disabled, className = 'checkbox--form', onChange }: Props) => (
<label className={clsx('checkbox', className)}>
<span className="checkbox__marker" />
<input
name={name}
type="checkbox"
className="checkbox__input"
disabled={disabled}
checked={value}
onChange={(e) => onChange(e.target.checked)}
/>
<span className="checkbox__label">
<span className="checkbox__label-text checkbox__label-text--long">
<span className="checkbox__label-title">{title}</span>
{subtitle && <span className="checkbox__label-subtitle">{subtitle}</span>}
</span>
</span>
</label>
);

View File

@@ -0,0 +1,63 @@
import React from 'react';
type Props = {
id?: string;
className?: string;
placeholder?: string;
type?: string;
disabled?: boolean;
autoComplete?: string;
isActionAvailable?: boolean;
removeField?: () => void;
normalizeOnBlur?: (value: string) => string;
value: string;
onChange: (value: string) => void;
onBlur: () => void;
};
export const InputGroup = ({
id,
className,
placeholder,
type,
disabled,
autoComplete,
isActionAvailable,
removeField,
normalizeOnBlur,
value,
onChange,
onBlur,
}: Props) => {
const handleBlur = (event: React.FocusEvent<HTMLInputElement>) => {
if (normalizeOnBlur) {
onChange(normalizeOnBlur(event.target.value));
}
onBlur();
};
return (
<div className="input-group">
<input
id={id}
placeholder={placeholder}
type={type}
className={className}
disabled={disabled}
autoComplete={autoComplete}
value={value}
onChange={(e) => onChange(e.target.value)}
onBlur={handleBlur}
/>
{isActionAvailable && (
<span className="input-group-append">
<button type="button" className="btn btn-secondary btn-icon btn-icon--green" onClick={removeField}>
<svg className="icon icon--24">
<use xlinkHref="#cross" />
</svg>
</button>
</span>
)}
</div>
);
};