forked from akai/readest
feat: more highlight colours (#3062)
* feat(highlight): extend types and constants for custom hex colours * feat(highlight): update colour to support hex strings * refactor(annotator): update component to accept hex colours * feat(highlight): add colour picker with max 4 custom colors * feat(settings): add custom colour editor with limit * refactor: custom highlight colors can only be modified in settings --------- Co-authored-by: Huang Xin <chrox.huang@gmail.com>
This commit is contained in:
@@ -9,7 +9,15 @@ import { useResponsiveSize } from '@/hooks/useResponsiveSize';
|
||||
import { saveSysSettings } from '@/helpers/settings';
|
||||
|
||||
const styles = ['highlight', 'underline', 'squiggly'] as HighlightStyle[];
|
||||
const colors = ['red', 'violet', 'blue', 'green', 'yellow'] as HighlightColor[];
|
||||
const defaultColors = ['red', 'violet', 'blue', 'green', 'yellow'] as HighlightColor[];
|
||||
|
||||
const getColorHex = (
|
||||
customColors: Record<HighlightColor, string>,
|
||||
color: HighlightColor,
|
||||
): string => {
|
||||
if (color.startsWith('#')) return color;
|
||||
return customColors[color as HighlightColor] ?? color;
|
||||
};
|
||||
|
||||
interface HighlightOptionsProps {
|
||||
isVertical: boolean;
|
||||
@@ -41,6 +49,7 @@ const HighlightOptions: React.FC<HighlightOptionsProps> = ({
|
||||
const einkBgColor = isDarkMode ? '#000000' : '#ffffff';
|
||||
const einkFgColor = isDarkMode ? '#ffffff' : '#000000';
|
||||
const customColors = globalReadSettings.customHighlightColors;
|
||||
const userColors = globalReadSettings.userHighlightColors ?? [];
|
||||
const [selectedStyle, setSelectedStyle] = useState<HighlightStyle>(_selectedStyle);
|
||||
const [selectedColor, setSelectedColor] = useState<HighlightColor>(_selectedColor);
|
||||
const size16 = useResponsiveSize(16);
|
||||
@@ -55,6 +64,7 @@ const HighlightOptions: React.FC<HighlightOptionsProps> = ({
|
||||
setSelectedColor(globalReadSettings.highlightStyles[style]);
|
||||
onHandleHighlight(true);
|
||||
};
|
||||
|
||||
const handleSelectColor = (color: HighlightColor) => {
|
||||
const newGlobalReadSettings = {
|
||||
...globalReadSettings,
|
||||
@@ -65,10 +75,11 @@ const HighlightOptions: React.FC<HighlightOptionsProps> = ({
|
||||
setSelectedColor(color);
|
||||
onHandleHighlight(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'highlight-options absolute flex items-center justify-between',
|
||||
'highlight-options absolute flex items-center justify-between gap-4',
|
||||
isVertical ? 'flex-col' : 'flex-row',
|
||||
)}
|
||||
style={{
|
||||
@@ -106,7 +117,9 @@ const HighlightOptions: React.FC<HighlightOptionsProps> = ({
|
||||
height: size16,
|
||||
...(style === 'highlight' &&
|
||||
selectedStyle === 'highlight' && {
|
||||
backgroundColor: isEink ? einkFgColor : customColors[selectedColor],
|
||||
backgroundColor: isEink
|
||||
? einkFgColor
|
||||
: getColorHex(customColors, selectedColor),
|
||||
color: isEink ? einkBgColor : '#d1d5db',
|
||||
paddingTop: '2px',
|
||||
}),
|
||||
@@ -123,7 +136,7 @@ const HighlightOptions: React.FC<HighlightOptionsProps> = ({
|
||||
selectedStyle === style
|
||||
? isEink
|
||||
? einkFgColor
|
||||
: customColors[selectedColor]
|
||||
: getColorHex(customColors, selectedColor)
|
||||
: '#d1d5db',
|
||||
...(style === 'squiggly' && { textDecorationStyle: 'wavy' }),
|
||||
}),
|
||||
@@ -138,31 +151,39 @@ const HighlightOptions: React.FC<HighlightOptionsProps> = ({
|
||||
|
||||
<div
|
||||
className={clsx(
|
||||
'not-eink:bg-gray-700 eink-bordered flex items-center justify-center gap-2 rounded-3xl',
|
||||
isVertical ? 'flex-col py-2' : 'flex-row px-2',
|
||||
'not-eink:bg-gray-700 eink-bordered flex items-center gap-2 rounded-3xl',
|
||||
isVertical ? 'flex-col overflow-y-auto py-2' : 'flex-row overflow-x-auto px-2',
|
||||
)}
|
||||
style={isVertical ? { width: size28 } : { height: size28 }}
|
||||
style={{
|
||||
...(isVertical ? { width: size28 } : { height: size28 }),
|
||||
scrollbarWidth: 'none',
|
||||
msOverflowStyle: 'none',
|
||||
}}
|
||||
>
|
||||
{colors
|
||||
{defaultColors
|
||||
.concat(userColors)
|
||||
.filter((c) => (isEink ? selectedColor === c : true))
|
||||
.map((color) => (
|
||||
<button
|
||||
key={color}
|
||||
onClick={() => handleSelectColor(color)}
|
||||
style={{
|
||||
width: size16,
|
||||
height: size16,
|
||||
backgroundColor: selectedColor !== color ? customColors[color] : 'transparent',
|
||||
}}
|
||||
className='rounded-full p-0'
|
||||
>
|
||||
{selectedColor === color && (
|
||||
<FaCheckCircle
|
||||
size={size16}
|
||||
style={{ fill: isEink ? einkFgColor : customColors[color] }}
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
<div key={color} className='flex items-center justify-center'>
|
||||
<button
|
||||
key={color}
|
||||
onClick={() => handleSelectColor(color)}
|
||||
style={{
|
||||
width: size16,
|
||||
height: size16,
|
||||
backgroundColor:
|
||||
selectedColor !== color ? customColors[color] || color : 'transparent',
|
||||
}}
|
||||
className='rounded-full p-0'
|
||||
>
|
||||
{selectedColor === color && (
|
||||
<FaCheckCircle
|
||||
size={size16}
|
||||
style={{ fill: isEink ? einkFgColor : customColors[color] || color }}
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user