Skip to content

Commit 61382f2

Browse files
committed
feat: add foreground color style for iOS
1 parent fa2109e commit 61382f2

15 files changed

Lines changed: 745 additions & 14 deletions

android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,10 @@ class EnrichedTextInputViewManager : SimpleViewManager<EnrichedTextInputView>(),
251251
view?.verifyAndToggleStyle(EnrichedSpans.UNORDERED_LIST)
252252
}
253253

254+
override fun toggleColor(view: EnrichedTextInputView?, color: String) {
255+
// no-op for now
256+
}
257+
254258
override fun addLink(view: EnrichedTextInputView?, start: Int, end: Int, text: String, url: String) {
255259
view?.addLink(start, end, text, url)
256260
}

example/src/App.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import { type MentionItem, MentionPopup } from './components/MentionPopup';
2727
import { useUserMention } from './useUserMention';
2828
import { useChannelMention } from './useChannelMention';
2929
import { HtmlSection } from './components/HtmlSection';
30+
import type { OnChangeColorEvent } from '../../src/EnrichedTextInputNativeComponent';
31+
import ColorPreview from './components/ColorPreview';
3032

3133
type StylesState = OnChangeStateEvent;
3234

@@ -38,6 +40,8 @@ interface Selection {
3840
text: string;
3941
}
4042

