Reader settings
This commit is contained in:
51
src/modules/reader/components/settings/ReaderSettings.tsx
Normal file
51
src/modules/reader/components/settings/ReaderSettings.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 Dialog from '@mui/material/Dialog';
|
||||
import DialogContent from '@mui/material/DialogContent';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useReaderStateMangaContext } from '@/modules/reader/contexts/state/ReaderStateMangaContext.tsx';
|
||||
import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
|
||||
import { ReaderSettingsTabs } from '@/modules/reader/components/settings/ReaderSettingsTabs.tsx';
|
||||
import { ReaderSettingTab } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
|
||||
export const ReaderSettings = ({ isOpen, close }: { isOpen: boolean; close: () => void }) => {
|
||||
const { manga } = useReaderStateMangaContext();
|
||||
const settings = ReaderService.useSettings();
|
||||
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
|
||||
if (!manga) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={isOpen}
|
||||
maxWidth="md"
|
||||
fullWidth
|
||||
onClose={close}
|
||||
hideBackdrop={activeTab === ReaderSettingTab.FILTER}
|
||||
>
|
||||
<DialogContent sx={{ p: 0 }}>
|
||||
<ReaderSettingsTabs
|
||||
activeTab={activeTab}
|
||||
setActiveTab={setActiveTab}
|
||||
settings={settings}
|
||||
updateSetting={(...args) => ReaderService.updateSetting(manga, ...args)}
|
||||
deleteSetting={(...args) => ReaderService.deleteSetting(manga, ...args)}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
159
src/modules/reader/components/settings/ReaderSettingsTabs.tsx
Normal file
159
src/modules/reader/components/settings/ReaderSettingsTabs.tsx
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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 Box from '@mui/material/Box';
|
||||
import Tab from '@mui/material/Tab';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TabsMenu } from '@/modules/core/components/tabs/TabsMenu.tsx';
|
||||
import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
|
||||
import { MediaQuery } from '@/modules/core/utils/MediaQuery.tsx';
|
||||
import { IReaderSettings, IReaderSettingsWithDefaultFlag } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { useReaderTapZoneContext } from '@/modules/reader/contexts/ReaderTapZoneContext.tsx';
|
||||
import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts';
|
||||
import { READER_SETTING_TABS, ReaderSettingTab } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
import { TabPanel } from '@/modules/core/components/tabs/TabPanel.tsx';
|
||||
import { ReaderLayoutSettings } from '@/modules/reader/components/settings/layout/ReaderLayoutSettings.tsx';
|
||||
import { ReaderGeneralSettings } from '@/modules/reader/components/settings/general/ReaderGeneralSettings.tsx';
|
||||
import { ReaderFilterSettings } from '@/modules/reader/components/settings/filters/ReaderFilterSettings.tsx';
|
||||
import { ReaderBehaviourSettings } from '@/modules/reader/components/settings/behaviour/ReaderBehaviourSettings.tsx';
|
||||
|
||||
interface BaseProps {
|
||||
activeTab: number;
|
||||
setActiveTab: (tab: number) => void;
|
||||
settings: IReaderSettingsWithDefaultFlag;
|
||||
updateSetting: (...args: OmitFirst<Parameters<typeof ReaderService.updateSetting>>) => void;
|
||||
}
|
||||
|
||||
interface DefaultSettingsProps extends BaseProps {
|
||||
areDefaultSettings: true;
|
||||
}
|
||||
|
||||
interface MangaSettingsProps extends BaseProps {
|
||||
deleteSetting: (setting: keyof IReaderSettings) => void;
|
||||
}
|
||||
|
||||
type Props =
|
||||
| (DefaultSettingsProps & PropertiesNever<Omit<MangaSettingsProps, keyof BaseProps>>)
|
||||
| (PropertiesNever<Omit<DefaultSettingsProps, keyof BaseProps>> & MangaSettingsProps);
|
||||
|
||||
export const ReaderSettingsTabs = ({
|
||||
activeTab,
|
||||
setActiveTab,
|
||||
areDefaultSettings,
|
||||
settings,
|
||||
updateSetting,
|
||||
deleteSetting,
|
||||
}: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { setShowPreview } = useReaderTapZoneContext();
|
||||
const isTouchDevice = MediaQuery.useIsTouchDevice();
|
||||
|
||||
const { mode: overlayMode } = ReaderService.useOverlayMode();
|
||||
|
||||
return (
|
||||
<>
|
||||
<TabsMenu
|
||||
value={activeTab}
|
||||
onChange={(_, newTab) => setActiveTab(newTab)}
|
||||
sx={{
|
||||
...applyStyles(!!areDefaultSettings, { zIndex: 2 }),
|
||||
...applyStyles(!areDefaultSettings, {
|
||||
backgroundColor: 'background.paper',
|
||||
backgroundImage: 'var(--Paper-overlay)',
|
||||
}),
|
||||
}}
|
||||
>
|
||||
{Object.values(READER_SETTING_TABS).map(({ id, label, supportsTouchDevices }) => {
|
||||
if (!supportsTouchDevices && isTouchDevice) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tab
|
||||
key={id}
|
||||
value={id}
|
||||
label={t(label)}
|
||||
sx={{ flexGrow: 1, maxWidth: 'unset', textTransform: 'none' }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</TabsMenu>
|
||||
<Box sx={{ p: areDefaultSettings ? undefined : 2, overflowX: 'hidden' }}>
|
||||
{Object.values(READER_SETTING_TABS).map(({ id, supportsTouchDevices }) => {
|
||||
if (!supportsTouchDevices && isTouchDevice) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (id as ReaderSettingTab) {
|
||||
case ReaderSettingTab.LAYOUT:
|
||||
return (
|
||||
<TabPanel key={id} index={id} currentIndex={activeTab}>
|
||||
<ReaderLayoutSettings
|
||||
settings={settings}
|
||||
updateSetting={(...args) => updateSetting(...args)}
|
||||
setShowPreview={setShowPreview!}
|
||||
// @ts-expect-error - TS2322: Type boolean is not assignable to type true
|
||||
isDefaultable={!areDefaultSettings}
|
||||
onDefault={(...args) => deleteSetting?.(...args)}
|
||||
/>
|
||||
</TabPanel>
|
||||
);
|
||||
case ReaderSettingTab.GENERAL:
|
||||
return (
|
||||
<TabPanel
|
||||
key={id}
|
||||
index={id}
|
||||
currentIndex={activeTab}
|
||||
sx={{ p: areDefaultSettings ? 2 : undefined }}
|
||||
>
|
||||
<ReaderGeneralSettings
|
||||
overlayMode={overlayMode}
|
||||
settings={settings}
|
||||
updateSetting={(...args) => updateSetting(...args)}
|
||||
// @ts-expect-error - TS2322: Type boolean is not assignable to type true
|
||||
isDefaultable={!areDefaultSettings}
|
||||
onDefault={(...args) => deleteSetting?.(...args)}
|
||||
/>
|
||||
</TabPanel>
|
||||
);
|
||||
case ReaderSettingTab.FILTER:
|
||||
return (
|
||||
<TabPanel
|
||||
key={id}
|
||||
index={id}
|
||||
currentIndex={activeTab}
|
||||
sx={{ p: areDefaultSettings ? 2 : undefined }}
|
||||
>
|
||||
<ReaderFilterSettings
|
||||
settings={settings}
|
||||
updateSetting={(...args) => updateSetting(...args)}
|
||||
/>
|
||||
</TabPanel>
|
||||
);
|
||||
case ReaderSettingTab.BEHAVIOUR:
|
||||
return (
|
||||
<TabPanel
|
||||
key={id}
|
||||
index={id}
|
||||
currentIndex={activeTab}
|
||||
sx={{ p: areDefaultSettings ? 2 : undefined }}
|
||||
>
|
||||
<ReaderBehaviourSettings
|
||||
settings={settings}
|
||||
updateSetting={(...args) => updateSetting(...args)}
|
||||
/>
|
||||
</TabPanel>
|
||||
);
|
||||
default:
|
||||
throw new Error(`Unexpected "ReaderSettingTab" (${id})`);
|
||||
}
|
||||
})}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -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 Stack from '@mui/material/Stack';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
|
||||
import { IReaderSettingsWithDefaultFlag } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.tsx';
|
||||
import { ReaderSettingExitMode } from '@/modules/reader/components/settings/behaviour/ReaderSettingExitMode.tsx';
|
||||
import { isOffsetDoubleSpreadPagesEditable } from '@/modules/reader/utils/ReaderSettings.utils.tsx';
|
||||
|
||||
export const ReaderBehaviourSettings = ({
|
||||
settings,
|
||||
updateSetting,
|
||||
}: {
|
||||
settings: IReaderSettingsWithDefaultFlag;
|
||||
updateSetting: (
|
||||
...args: OmitFirst<Parameters<typeof ReaderService.updateSetting>>
|
||||
) => ReturnType<typeof ReaderService.updateSetting>;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Stack sx={{ gap: 2 }}>
|
||||
<ReaderSettingExitMode
|
||||
exitMode={settings.exitMode}
|
||||
setExitMode={(value) => updateSetting('exitMode', value)}
|
||||
/>
|
||||
<CheckboxInput
|
||||
label={t('reader.settings.label.skip_dup_chapters')}
|
||||
checked={settings.shouldSkipDupChapters}
|
||||
onChange={(_, checked) => updateSetting('shouldSkipDupChapters', checked)}
|
||||
/>
|
||||
{isOffsetDoubleSpreadPagesEditable(settings.readingMode.value) && (
|
||||
<CheckboxInput
|
||||
label={t('reader.settings.label.offset_double_spread')}
|
||||
checked={settings.shouldOffsetDoubleSpreads.value}
|
||||
onChange={(_, checked) => updateSetting('shouldOffsetDoubleSpreads', checked)}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettings, ReaderExitMode } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { ValueToDisplayData } from '@/modules/core/Core.types.ts';
|
||||
import { ButtonSelectInput } from '@/modules/core/components/inputs/ButtonSelectInput.tsx';
|
||||
|
||||
const VALUE_TO_DISPLAY_DATA: ValueToDisplayData<ReaderExitMode> = {
|
||||
[ReaderExitMode.PREVIOUS]: {
|
||||
title: 'global.label.previous',
|
||||
icon: null,
|
||||
},
|
||||
[ReaderExitMode.MANGA]: {
|
||||
title: 'manga.title_one',
|
||||
icon: null,
|
||||
},
|
||||
};
|
||||
|
||||
const READER_EXIT_MODE_VALUES = Object.values(ReaderExitMode).filter((value) => typeof value === 'number');
|
||||
|
||||
export const ReaderSettingExitMode = ({
|
||||
exitMode,
|
||||
setExitMode,
|
||||
}: Pick<IReaderSettings, 'exitMode'> & {
|
||||
setExitMode: (mode: ReaderExitMode) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<ButtonSelectInput
|
||||
label={t('reader.settings.exit_mode')}
|
||||
value={exitMode}
|
||||
values={READER_EXIT_MODE_VALUES}
|
||||
setValue={setExitMode}
|
||||
valueToDisplayData={VALUE_TO_DISPLAY_DATA}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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 { ReaderSettingBrightness } from '@/modules/reader/components/settings/filters/ReaderSettingBrightness.tsx';
|
||||
import { ReaderSettingContrast } from '@/modules/reader/components/settings/filters/ReaderSettingContrast.tsx';
|
||||
import { ReaderSettingSaturate } from '@/modules/reader/components/settings/filters/ReaderSettingSaturate.tsx';
|
||||
import { ReaderSettingHue } from '@/modules/reader/components/settings/filters/ReaderSettingHue.tsx';
|
||||
import { ReaderSettingRGBA } from '@/modules/reader/components/settings/filters/ReaderSettingRGBA.tsx';
|
||||
import { ReaderSettingSepia } from '@/modules/reader/components/settings/filters/ReaderSettingSepia.tsx';
|
||||
import { ReaderSettingGrayscale } from '@/modules/reader/components/settings/filters/ReaderSettingGrayscale.tsx';
|
||||
import { ReaderSettingInvert } from '@/modules/reader/components/settings/filters/ReaderSettingInvert.tsx';
|
||||
import { ReaderSettingsTypeProps } from '@/modules/reader/types/Reader.types.ts';
|
||||
|
||||
export const ReaderFilterSettings = ({ settings: { customFilter }, updateSetting }: ReaderSettingsTypeProps) => (
|
||||
<Stack sx={{ gap: 2 }}>
|
||||
<ReaderSettingBrightness
|
||||
brightness={customFilter.brightness}
|
||||
updateSetting={(key, value, commit) =>
|
||||
updateSetting(
|
||||
'customFilter',
|
||||
{
|
||||
...customFilter,
|
||||
[key]: value,
|
||||
},
|
||||
commit,
|
||||
)
|
||||
}
|
||||
/>
|
||||
<ReaderSettingContrast
|
||||
contrast={customFilter.contrast}
|
||||
updateSetting={(key, value, commit) =>
|
||||
updateSetting(
|
||||
'customFilter',
|
||||
{
|
||||
...customFilter,
|
||||
[key]: value,
|
||||
},
|
||||
commit,
|
||||
)
|
||||
}
|
||||
/>
|
||||
<ReaderSettingSaturate
|
||||
saturate={customFilter.saturate}
|
||||
updateSetting={(key, value, commit) =>
|
||||
updateSetting(
|
||||
'customFilter',
|
||||
{
|
||||
...customFilter,
|
||||
[key]: value,
|
||||
},
|
||||
commit,
|
||||
)
|
||||
}
|
||||
/>
|
||||
<ReaderSettingHue
|
||||
hue={customFilter.hue}
|
||||
updateSetting={(key, value, commit) =>
|
||||
updateSetting(
|
||||
'customFilter',
|
||||
{
|
||||
...customFilter,
|
||||
[key]: value,
|
||||
},
|
||||
commit,
|
||||
)
|
||||
}
|
||||
/>
|
||||
<ReaderSettingRGBA
|
||||
rgba={customFilter.rgba}
|
||||
updateSetting={(key, value, commit) =>
|
||||
updateSetting(
|
||||
'customFilter',
|
||||
{
|
||||
...customFilter,
|
||||
[key]: value,
|
||||
},
|
||||
commit,
|
||||
)
|
||||
}
|
||||
/>
|
||||
<ReaderSettingSepia
|
||||
sepia={customFilter.sepia}
|
||||
updateSetting={(value) =>
|
||||
updateSetting('customFilter', {
|
||||
...customFilter,
|
||||
sepia: value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<ReaderSettingGrayscale
|
||||
grayscale={customFilter.grayscale}
|
||||
updateSetting={(value) =>
|
||||
updateSetting('customFilter', {
|
||||
...customFilter,
|
||||
grayscale: value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<ReaderSettingInvert
|
||||
invert={customFilter.invert}
|
||||
updateSetting={(value) =>
|
||||
updateSetting('customFilter', {
|
||||
...customFilter,
|
||||
invert: value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettings } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.tsx';
|
||||
import { SliderInput } from '@/modules/core/components/inputs/SliderInput.tsx';
|
||||
|
||||
import { DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
|
||||
export const ReaderSettingBrightness = ({
|
||||
brightness,
|
||||
updateSetting,
|
||||
}: Pick<IReaderSettings['customFilter'], 'brightness'> & {
|
||||
updateSetting: <Filter extends keyof IReaderSettings['customFilter']>(
|
||||
filter: Filter,
|
||||
value: IReaderSettings['customFilter'][Filter],
|
||||
commit: boolean,
|
||||
) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<CheckboxInput
|
||||
label={t('reader.settings.custom_filter.brightness.title')}
|
||||
checked={brightness.enabled}
|
||||
onChange={(_, checked) => updateSetting('brightness', { ...brightness, enabled: checked }, true)}
|
||||
/>
|
||||
{brightness.enabled && (
|
||||
<SliderInput
|
||||
label={t('reader.settings.custom_filter.brightness.value')}
|
||||
value={brightness.value}
|
||||
slotProps={{
|
||||
slider: {
|
||||
defaultValue: DEFAULT_READER_SETTINGS.customFilter.brightness.value,
|
||||
value: brightness.value,
|
||||
step: 1,
|
||||
min: 5,
|
||||
max: 200,
|
||||
onChange: (_, value) => {
|
||||
updateSetting('brightness', { ...brightness, value: value as number }, false);
|
||||
},
|
||||
onChangeCommitted: (_, value) => {
|
||||
updateSetting('brightness', { ...brightness, value: value as number }, true);
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettings } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.tsx';
|
||||
import { SliderInput } from '@/modules/core/components/inputs/SliderInput.tsx';
|
||||
import { DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
|
||||
export const ReaderSettingContrast = ({
|
||||
contrast,
|
||||
updateSetting,
|
||||
}: Pick<IReaderSettings['customFilter'], 'contrast'> & {
|
||||
updateSetting: <Filter extends keyof IReaderSettings['customFilter']>(
|
||||
filter: Filter,
|
||||
value: IReaderSettings['customFilter'][Filter],
|
||||
commit: boolean,
|
||||
) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<CheckboxInput
|
||||
label={t('reader.settings.custom_filter.contrast.title')}
|
||||
checked={contrast.enabled}
|
||||
onChange={(_, checked) => updateSetting('contrast', { ...contrast, enabled: checked }, true)}
|
||||
/>
|
||||
{contrast.enabled && (
|
||||
<SliderInput
|
||||
label={t('reader.settings.custom_filter.contrast.value')}
|
||||
value={contrast.value}
|
||||
slotProps={{
|
||||
slider: {
|
||||
defaultValue: DEFAULT_READER_SETTINGS.customFilter.contrast.value,
|
||||
value: contrast.value,
|
||||
step: 1,
|
||||
min: 5,
|
||||
max: 200,
|
||||
onChange: (_, newValue) => {
|
||||
updateSetting('contrast', { ...contrast, value: newValue as number }, false);
|
||||
},
|
||||
onChangeCommitted: (_, newValue) => {
|
||||
updateSetting('contrast', { ...contrast, value: newValue as number }, true);
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettings } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.tsx';
|
||||
|
||||
export const ReaderSettingGrayscale = ({
|
||||
grayscale,
|
||||
updateSetting,
|
||||
}: Pick<IReaderSettings['customFilter'], 'grayscale'> & {
|
||||
updateSetting: (grayscale: boolean) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<CheckboxInput
|
||||
label={t('reader.settings.custom_filter.grayscale')}
|
||||
checked={grayscale}
|
||||
onChange={(_, checked) => updateSetting(checked)}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettings } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.tsx';
|
||||
import { SliderInput } from '@/modules/core/components/inputs/SliderInput.tsx';
|
||||
import { DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
|
||||
export const ReaderSettingHue = ({
|
||||
hue,
|
||||
updateSetting,
|
||||
}: Pick<IReaderSettings['customFilter'], 'hue'> & {
|
||||
updateSetting: <Filter extends keyof IReaderSettings['customFilter']>(
|
||||
filter: Filter,
|
||||
value: IReaderSettings['customFilter'][Filter],
|
||||
commit: boolean,
|
||||
) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<CheckboxInput
|
||||
label={t('reader.settings.custom_filter.hue.title')}
|
||||
checked={hue.enabled}
|
||||
onChange={(_, checked) => updateSetting('hue', { ...hue, enabled: checked }, true)}
|
||||
/>
|
||||
{hue.enabled && (
|
||||
<SliderInput
|
||||
label={t('reader.settings.custom_filter.hue.value')}
|
||||
value={hue.value}
|
||||
slotProps={{
|
||||
slider: {
|
||||
defaultValue: DEFAULT_READER_SETTINGS.customFilter.hue.value,
|
||||
value: hue.value,
|
||||
step: 1,
|
||||
min: 0,
|
||||
max: 200,
|
||||
onChange: (_, newValue) => {
|
||||
updateSetting('hue', { ...hue, value: newValue as number }, false);
|
||||
},
|
||||
onChangeCommitted: (_, newValue) => {
|
||||
updateSetting('hue', { ...hue, value: newValue as number }, true);
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettings } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.tsx';
|
||||
|
||||
export const ReaderSettingInvert = ({
|
||||
invert,
|
||||
updateSetting,
|
||||
}: Pick<IReaderSettings['customFilter'], 'invert'> & {
|
||||
updateSetting: (invert: boolean) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<CheckboxInput
|
||||
label={t('reader.settings.custom_filter.invert')}
|
||||
checked={invert}
|
||||
onChange={(_, checked) => updateSetting(checked)}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettings } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.tsx';
|
||||
import { TranslationKey } from '@/Base.types.ts';
|
||||
import { SliderInput } from '@/modules/core/components/inputs/SliderInput.tsx';
|
||||
|
||||
import { DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
|
||||
type RGBAType = keyof IReaderSettings['customFilter']['rgba']['value'];
|
||||
|
||||
const RGBA_TYPE_TO_TRANSLATION_KEY: Record<RGBAType, TranslationKey> = {
|
||||
red: 'reader.settings.custom_filter.rgba.red',
|
||||
green: 'reader.settings.custom_filter.rgba.green',
|
||||
blue: 'reader.settings.custom_filter.rgba.blue',
|
||||
alpha: 'reader.settings.custom_filter.rgba.alpha',
|
||||
};
|
||||
|
||||
const RGBA_TYPE_TO_MAX_VALUE: Record<RGBAType, number> = {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255,
|
||||
alpha: 100,
|
||||
};
|
||||
|
||||
export const ReaderSettingRGBA = ({
|
||||
rgba,
|
||||
updateSetting,
|
||||
}: Pick<IReaderSettings['customFilter'], 'rgba'> & {
|
||||
updateSetting: <Filter extends keyof IReaderSettings['customFilter']>(
|
||||
filter: Filter,
|
||||
value: IReaderSettings['customFilter'][Filter],
|
||||
commit: boolean,
|
||||
) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<CheckboxInput
|
||||
label={t('reader.settings.custom_filter.rgba.title')}
|
||||
checked={rgba.enabled}
|
||||
onChange={(_, checked) => updateSetting('rgba', { ...rgba, enabled: checked }, true)}
|
||||
/>
|
||||
{rgba.enabled && (
|
||||
<Stack sx={{ gap: 1 }}>
|
||||
{Object.entries(rgba.value).map(([key, value]) => (
|
||||
<SliderInput
|
||||
key={key}
|
||||
label={t(RGBA_TYPE_TO_TRANSLATION_KEY[key as RGBAType])}
|
||||
value={value}
|
||||
slotProps={{
|
||||
slider: {
|
||||
value,
|
||||
defaultValue: DEFAULT_READER_SETTINGS.customFilter.rgba.value[key as RGBAType],
|
||||
step: 1,
|
||||
min: 0,
|
||||
max: RGBA_TYPE_TO_MAX_VALUE[key as RGBAType],
|
||||
onChange: (_, newValue) => {
|
||||
updateSetting(
|
||||
'rgba',
|
||||
{ ...rgba, value: { ...rgba.value, [key]: newValue } },
|
||||
false,
|
||||
);
|
||||
},
|
||||
onChangeCommitted: (_, newValue) => {
|
||||
updateSetting(
|
||||
'rgba',
|
||||
{ ...rgba, value: { ...rgba.value, [key]: newValue } },
|
||||
true,
|
||||
);
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettings } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.tsx';
|
||||
import { SliderInput } from '@/modules/core/components/inputs/SliderInput.tsx';
|
||||
import { DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
|
||||
export const ReaderSettingSaturate = ({
|
||||
saturate,
|
||||
updateSetting,
|
||||
}: Pick<IReaderSettings['customFilter'], 'saturate'> & {
|
||||
updateSetting: <Filter extends keyof IReaderSettings['customFilter']>(
|
||||
filter: Filter,
|
||||
value: IReaderSettings['customFilter'][Filter],
|
||||
commit: boolean,
|
||||
) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<CheckboxInput
|
||||
label={t('reader.settings.custom_filter.saturate.title')}
|
||||
checked={saturate.enabled}
|
||||
onChange={(_, checked) => updateSetting('saturate', { ...saturate, enabled: checked }, true)}
|
||||
/>
|
||||
{saturate.enabled && (
|
||||
<SliderInput
|
||||
label={t('reader.settings.custom_filter.saturate.value')}
|
||||
value={saturate.value}
|
||||
slotProps={{
|
||||
slider: {
|
||||
defaultValue: DEFAULT_READER_SETTINGS.customFilter.saturate.value,
|
||||
value: saturate.value,
|
||||
step: 1,
|
||||
min: 0,
|
||||
max: 200,
|
||||
onChange: (_, value) => {
|
||||
updateSetting('saturate', { ...saturate, value: value as number }, false);
|
||||
},
|
||||
onChangeCommitted: (_, value) => {
|
||||
updateSetting('saturate', { ...saturate, value: value as number }, true);
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettings } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.tsx';
|
||||
|
||||
export const ReaderSettingSepia = ({
|
||||
sepia,
|
||||
updateSetting,
|
||||
}: Pick<IReaderSettings['customFilter'], 'sepia'> & {
|
||||
updateSetting: (sepia: boolean) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<CheckboxInput
|
||||
label={t('reader.settings.custom_filter.sepia')}
|
||||
checked={sepia}
|
||||
onChange={(_, checked) => updateSetting(checked)}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import { ReaderSettingProgressBarType } from '@/modules/reader/components/settings/general/ReaderSettingProgressBarType.tsx';
|
||||
import { ReaderSettingProgressBarSize } from '@/modules/reader/components/settings/general/ReaderSettingProgressBarSize.tsx';
|
||||
import { ReaderSettingProgressBarPosition } from '@/modules/reader/components/settings/general/ReaderSettingProgressBarPosition.tsx';
|
||||
import {
|
||||
IReaderSettings,
|
||||
ProgressBarType,
|
||||
ReaderOverlayMode,
|
||||
ReaderSettingsTypeProps,
|
||||
} from '@/modules/reader/types/Reader.types.ts';
|
||||
import { ReaderSettingOverlayMode } from '@/modules/reader/components/settings/general/ReaderSettingOverlayMode.tsx';
|
||||
import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.tsx';
|
||||
import { ReaderSettingBackgroundColor } from '@/modules/reader/components/settings/general/ReaderSettingBackgroundColor.tsx';
|
||||
|
||||
export const ReaderGeneralSettings = ({
|
||||
overlayMode,
|
||||
settings,
|
||||
updateSetting,
|
||||
}: Pick<IReaderSettings, 'overlayMode'> & ReaderSettingsTypeProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Stack sx={{ gap: 2 }}>
|
||||
<ReaderSettingOverlayMode
|
||||
overlayMode={settings.overlayMode}
|
||||
setOverlayMode={(value) => updateSetting('overlayMode', value)}
|
||||
/>
|
||||
<ReaderSettingBackgroundColor
|
||||
backgroundColor={settings.backgroundColor}
|
||||
updateSetting={(value) => updateSetting('backgroundColor', value)}
|
||||
/>
|
||||
<ReaderSettingProgressBarType
|
||||
overlayMode={overlayMode}
|
||||
progressBarType={settings.progressBarType}
|
||||
setProgressBarType={(value) => updateSetting('progressBarType', value)}
|
||||
/>
|
||||
<ReaderSettingProgressBarSize
|
||||
overlayMode={overlayMode}
|
||||
progressBarType={settings.progressBarType}
|
||||
progressBarSize={settings.progressBarSize}
|
||||
setProgressBarSize={(...args) => updateSetting('progressBarSize', ...args)}
|
||||
/>
|
||||
<ReaderSettingProgressBarPosition
|
||||
overlayMode={overlayMode}
|
||||
progressBarPosition={settings.progressBarPosition}
|
||||
setProgressBarPosition={(value) => updateSetting('progressBarPosition', value)}
|
||||
/>
|
||||
{(settings.progressBarType === ProgressBarType.HIDDEN || overlayMode === ReaderOverlayMode.MOBILE) && (
|
||||
<CheckboxInput
|
||||
label={t('reader.settings.label.show_page_number')}
|
||||
checked={settings.shouldShowPageNumber}
|
||||
onChange={(_, checked) => updateSetting('shouldShowPageNumber', checked)}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import { ValueToDisplayData } from '@/modules/core/Core.types.ts';
|
||||
import { ButtonSelectInput } from '@/modules/core/components/inputs/ButtonSelectInput.tsx';
|
||||
import { IReaderSettingsWithDefaultFlag, ReaderBackgroundColor } from '@/modules/reader/types/Reader.types.ts';
|
||||
|
||||
const VALUE_TO_DISPLAY_DATA: ValueToDisplayData<ReaderBackgroundColor> = {
|
||||
[ReaderBackgroundColor.THEME]: {
|
||||
title: 'settings.appearance.theme.title',
|
||||
icon: null,
|
||||
},
|
||||
[ReaderBackgroundColor.BLACK]: {
|
||||
title: 'global.colors.black',
|
||||
icon: null,
|
||||
},
|
||||
[ReaderBackgroundColor.GRAY]: {
|
||||
title: 'global.colors.gray',
|
||||
icon: null,
|
||||
},
|
||||
[ReaderBackgroundColor.WHITE]: {
|
||||
title: 'global.colors.white',
|
||||
icon: null,
|
||||
},
|
||||
};
|
||||
|
||||
const READER_BACKGROUND_COLOR_VALUES = Object.values(ReaderBackgroundColor).filter(
|
||||
(value) => typeof value === 'number',
|
||||
);
|
||||
|
||||
export const ReaderSettingBackgroundColor = ({
|
||||
backgroundColor,
|
||||
updateSetting,
|
||||
}: Pick<IReaderSettingsWithDefaultFlag, 'backgroundColor'> & {
|
||||
updateSetting: (color: ReaderBackgroundColor) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<ButtonSelectInput
|
||||
label={t('reader.settings.background_color')}
|
||||
value={backgroundColor}
|
||||
values={READER_BACKGROUND_COLOR_VALUES}
|
||||
setValue={updateSetting}
|
||||
valueToDisplayData={VALUE_TO_DISPLAY_DATA}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import PhoneIphoneIcon from '@mui/icons-material/PhoneIphone';
|
||||
import ComputerIcon from '@mui/icons-material/Computer';
|
||||
import AutoModeIcon from '@mui/icons-material/AutoMode';
|
||||
import { IReaderSettings, ReaderOverlayMode } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { ValueToDisplayData } from '@/modules/core/Core.types.ts';
|
||||
import { ButtonSelectInput } from '@/modules/core/components/inputs/ButtonSelectInput.tsx';
|
||||
|
||||
const VALUE_TO_DISPLAY_DATA: ValueToDisplayData<ReaderOverlayMode> = {
|
||||
[ReaderOverlayMode.AUTO]: {
|
||||
title: 'global.label.auto',
|
||||
icon: <AutoModeIcon />,
|
||||
},
|
||||
[ReaderOverlayMode.DESKTOP]: {
|
||||
title: 'global.label.desktop',
|
||||
icon: <ComputerIcon />,
|
||||
},
|
||||
[ReaderOverlayMode.MOBILE]: {
|
||||
title: 'global.label.mobile',
|
||||
icon: <PhoneIphoneIcon />,
|
||||
},
|
||||
};
|
||||
|
||||
const READING_MODE_VALUES = Object.values(ReaderOverlayMode).filter((value) => typeof value === 'number');
|
||||
|
||||
export const ReaderSettingOverlayMode = ({
|
||||
overlayMode,
|
||||
setOverlayMode,
|
||||
}: Pick<IReaderSettings, 'overlayMode'> & {
|
||||
setOverlayMode: (mode: ReaderOverlayMode) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<ButtonSelectInput
|
||||
label={t('reader.settings.overlay_mode')}
|
||||
value={overlayMode}
|
||||
values={READING_MODE_VALUES}
|
||||
setValue={setOverlayMode}
|
||||
valueToDisplayData={VALUE_TO_DISPLAY_DATA}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
|
||||
import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew';
|
||||
import { IReaderSettings, ProgressBarPosition, ReaderOverlayMode } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { ValueToDisplayData } from '@/modules/core/Core.types.ts';
|
||||
import { ButtonSelectInput } from '@/modules/core/components/inputs/ButtonSelectInput.tsx';
|
||||
|
||||
const VALUE_TO_DISPLAY_DATA: ValueToDisplayData<ProgressBarPosition> = {
|
||||
[ProgressBarPosition.BOTTOM]: {
|
||||
title: 'global.label.bottom',
|
||||
icon: <ArrowBackIosNewIcon sx={{ transform: 'rotate(90deg)' }} />,
|
||||
},
|
||||
[ProgressBarPosition.LEFT]: {
|
||||
title: 'global.label.left',
|
||||
icon: <ArrowForwardIosIcon />,
|
||||
},
|
||||
[ProgressBarPosition.RIGHT]: {
|
||||
title: 'global.label.right',
|
||||
icon: <ArrowBackIosNewIcon />,
|
||||
},
|
||||
};
|
||||
|
||||
const PROGRESS_BAR_POSITION_VALUES = Object.values(ProgressBarPosition).filter((value) => typeof value === 'number');
|
||||
|
||||
export const ReaderSettingProgressBarPosition = ({
|
||||
overlayMode,
|
||||
progressBarPosition,
|
||||
setProgressBarPosition,
|
||||
}: Pick<IReaderSettings, 'progressBarPosition' | 'overlayMode'> & {
|
||||
setProgressBarPosition: (position: ProgressBarPosition) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (overlayMode !== ReaderOverlayMode.DESKTOP) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ButtonSelectInput
|
||||
label={t('reader.settings.progress_bar.position')}
|
||||
value={progressBarPosition}
|
||||
values={PROGRESS_BAR_POSITION_VALUES}
|
||||
setValue={setProgressBarPosition}
|
||||
valueToDisplayData={VALUE_TO_DISPLAY_DATA}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettings, ProgressBarType, ReaderOverlayMode } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { SliderInput } from '@/modules/core/components/inputs/SliderInput.tsx';
|
||||
import { DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
|
||||
export const ReaderSettingProgressBarSize = ({
|
||||
overlayMode,
|
||||
progressBarType,
|
||||
progressBarSize,
|
||||
setProgressBarSize,
|
||||
}: Pick<IReaderSettings, 'progressBarType' | 'progressBarSize' | 'overlayMode'> & {
|
||||
setProgressBarSize: (size: number, commit: boolean) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const isChangeable = overlayMode === ReaderOverlayMode.DESKTOP && progressBarType === ProgressBarType.STANDARD;
|
||||
|
||||
if (!isChangeable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<SliderInput
|
||||
label={t('reader.settings.progress_bar.size')}
|
||||
value={t('global.value', { value: progressBarSize, unit: t('global.unit.px') })}
|
||||
slotProps={{
|
||||
slider: {
|
||||
defaultValue: DEFAULT_READER_SETTINGS.progressBarSize,
|
||||
value: progressBarSize,
|
||||
step: 1,
|
||||
min: 2,
|
||||
max: 20,
|
||||
onChange: (_, value) => {
|
||||
setProgressBarSize(value as number, false);
|
||||
},
|
||||
onChangeCommitted: (_, value) => {
|
||||
setProgressBarSize(value as number, true);
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettings, ProgressBarType, ReaderOverlayMode } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { ValueToDisplayData } from '@/modules/core/Core.types.ts';
|
||||
import { HiddenProgressBarIcon } from '@/assets/icons/svg/HiddenProgressBarIcon.tsx';
|
||||
import { StandardProgressBarIcon } from '@/assets/icons/svg/StandardProgressBarIcon.tsx';
|
||||
import { ButtonSelectInput } from '@/modules/core/components/inputs/ButtonSelectInput.tsx';
|
||||
|
||||
const VALUE_TO_DISPLAY_DATA: ValueToDisplayData<ProgressBarType> = {
|
||||
[ProgressBarType.HIDDEN]: {
|
||||
title: 'global.label.hidden',
|
||||
icon: <HiddenProgressBarIcon />,
|
||||
},
|
||||
[ProgressBarType.STANDARD]: {
|
||||
title: 'global.label.standard',
|
||||
icon: <StandardProgressBarIcon />,
|
||||
},
|
||||
};
|
||||
|
||||
const PROGRESS_BAR_TYPE_VALUES = Object.values(ProgressBarType).filter((value) => typeof value === 'number');
|
||||
|
||||
export const ReaderSettingProgressBarType = ({
|
||||
overlayMode,
|
||||
progressBarType,
|
||||
setProgressBarType,
|
||||
}: Pick<IReaderSettings, 'progressBarType' | 'overlayMode'> & {
|
||||
setProgressBarType: (progressBarType: ProgressBarType) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (overlayMode !== ReaderOverlayMode.DESKTOP) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ButtonSelectInput
|
||||
label={t('reader.settings.progress_bar.style')}
|
||||
value={progressBarType}
|
||||
values={PROGRESS_BAR_TYPE_VALUES}
|
||||
setValue={setProgressBarType}
|
||||
valueToDisplayData={VALUE_TO_DISPLAY_DATA}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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 { ContextType } from 'react';
|
||||
import { ReaderSettingReadingMode } from '@/modules/reader/components/settings/layout/ReaderSettingReadingMode.tsx';
|
||||
import { ReaderSettingReadingDirection } from '@/modules/reader/components/settings/layout/ReaderSettingReadingDirection.tsx';
|
||||
import { ReaderSettingTapZoneLayout } from '@/modules/reader/components/settings/layout/ReaderSettingTapZoneLayout.tsx';
|
||||
import { ReaderTapZoneContext } from '@/modules/reader/contexts/ReaderTapZoneContext.tsx';
|
||||
import { ReaderSettingTapZoneInvertMode } from '@/modules/reader/components/settings/layout/ReaderSettingTapZoneInvertMode.tsx';
|
||||
import { ReaderSettingPageScaleMode } from '@/modules/reader/components/settings/layout/ReaderSettingPageScaleMode.tsx';
|
||||
import { ReaderSettingStretchPage } from '@/modules/reader/components/settings/layout/ReaderSettingStretchPage.tsx';
|
||||
import { ReaderSettingsTypeProps } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { ReaderSettingPageGap } from '@/modules/reader/components/settings/layout/ReaderSettingPageGap.tsx';
|
||||
import { ReaderSettingWidth } from '@/modules/reader/components/settings/layout/ReaderSettingWidth.tsx';
|
||||
|
||||
export const ReaderLayoutSettings = ({
|
||||
setShowPreview,
|
||||
settings,
|
||||
updateSetting,
|
||||
isDefaultable,
|
||||
onDefault,
|
||||
}: ReaderSettingsTypeProps & {
|
||||
setShowPreview: ContextType<typeof ReaderTapZoneContext>['setShowPreview'];
|
||||
}) => (
|
||||
<Stack sx={{ gap: 2 }}>
|
||||
<ReaderSettingReadingMode
|
||||
readingMode={settings.readingMode}
|
||||
setReadingMode={(value) => updateSetting('readingMode', value)}
|
||||
isDefaultable={isDefaultable}
|
||||
onDefault={() => onDefault?.('readingMode')}
|
||||
/>
|
||||
<ReaderSettingPageGap
|
||||
pageGap={settings.pageGap}
|
||||
readingMode={settings.readingMode}
|
||||
updateSetting={(...args) => updateSetting('pageGap', ...args)}
|
||||
/>
|
||||
<ReaderSettingReadingDirection
|
||||
readingDirection={settings.readingDirection}
|
||||
setReadingDirection={(value) => updateSetting('readingDirection', value)}
|
||||
isDefaultable={isDefaultable}
|
||||
onDefault={() => onDefault?.('readingDirection')}
|
||||
/>
|
||||
<ReaderSettingTapZoneLayout
|
||||
tapZoneLayout={settings.tapZoneLayout}
|
||||
setTapZoneLayout={(value) => {
|
||||
setShowPreview(true);
|
||||
updateSetting('tapZoneLayout', value);
|
||||
}}
|
||||
isDefaultable={isDefaultable}
|
||||
onDefault={() => {
|
||||
setShowPreview(true);
|
||||
onDefault?.('tapZoneLayout');
|
||||
}}
|
||||
/>
|
||||
<ReaderSettingTapZoneInvertMode
|
||||
tapZoneInvertMode={settings.tapZoneInvertMode}
|
||||
setTapZoneInvertMode={(value) => {
|
||||
setShowPreview(true);
|
||||
updateSetting('tapZoneInvertMode', value);
|
||||
}}
|
||||
isDefaultable={isDefaultable}
|
||||
onDefault={() => {
|
||||
setShowPreview(true);
|
||||
onDefault?.('tapZoneInvertMode');
|
||||
}}
|
||||
/>
|
||||
<ReaderSettingPageScaleMode
|
||||
pageScaleMode={settings.pageScaleMode}
|
||||
setPageScaleMode={(value) => updateSetting('pageScaleMode', value)}
|
||||
isDefaultable={isDefaultable}
|
||||
onDefault={() => onDefault?.('pageScaleMode')}
|
||||
/>
|
||||
<ReaderSettingStretchPage
|
||||
pageScaleMode={settings.pageScaleMode.value}
|
||||
shouldStretchPage={settings.shouldStretchPage.value}
|
||||
setShouldStretchPage={(value) => updateSetting('shouldStretchPage', value)}
|
||||
/>
|
||||
<ReaderSettingWidth
|
||||
readerWidth={settings.readerWidth.value}
|
||||
pageScaleMode={settings.pageScaleMode.value}
|
||||
updateSetting={(...args) => updateSetting(...args)}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettingsWithDefaultFlag, ReadingMode } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { SliderInput } from '@/modules/core/components/inputs/SliderInput.tsx';
|
||||
import { DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
|
||||
export const ReaderSettingPageGap = ({
|
||||
pageGap,
|
||||
readingMode,
|
||||
updateSetting,
|
||||
}: Pick<IReaderSettingsWithDefaultFlag, 'pageGap' | 'readingMode'> & {
|
||||
updateSetting: (gap: number, commit: boolean) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
{[ReadingMode.CONTINUOUS_HORIZONTAL, ReadingMode.CONTINUOUS_VERTICAL].includes(readingMode.value) && (
|
||||
<SliderInput
|
||||
label={t('reader.settings.label.page_gap')}
|
||||
value={t('global.value', { value: pageGap.value, unit: t('global.unit.px') })}
|
||||
slotProps={{
|
||||
slider: {
|
||||
defaultValue: DEFAULT_READER_SETTINGS.readerWidth.value,
|
||||
value: pageGap.value,
|
||||
step: 1,
|
||||
min: 0,
|
||||
max: 20,
|
||||
onChange: (_, value) => {
|
||||
updateSetting(value as number, false);
|
||||
},
|
||||
onChangeCommitted: (_, value) => {
|
||||
updateSetting(value as number, true);
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
IReaderSettings,
|
||||
IReaderSettingsWithDefaultFlag,
|
||||
ReadingDirection,
|
||||
} from '@/modules/reader/types/Reader.types.ts';
|
||||
import {
|
||||
PAGE_SCALE_VALUE_TO_DISPLAY_DATA,
|
||||
READER_PAGE_SCALE_MODE_VALUES,
|
||||
} from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
import { ButtonSelectInput } from '@/modules/core/components/inputs/ButtonSelectInput.tsx';
|
||||
import { MultiValueButtonDefaultableProps } from '@/modules/core/Core.types.ts';
|
||||
|
||||
export const ReaderSettingPageScaleMode = ({
|
||||
pageScaleMode,
|
||||
setPageScaleMode,
|
||||
...buttonSelectInputProps
|
||||
}: Pick<IReaderSettingsWithDefaultFlag, 'pageScaleMode'> &
|
||||
Pick<MultiValueButtonDefaultableProps<ReadingDirection>, 'isDefaultable' | 'onDefault'> & {
|
||||
setPageScaleMode: (mode: IReaderSettings['pageScaleMode']) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<ButtonSelectInput
|
||||
{...buttonSelectInputProps}
|
||||
label={t('reader.settings.page_scale.title')}
|
||||
value={pageScaleMode.isDefault ? undefined : pageScaleMode.value}
|
||||
values={READER_PAGE_SCALE_MODE_VALUES}
|
||||
setValue={setPageScaleMode}
|
||||
valueToDisplayData={PAGE_SCALE_VALUE_TO_DISPLAY_DATA}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 ArrowCircleLeftIcon from '@mui/icons-material/ArrowCircleLeft';
|
||||
import ArrowCircleRightIcon from '@mui/icons-material/ArrowCircleRight';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettingsWithDefaultFlag, ReadingDirection } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { MultiValueButtonDefaultableProps, ValueToDisplayData } from '@/modules/core/Core.types.ts';
|
||||
import { ButtonSelectInput } from '@/modules/core/components/inputs/ButtonSelectInput.tsx';
|
||||
|
||||
const VALUE_TO_DISPLAY_DATA: ValueToDisplayData<ReadingDirection> = {
|
||||
[ReadingDirection.LTR]: {
|
||||
title: 'reader.settings.reading_direction.ltr',
|
||||
icon: <ArrowCircleRightIcon />,
|
||||
},
|
||||
[ReadingDirection.RTL]: {
|
||||
title: 'reader.settings.reading_direction.rtl',
|
||||
icon: <ArrowCircleLeftIcon />,
|
||||
},
|
||||
};
|
||||
|
||||
const READING_DIRECTION_VALUES = Object.values(ReadingDirection).filter((value) => typeof value === 'number');
|
||||
|
||||
export const ReaderSettingReadingDirection = ({
|
||||
readingDirection,
|
||||
setReadingDirection,
|
||||
...buttonSelectInputProps
|
||||
}: Pick<IReaderSettingsWithDefaultFlag, 'readingDirection'> &
|
||||
Pick<MultiValueButtonDefaultableProps<ReadingDirection>, 'isDefaultable' | 'onDefault'> & {
|
||||
setReadingDirection: (readingDirection: ReadingDirection) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<ButtonSelectInput
|
||||
{...buttonSelectInputProps}
|
||||
label={t('reader.settings.label.reading_direction')}
|
||||
value={readingDirection.isDefault ? undefined : readingDirection.value}
|
||||
values={READING_DIRECTION_VALUES}
|
||||
setValue={setReadingDirection}
|
||||
valueToDisplayData={VALUE_TO_DISPLAY_DATA}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettingsWithDefaultFlag, ReadingMode } from '@/modules/reader/types/Reader.types.ts';
|
||||
import {
|
||||
READING_MODE_VALUE_TO_DISPLAY_DATA,
|
||||
READING_MODE_VALUES,
|
||||
} from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
import { ButtonSelectInput } from '@/modules/core/components/inputs/ButtonSelectInput.tsx';
|
||||
import { MultiValueButtonDefaultableProps } from '@/modules/core/Core.types.ts';
|
||||
|
||||
export const ReaderSettingReadingMode = ({
|
||||
readingMode,
|
||||
setReadingMode,
|
||||
...buttonSelectInputProps
|
||||
}: Pick<IReaderSettingsWithDefaultFlag, 'readingMode'> &
|
||||
Pick<MultiValueButtonDefaultableProps<ReadingMode>, 'isDefaultable' | 'onDefault'> & {
|
||||
setReadingMode: (mode: ReadingMode) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<ButtonSelectInput
|
||||
{...buttonSelectInputProps}
|
||||
label={t('reader.settings.label.reading_mode')}
|
||||
value={readingMode.isDefault ? undefined : readingMode.value}
|
||||
values={READING_MODE_VALUES}
|
||||
setValue={setReadingMode}
|
||||
valueToDisplayData={READING_MODE_VALUE_TO_DISPLAY_DATA}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import Button from '@mui/material/Button';
|
||||
import Box from '@mui/material/Box';
|
||||
import { IReaderSettings } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { READER_PAGE_SCALE_MODE_TO_SCALING_ALLOWED } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
|
||||
export const ReaderSettingStretchPage = ({
|
||||
pageScaleMode,
|
||||
shouldStretchPage,
|
||||
setShouldStretchPage,
|
||||
}: Pick<IReaderSettings, 'pageScaleMode' | 'shouldStretchPage'> & {
|
||||
setShouldStretchPage: (mode: IReaderSettings['shouldStretchPage']) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!READER_PAGE_SCALE_MODE_TO_SCALING_ALLOWED[pageScaleMode]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Button
|
||||
onClick={() => setShouldStretchPage(!shouldStretchPage)}
|
||||
variant={shouldStretchPage ? 'contained' : 'outlined'}
|
||||
>
|
||||
{t('reader.settings.page_scale.stretch')}
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import { MultiValueButtonDefaultableProps, ValueToDisplayData } from '@/modules/core/Core.types.ts';
|
||||
import {
|
||||
IReaderSettings,
|
||||
IReaderSettingsWithDefaultFlag,
|
||||
ReadingDirection,
|
||||
} from '@/modules/reader/types/Reader.types.ts';
|
||||
import { TapZoneInvertMode } from '@/modules/reader/types/TapZoneLayout.types.ts';
|
||||
import { ButtonSelectInput } from '@/modules/core/components/inputs/ButtonSelectInput.tsx';
|
||||
|
||||
enum TapZonesInvertOption {
|
||||
NONE,
|
||||
HORIZONTAL,
|
||||
VERTICAL,
|
||||
BOTH,
|
||||
}
|
||||
|
||||
const VALUE_TO_DISPLAY_DATA: ValueToDisplayData<TapZonesInvertOption> = {
|
||||
[TapZonesInvertOption.NONE]: {
|
||||
title: 'global.label.none',
|
||||
icon: null,
|
||||
},
|
||||
[TapZonesInvertOption.HORIZONTAL]: {
|
||||
title: 'global.label.horizontal',
|
||||
icon: null,
|
||||
},
|
||||
[TapZonesInvertOption.VERTICAL]: {
|
||||
title: 'global.label.vertical',
|
||||
icon: null,
|
||||
},
|
||||
[TapZonesInvertOption.BOTH]: {
|
||||
title: 'global.label.both',
|
||||
icon: null,
|
||||
},
|
||||
};
|
||||
|
||||
const TAP_ZONES_INVERT_OPTION_VALUES = Object.values(TapZonesInvertOption).filter((value) => typeof value === 'number');
|
||||
|
||||
const TAP_ZONES_INVERT_OPTION_TO_SETTING: Record<TapZonesInvertOption, TapZoneInvertMode> = {
|
||||
[TapZonesInvertOption.NONE]: {
|
||||
vertical: false,
|
||||
horizontal: false,
|
||||
},
|
||||
[TapZonesInvertOption.HORIZONTAL]: {
|
||||
vertical: false,
|
||||
horizontal: true,
|
||||
},
|
||||
[TapZonesInvertOption.VERTICAL]: {
|
||||
vertical: true,
|
||||
horizontal: false,
|
||||
},
|
||||
[TapZonesInvertOption.BOTH]: {
|
||||
vertical: true,
|
||||
horizontal: true,
|
||||
},
|
||||
};
|
||||
|
||||
const convertTapZoneInvertModeToOption = ({ vertical, horizontal }: TapZoneInvertMode): TapZonesInvertOption => {
|
||||
if (vertical && horizontal) {
|
||||
return TapZonesInvertOption.BOTH;
|
||||
}
|
||||
|
||||
if (vertical) {
|
||||
return TapZonesInvertOption.VERTICAL;
|
||||
}
|
||||
|
||||
if (horizontal) {
|
||||
return TapZonesInvertOption.HORIZONTAL;
|
||||
}
|
||||
|
||||
return TapZonesInvertOption.NONE;
|
||||
};
|
||||
|
||||
export const ReaderSettingTapZoneInvertMode = ({
|
||||
tapZoneInvertMode,
|
||||
setTapZoneInvertMode,
|
||||
...buttonSelectInputProps
|
||||
}: Pick<IReaderSettingsWithDefaultFlag, 'tapZoneInvertMode'> &
|
||||
Pick<MultiValueButtonDefaultableProps<ReadingDirection>, 'isDefaultable' | 'onDefault'> & {
|
||||
setTapZoneInvertMode: (invert: IReaderSettings['tapZoneInvertMode']) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<ButtonSelectInput
|
||||
{...buttonSelectInputProps}
|
||||
label={t('reader.settings.tap_zones.invert')}
|
||||
value={tapZoneInvertMode.isDefault ? undefined : convertTapZoneInvertModeToOption(tapZoneInvertMode.value)}
|
||||
values={TAP_ZONES_INVERT_OPTION_VALUES}
|
||||
setValue={(value) => setTapZoneInvertMode(TAP_ZONES_INVERT_OPTION_TO_SETTING[value])}
|
||||
valueToDisplayData={VALUE_TO_DISPLAY_DATA}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import { TapZoneLayouts } from '@/modules/reader/types/TapZoneLayout.types.ts';
|
||||
import { MultiValueButtonDefaultableProps, ValueToDisplayData } from '@/modules/core/Core.types.ts';
|
||||
import { IReaderSettingsWithDefaultFlag, ReadingDirection } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { ButtonSelectInput } from '@/modules/core/components/inputs/ButtonSelectInput.tsx';
|
||||
|
||||
const VALUE_TO_DISPLAY_DATA: ValueToDisplayData<TapZoneLayouts> = {
|
||||
[TapZoneLayouts.EDGE]: {
|
||||
title: 'reader.settings.tap_zones.edge',
|
||||
icon: null,
|
||||
},
|
||||
[TapZoneLayouts.KINDLE]: {
|
||||
title: 'reader.settings.tap_zones.kindle',
|
||||
icon: null,
|
||||
},
|
||||
[TapZoneLayouts.L_SHAPE]: {
|
||||
title: 'reader.settings.tap_zones.l_shape',
|
||||
icon: null,
|
||||
},
|
||||
[TapZoneLayouts.RIGHT_LEFT]: {
|
||||
title: 'reader.settings.tap_zones.right_left',
|
||||
icon: null,
|
||||
},
|
||||
};
|
||||
|
||||
const READER_TAP_ZONE_LAYOUT_VALUES = Object.values(TapZoneLayouts).filter((value) => typeof value === 'number');
|
||||
|
||||
export const ReaderSettingTapZoneLayout = ({
|
||||
tapZoneLayout,
|
||||
setTapZoneLayout,
|
||||
...buttonSelectInputProps
|
||||
}: Pick<IReaderSettingsWithDefaultFlag, 'tapZoneLayout'> &
|
||||
Pick<MultiValueButtonDefaultableProps<ReadingDirection>, 'isDefaultable' | 'onDefault'> & {
|
||||
setTapZoneLayout: (layout: TapZoneLayouts) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<ButtonSelectInput
|
||||
{...buttonSelectInputProps}
|
||||
label={t('reader.settings.tap_zones.title')}
|
||||
value={tapZoneLayout.isDefault ? undefined : tapZoneLayout.value}
|
||||
values={READER_TAP_ZONE_LAYOUT_VALUES}
|
||||
setValue={setTapZoneLayout}
|
||||
valueToDisplayData={VALUE_TO_DISPLAY_DATA}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 { useTranslation } from 'react-i18next';
|
||||
import { IReaderSettings } from '@/modules/reader/types/Reader.types.ts';
|
||||
import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.tsx';
|
||||
import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
|
||||
import { SliderInput } from '@/modules/core/components/inputs/SliderInput.tsx';
|
||||
import { DEFAULT_READER_SETTINGS } from '@/modules/reader/constants/ReaderSettings.constants.tsx';
|
||||
import { isReaderWidthEditable } from '@/modules/reader/utils/ReaderSettings.utils.tsx';
|
||||
|
||||
export const ReaderSettingWidth = ({
|
||||
readerWidth,
|
||||
pageScaleMode,
|
||||
updateSetting,
|
||||
}: Pick<IReaderSettings, 'readerWidth' | 'pageScaleMode'> & {
|
||||
updateSetting: (...args: OmitFirst<Parameters<typeof ReaderService.updateSetting>>) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!isReaderWidthEditable(pageScaleMode)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<CheckboxInput
|
||||
label={t('reader.settings.label.limit_reader_width')}
|
||||
checked={readerWidth.enabled}
|
||||
onChange={(_, checked) => updateSetting('readerWidth', { ...readerWidth, enabled: checked }, true)}
|
||||
/>
|
||||
{readerWidth.enabled && (
|
||||
<SliderInput
|
||||
label={t('reader.settings.label.reader_width')}
|
||||
value={t('global.value', { value: readerWidth.value, unit: '%' })}
|
||||
slotProps={{
|
||||
slider: {
|
||||
defaultValue: DEFAULT_READER_SETTINGS.readerWidth.value,
|
||||
value: readerWidth.value,
|
||||
step: 1,
|
||||
min: 10,
|
||||
max: 100,
|
||||
onChange: (_, value) => {
|
||||
updateSetting('readerWidth', { ...readerWidth, value: value as number }, false);
|
||||
},
|
||||
onChangeCommitted: (_, value) => {
|
||||
updateSetting('readerWidth', { ...readerWidth, value: value as number }, true);
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user