Rename folder "modules" to "features"
This commit is contained in:
29
src/features/core/components/inputs/ButtonSelectInput.tsx
Normal file
29
src/features/core/components/inputs/ButtonSelectInput.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { ComponentProps } from 'react';
|
||||
|
||||
import { ButtonSelect } from '@/features/core/components/buttons/ButtonSelect.tsx';
|
||||
|
||||
export const ButtonSelectInput = <Value extends string | number>({
|
||||
label,
|
||||
description,
|
||||
...buttonSelectProps
|
||||
}: ComponentProps<typeof ButtonSelect<Value>> & { label: string; description?: string }) => (
|
||||
<Stack>
|
||||
<Typography>{label}</Typography>
|
||||
{description && (
|
||||
<Typography variant="body2" color="textDisabled">
|
||||
{description}
|
||||
</Typography>
|
||||
)}
|
||||
<ButtonSelect {...buttonSelectProps} />
|
||||
</Stack>
|
||||
);
|
||||
16
src/features/core/components/inputs/CheckboxContainer.ts
Normal file
16
src/features/core/components/inputs/CheckboxContainer.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
export const CheckboxContainer = styled('div')({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
maxHeight: '170px',
|
||||
overflow: 'auto',
|
||||
});
|
||||
19
src/features/core/components/inputs/CheckboxInput.tsx
Normal file
19
src/features/core/components/inputs/CheckboxInput.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import Checkbox, { CheckboxProps } from '@mui/material/Checkbox';
|
||||
import FormControlLabel, { FormControlLabelProps } from '@mui/material/FormControlLabel';
|
||||
import React from 'react';
|
||||
|
||||
interface IProps extends CheckboxProps {
|
||||
label?: FormControlLabelProps['label'];
|
||||
}
|
||||
|
||||
export const CheckboxInput: React.FC<IProps> = ({ label, sx, ...rest }) => (
|
||||
<FormControlLabel control={<Checkbox {...rest} />} label={label} sx={sx} />
|
||||
);
|
||||
108
src/features/core/components/inputs/LanguageSelect.tsx
Normal file
108
src/features/core/components/inputs/LanguageSelect.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { useMemo, useState } from 'react';
|
||||
import Button from '@mui/material/Button';
|
||||
import DialogTitle from '@mui/material/DialogTitle';
|
||||
import DialogContent from '@mui/material/DialogContent';
|
||||
import DialogActions from '@mui/material/DialogActions';
|
||||
import Dialog from '@mui/material/Dialog';
|
||||
import Switch from '@mui/material/Switch';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import FilterListIcon from '@mui/icons-material/FilterList';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import ListItem from '@mui/material/ListItem';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Virtuoso } from 'react-virtuoso';
|
||||
import { CustomTooltip } from '@/features/core/components/CustomTooltip.tsx';
|
||||
import { translateExtensionLanguage } from '@/features/extension/Extensions.utils.ts';
|
||||
import { languageSortComparator, toUniqueLanguageCodes } from '@/features/core/utils/Languages.ts';
|
||||
|
||||
interface IProps {
|
||||
selectedLanguages: string[];
|
||||
setSelectedLanguages: (languages: string[]) => void;
|
||||
languages: string[];
|
||||
}
|
||||
|
||||
export function LanguageSelect(props: IProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { selectedLanguages, setSelectedLanguages, languages } = props;
|
||||
const [tmpSelectedLanguages, setTmpSelectedLanguages] = useState(toUniqueLanguageCodes(selectedLanguages));
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
|
||||
const languagesSortedBySelectState = useMemo(
|
||||
() =>
|
||||
toUniqueLanguageCodes([
|
||||
...tmpSelectedLanguages.toSorted(languageSortComparator),
|
||||
...languages.toSorted(languageSortComparator),
|
||||
]),
|
||||
[languages, tmpSelectedLanguages],
|
||||
);
|
||||
|
||||
const handleCancel = () => {
|
||||
setOpen(false);
|
||||
setTmpSelectedLanguages(toUniqueLanguageCodes(selectedLanguages));
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
setOpen(false);
|
||||
setSelectedLanguages(toUniqueLanguageCodes(tmpSelectedLanguages));
|
||||
};
|
||||
|
||||
const handleChange = (language: string, selected: boolean) => {
|
||||
if (selected) {
|
||||
setTmpSelectedLanguages([...tmpSelectedLanguages, language]);
|
||||
} else {
|
||||
setTmpSelectedLanguages(tmpSelectedLanguages.toSpliced(tmpSelectedLanguages.indexOf(language), 1));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<CustomTooltip title={t('settings.title')}>
|
||||
<IconButton onClick={() => setOpen(true)} aria-label="display more actions" edge="end" color="inherit">
|
||||
<FilterListIcon />
|
||||
</IconButton>
|
||||
</CustomTooltip>
|
||||
<Dialog fullWidth maxWidth="xs" open={open} onClose={handleCancel}>
|
||||
<DialogTitle>{t('global.language.title.enabled_languages')}</DialogTitle>
|
||||
<DialogContent dividers sx={{ padding: 0 }}>
|
||||
<Virtuoso
|
||||
style={{
|
||||
height: languagesSortedBySelectState.length * 54,
|
||||
minHeight: '25vh',
|
||||
maxHeight: '50vh',
|
||||
}}
|
||||
data={languagesSortedBySelectState}
|
||||
increaseViewportBy={400}
|
||||
computeItemKey={(index) => languagesSortedBySelectState[index]}
|
||||
itemContent={(_index, language) => (
|
||||
<ListItem>
|
||||
<ListItemText primary={translateExtensionLanguage(language)} />
|
||||
|
||||
<Switch
|
||||
checked={tmpSelectedLanguages.includes(language)}
|
||||
onChange={(e) => handleChange(language, e.target.checked)}
|
||||
/>
|
||||
</ListItem>
|
||||
)}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button autoFocus onClick={handleCancel} color="primary">
|
||||
{t('global.button.cancel')}
|
||||
</Button>
|
||||
<Button onClick={handleOk} color="primary">
|
||||
{t('global.button.ok')}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
47
src/features/core/components/inputs/PasswordTextField.tsx
Normal file
47
src/features/core/components/inputs/PasswordTextField.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import InputAdornment from '@mui/material/InputAdornment';
|
||||
import TextField, { TextFieldProps } from '@mui/material/TextField';
|
||||
import { useState } from 'react';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import Visibility from '@mui/icons-material/Visibility';
|
||||
import VisibilityOff from '@mui/icons-material/VisibilityOff';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const PasswordTextField = (props: TextFieldProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
const handleClickShowPassword = () => setShowPassword((show) => !show);
|
||||
|
||||
return (
|
||||
<TextField
|
||||
id="password"
|
||||
name="password"
|
||||
label={t('global.label.password')}
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<IconButton
|
||||
aria-label="toggle password visibility"
|
||||
onClick={handleClickShowPassword}
|
||||
edge="end"
|
||||
>
|
||||
{showPassword ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
},
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
19
src/features/core/components/inputs/RadioInput.tsx
Normal file
19
src/features/core/components/inputs/RadioInput.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
import Radio, { RadioProps } from '@mui/material/Radio';
|
||||
import React from 'react';
|
||||
|
||||
export interface RadioInputProps extends RadioProps {
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export const RadioInput: React.FC<RadioInputProps> = ({ label, sx, ...rest }) => (
|
||||
<FormControlLabel control={<Radio {...rest} />} label={label} sx={sx} />
|
||||
);
|
||||
37
src/features/core/components/inputs/SearchTextField.tsx
Normal file
37
src/features/core/components/inputs/SearchTextField.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import TextField, { TextFieldProps } from '@mui/material/TextField';
|
||||
import IconButton, { IconButtonProps } from '@mui/material/IconButton';
|
||||
import InputAdornment from '@mui/material/InputAdornment';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
|
||||
export const SearchTextField = ({
|
||||
onCancel,
|
||||
cancelButtonProps,
|
||||
...textFieldProps
|
||||
}: TextFieldProps & { onCancel: () => void; cancelButtonProps?: IconButtonProps }) => (
|
||||
<TextField
|
||||
{...textFieldProps}
|
||||
slotProps={{
|
||||
input: {
|
||||
...textFieldProps.InputProps,
|
||||
sx: {
|
||||
color: 'inherit',
|
||||
},
|
||||
endAdornment: textFieldProps.InputProps?.endAdornment ?? (
|
||||
<InputAdornment position="end">
|
||||
<IconButton {...cancelButtonProps} onClick={() => onCancel()}>
|
||||
<CancelIcon />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
19
src/features/core/components/inputs/Select.tsx
Normal file
19
src/features/core/components/inputs/Select.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import MuiSelect from '@mui/material/Select';
|
||||
|
||||
export const Select = <Value,>({
|
||||
children,
|
||||
maxSelectionHeightPx = 250,
|
||||
...props
|
||||
}: React.ComponentProps<typeof MuiSelect<Value>> & { maxSelectionHeightPx?: number }) => (
|
||||
<MuiSelect<Value> MenuProps={{ PaperProps: { style: { maxHeight: maxSelectionHeightPx } } }} {...props}>
|
||||
{children}
|
||||
</MuiSelect>
|
||||
);
|
||||
41
src/features/core/components/inputs/SliderInput.tsx
Normal file
41
src/features/core/components/inputs/SliderInput.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import Slider, { SliderProps } from '@mui/material/Slider';
|
||||
import Typography, { TypographyProps } from '@mui/material/Typography';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import { ResetButton } from '@/features/core/components/buttons/ResetButton.tsx';
|
||||
|
||||
export const SliderInput = ({
|
||||
label,
|
||||
value,
|
||||
onDefault,
|
||||
slotProps,
|
||||
}: {
|
||||
label: string;
|
||||
value: number | string;
|
||||
onDefault?: () => void;
|
||||
slotProps?: {
|
||||
label?: TypographyProps;
|
||||
value?: TypographyProps;
|
||||
slider?: SliderProps;
|
||||
};
|
||||
}) => (
|
||||
<Stack sx={{ flexDirection: 'row', gap: 2, alignItems: 'center' }}>
|
||||
<Stack sx={{ flexBasis: '25%' }}>
|
||||
<Typography {...slotProps?.label} sx={{ ...slotProps?.label?.sx }}>
|
||||
{label}
|
||||
</Typography>
|
||||
<Typography {...slotProps?.value} sx={{ ...slotProps?.value?.sx }}>
|
||||
{value}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Slider {...slotProps?.slider} sx={{ flexBasis: '75%', ...slotProps?.slider?.sx }} />
|
||||
{onDefault && <ResetButton asIconButton onClick={onDefault} />}
|
||||
</Stack>
|
||||
);
|
||||
23
src/features/core/components/inputs/SortRadioInput.tsx
Normal file
23
src/features/core/components/inputs/SortRadioInput.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import ArrowDownward from '@mui/icons-material/ArrowDownward';
|
||||
import ArrowUpward from '@mui/icons-material/ArrowUpward';
|
||||
import { memo } from 'react';
|
||||
import { RadioInput, RadioInputProps } from '@/features/core/components/inputs/RadioInput.tsx';
|
||||
|
||||
interface IProps extends RadioInputProps {
|
||||
sortDescending?: boolean | null | undefined;
|
||||
}
|
||||
|
||||
export const SortRadioInput = memo(({ sortDescending, ...rest }: IProps) => (
|
||||
<RadioInput
|
||||
checkedIcon={sortDescending ? <ArrowDownward color="primary" /> : <ArrowUpward color="primary" />}
|
||||
{...rest}
|
||||
/>
|
||||
));
|
||||
48
src/features/core/components/inputs/ThreeStateCheckbox.tsx
Normal file
48
src/features/core/components/inputs/ThreeStateCheckbox.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import DisabledByDefaultRounded from '@mui/icons-material/DisabledByDefaultRounded';
|
||||
import Checkbox, { CheckboxProps } from '@mui/material/Checkbox';
|
||||
import React, { useCallback } from 'react';
|
||||
|
||||
type CheckState = boolean | undefined | null;
|
||||
|
||||
function nextState(state: CheckState): CheckState {
|
||||
if (state === true) return false;
|
||||
if (state === false) return undefined;
|
||||
return true;
|
||||
}
|
||||
|
||||
export interface ThreeStateCheckboxProps extends Omit<CheckboxProps, 'checked' | 'onChange'> {
|
||||
checked?: boolean | undefined | null;
|
||||
onChange?: (checked: boolean | undefined | null) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* When checked is true, checkbox contains checkmark
|
||||
* When checked is false, checkbox contains cross
|
||||
* When checked is null or undefined, checkbox is empty
|
||||
*/
|
||||
export const ThreeStateCheckbox: React.FC<ThreeStateCheckboxProps> = ({ checked, onChange, ...rest }) => {
|
||||
const handleChange = useCallback(() => {
|
||||
if (onChange) {
|
||||
const newState = nextState(checked);
|
||||
onChange(newState);
|
||||
}
|
||||
}, [onChange]);
|
||||
|
||||
return (
|
||||
<Checkbox
|
||||
indeterminateIcon={<DisabledByDefaultRounded />}
|
||||
checked={checked === true}
|
||||
indeterminate={checked === false}
|
||||
onChange={handleChange}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
import React from 'react';
|
||||
import { ThreeStateCheckbox, ThreeStateCheckboxProps } from '@/features/core/components/inputs/ThreeStateCheckbox.tsx';
|
||||
|
||||
interface IProps extends ThreeStateCheckboxProps {
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export const ThreeStateCheckboxInput: React.FC<IProps> = ({ label, sx, ...rest }) => (
|
||||
<FormControlLabel control={<ThreeStateCheckbox {...rest} />} label={label} sx={sx} />
|
||||
);
|
||||
Reference in New Issue
Block a user