43+
const PRIMARY_COLOR = '#000000';
44+
4145
const DEFAULT_STYLE: StylesState = {
4246
isBold: false,
4347
isItalic: false,
@@ -54,6 +58,7 @@ const DEFAULT_STYLE: StylesState = {
5458
isLink: false,
5559
isImage: false,
5660
isMention: false,
61+
isColored: false,
5762
};
5863

5964
const DEFAULT_LINK_STATE = {
@@ -82,6 +87,7 @@ export default function App() {
8287
const [stylesState, setStylesState] = useState<StylesState>(DEFAULT_STYLE);
8388
const [currentLink, setCurrentLink] =
8489
useState<CurrentLinkState>(DEFAULT_LINK_STATE);
90+
const [selectionColor, setSelectionColor] = useState<string>(PRIMARY_COLOR);
8591

8692
const ref = useRef<EnrichedTextInputInstance>(null);
8793

@@ -257,6 +263,14 @@ export default function App() {
257263
setSelection(e.nativeEvent);
258264
};
259265

266+
const handleSelectionColorChange = (
267+
e: NativeSyntheticEvent<OnChangeColorEvent>
268+
) => {
269+
if (e.nativeEvent.color) {
270+
setSelectionColor(e.nativeEvent.color);
271+
}
272+
};
273+
260274
return (
261275
<>
262276
<ScrollView
@@ -275,6 +289,7 @@ export default function App() {
275289
selectionColor="deepskyblue"
276290
cursorColor="dodgerblue"
277291
autoCapitalize="sentences"
292+
onColorChangeInSelection={handleSelectionColorChange}
278293
onChangeText={handleChangeText}
279294
onChangeHtml={handleChangeHtml}
280295
onChangeState={handleChangeState}
@@ -293,6 +308,7 @@ export default function App() {
293308
<Toolbar
294309
stylesState={stylesState}
295310
editorRef={ref}
311+
selectionColor={selectionColor}
296312
onOpenLinkModal={openLinkModal}
297313
onSelectImage={selectImage}
298314
/>
@@ -308,6 +324,7 @@ export default function App() {
308324
/>
309325
<HtmlSection currentHtml={currentHtml} />
310326
{DEBUG_SCROLLABLE && <View style={styles.scrollPlaceholder} />}
327+
<ColorPreview color={selectionColor} />
311328
</ScrollView>
312329
<LinkModal
313330
isOpen={isLinkModalOpen}
@@ -368,7 +385,7 @@ const htmlStyle: HtmlStyle = {
368385
backgroundColor: 'yellow',
369386
},
370387
a: {
371-
color: 'green',
388+
color: 'blue',
372389
textDecorationLine: 'underline',
373390
},
374391
mention: {
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { StyleSheet, Text, View } from 'react-native';
2+
3+
type Props = {
4+
color: string;
5+
};
6+
7+
const ColorPreview: React.FC<Props> = ({ color }) => {
8+
return (
9+
<>
10+
<View
11+
style={[
12+
styles.preview,
13+
{
14+
backgroundColor: color,
15+
},
16+
]}
17+
/>
18+
<Text>{color}</Text>
19+
</>
20+
);
21+
};
22+
23+
const styles = StyleSheet.create({
24+
preview: {
25+
marginVertical: 8,
26+
width: 40,
27+
height: 40,
28+
borderRadius: 20,
29+
},
30+
});
31+
32+
export default ColorPreview;

example/src/components/Toolbar.tsx

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {
55
EnrichedTextInputInstance,
66
} from 'react-native-enriched';
77
import type { FC } from 'react';
8+
import { ToolbarColorButton } from './ToolbarColorButton';
89

910
const STYLE_ITEMS = [
1011
{
@@ -67,6 +68,16 @@ const STYLE_ITEMS = [
6768
name: 'ordered-list',
6869
icon: 'list-ol',
6970
},
71+
{
72+
name: 'color',
73+
value: '#FF0000',
74+
text: 'A',
75+
},
76+
{
77+
name: 'color',
78+
value: '#E6FF5C',
79+
text: 'A',
80+
},
7081
] as const;
7182

7283
type Item = (typeof STYLE_ITEMS)[number];
@@ -77,13 +88,15 @@ export interface ToolbarProps {
7788
editorRef?: React.RefObject<EnrichedTextInputInstance | null>;
7889
onOpenLinkModal: () => void;
7990
onSelectImage: () => void;
91+
selectionColor: string | null;
8092
}
8193

8294
export const Toolbar: FC<ToolbarProps> = ({
8395
stylesState,
8496
editorRef,
8597
onOpenLinkModal,
8698
onSelectImage,
99+
selectionColor,
87100
}) => {
88101
const handlePress = (item: Item) => {
89102
const currentRef = editorRef?.current;
@@ -138,6 +151,10 @@ export const Toolbar: FC<ToolbarProps> = ({
138151
}
139152
};
140153

154+
const handleColorButtonPress = (color: string) => {
155+
editorRef?.current?.toggleColor(color);
156+
};
157+
141158
const isActive = (item: Item) => {
142159
switch (item.name) {
143160
case 'bold':
@@ -176,7 +193,14 @@ export const Toolbar: FC<ToolbarProps> = ({
176193
};
177194

178195
const renderItem = ({ item }: ListRenderItemInfo<Item>) => {
179-
return (
196+
return item.name === 'color' ? (
197+
<ToolbarColorButton
198+
onPress={handleColorButtonPress}
199+
color={item.value}
200+
text={item.text}
201+
isActive={stylesState.isColored && selectionColor === item.value}
202+
/>
203+
) : (
180204
<ToolbarButton
181205
{...item}
182206
isActive={isActive(item)}
@@ -185,7 +209,8 @@ export const Toolbar: FC<ToolbarProps> = ({
185209
);
186210
};
187211

188-
const keyExtractor = (item: Item) => item.name;
212+
const keyExtractor = (item: Item) =>
213+
item.name === 'color' ? item.value : item.name;
189214

190215
return (
191216
<FlatList
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { useMemo, type FC } from 'react';
2+
import { Pressable, StyleSheet, Text } from 'react-native';
3+
4+
interface ColorButtonProps {
5+
text: string;
6+
isActive: boolean;
7+
onPress: (color: string) => void;
8+
color: string;
9+
}
10+
11+
export const ToolbarColorButton: FC<ColorButtonProps> = ({
12+
text,
13+
isActive,
14+
onPress,
15+
color,
16+
}) => {
17+
const handlePress = () => {
18+
onPress(color);
19+
};
20+
21+
const containerStyle = useMemo(
22+
() => [
23+
styles.container,
24+
{ backgroundColor: isActive ? color : 'rgba(0, 26, 114, 0.8)' },
25+
],
26+
[isActive, color]
27+
);
28+
29+
return (
30+
<Pressable style={containerStyle} onPress={handlePress}>
31+
<Text style={[styles.text, !isActive && { color }]}>{text}</Text>
32+
</Pressable>
33+
);
34+
};
35+
36+
const styles = StyleSheet.create({
37+
container: {
38+
justifyContent: 'center',
39+
alignItems: 'center',
40+
width: 56,
41+
height: 56,
42+
},
43+
text: {
44+
color: 'white',
45+
fontSize: 20,
46+
},
47+
});

0 commit comments

Comments
 (0)