forked from akai/readest
feat: add options to adjust paragraph margin, word spacing, letter spacing, text indent and font weight (#310)
This commit is contained in:
@@ -8,8 +8,8 @@ import { useDefaultIconSize, useResponsiveSize } from '@/hooks/useResponsiveSize
|
||||
interface DropdownProps {
|
||||
family?: string;
|
||||
selected: string;
|
||||
options: { option: string; label: string }[];
|
||||
moreOptions?: string[];
|
||||
options: { option: string; label?: string }[];
|
||||
moreOptions?: { option: string; label?: string }[];
|
||||
onSelect: (option: string) => void;
|
||||
onGetFontFamily: (option: string, family: string) => string;
|
||||
}
|
||||
@@ -25,7 +25,8 @@ const FontDropdown: React.FC<DropdownProps> = ({
|
||||
const _ = useTranslation();
|
||||
const iconSize16 = useResponsiveSize(16);
|
||||
const defaultIconSize = useDefaultIconSize();
|
||||
const selectedOption = options.find((option) => option.option === selected) || options[0]!;
|
||||
const allOptions = [...options, ...(moreOptions ?? [])];
|
||||
const selectedOption = allOptions.find((option) => option.option === selected) ?? allOptions[0]!;
|
||||
return (
|
||||
<div className='dropdown dropdown-top'>
|
||||
<button
|
||||
@@ -68,14 +69,14 @@ const FontDropdown: React.FC<DropdownProps> = ({
|
||||
'!mr-5 mb-[-46px] inline max-h-80 w-[200px] overflow-y-scroll',
|
||||
)}
|
||||
>
|
||||
{moreOptions.map((option) => (
|
||||
<li key={option} onClick={() => onSelect(option)}>
|
||||
{moreOptions.map((option, index) => (
|
||||
<li key={`${index}-${option.option}`} onClick={() => onSelect(option.option)}>
|
||||
<div className='flex items-center px-2'>
|
||||
<span style={{ minWidth: `${defaultIconSize}px` }}>
|
||||
{selected === option && <MdCheck className='text-base-content' />}
|
||||
{selected === option.option && <MdCheck className='text-base-content' />}
|
||||
</span>
|
||||
<span style={{ fontFamily: onGetFontFamily(option, family ?? '') }}>
|
||||
{option}
|
||||
<span style={{ fontFamily: onGetFontFamily(option.option, family ?? '') }}>
|
||||
{option.label || option.option}
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@@ -50,7 +50,7 @@ const FontFace = ({
|
||||
<FontDropdown
|
||||
family={family}
|
||||
options={options.map((option) => ({ option, label: option }))}
|
||||
moreOptions={moreOptions}
|
||||
moreOptions={moreOptions?.map((option) => ({ option, label: option })) ?? []}
|
||||
selected={selected}
|
||||
onSelect={onSelect}
|
||||
onGetFontFamily={handleFontFaceFont}
|
||||
@@ -106,6 +106,7 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
const [serifFont, setSerifFont] = useState(viewSettings.serifFont!);
|
||||
const [sansSerifFont, setSansSerifFont] = useState(viewSettings.sansSerifFont!);
|
||||
const [monospaceFont, setMonospaceFont] = useState(viewSettings.monospaceFont!);
|
||||
const [fontWeight, setFontWeight] = useState(viewSettings.fontWeight!);
|
||||
|
||||
useEffect(() => {
|
||||
if (isTauriAppPlatform() && FONT_ENUM_SUPPORTED_OS_PLATFORMS.includes(osPlatform)) {
|
||||
@@ -149,6 +150,17 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [minFontSize]);
|
||||
|
||||
useEffect(() => {
|
||||
viewSettings.fontWeight = fontWeight;
|
||||
setViewSettings(bookKey, viewSettings);
|
||||
if (isFontLayoutSettingsGlobal) {
|
||||
settings.globalViewSettings.fontWeight = fontWeight;
|
||||
setSettings(settings);
|
||||
}
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [fontWeight]);
|
||||
|
||||
useEffect(() => {
|
||||
viewSettings.serifFont = serifFont;
|
||||
setViewSettings(bookKey, viewSettings);
|
||||
@@ -232,6 +244,23 @@ const FontPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='w-full'>
|
||||
<h2 className='mb-2 font-medium'>{_('Font Weight')}</h2>
|
||||
<div className='card border-base-200 border shadow'>
|
||||
<div className='divide-base-200 divide-y'>
|
||||
<NumberInput
|
||||
className='config-item-top'
|
||||
label={_('Font Weight')}
|
||||
value={fontWeight}
|
||||
onChange={setFontWeight}
|
||||
min={100}
|
||||
max={900}
|
||||
step={100}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='w-full'>
|
||||
<h2 className='mb-2 font-medium'>{_('Font Family')}</h2>
|
||||
<div className='card border-base-200 border shadow'>
|
||||
|
||||
@@ -22,7 +22,11 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
const viewSettings = getViewSettings(bookKey)!;
|
||||
const { themeCode } = useTheme();
|
||||
|
||||
const [paragraphMargin, setParagraphMargin] = useState(viewSettings.paragraphMargin!);
|
||||
const [lineHeight, setLineHeight] = useState(viewSettings.lineHeight!);
|
||||
const [wordSpacing, setWordSpacing] = useState(viewSettings.wordSpacing!);
|
||||
const [letterSpacing, setLetterSpacing] = useState(viewSettings.letterSpacing!);
|
||||
const [textIndent, setTextIndent] = useState(viewSettings.textIndent!);
|
||||
const [fullJustification, setFullJustification] = useState(viewSettings.fullJustification!);
|
||||
const [hyphenation, setHyphenation] = useState(viewSettings.hyphenation!);
|
||||
const [marginPx, setMarginPx] = useState(viewSettings.marginPx!);
|
||||
@@ -32,6 +36,17 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
const [maxBlockSize, setMaxBlockSize] = useState(viewSettings.maxBlockSize!);
|
||||
const [writingMode, setWritingMode] = useState(viewSettings.writingMode!);
|
||||
|
||||
useEffect(() => {
|
||||
viewSettings.paragraphMargin = paragraphMargin;
|
||||
setViewSettings(bookKey, viewSettings);
|
||||
if (isFontLayoutSettingsGlobal) {
|
||||
settings.globalViewSettings.paragraphMargin = paragraphMargin;
|
||||
setSettings(settings);
|
||||
}
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [paragraphMargin]);
|
||||
|
||||
useEffect(() => {
|
||||
viewSettings.lineHeight = lineHeight;
|
||||
setViewSettings(bookKey, viewSettings);
|
||||
@@ -43,6 +58,39 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [lineHeight]);
|
||||
|
||||
useEffect(() => {
|
||||
viewSettings.wordSpacing = wordSpacing;
|
||||
setViewSettings(bookKey, viewSettings);
|
||||
if (isFontLayoutSettingsGlobal) {
|
||||
settings.globalViewSettings.wordSpacing = wordSpacing;
|
||||
setSettings(settings);
|
||||
}
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [wordSpacing]);
|
||||
|
||||
useEffect(() => {
|
||||
viewSettings.letterSpacing = letterSpacing;
|
||||
setViewSettings(bookKey, viewSettings);
|
||||
if (isFontLayoutSettingsGlobal) {
|
||||
settings.globalViewSettings.letterSpacing = letterSpacing;
|
||||
setSettings(settings);
|
||||
}
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [letterSpacing]);
|
||||
|
||||
useEffect(() => {
|
||||
viewSettings.textIndent = textIndent;
|
||||
setViewSettings(bookKey, viewSettings);
|
||||
if (isFontLayoutSettingsGlobal) {
|
||||
settings.globalViewSettings.textIndent = textIndent;
|
||||
setSettings(settings);
|
||||
}
|
||||
view?.renderer.setStyles?.(getStyles(viewSettings, themeCode));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [textIndent]);
|
||||
|
||||
useEffect(() => {
|
||||
viewSettings.fullJustification = fullJustification;
|
||||
setViewSettings(bookKey, viewSettings);
|
||||
@@ -188,13 +236,49 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
<div className='divide-base-200 divide-y'>
|
||||
<NumberInput
|
||||
className='config-item-top'
|
||||
label={_('Line Height')}
|
||||
label={_('Paragraph Margin')}
|
||||
value={paragraphMargin}
|
||||
onChange={setParagraphMargin}
|
||||
min={0}
|
||||
max={4}
|
||||
step={0.5}
|
||||
/>
|
||||
<NumberInput
|
||||
className='config-item-top'
|
||||
label={_('Line Spacing')}
|
||||
value={lineHeight}
|
||||
onChange={setLineHeight}
|
||||
min={1.0}
|
||||
max={3.0}
|
||||
step={0.1}
|
||||
/>
|
||||
<NumberInput
|
||||
className='config-item-top'
|
||||
label={_('Word Spacing')}
|
||||
value={wordSpacing}
|
||||
onChange={setWordSpacing}
|
||||
min={-4}
|
||||
max={8}
|
||||
step={0.5}
|
||||
/>
|
||||
<NumberInput
|
||||
className='config-item-top'
|
||||
label={_('Letter Spacing')}
|
||||
value={letterSpacing}
|
||||
onChange={setLetterSpacing}
|
||||
min={-2}
|
||||
max={4}
|
||||
step={0.1}
|
||||
/>
|
||||
<NumberInput
|
||||
className='config-item-top'
|
||||
label={_('Text Indent')}
|
||||
value={textIndent}
|
||||
onChange={setTextIndent}
|
||||
min={-2}
|
||||
max={4}
|
||||
step={1}
|
||||
/>
|
||||
<div className='config-item config-item-bottom'>
|
||||
<span className=''>{_('Full Justification')}</span>
|
||||
<input
|
||||
|
||||
Reference in New Issue
Block a user