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
26 changes: 2 additions & 24 deletions src/MarkdownTextInput.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import {StyleSheet, TextInput, processColor} from 'react-native';
import {StyleSheet, TextInput} from 'react-native';
import React from 'react';
import type {TextInputProps} from 'react-native';
import {createSerializable, createWorkletRuntime} from 'react-native-worklets';
import type {SerializableRef, WorkletFunction, WorkletRuntime} from 'react-native-worklets';
import MarkdownTextInputDecoratorViewNativeComponent from './MarkdownTextInputDecoratorViewNativeComponent';
import type {MarkdownStyle} from './MarkdownTextInputDecoratorViewNativeComponent';
import NativeLiveMarkdownModule from './NativeLiveMarkdownModule';
import {mergeMarkdownStyleWithDefault} from './styleUtils';
import {processMarkdownStyle} from './styleUtils';
import type {PartialMarkdownStyle} from './styleUtils';
import type {InlineImagesInputProps, MarkdownRange} from './commonTypes';

Expand Down Expand Up @@ -70,27 +69,6 @@ type FormatSelectionResult = {

type MarkdownTextInput = TextInput & React.Component<MarkdownTextInputProps>;

function processColorsInMarkdownStyle(input: MarkdownStyle): MarkdownStyle {
const output = JSON.parse(JSON.stringify(input));

Object.keys(output).forEach((key) => {
const obj = output[key];
Object.keys(obj).forEach((prop) => {
// TODO: use ReactNativeStyleAttributes from 'react-native/Libraries/Components/View/ReactNativeStyleAttributes'
if (!(prop === 'color' || prop.endsWith('Color'))) {
return;
}
obj[prop] = processColor(obj[prop]);
});
});

return output as MarkdownStyle;
}

function processMarkdownStyle(input: PartialMarkdownStyle | undefined): MarkdownStyle {
return processColorsInMarkdownStyle(mergeMarkdownStyleWithDefault(input));
}

const MarkdownTextInput = React.forwardRef<MarkdownTextInput, MarkdownTextInputProps>((props, ref) => {
const markdownStyle = React.useMemo(() => processMarkdownStyle(props.markdownStyle), [props.markdownStyle]);

Expand Down
46 changes: 46 additions & 0 deletions src/__tests__/processMarkdownStyle.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {processColor} from 'react-native';
import {processMarkdownStyle} from '../styleUtils';

describe('processMarkdownStyle', () => {
it('falls back to the default color when markdownStyle has an invalid color name', () => {
const style = processMarkdownStyle({
pre: {
backgroundColor: 'tranparent',
},
});

expect(style.pre.backgroundColor).toBe(processColor('lightgray'));
});

it('falls back to the default color when markdownStyle uses CSS none', () => {
const style = processMarkdownStyle({
pre: {
backgroundColor: 'none',
},
});

expect(style.pre.backgroundColor).toBe(processColor('lightgray'));
});

it('still processes valid colors', () => {
const style = processMarkdownStyle({
pre: {
backgroundColor: 'red',
},
});

expect(style.pre.backgroundColor).toBe(processColor('red'));
});

it('does not let one invalid color wipe other processed colors', () => {
const style = processMarkdownStyle({
pre: {
color: 'blue',
backgroundColor: 'tranparent',
},
});

expect(style.pre.color).toBe(processColor('blue'));
expect(style.pre.backgroundColor).toBe(processColor('lightgray'));
});
});
29 changes: 27 additions & 2 deletions src/styleUtils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Platform} from 'react-native';
import {Platform, processColor} from 'react-native';
import type {MarkdownStyle} from './MarkdownTextInputDecoratorViewNativeComponent';

type PartialMarkdownStyle = Partial<{
Expand Down Expand Up @@ -116,6 +116,31 @@ function parseStringWithUnitToNumber(value: string | number | null): number {
return value ? parseInt(value.replace('px', ''), 10) : 0;
}

function processColorsInMarkdownStyle(input: MarkdownStyle): MarkdownStyle {
const output = JSON.parse(JSON.stringify(input));
const defaults = JSON.parse(JSON.stringify(makeDefaultMarkdownStyle()));

Object.keys(output).forEach((key) => {
const obj = output[key];
const defaultObj = defaults[key];
Object.keys(obj).forEach((prop) => {
// TODO: use ReactNativeStyleAttributes from 'react-native/Libraries/Components/View/ReactNativeStyleAttributes'
if (!(prop === 'color' || prop.endsWith('Color'))) {
return;
}
const processed = processColor(obj[prop]);
// processColor returns null/undefined for invalid names; nil colors crash iOS.
obj[prop] = processed ?? processColor(defaultObj?.[prop]);
});
});

return output as MarkdownStyle;
}

function processMarkdownStyle(input: PartialMarkdownStyle | undefined): MarkdownStyle {
return processColorsInMarkdownStyle(mergeMarkdownStyleWithDefault(input));
}

export type {PartialMarkdownStyle};

export {mergeMarkdownStyleWithDefault, parseStringWithUnitToNumber};
export {mergeMarkdownStyleWithDefault, parseStringWithUnitToNumber, processMarkdownStyle};
Loading