mirror of
https://github.com/Sonarr/Sonarr.git
synced 2025-04-23 13:57:06 -04:00
Improve typings in FormInputGroup
This commit is contained in:
parent
b218461678
commit
6838f068bc
44 changed files with 321 additions and 281 deletions
|
@ -7,8 +7,9 @@ import {
|
|||
import { InputChanged } from 'typings/inputs';
|
||||
import AutoSuggestInput from './AutoSuggestInput';
|
||||
|
||||
interface AutoCompleteInputProps {
|
||||
export interface AutoCompleteInputProps {
|
||||
name: string;
|
||||
readOnly?: boolean;
|
||||
value?: string;
|
||||
values: string[];
|
||||
onChange: (change: InputChanged<string>) => unknown;
|
||||
|
|
|
@ -16,7 +16,7 @@ import FormInputButton from './FormInputButton';
|
|||
import TextInput from './TextInput';
|
||||
import styles from './CaptchaInput.css';
|
||||
|
||||
interface CaptchaInputProps {
|
||||
export interface CaptchaInputProps {
|
||||
className?: string;
|
||||
name: string;
|
||||
value?: string;
|
||||
|
|
|
@ -41,10 +41,11 @@
|
|||
.checkbox:focus + .input {
|
||||
outline: 0;
|
||||
border-color: var(--inputFocusBorderColor);
|
||||
box-shadow: inset 0 1px 1px var(--inputBoxShadowColor), 0 0 8px var(--inputFocusBoxShadowColor);
|
||||
box-shadow: inset 0 1px 1px var(--inputBoxShadowColor),
|
||||
0 0 8px var(--inputFocusBoxShadowColor);
|
||||
}
|
||||
|
||||
.dangerIsChecked {
|
||||
.danger {
|
||||
border-color: var(--dangerColor);
|
||||
background-color: var(--dangerColor);
|
||||
|
||||
|
@ -53,7 +54,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.primaryIsChecked {
|
||||
.primary {
|
||||
border-color: var(--primaryColor);
|
||||
background-color: var(--primaryColor);
|
||||
|
||||
|
@ -62,7 +63,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.successIsChecked {
|
||||
.success {
|
||||
border-color: var(--successColor);
|
||||
background-color: var(--successColor);
|
||||
|
||||
|
@ -71,7 +72,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.warningIsChecked {
|
||||
.warning {
|
||||
border-color: var(--warningColor);
|
||||
background-color: var(--warningColor);
|
||||
|
||||
|
|
|
@ -3,16 +3,16 @@
|
|||
interface CssExports {
|
||||
'checkbox': string;
|
||||
'container': string;
|
||||
'dangerIsChecked': string;
|
||||
'danger': string;
|
||||
'helpText': string;
|
||||
'input': string;
|
||||
'isDisabled': string;
|
||||
'isIndeterminate': string;
|
||||
'isNotChecked': string;
|
||||
'label': string;
|
||||
'primaryIsChecked': string;
|
||||
'successIsChecked': string;
|
||||
'warningIsChecked': string;
|
||||
'primary': string;
|
||||
'success': string;
|
||||
'warning': string;
|
||||
}
|
||||
export const cssExports: CssExports;
|
||||
export default cssExports;
|
||||
|
|
|
@ -11,7 +11,7 @@ interface ChangeEvent<T = Element> extends SyntheticEvent<T, MouseEvent> {
|
|||
target: EventTarget & T;
|
||||
}
|
||||
|
||||
interface CheckInputProps {
|
||||
export interface CheckInputProps {
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
name: string;
|
||||
|
@ -45,7 +45,6 @@ function CheckInput(props: CheckInputProps) {
|
|||
const isChecked = value === checkedValue;
|
||||
const isUnchecked = value === uncheckedValue;
|
||||
const isIndeterminate = !isChecked && !isUnchecked;
|
||||
const isCheckClass: keyof typeof styles = `${kind}IsChecked`;
|
||||
|
||||
const toggleChecked = useCallback(
|
||||
(checked: boolean, shiftKey: boolean) => {
|
||||
|
@ -112,7 +111,7 @@ function CheckInput(props: CheckInputProps) {
|
|||
<div
|
||||
className={classNames(
|
||||
className,
|
||||
isChecked ? styles[isCheckClass] : styles.isNotChecked,
|
||||
isChecked ? styles[kind] : styles.isNotChecked,
|
||||
isIndeterminate && styles.isIndeterminate,
|
||||
isDisabled && styles.isDisabled
|
||||
)}
|
||||
|
|
|
@ -1,51 +1,73 @@
|
|||
import React, { ComponentType, FocusEvent, ReactNode } from 'react';
|
||||
import React, { ElementType, ReactNode } from 'react';
|
||||
import Link from 'Components/Link/Link';
|
||||
import DownloadProtocol from 'DownloadClient/DownloadProtocol';
|
||||
import { inputTypes } from 'Helpers/Props';
|
||||
import { InputType } from 'Helpers/Props/inputTypes';
|
||||
import { Kind } from 'Helpers/Props/kinds';
|
||||
import { InputChanged } from 'typings/inputs';
|
||||
import { ValidationError, ValidationWarning } from 'typings/pending';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import AutoCompleteInput from './AutoCompleteInput';
|
||||
import CaptchaInput from './CaptchaInput';
|
||||
import CheckInput from './CheckInput';
|
||||
import AutoCompleteInput, { AutoCompleteInputProps } from './AutoCompleteInput';
|
||||
import CaptchaInput, { CaptchaInputProps } from './CaptchaInput';
|
||||
import CheckInput, { CheckInputProps } from './CheckInput';
|
||||
import { FormInputButtonProps } from './FormInputButton';
|
||||
import FormInputHelpText from './FormInputHelpText';
|
||||
import KeyValueListInput from './KeyValueListInput';
|
||||
import NumberInput from './NumberInput';
|
||||
import OAuthInput from './OAuthInput';
|
||||
import KeyValueListInput, { KeyValueListInputProps } from './KeyValueListInput';
|
||||
import NumberInput, { NumberInputProps } from './NumberInput';
|
||||
import OAuthInput, { OAuthInputProps } from './OAuthInput';
|
||||
import PasswordInput from './PasswordInput';
|
||||
import PathInput from './PathInput';
|
||||
import DownloadClientSelectInput from './Select/DownloadClientSelectInput';
|
||||
import EnhancedSelectInput from './Select/EnhancedSelectInput';
|
||||
import IndexerFlagsSelectInput from './Select/IndexerFlagsSelectInput';
|
||||
import IndexerSelectInput from './Select/IndexerSelectInput';
|
||||
import LanguageSelectInput from './Select/LanguageSelectInput';
|
||||
import MonitorEpisodesSelectInput from './Select/MonitorEpisodesSelectInput';
|
||||
import MonitorNewItemsSelectInput from './Select/MonitorNewItemsSelectInput';
|
||||
import ProviderDataSelectInput from './Select/ProviderOptionSelectInput';
|
||||
import QualityProfileSelectInput from './Select/QualityProfileSelectInput';
|
||||
import RootFolderSelectInput from './Select/RootFolderSelectInput';
|
||||
import SeriesTypeSelectInput from './Select/SeriesTypeSelectInput';
|
||||
import UMaskInput from './Select/UMaskInput';
|
||||
import DeviceInput from './Tag/DeviceInput';
|
||||
import SeriesTagInput from './Tag/SeriesTagInput';
|
||||
import TagSelectInput from './Tag/TagSelectInput';
|
||||
import TextTagInput from './Tag/TextTagInput';
|
||||
import TextArea from './TextArea';
|
||||
import TextInput from './TextInput';
|
||||
import PathInput, { PathInputProps } from './PathInput';
|
||||
import DownloadClientSelectInput, {
|
||||
DownloadClientSelectInputProps,
|
||||
} from './Select/DownloadClientSelectInput';
|
||||
import EnhancedSelectInput, {
|
||||
EnhancedSelectInputProps,
|
||||
} from './Select/EnhancedSelectInput';
|
||||
import IndexerFlagsSelectInput, {
|
||||
IndexerFlagsSelectInputProps,
|
||||
} from './Select/IndexerFlagsSelectInput';
|
||||
import IndexerSelectInput, {
|
||||
IndexerSelectInputProps,
|
||||
} from './Select/IndexerSelectInput';
|
||||
import LanguageSelectInput, {
|
||||
LanguageSelectInputProps,
|
||||
} from './Select/LanguageSelectInput';
|
||||
import MonitorEpisodesSelectInput, {
|
||||
MonitorEpisodesSelectInputProps,
|
||||
} from './Select/MonitorEpisodesSelectInput';
|
||||
import MonitorNewItemsSelectInput, {
|
||||
MonitorNewItemsSelectInputProps,
|
||||
} from './Select/MonitorNewItemsSelectInput';
|
||||
import ProviderDataSelectInput, {
|
||||
ProviderOptionSelectInputProps,
|
||||
} from './Select/ProviderOptionSelectInput';
|
||||
import QualityProfileSelectInput, {
|
||||
QualityProfileSelectInputProps,
|
||||
} from './Select/QualityProfileSelectInput';
|
||||
import RootFolderSelectInput, {
|
||||
RootFolderSelectInputProps,
|
||||
} from './Select/RootFolderSelectInput';
|
||||
import SeriesTypeSelectInput, {
|
||||
SeriesTypeSelectInputProps,
|
||||
} from './Select/SeriesTypeSelectInput';
|
||||
import UMaskInput, { UMaskInputProps } from './Select/UMaskInput';
|
||||
import DeviceInput, { DeviceInputProps } from './Tag/DeviceInput';
|
||||
import SeriesTagInput, { SeriesTagInputProps } from './Tag/SeriesTagInput';
|
||||
import TagSelectInput, { TagSelectInputProps } from './Tag/TagSelectInput';
|
||||
import TextTagInput, { TextTagInputProps } from './Tag/TextTagInput';
|
||||
import TextArea, { TextAreaProps } from './TextArea';
|
||||
import TextInput, { TextInputProps } from './TextInput';
|
||||
import styles from './FormInputGroup.css';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const componentMap: Record<InputType, ComponentType<any>> = {
|
||||
const componentMap: Record<InputType, ElementType> = {
|
||||
autoComplete: AutoCompleteInput,
|
||||
captcha: CaptchaInput,
|
||||
check: CheckInput,
|
||||
date: TextInput,
|
||||
device: DeviceInput,
|
||||
downloadClientSelect: DownloadClientSelectInput,
|
||||
dynamicSelect: ProviderDataSelectInput,
|
||||
file: TextInput,
|
||||
float: NumberInput,
|
||||
indexerFlagsSelect: IndexerFlagsSelectInput,
|
||||
indexerSelect: IndexerSelectInput,
|
||||
keyValueList: KeyValueListInput,
|
||||
languageSelect: LanguageSelectInput,
|
||||
monitorEpisodesSelect: MonitorEpisodesSelectInput,
|
||||
|
@ -55,21 +77,84 @@ const componentMap: Record<InputType, ComponentType<any>> = {
|
|||
password: PasswordInput,
|
||||
path: PathInput,
|
||||
qualityProfileSelect: QualityProfileSelectInput,
|
||||
indexerSelect: IndexerSelectInput,
|
||||
indexerFlagsSelect: IndexerFlagsSelectInput,
|
||||
downloadClientSelect: DownloadClientSelectInput,
|
||||
rootFolderSelect: RootFolderSelectInput,
|
||||
select: EnhancedSelectInput,
|
||||
dynamicSelect: ProviderDataSelectInput,
|
||||
tag: SeriesTagInput,
|
||||
seriesTag: SeriesTagInput,
|
||||
seriesTypeSelect: SeriesTypeSelectInput,
|
||||
tag: SeriesTagInput,
|
||||
tagSelect: TagSelectInput,
|
||||
text: TextInput,
|
||||
textArea: TextArea,
|
||||
textTag: TextTagInput,
|
||||
tagSelect: TagSelectInput,
|
||||
umask: UMaskInput,
|
||||
};
|
||||
} as const;
|
||||
|
||||
// type Components = typeof componentMap;
|
||||
|
||||
type PickProps<V, C extends InputType> = C extends 'text'
|
||||
? TextInputProps
|
||||
: C extends 'autoComplete'
|
||||
? AutoCompleteInputProps
|
||||
: C extends 'captcha'
|
||||
? CaptchaInputProps
|
||||
: C extends 'check'
|
||||
? CheckInputProps
|
||||
: C extends 'date'
|
||||
? TextInputProps
|
||||
: C extends 'device'
|
||||
? DeviceInputProps
|
||||
: C extends 'downloadClientSelect'
|
||||
? DownloadClientSelectInputProps
|
||||
: C extends 'dynamicSelect'
|
||||
? ProviderOptionSelectInputProps
|
||||
: C extends 'file'
|
||||
? TextInputProps
|
||||
: C extends 'float'
|
||||
? TextInputProps
|
||||
: C extends 'indexerFlagsSelect'
|
||||
? IndexerFlagsSelectInputProps
|
||||
: C extends 'indexerSelect'
|
||||
? IndexerSelectInputProps
|
||||
: C extends 'keyValueList'
|
||||
? KeyValueListInputProps
|
||||
: C extends 'languageSelect'
|
||||
? LanguageSelectInputProps
|
||||
: C extends 'monitorEpisodesSelect'
|
||||
? MonitorEpisodesSelectInputProps
|
||||
: C extends 'monitorNewItemsSelect'
|
||||
? MonitorNewItemsSelectInputProps
|
||||
: C extends 'number'
|
||||
? NumberInputProps
|
||||
: C extends 'oauth'
|
||||
? OAuthInputProps
|
||||
: C extends 'password'
|
||||
? TextInputProps
|
||||
: C extends 'path'
|
||||
? PathInputProps
|
||||
: C extends 'qualityProfileSelect'
|
||||
? QualityProfileSelectInputProps
|
||||
: C extends 'rootFolderSelect'
|
||||
? RootFolderSelectInputProps
|
||||
: C extends 'select'
|
||||
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
EnhancedSelectInputProps<any, V>
|
||||
: C extends 'seriesTag'
|
||||
? SeriesTagInputProps
|
||||
: C extends 'seriesTypeSelect'
|
||||
? SeriesTypeSelectInputProps
|
||||
: C extends 'tag'
|
||||
? SeriesTagInputProps
|
||||
: C extends 'tagSelect'
|
||||
? TagSelectInputProps
|
||||
: C extends 'text'
|
||||
? TextInputProps
|
||||
: C extends 'textArea'
|
||||
? TextAreaProps
|
||||
: C extends 'textTag'
|
||||
? TextTagInputProps
|
||||
: C extends 'umask'
|
||||
? UMaskInputProps
|
||||
: never;
|
||||
|
||||
export interface FormInputGroupValues<T> {
|
||||
key: T;
|
||||
|
@ -82,53 +167,37 @@ export interface ValidationMessage {
|
|||
message: string;
|
||||
}
|
||||
|
||||
interface FormInputGroupProps<T> {
|
||||
export type FormInputGroupProps<V, C extends InputType> = Omit<
|
||||
PickProps<V, C>,
|
||||
'className'
|
||||
> & {
|
||||
type: C;
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
inputClassName?: string;
|
||||
autoFocus?: boolean;
|
||||
autocomplete?: string;
|
||||
name: string;
|
||||
value?: unknown;
|
||||
values?: FormInputGroupValues<unknown>[];
|
||||
isDisabled?: boolean;
|
||||
type?: InputType;
|
||||
kind?: Kind;
|
||||
min?: number;
|
||||
max?: number;
|
||||
unit?: string;
|
||||
buttons?: ReactNode | ReactNode[];
|
||||
helpText?: string;
|
||||
helpTexts?: string[];
|
||||
helpTextWarning?: string;
|
||||
helpLink?: string;
|
||||
placeholder?: string;
|
||||
autoFocus?: boolean;
|
||||
includeFiles?: boolean;
|
||||
includeMissingValue?: boolean;
|
||||
includeNoChange?: boolean;
|
||||
includeNoChangeDisabled?: boolean;
|
||||
valueOptions?: object;
|
||||
selectedValueOptions?: object;
|
||||
selectOptionsProviderAction?: string;
|
||||
indexerFlags?: number;
|
||||
pending?: boolean;
|
||||
protocol?: DownloadProtocol;
|
||||
canEdit?: boolean;
|
||||
includeAny?: boolean;
|
||||
delimiters?: string[];
|
||||
readOnly?: boolean;
|
||||
placeholder?: string;
|
||||
unit?: string;
|
||||
errors?: (ValidationMessage | ValidationError)[];
|
||||
warnings?: (ValidationMessage | ValidationWarning)[];
|
||||
onChange: (change: InputChanged<T>) => void;
|
||||
onFocus?: (event: FocusEvent<HTMLInputElement>) => void;
|
||||
}
|
||||
};
|
||||
|
||||
function FormInputGroup<T>(props: FormInputGroupProps<T>) {
|
||||
function FormInputGroup<T, C extends InputType>(
|
||||
props: FormInputGroupProps<T, C>
|
||||
) {
|
||||
const {
|
||||
className = styles.inputGroup,
|
||||
containerClassName = styles.inputGroupContainer,
|
||||
inputClassName,
|
||||
type = 'text',
|
||||
type,
|
||||
unit,
|
||||
buttons = [],
|
||||
helpText,
|
||||
|
@ -153,6 +222,7 @@ function FormInputGroup<T>(props: FormInputGroupProps<T>) {
|
|||
<div className={containerClassName}>
|
||||
<div className={className}>
|
||||
<div className={styles.inputContainer}>
|
||||
{/* @ts-expect-error - tpyes are validated already */}
|
||||
<InputComponent
|
||||
className={inputClassName}
|
||||
helpText={helpText}
|
||||
|
|
|
@ -24,7 +24,8 @@ function parseValue(
|
|||
return newValue;
|
||||
}
|
||||
|
||||
interface NumberInputProps extends Omit<TextInputProps, 'value' | 'onChange'> {
|
||||
export interface NumberInputProps
|
||||
extends Omit<TextInputProps, 'value' | 'onChange'> {
|
||||
value?: number | null;
|
||||
min?: number;
|
||||
max?: number;
|
||||
|
|
|
@ -6,7 +6,7 @@ import { kinds } from 'Helpers/Props';
|
|||
import { resetOAuth, startOAuth } from 'Store/Actions/oAuthActions';
|
||||
import { InputOnChange } from 'typings/inputs';
|
||||
|
||||
interface OAuthInputProps {
|
||||
export interface OAuthInputProps {
|
||||
label?: string;
|
||||
name: string;
|
||||
provider: string;
|
||||
|
|
|
@ -24,7 +24,7 @@ import AutoSuggestInput from './AutoSuggestInput';
|
|||
import FormInputButton from './FormInputButton';
|
||||
import styles from './PathInput.css';
|
||||
|
||||
interface PathInputProps {
|
||||
export interface PathInputProps {
|
||||
className?: string;
|
||||
name: string;
|
||||
value?: string;
|
||||
|
|
|
@ -120,6 +120,7 @@ function ProviderFieldFormGroup<T>({
|
|||
helpTextWarning={helpTextWarning}
|
||||
helpLink={helpLink}
|
||||
placeholder={placeholder}
|
||||
// @ts-expect-error - this isn;'t available on all types
|
||||
selectOptionsProviderAction={selectOptionsProviderAction}
|
||||
value={value}
|
||||
values={selectValues}
|
||||
|
|
|
@ -51,8 +51,11 @@ function createDownloadClientsSelector(
|
|||
);
|
||||
}
|
||||
|
||||
interface DownloadClientSelectInputProps
|
||||
extends EnhancedSelectInputProps<EnhancedSelectInputValue<number>, number> {
|
||||
export interface DownloadClientSelectInputProps
|
||||
extends Omit<
|
||||
EnhancedSelectInputProps<EnhancedSelectInputValue<number>, number>,
|
||||
'values'
|
||||
> {
|
||||
name: string;
|
||||
value: number;
|
||||
includeAny?: boolean;
|
||||
|
|
|
@ -30,7 +30,7 @@ const selectIndexerFlagsValues = (selectedFlags: number) =>
|
|||
}
|
||||
);
|
||||
|
||||
interface IndexerFlagsSelectInputProps {
|
||||
export interface IndexerFlagsSelectInputProps {
|
||||
name: string;
|
||||
indexerFlags: number;
|
||||
onChange(payload: EnhancedSelectInputChanged<number>): void;
|
||||
|
|
|
@ -38,7 +38,7 @@ function createIndexersSelector(includeAny: boolean) {
|
|||
);
|
||||
}
|
||||
|
||||
interface IndexerSelectInputConnectorProps {
|
||||
export interface IndexerSelectInputProps {
|
||||
name: string;
|
||||
value: number;
|
||||
includeAny?: boolean;
|
||||
|
@ -50,7 +50,7 @@ function IndexerSelectInput({
|
|||
value,
|
||||
includeAny = false,
|
||||
onChange,
|
||||
}: IndexerSelectInputConnectorProps) {
|
||||
}: IndexerSelectInputProps) {
|
||||
const dispatch = useDispatch();
|
||||
const { isFetching, isPopulated, values } = useSelector(
|
||||
createIndexersSelector(includeAny)
|
||||
|
|
|
@ -12,20 +12,20 @@ interface LanguageSelectInputOnChangeProps {
|
|||
value: number | string | Language;
|
||||
}
|
||||
|
||||
interface LanguageSelectInputProps {
|
||||
export interface LanguageSelectInputProps {
|
||||
name: string;
|
||||
value: number | string | Language;
|
||||
includeNoChange: boolean;
|
||||
includeNoChange?: boolean;
|
||||
includeNoChangeDisabled?: boolean;
|
||||
includeMixed: boolean;
|
||||
includeMixed?: boolean;
|
||||
onChange: (payload: LanguageSelectInputOnChangeProps) => void;
|
||||
}
|
||||
|
||||
export default function LanguageSelectInput({
|
||||
value,
|
||||
includeNoChange,
|
||||
includeNoChange = false,
|
||||
includeNoChangeDisabled,
|
||||
includeMixed,
|
||||
includeMixed = false,
|
||||
onChange,
|
||||
...otherProps
|
||||
}: LanguageSelectInputProps) {
|
||||
|
|
|
@ -6,13 +6,13 @@ import EnhancedSelectInput, {
|
|||
EnhancedSelectInputValue,
|
||||
} from './EnhancedSelectInput';
|
||||
|
||||
interface MonitorEpisodesSelectInputProps
|
||||
export interface MonitorEpisodesSelectInputProps
|
||||
extends Omit<
|
||||
EnhancedSelectInputProps<EnhancedSelectInputValue<string>, string>,
|
||||
'values'
|
||||
> {
|
||||
includeNoChange: boolean;
|
||||
includeMixed: boolean;
|
||||
includeNoChange?: boolean;
|
||||
includeMixed?: boolean;
|
||||
}
|
||||
|
||||
function MonitorEpisodesSelectInput(props: MonitorEpisodesSelectInputProps) {
|
||||
|
|
|
@ -5,19 +5,20 @@ import EnhancedSelectInput, {
|
|||
EnhancedSelectInputValue,
|
||||
} from './EnhancedSelectInput';
|
||||
|
||||
interface MonitorNewItemsSelectInputProps
|
||||
export interface MonitorNewItemsSelectInputProps
|
||||
extends Omit<
|
||||
EnhancedSelectInputProps<EnhancedSelectInputValue<string>, string>,
|
||||
'values'
|
||||
> {
|
||||
includeNoChange?: boolean;
|
||||
includeNoChangeDisabled?: boolean;
|
||||
includeMixed?: boolean;
|
||||
onChange: (...args: unknown[]) => unknown;
|
||||
}
|
||||
|
||||
function MonitorNewItemsSelectInput(props: MonitorNewItemsSelectInputProps) {
|
||||
const {
|
||||
includeNoChange = false,
|
||||
includeNoChangeDisabled = true,
|
||||
includeMixed = false,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
@ -30,7 +31,7 @@ function MonitorNewItemsSelectInput(props: MonitorNewItemsSelectInputProps) {
|
|||
values.unshift({
|
||||
key: 'noChange',
|
||||
value: 'No Change',
|
||||
isDisabled: true,
|
||||
isDisabled: includeNoChangeDisabled,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ function createProviderOptionsSelector(
|
|||
);
|
||||
}
|
||||
|
||||
interface ProviderOptionSelectInputProps
|
||||
export interface ProviderOptionSelectInputProps
|
||||
extends Omit<
|
||||
EnhancedSelectInputProps<EnhancedSelectInputValue<unknown>, unknown>,
|
||||
'values'
|
||||
|
|
|
@ -56,7 +56,7 @@ function createQualityProfilesSelector(
|
|||
);
|
||||
}
|
||||
|
||||
interface QualityProfileSelectInputConnectorProps
|
||||
export interface QualityProfileSelectInputProps
|
||||
extends Omit<
|
||||
EnhancedSelectInputProps<
|
||||
EnhancedSelectInputValue<number | string>,
|
||||
|
@ -78,7 +78,7 @@ function QualityProfileSelectInput({
|
|||
includeMixed = false,
|
||||
onChange,
|
||||
...otherProps
|
||||
}: QualityProfileSelectInputConnectorProps) {
|
||||
}: QualityProfileSelectInputProps) {
|
||||
const values = useSelector(
|
||||
createQualityProfilesSelector(
|
||||
includeNoChange,
|
||||
|
|
|
@ -24,16 +24,16 @@ export interface RootFolderSelectInputValue
|
|||
isMissing?: boolean;
|
||||
}
|
||||
|
||||
interface RootFolderSelectInputProps
|
||||
export interface RootFolderSelectInputProps
|
||||
extends Omit<
|
||||
EnhancedSelectInputProps<EnhancedSelectInputValue<string>, string>,
|
||||
'value' | 'values'
|
||||
> {
|
||||
name: string;
|
||||
value?: string;
|
||||
isSaving: boolean;
|
||||
saveError?: object;
|
||||
includeNoChange: boolean;
|
||||
includeMissingValue?: boolean;
|
||||
includeNoChange?: boolean;
|
||||
includeNoChangeDisabled?: boolean;
|
||||
}
|
||||
|
||||
function createRootFolderOptionsSelector(
|
||||
|
@ -107,13 +107,20 @@ function createRootFolderOptionsSelector(
|
|||
function RootFolderSelectInput({
|
||||
name,
|
||||
value,
|
||||
includeMissingValue = true,
|
||||
includeNoChange = false,
|
||||
includeNoChangeDisabled = true,
|
||||
onChange,
|
||||
...otherProps
|
||||
}: RootFolderSelectInputProps) {
|
||||
const dispatch = useDispatch();
|
||||
const { values, isSaving, saveError } = useSelector(
|
||||
createRootFolderOptionsSelector(value, true, includeNoChange, false)
|
||||
createRootFolderOptionsSelector(
|
||||
value,
|
||||
includeMissingValue,
|
||||
includeNoChange,
|
||||
includeNoChangeDisabled
|
||||
)
|
||||
);
|
||||
const [isAddNewRootFolderModalOpen, setIsAddNewRootFolderModalOpen] =
|
||||
useState(false);
|
||||
|
|
|
@ -8,11 +8,14 @@ import EnhancedSelectInput, {
|
|||
import SeriesTypeSelectInputOption from './SeriesTypeSelectInputOption';
|
||||
import SeriesTypeSelectInputSelectedValue from './SeriesTypeSelectInputSelectedValue';
|
||||
|
||||
interface SeriesTypeSelectInputProps
|
||||
extends EnhancedSelectInputProps<EnhancedSelectInputValue<string>, string> {
|
||||
includeNoChange: boolean;
|
||||
export interface SeriesTypeSelectInputProps
|
||||
extends Omit<
|
||||
EnhancedSelectInputProps<EnhancedSelectInputValue<string>, string>,
|
||||
'values'
|
||||
> {
|
||||
includeNoChange?: boolean;
|
||||
includeNoChangeDisabled?: boolean;
|
||||
includeMixed: boolean;
|
||||
includeMixed?: boolean;
|
||||
}
|
||||
|
||||
export interface ISeriesTypeOption {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
/* eslint-disable no-bitwise */
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { SyntheticEvent } from 'react';
|
||||
import { InputChanged } from 'typings/inputs';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
@ -67,7 +66,7 @@ function formatPermissions(permissions: number) {
|
|||
return result;
|
||||
}
|
||||
|
||||
interface UMaskInputProps {
|
||||
export interface UMaskInputProps {
|
||||
name: string;
|
||||
value: string;
|
||||
hasError?: boolean;
|
||||
|
@ -129,14 +128,4 @@ function UMaskInput({ name, value, onChange }: UMaskInputProps) {
|
|||
);
|
||||
}
|
||||
|
||||
UMaskInput.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
hasError: PropTypes.bool,
|
||||
hasWarning: PropTypes.bool,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onFocus: PropTypes.func,
|
||||
onBlur: PropTypes.func,
|
||||
};
|
||||
|
||||
export default UMaskInput;
|
||||
|
|
|
@ -19,7 +19,7 @@ interface DeviceTag {
|
|||
name: string;
|
||||
}
|
||||
|
||||
interface DeviceInputProps extends TagInputProps<DeviceTag> {
|
||||
export interface DeviceInputProps extends TagInputProps<DeviceTag> {
|
||||
className?: string;
|
||||
name: string;
|
||||
value: string[];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useCallback, useMemo } from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { addTag } from 'Store/Actions/tagActions';
|
||||
|
@ -12,10 +12,10 @@ interface SeriesTag extends TagBase {
|
|||
name: string;
|
||||
}
|
||||
|
||||
interface SeriesTagInputProps {
|
||||
export interface SeriesTagInputProps {
|
||||
name: string;
|
||||
value: number | number[];
|
||||
onChange: (change: InputChanged<number | number[]>) => void;
|
||||
value: number[];
|
||||
onChange: (change: InputChanged<number[]>) => void;
|
||||
}
|
||||
|
||||
const VALID_TAG_REGEX = new RegExp('[^-_a-z0-9]', 'i');
|
||||
|
@ -65,42 +65,22 @@ export default function SeriesTagInput({
|
|||
onChange,
|
||||
}: SeriesTagInputProps) {
|
||||
const dispatch = useDispatch();
|
||||
const isArray = Array.isArray(value);
|
||||
|
||||
const arrayValue = useMemo(() => {
|
||||
if (isArray) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return value === 0 ? [] : [value];
|
||||
}, [isArray, value]);
|
||||
|
||||
const { tags, tagList, allTags } = useSelector(
|
||||
createSeriesTagsSelector(arrayValue)
|
||||
createSeriesTagsSelector(value)
|
||||
);
|
||||
|
||||
const handleTagCreated = useCallback(
|
||||
(tag: SeriesTag) => {
|
||||
if (isArray) {
|
||||
onChange({ name, value: [...value, tag.id] });
|
||||
} else {
|
||||
onChange({
|
||||
name,
|
||||
value: tag.id,
|
||||
});
|
||||
}
|
||||
onChange({ name, value: [...value, tag.id] });
|
||||
},
|
||||
[name, value, isArray, onChange]
|
||||
[name, value, onChange]
|
||||
);
|
||||
|
||||
const handleTagAdd = useCallback(
|
||||
(newTag: SeriesTag) => {
|
||||
if (newTag.id) {
|
||||
if (isArray) {
|
||||
onChange({ name, value: [...value, newTag.id] });
|
||||
} else {
|
||||
onChange({ name, value: newTag.id });
|
||||
}
|
||||
onChange({ name, value: [...value, newTag.id] });
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -116,21 +96,17 @@ export default function SeriesTagInput({
|
|||
);
|
||||
}
|
||||
},
|
||||
[name, value, isArray, allTags, handleTagCreated, onChange, dispatch]
|
||||
[name, value, allTags, handleTagCreated, onChange, dispatch]
|
||||
);
|
||||
|
||||
const handleTagDelete = useCallback(
|
||||
({ index }: { index: number }) => {
|
||||
if (isArray) {
|
||||
const newValue = value.slice();
|
||||
newValue.splice(index, 1);
|
||||
const newValue = value.slice();
|
||||
newValue.splice(index, 1);
|
||||
|
||||
onChange({ name, value: newValue });
|
||||
} else {
|
||||
onChange({ name, value: 0 });
|
||||
}
|
||||
onChange({ name, value: newValue });
|
||||
},
|
||||
[name, value, isArray, onChange]
|
||||
[name, value, onChange]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, {
|
||||
KeyboardEvent,
|
||||
Ref,
|
||||
|
@ -16,9 +15,7 @@ import {
|
|||
SuggestionsFetchRequestedParams,
|
||||
} from 'react-autosuggest';
|
||||
import useDebouncedCallback from 'Helpers/Hooks/useDebouncedCallback';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import { Kind } from 'Helpers/Props/kinds';
|
||||
import tagShape from 'Helpers/Props/Shapes/tagShape';
|
||||
import { InputChanged } from 'typings/inputs';
|
||||
import AutoSuggestInput from '../AutoSuggestInput';
|
||||
import TagInputInput from './TagInputInput';
|
||||
|
@ -337,23 +334,4 @@ function TagInput<T extends TagBase>({
|
|||
);
|
||||
}
|
||||
|
||||
TagInput.propTypes = {
|
||||
className: PropTypes.string,
|
||||
inputContainerClassName: PropTypes.string,
|
||||
tags: PropTypes.arrayOf(PropTypes.shape(tagShape)).isRequired,
|
||||
tagList: PropTypes.arrayOf(PropTypes.shape(tagShape)).isRequired,
|
||||
allowNew: PropTypes.bool,
|
||||
kind: PropTypes.oneOf(kinds.all),
|
||||
placeholder: PropTypes.string,
|
||||
delimiters: PropTypes.arrayOf(PropTypes.string),
|
||||
minQueryLength: PropTypes.number,
|
||||
canEdit: PropTypes.bool,
|
||||
hasError: PropTypes.bool,
|
||||
hasWarning: PropTypes.bool,
|
||||
tagComponent: PropTypes.elementType,
|
||||
onTagAdd: PropTypes.func.isRequired,
|
||||
onTagDelete: PropTypes.func.isRequired,
|
||||
onTagReplace: PropTypes.func,
|
||||
};
|
||||
|
||||
export default TagInput;
|
||||
|
|
|
@ -13,7 +13,7 @@ interface TagSelectValue {
|
|||
order: number;
|
||||
}
|
||||
|
||||
interface TagSelectInputProps extends TagInputProps<SelectTag> {
|
||||
export interface TagSelectInputProps extends TagInputProps<SelectTag> {
|
||||
name: string;
|
||||
value: number[];
|
||||
values: TagSelectValue[];
|
||||
|
|
|
@ -8,7 +8,11 @@ interface TextTag extends TagBase {
|
|||
name: string;
|
||||
}
|
||||
|
||||
interface TextTagInputProps extends TagInputProps<TextTag> {
|
||||
export interface TextTagInputProps
|
||||
extends Omit<
|
||||
TagInputProps<TextTag>,
|
||||
'tags' | 'tagList' | 'onTagAdd' | 'onTagDelete'
|
||||
> {
|
||||
name: string;
|
||||
value: string | string[];
|
||||
onChange: (change: InputChanged<string[]>) => unknown;
|
||||
|
|
|
@ -9,7 +9,7 @@ import React, {
|
|||
import { InputChanged } from 'typings/inputs';
|
||||
import styles from './TextArea.css';
|
||||
|
||||
interface TextAreaProps {
|
||||
export interface TextAreaProps {
|
||||
className?: string;
|
||||
readOnly?: boolean;
|
||||
autoFocus?: boolean;
|
||||
|
|
|
@ -10,7 +10,7 @@ import React, {
|
|||
import { FileInputChanged, InputChanged } from 'typings/inputs';
|
||||
import styles from './TextInput.css';
|
||||
|
||||
interface CommonTextInputProps {
|
||||
export interface CommonTextInputProps {
|
||||
className?: string;
|
||||
readOnly?: boolean;
|
||||
autoFocus?: boolean;
|
||||
|
@ -23,7 +23,7 @@ interface CommonTextInputProps {
|
|||
step?: number;
|
||||
min?: number;
|
||||
max?: number;
|
||||
onFocus?: (event: FocusEvent) => void;
|
||||
onFocus?: (event: FocusEvent<HTMLInputElement, Element>) => void;
|
||||
onBlur?: (event: SyntheticEvent) => void;
|
||||
onCopy?: (event: SyntheticEvent) => void;
|
||||
onSelectionChange?: (start: number | null, end: number | null) => void;
|
||||
|
@ -102,7 +102,7 @@ function TextInput({
|
|||
);
|
||||
|
||||
const handleFocus = useCallback(
|
||||
(event: FocusEvent) => {
|
||||
(event: FocusEvent<HTMLInputElement, Element>) => {
|
||||
onFocus?.(event);
|
||||
|
||||
selectionChanged();
|
||||
|
|
|
@ -61,10 +61,10 @@ function TableOptionsModal({
|
|||
dropIndex > dragIndex;
|
||||
|
||||
const handlePageSizeChange = useCallback(
|
||||
({ value }: InputChanged<number>) => {
|
||||
({ value }: InputChanged<number | null>) => {
|
||||
let error: string | null = null;
|
||||
|
||||
if (value < 5) {
|
||||
if (value === null || value < 5) {
|
||||
error = translate('TablePageSizeMinimum', {
|
||||
minimumValue: '5',
|
||||
});
|
||||
|
@ -76,7 +76,7 @@ function TableOptionsModal({
|
|||
onTableOptionChange({ pageSize: value });
|
||||
}
|
||||
|
||||
setPageSize(value);
|
||||
setPageSize(value ?? 0);
|
||||
setPageSizeError(error);
|
||||
},
|
||||
[maxPageSize, onTableOptionChange]
|
||||
|
|
|
@ -78,8 +78,8 @@ function SelectQualityModalContent(props: SelectQualityModalContentProps) {
|
|||
}, [items]);
|
||||
|
||||
const onQualityChange = useCallback(
|
||||
({ value }: { value: string }) => {
|
||||
setQualityId(parseInt(value));
|
||||
({ value }: { value: number }) => {
|
||||
setQualityId(value);
|
||||
},
|
||||
[setQualityId]
|
||||
);
|
||||
|
@ -118,7 +118,7 @@ function SelectQualityModalContent(props: SelectQualityModalContentProps) {
|
|||
<ModalHeader>{modalTitle} - Select Quality</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
{isFetching && <LoadingIndicator />}
|
||||
{isFetching ? <LoadingIndicator /> : null}
|
||||
|
||||
{!isFetching && error ? (
|
||||
<Alert kind={kinds.DANGER}>{translate('QualitiesLoadError')}</Alert>
|
||||
|
|
|
@ -241,6 +241,7 @@ function EditSeriesModalContent({
|
|||
<Icon name={icons.ROOT_FOLDER} />
|
||||
</FormInputButton>,
|
||||
]}
|
||||
includeFiles={false}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
|
|
@ -137,7 +137,7 @@ function DeleteSeriesModalContent(props: DeleteSeriesModalContentProps) {
|
|||
? translate('DeleteSeriesFoldersHelpText')
|
||||
: translate('DeleteSeriesFolderHelpText')
|
||||
}
|
||||
kind={kinds.DANGER}
|
||||
kind="danger"
|
||||
onChange={onDeleteFilesChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
|
|
@ -9,6 +9,7 @@ import ModalFooter from 'Components/Modal/ModalFooter';
|
|||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { inputTypes } from 'Helpers/Props';
|
||||
import MoveSeriesModal from 'Series/MoveSeries/MoveSeriesModal';
|
||||
import { InputChanged } from 'typings/inputs';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './EditSeriesModalContent.css';
|
||||
|
||||
|
@ -142,25 +143,25 @@ function EditSeriesModalContent(props: EditSeriesModalContentProps) {
|
|||
);
|
||||
|
||||
const onInputChange = useCallback(
|
||||
({ name, value }: { name: string; value: string }) => {
|
||||
({ name, value }: InputChanged) => {
|
||||
switch (name) {
|
||||
case 'monitored':
|
||||
setMonitored(value);
|
||||
setMonitored(value as string);
|
||||
break;
|
||||
case 'monitorNewItems':
|
||||
setMonitorNewItems(value);
|
||||
setMonitorNewItems(value as string);
|
||||
break;
|
||||
case 'qualityProfileId':
|
||||
setQualityProfileId(value);
|
||||
setQualityProfileId(value as string);
|
||||
break;
|
||||
case 'seriesType':
|
||||
setSeriesType(value);
|
||||
setSeriesType(value as string);
|
||||
break;
|
||||
case 'seasonFolder':
|
||||
setSeasonFolder(value);
|
||||
setSeasonFolder(value as string);
|
||||
break;
|
||||
case 'rootFolderPath':
|
||||
setRootFolderPath(value);
|
||||
setRootFolderPath(value as string);
|
||||
break;
|
||||
default:
|
||||
console.warn('EditSeriesModalContent Unknown Input');
|
||||
|
|
|
@ -8,6 +8,7 @@ import ModalContent from 'Components/Modal/ModalContent';
|
|||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { inputTypes } from 'Helpers/Props';
|
||||
import { InputChanged } from 'typings/inputs';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './ManageDownloadClientsEditModalContent.css';
|
||||
|
||||
|
@ -57,7 +58,7 @@ function ManageDownloadClientsEditModalContent(
|
|||
const [removeCompletedDownloads, setRemoveCompletedDownloads] =
|
||||
useState(NO_CHANGE);
|
||||
const [removeFailedDownloads, setRemoveFailedDownloads] = useState(NO_CHANGE);
|
||||
const [priority, setPriority] = useState<null | string | number>(null);
|
||||
const [priority, setPriority] = useState<null | number>(null);
|
||||
|
||||
const save = useCallback(() => {
|
||||
let hasChanges = false;
|
||||
|
@ -97,29 +98,26 @@ function ManageDownloadClientsEditModalContent(
|
|||
onModalClose,
|
||||
]);
|
||||
|
||||
const onInputChange = useCallback(
|
||||
({ name, value }: { name: string; value: string }) => {
|
||||
switch (name) {
|
||||
case 'enable':
|
||||
setEnable(value);
|
||||
break;
|
||||
case 'priority':
|
||||
setPriority(value);
|
||||
break;
|
||||
case 'removeCompletedDownloads':
|
||||
setRemoveCompletedDownloads(value);
|
||||
break;
|
||||
case 'removeFailedDownloads':
|
||||
setRemoveFailedDownloads(value);
|
||||
break;
|
||||
default:
|
||||
console.warn(
|
||||
`EditDownloadClientsModalContent Unknown Input: '${name}'`
|
||||
);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
const onInputChange = useCallback(({ name, value }: InputChanged) => {
|
||||
switch (name) {
|
||||
case 'enable':
|
||||
setEnable(value as string);
|
||||
break;
|
||||
case 'priority':
|
||||
setPriority(value as number);
|
||||
break;
|
||||
case 'removeCompletedDownloads':
|
||||
setRemoveCompletedDownloads(value as string);
|
||||
break;
|
||||
case 'removeFailedDownloads':
|
||||
setRemoveFailedDownloads(value as string);
|
||||
break;
|
||||
default:
|
||||
console.warn(
|
||||
`EditDownloadClientsModalContent Unknown Input: '${name}'`
|
||||
);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const selectedCount = downloadClientIds.length;
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ function BackupSettings({
|
|||
type={inputTypes.PATH}
|
||||
name="backupFolder"
|
||||
helpText={translate('BackupFolderHelpText')}
|
||||
includeFiles={false}
|
||||
onChange={onInputChange}
|
||||
{...backupFolder}
|
||||
/>
|
||||
|
|
|
@ -66,8 +66,6 @@ function UpdateSettings({
|
|||
}
|
||||
helpLink="https://wiki.servarr.com/sonarr/settings#updates"
|
||||
{...branch}
|
||||
// @ts-expect-error - FormInputGroup doesn't accept a values prop
|
||||
// of string[] which is needed for AutoCompleteInput
|
||||
values={branchValues}
|
||||
readOnly={usingExternalUpdateMechanism}
|
||||
onChange={onInputChange}
|
||||
|
|
|
@ -22,6 +22,7 @@ import {
|
|||
} from 'Store/Actions/settingsActions';
|
||||
import selectSettings from 'Store/Selectors/selectSettings';
|
||||
import ImportListExclusion from 'typings/ImportListExclusion';
|
||||
import { InputChanged } from 'typings/inputs';
|
||||
import { PendingSection } from 'typings/pending';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './EditImportListExclusionModalContent.css';
|
||||
|
@ -102,9 +103,9 @@ function EditImportListExclusionModalContent({
|
|||
}, [dispatch, id]);
|
||||
|
||||
const onInputChange = useCallback(
|
||||
(payload: { name: string; value: string | number }) => {
|
||||
(change: InputChanged) => {
|
||||
// @ts-expect-error 'setImportListExclusionValue' isn't typed yet
|
||||
dispatch(setImportListExclusionValue(payload));
|
||||
dispatch(setImportListExclusionValue(change));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
|
|
@ -8,6 +8,7 @@ import ModalContent from 'Components/Modal/ModalContent';
|
|||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { inputTypes } from 'Helpers/Props';
|
||||
import { InputChanged } from 'typings/inputs';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './ManageImportListsEditModalContent.css';
|
||||
|
||||
|
@ -90,24 +91,21 @@ function ManageImportListsEditModalContent(
|
|||
onModalClose,
|
||||
]);
|
||||
|
||||
const onInputChange = useCallback(
|
||||
({ name, value }: { name: string; value: string }) => {
|
||||
switch (name) {
|
||||
case 'enableAutomaticAdd':
|
||||
setEnableAutomaticAdd(value);
|
||||
break;
|
||||
case 'qualityProfileId':
|
||||
setQualityProfileId(value);
|
||||
break;
|
||||
case 'rootFolderPath':
|
||||
setRootFolderPath(value);
|
||||
break;
|
||||
default:
|
||||
console.warn(`EditImportListModalContent Unknown Input: '${name}'`);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
const onInputChange = useCallback(({ name, value }: InputChanged) => {
|
||||
switch (name) {
|
||||
case 'enableAutomaticAdd':
|
||||
setEnableAutomaticAdd(value as string);
|
||||
break;
|
||||
case 'qualityProfileId':
|
||||
setQualityProfileId(value as number);
|
||||
break;
|
||||
case 'rootFolderPath':
|
||||
setRootFolderPath(value as string);
|
||||
break;
|
||||
default:
|
||||
console.warn(`EditImportListModalContent Unknown Input: '${name}'`);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const selectedCount = importListIds.length;
|
||||
|
||||
|
|
|
@ -107,6 +107,7 @@ function ImportListOptions({
|
|||
{...listSyncLevel}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{listSyncLevel.value === 'keepAndTag' ? (
|
||||
<FormGroup
|
||||
advancedSettings={showAdvancedSettings}
|
||||
|
@ -115,7 +116,7 @@ function ImportListOptions({
|
|||
<FormLabel>{translate('ListSyncTag')}</FormLabel>
|
||||
<FormInputGroup
|
||||
{...listSyncTag}
|
||||
type={inputTypes.TAG}
|
||||
type={inputTypes.SERIES_TAG}
|
||||
name="listSyncTag"
|
||||
value={listSyncTag.value === 0 ? [] : [listSyncTag.value]}
|
||||
helpText={translate('ListSyncTagHelpText')}
|
||||
|
|
|
@ -8,6 +8,7 @@ import ModalContent from 'Components/Modal/ModalContent';
|
|||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { inputTypes } from 'Helpers/Props';
|
||||
import { InputChanged } from 'typings/inputs';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './ManageIndexersEditModalContent.css';
|
||||
|
||||
|
@ -57,7 +58,7 @@ function ManageIndexersEditModalContent(
|
|||
const [enableAutomaticSearch, setEnableAutomaticSearch] = useState(NO_CHANGE);
|
||||
const [enableInteractiveSearch, setEnableInteractiveSearch] =
|
||||
useState(NO_CHANGE);
|
||||
const [priority, setPriority] = useState<null | string | number>(null);
|
||||
const [priority, setPriority] = useState<null | number>(null);
|
||||
|
||||
const save = useCallback(() => {
|
||||
let hasChanges = false;
|
||||
|
@ -97,27 +98,24 @@ function ManageIndexersEditModalContent(
|
|||
onModalClose,
|
||||
]);
|
||||
|
||||
const onInputChange = useCallback(
|
||||
({ name, value }: { name: string; value: string }) => {
|
||||
switch (name) {
|
||||
case 'enableRss':
|
||||
setEnableRss(value);
|
||||
break;
|
||||
case 'enableAutomaticSearch':
|
||||
setEnableAutomaticSearch(value);
|
||||
break;
|
||||
case 'enableInteractiveSearch':
|
||||
setEnableInteractiveSearch(value);
|
||||
break;
|
||||
case 'priority':
|
||||
setPriority(value);
|
||||
break;
|
||||
default:
|
||||
console.warn(`EditIndexersModalContent Unknown Input: '${name}'`);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
const onInputChange = useCallback(({ name, value }: InputChanged) => {
|
||||
switch (name) {
|
||||
case 'enableRss':
|
||||
setEnableRss(value as string);
|
||||
break;
|
||||
case 'enableAutomaticSearch':
|
||||
setEnableAutomaticSearch(value as string);
|
||||
break;
|
||||
case 'enableInteractiveSearch':
|
||||
setEnableInteractiveSearch(value as string);
|
||||
break;
|
||||
case 'priority':
|
||||
setPriority(value as number);
|
||||
break;
|
||||
default:
|
||||
console.warn(`EditIndexersModalContent Unknown Input: '${name}'`);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const selectedCount = indexerIds.length;
|
||||
|
||||
|
|
|
@ -464,6 +464,7 @@ function MediaManagement() {
|
|||
type={inputTypes.PATH}
|
||||
name="recycleBin"
|
||||
helpText={translate('RecyclingBinHelpText')}
|
||||
includeFiles={false}
|
||||
onChange={handleInputChange}
|
||||
{...settings.recycleBin}
|
||||
/>
|
||||
|
@ -537,7 +538,6 @@ function MediaManagement() {
|
|||
name="chownGroup"
|
||||
helpText={translate('ChownGroupHelpText')}
|
||||
helpTextWarning={translate('ChownGroupHelpTextWarning')}
|
||||
values={fileDateOptions}
|
||||
onChange={handleInputChange}
|
||||
{...settings.chownGroup}
|
||||
/>
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
setNamingSettingsValue,
|
||||
} from 'Store/Actions/settingsActions';
|
||||
import createSettingsSectionSelector from 'Store/Selectors/createSettingsSectionSelector';
|
||||
import { InputChanged } from 'typings/inputs';
|
||||
import NamingConfig from 'typings/Settings/NamingConfig';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import NamingModal from './NamingModal';
|
||||
|
@ -88,9 +89,9 @@ function Naming() {
|
|||
}, [dispatch]);
|
||||
|
||||
const handleInputChange = useCallback(
|
||||
({ name, value }: { name: string; value: string }) => {
|
||||
(change: InputChanged) => {
|
||||
// @ts-expect-error 'setNamingSettingsValue' isn't typed yet
|
||||
dispatch(setNamingSettingsValue({ name, value }));
|
||||
dispatch(setNamingSettingsValue(change));
|
||||
|
||||
if (namingExampleTimeout.current) {
|
||||
clearTimeout(namingExampleTimeout.current);
|
||||
|
|
|
@ -21,11 +21,15 @@ import {
|
|||
setDelayProfileValue,
|
||||
} from 'Store/Actions/settingsActions';
|
||||
import selectSettings from 'Store/Selectors/selectSettings';
|
||||
import DelayProfile from 'typings/DelayProfile';
|
||||
import { InputChanged } from 'typings/inputs';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './EditDelayProfileModalContent.css';
|
||||
|
||||
const newDelayProfile: Record<string, boolean | number | number[] | string> = {
|
||||
const newDelayProfile: DelayProfile & { [key: string]: unknown } = {
|
||||
id: 0,
|
||||
name: '',
|
||||
order: 0,
|
||||
enableUsenet: true,
|
||||
enableTorrent: true,
|
||||
preferredProtocol: 'usenet',
|
||||
|
@ -72,7 +76,11 @@ function createDelayProfileSelector(id: number | undefined) {
|
|||
delayProfiles;
|
||||
|
||||
const profile = id ? items.find((i) => i.id === id) : newDelayProfile;
|
||||
const settings = selectSettings(profile!, pendingChanges, saveError);
|
||||
const settings = selectSettings<DelayProfile>(
|
||||
profile!,
|
||||
pendingChanges,
|
||||
saveError
|
||||
);
|
||||
|
||||
return {
|
||||
isFetching,
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
setReleaseProfileValue,
|
||||
} from 'Store/Actions/Settings/releaseProfiles';
|
||||
import selectSettings from 'Store/Selectors/selectSettings';
|
||||
import { InputChanged } from 'typings/inputs';
|
||||
import ReleaseProfile from 'typings/Settings/ReleaseProfile';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './EditReleaseProfileModalContent.css';
|
||||
|
@ -101,9 +102,9 @@ function EditReleaseProfileModalContent({
|
|||
}, [dispatch, id]);
|
||||
|
||||
const handleInputChange = useCallback(
|
||||
(payload: { name: string; value: string | number }) => {
|
||||
(change: InputChanged) => {
|
||||
// @ts-expect-error 'setReleaseProfileValue' isn't typed yet
|
||||
dispatch(setReleaseProfileValue(payload));
|
||||
dispatch(setReleaseProfileValue(change));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
@ -124,7 +125,6 @@ function EditReleaseProfileModalContent({
|
|||
name="name"
|
||||
{...name}
|
||||
placeholder={translate('OptionalName')}
|
||||
canEdit={true}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue