mirror of https://github.com/morpheus65535/bazarr
217 lines
5.1 KiB
TypeScript
217 lines
5.1 KiB
TypeScript
|
import { isArray, isBoolean, isNull, isNumber, isString } from "lodash";
|
||
|
import React, { FunctionComponent, useEffect } from "react";
|
||
|
import {
|
||
|
Button as BSButton,
|
||
|
ButtonProps as BSButtonProps,
|
||
|
Form,
|
||
|
} from "react-bootstrap";
|
||
|
import { useCollapse, useLatest } from ".";
|
||
|
import {
|
||
|
Chips as CChips,
|
||
|
ChipsProps as CChipsProps,
|
||
|
Selector as CSelector,
|
||
|
SelectorProps as CSelectorProps,
|
||
|
Slider as CSlider,
|
||
|
SliderProps as CSliderProps,
|
||
|
} from "../../components";
|
||
|
import { isReactText } from "../../utilites";
|
||
|
import { OverrideFuncType, useSingleUpdate } from "./hooks";
|
||
|
|
||
|
export const Message: FunctionComponent<{
|
||
|
type?: "warning" | "info";
|
||
|
}> = ({ type, children }) => {
|
||
|
const cls = ["pr-4"];
|
||
|
cls.push(type === "warning" ? "text-warning" : "text-muted");
|
||
|
|
||
|
return <Form.Text className={cls.join(" ")}>{children}</Form.Text>;
|
||
|
};
|
||
|
|
||
|
export interface BaseInput<T> {
|
||
|
disabled?: boolean;
|
||
|
settingKey: string;
|
||
|
override?: OverrideFuncType<T>;
|
||
|
beforeStaged?: (v: T) => any;
|
||
|
}
|
||
|
|
||
|
export interface TextProps extends BaseInput<React.ReactText> {
|
||
|
placeholder?: React.ReactText;
|
||
|
password?: boolean;
|
||
|
controlled?: boolean;
|
||
|
}
|
||
|
|
||
|
export const Text: FunctionComponent<TextProps> = ({
|
||
|
placeholder,
|
||
|
disabled,
|
||
|
beforeStaged,
|
||
|
controlled,
|
||
|
override,
|
||
|
password,
|
||
|
settingKey,
|
||
|
}) => {
|
||
|
const value = useLatest<React.ReactText>(settingKey, isReactText, override);
|
||
|
|
||
|
const update = useSingleUpdate();
|
||
|
const collapse = useCollapse();
|
||
|
|
||
|
return (
|
||
|
<Form.Control
|
||
|
type={password ? "password" : "text"}
|
||
|
placeholder={placeholder?.toString()}
|
||
|
disabled={disabled}
|
||
|
defaultValue={controlled ? undefined : value ?? undefined}
|
||
|
value={controlled ? value ?? undefined : undefined}
|
||
|
onChange={(e) => {
|
||
|
const val = e.currentTarget.value;
|
||
|
collapse(val.toString());
|
||
|
const value = beforeStaged ? beforeStaged(val) : val;
|
||
|
update(value, settingKey);
|
||
|
}}
|
||
|
></Form.Control>
|
||
|
);
|
||
|
};
|
||
|
|
||
|
export interface CheckProps extends BaseInput<boolean> {
|
||
|
label?: string;
|
||
|
inline?: boolean;
|
||
|
}
|
||
|
|
||
|
export const Check: FunctionComponent<CheckProps> = ({
|
||
|
label,
|
||
|
inline,
|
||
|
override,
|
||
|
disabled,
|
||
|
settingKey,
|
||
|
}) => {
|
||
|
const update = useSingleUpdate();
|
||
|
const collapse = useCollapse();
|
||
|
|
||
|
const value = useLatest<boolean>(settingKey, isBoolean, override);
|
||
|
|
||
|
useEffect(() => collapse(value ?? false), [collapse, value]);
|
||
|
|
||
|
return (
|
||
|
<Form.Check
|
||
|
custom
|
||
|
type="checkbox"
|
||
|
id={settingKey}
|
||
|
inline={inline}
|
||
|
label={label}
|
||
|
onChange={(e) => {
|
||
|
const { checked } = e.currentTarget;
|
||
|
update(checked, settingKey);
|
||
|
}}
|
||
|
disabled={disabled}
|
||
|
checked={value ?? undefined}
|
||
|
></Form.Check>
|
||
|
);
|
||
|
};
|
||
|
|
||
|
function selectorValidator<T>(v: any): v is T {
|
||
|
return isString(v) || isNumber(v) || isArray(v);
|
||
|
}
|
||
|
|
||
|
type SelectorProps<T, M extends boolean> = BaseInput<SelectorValueType<T, M>> &
|
||
|
CSelectorProps<T, M>;
|
||
|
|
||
|
export function Selector<
|
||
|
T extends string | string[] | number | number[],
|
||
|
M extends boolean = false
|
||
|
>(props: SelectorProps<T, M>) {
|
||
|
const update = useSingleUpdate();
|
||
|
const collapse = useCollapse();
|
||
|
|
||
|
const { settingKey, override, beforeStaged, ...selector } = props;
|
||
|
|
||
|
const value = useLatest<SelectorValueType<T, M>>(
|
||
|
settingKey,
|
||
|
selectorValidator,
|
||
|
override
|
||
|
);
|
||
|
|
||
|
useEffect(() => {
|
||
|
if (isString(value) || isNull(value)) {
|
||
|
collapse(value ?? "");
|
||
|
}
|
||
|
});
|
||
|
|
||
|
return (
|
||
|
<CSelector
|
||
|
{...selector}
|
||
|
// TODO: Force as any
|
||
|
defaultValue={value as any}
|
||
|
onChange={(v) => {
|
||
|
v = beforeStaged ? beforeStaged(v) : v;
|
||
|
update(v, settingKey);
|
||
|
}}
|
||
|
></CSelector>
|
||
|
);
|
||
|
}
|
||
|
|
||
|
type SliderProps = {} & BaseInput<number> &
|
||
|
Omit<CSliderProps, "onChange" | "onAfterChange">;
|
||
|
|
||
|
export const Slider: FunctionComponent<SliderProps> = (props) => {
|
||
|
const { settingKey, override, ...slider } = props;
|
||
|
|
||
|
const update = useSingleUpdate();
|
||
|
|
||
|
const defaultValue = useLatest<number>(settingKey, isNumber, override);
|
||
|
|
||
|
return (
|
||
|
<CSlider
|
||
|
onAfterChange={(v) => {
|
||
|
update(v, settingKey);
|
||
|
}}
|
||
|
defaultValue={defaultValue ?? undefined}
|
||
|
{...slider}
|
||
|
></CSlider>
|
||
|
);
|
||
|
};
|
||
|
|
||
|
type ChipsProp = {} & BaseInput<string[]> &
|
||
|
Omit<CChipsProps, "onChange" | "defaultValue">;
|
||
|
|
||
|
export const Chips: FunctionComponent<ChipsProp> = (props) => {
|
||
|
const { settingKey, override, ...chips } = props;
|
||
|
|
||
|
const update = useSingleUpdate();
|
||
|
|
||
|
const defaultValue = useLatest<string[]>(settingKey, isArray, override);
|
||
|
|
||
|
return (
|
||
|
<CChips
|
||
|
defaultValue={defaultValue ?? undefined}
|
||
|
onChange={(v) => {
|
||
|
update(v, settingKey);
|
||
|
}}
|
||
|
{...chips}
|
||
|
></CChips>
|
||
|
);
|
||
|
};
|
||
|
|
||
|
type ButtonProps = {
|
||
|
onClick?: (
|
||
|
update: (v: any, key: string) => void,
|
||
|
key: string,
|
||
|
value?: string
|
||
|
) => void;
|
||
|
} & Omit<BaseInput<string>, "override" | "beforeStaged">;
|
||
|
|
||
|
export const Button: FunctionComponent<Override<ButtonProps, BSButtonProps>> = (
|
||
|
props
|
||
|
) => {
|
||
|
const { onClick, settingKey, ...button } = props;
|
||
|
|
||
|
const value = useLatest<string>(settingKey, isString);
|
||
|
const update = useSingleUpdate();
|
||
|
|
||
|
return (
|
||
|
<BSButton
|
||
|
onClick={() => {
|
||
|
onClick && onClick(update, settingKey, value ?? undefined);
|
||
|
}}
|
||
|
{...button}
|
||
|
></BSButton>
|
||
|
);
|
||
|
};
|