Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,14 @@ class EnrichedTextInputViewManager : SimpleViewManager<EnrichedTextInputView>(),
view?.verifyAndToggleStyle(EnrichedSpans.UNORDERED_LIST)
}

override fun setColor(view: EnrichedTextInputView?, color: String) {
// no-op for now
}

override fun removeColor(view: EnrichedTextInputView?) {
// no-op for now
}

override fun addLink(view: EnrichedTextInputView?, start: Int, end: Int, text: String, url: String) {
view?.addLink(start, end, text, url)
}
Expand Down
28 changes: 27 additions & 1 deletion example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import {
DEFAULT_IMAGE_WIDTH,
prepareImageDimensions,
} from './utils/prepareImageDimensions';
import type { OnChangeColorEvent } from '../../src/EnrichedTextInputNativeComponent';
import { ColorPreview } from './components/ColorPreview';

type StylesState = OnChangeStateEvent;

Expand All @@ -44,6 +46,8 @@ interface Selection {
text: string;
}

const PRIMARY_COLOR = '#000000';

const DEFAULT_STYLE: StylesState = {
isBold: false,
isItalic: false,
Expand All @@ -60,6 +64,7 @@ const DEFAULT_STYLE: StylesState = {
isLink: false,
isImage: false,
isMention: false,
isColored: false,
};

const DEFAULT_LINK_STATE = {
Expand Down Expand Up @@ -89,6 +94,7 @@ export default function App() {
const [stylesState, setStylesState] = useState<StylesState>(DEFAULT_STYLE);
const [currentLink, setCurrentLink] =
useState<CurrentLinkState>(DEFAULT_LINK_STATE);
const [selectionColor, setSelectionColor] = useState<string>(PRIMARY_COLOR);

const ref = useRef<EnrichedTextInputInstance>(null);

Expand Down Expand Up @@ -292,6 +298,18 @@ export default function App() {
setSelection(e.nativeEvent);
};

const handleSelectionColorChange = (
e: NativeSyntheticEvent<OnChangeColorEvent>
) => {
if (e.nativeEvent.color) {
setSelectionColor(e.nativeEvent.color);
}
};

const handleRemoveColor = () => {
ref.current?.removeColor();
};

return (
<>
<ScrollView
Expand All @@ -310,6 +328,7 @@ export default function App() {
selectionColor="deepskyblue"
cursorColor="dodgerblue"
autoCapitalize="sentences"
onColorChangeInSelection={handleSelectionColorChange}
onChangeText={handleChangeText}
onChangeHtml={handleChangeHtml}
onChangeState={handleChangeState}
Expand All @@ -328,6 +347,7 @@ export default function App() {
<Toolbar
stylesState={stylesState}
editorRef={ref}
selectionColor={selectionColor}
onOpenLinkModal={openLinkModal}
onSelectImage={openImageModal}
/>
Expand All @@ -341,8 +361,14 @@ export default function App() {
onPress={openValueModal}
style={styles.valueButton}
/>
<Button
title="Remove color"
onPress={handleRemoveColor}
style={styles.valueButton}
/>
<HtmlSection currentHtml={currentHtml} />
{DEBUG_SCROLLABLE && <View style={styles.scrollPlaceholder} />}
<ColorPreview color={selectionColor} />
</ScrollView>
<LinkModal
isOpen={isLinkModalOpen}
Expand Down Expand Up @@ -408,7 +434,7 @@ const htmlStyle: HtmlStyle = {
backgroundColor: 'yellow',
},
a: {
color: 'green',
color: 'blue',
textDecorationLine: 'underline',
},
mention: {
Expand Down
31 changes: 31 additions & 0 deletions example/src/components/ColorPreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { StyleSheet, Text, View } from 'react-native';
import { type FC } from 'react';

type Props = {
color: string;
};

export const ColorPreview: FC<Props> = ({ color }) => {
return (
<>
<View
style={[
styles.preview,
{
backgroundColor: color,
},
]}
/>
<Text>{color}</Text>
</>
);
};

const styles = StyleSheet.create({
preview: {
marginVertical: 8,
width: 40,
height: 40,
borderRadius: 20,
},
});
29 changes: 27 additions & 2 deletions example/src/components/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
EnrichedTextInputInstance,
} from 'react-native-enriched';
import type { FC } from 'react';
import { ToolbarColorButton } from './ToolbarColorButton';

const STYLE_ITEMS = [
{
Expand Down Expand Up @@ -67,6 +68,16 @@ const STYLE_ITEMS = [
name: 'ordered-list',
icon: 'list-ol',
},
{
name: 'color',
value: '#FF0000',
text: 'A',
},
{
name: 'color',
value: '#E6FF5C',
text: 'A',
},
] as const;

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

export const Toolbar: FC<ToolbarProps> = ({
stylesState,
editorRef,
onOpenLinkModal,
onSelectImage,
selectionColor,
}) => {
const handlePress = (item: Item) => {
const currentRef = editorRef?.current;
Expand Down Expand Up @@ -138,6 +151,10 @@ export const Toolbar: FC<ToolbarProps> = ({
}
};

const handleColorButtonPress = (color: string) => {
editorRef?.current?.setColor(color);
};

const isActive = (item: Item) => {
switch (item.name) {
case 'bold':
Expand Down Expand Up @@ -176,7 +193,14 @@ export const Toolbar: FC<ToolbarProps> = ({
};

const renderItem = ({ item }: ListRenderItemInfo<Item>) => {
return (
return item.name === 'color' ? (
<ToolbarColorButton
onPress={handleColorButtonPress}
color={item.value}
text={item.text}
isActive={stylesState.isColored && selectionColor === item.value}
/>
) : (
<ToolbarButton
{...item}
isActive={isActive(item)}
Expand All @@ -185,7 +209,8 @@ export const Toolbar: FC<ToolbarProps> = ({
);
};

const keyExtractor = (item: Item) => item.name;
const keyExtractor = (item: Item) =>
item.name === 'color' ? item.value : item.name;

return (
<FlatList
Expand Down
47 changes: 47 additions & 0 deletions example/src/components/ToolbarColorButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useMemo, type FC } from 'react';
import { Pressable, StyleSheet, Text } from 'react-native';

interface ColorButtonProps {
text: string;
isActive: boolean;
onPress: (color: string) => void;
color: string;
}

export const ToolbarColorButton: FC<ColorButtonProps> = ({
text,
isActive,
onPress,
color,
}) => {
const handlePress = () => {
onPress(color);
};

const containerStyle = useMemo(
() => [
styles.container,
{ backgroundColor: isActive ? color : 'rgba(0, 26, 114, 0.8)' },
],
[isActive, color]
);
Comment on lines +21 to +27
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useMemo seems redundant here, it's simple computation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I added this because of the warning about embedded styles.
  2. useMemo is useful here because it is an array that will create a new reference each time it is re-rendered and cause re-rendering in child components. Ideally, all values that arecallbacks, arrays and objects should be wrapped with useCallback/useMemo if you are not using react-compiler (sometimes even if you are using it).


return (
<Pressable style={containerStyle} onPress={handlePress}>
<Text style={[styles.text, !isActive && { color }]}>{text}</Text>
</Pressable>
);
};

const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center',
width: 56,
height: 56,
},
text: {
color: 'white',
fontSize: 20,
},
});
Loading
Loading