Skip to content

Commit 40293ba

Browse files
authored
Merge pull request meliorence#146 from archriss/dev
v3.10.0
2 parents f5863da + 4f28049 commit 40293ba

File tree

4 files changed

+104
-44
lines changed

4 files changed

+104
-44
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export default class Demo extends Component {
6969
Prop | Description | Type | Required/Default
7070
------ | ------ | ------ | ------
7171
`renderers` | Your [custom renderers](#creating-custom-renderers) | `object` | Optional, some default ones are supplied (`<a>`, `<img>`...)
72+
`renderersProps` | Set of props accessible into your [custom renderers](#creating-custom-renderers) in `passProps` (4th argument) | `object` | Optional
7273
`html` | HTML string to parse and render | `string` | Required
7374
`uri` | *(experimental)* remote website to parse and render | `string` | Optional
7475
`decodeEntities` | Decode HTML entities of your content | `bool` | Optional, defaults to `true`
@@ -137,7 +138,7 @@ Your renderers functions receive several arguments that will be very useful to m
137138
* `htmlAttribs`: attributes attached to the node, parsed in a react-native way
138139
* `children` : array with the children of the node
139140
* `convertedCSSStyles` : conversion of the `style` attribute from CSS to react-native's stylesheet
140-
* `passProps` : various useful information : `groupInfo`, `parentTagName`, `parentIsText`...
141+
* `passProps` : various useful information : your `renderersProps`, `groupInfo`, `parentTagName`, `parentIsText`...
141142

142143
### Making your custom component block or inline
143144

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-native-render-html",
3-
"version": "3.9.1",
3+
"version": "3.10.0",
44
"author": "Archriss",
55
"license": "BSD-2-Clause",
66
"repository": "https://github.com/archriss/react-native-render-html",

src/HTML.js

Lines changed: 40 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
import React, { PureComponent } from 'react';
22
import PropTypes from 'prop-types';
33
import { View, Text, ViewPropTypes, ActivityIndicator, Dimensions } from 'react-native';
4-
import { BLOCK_TAGS, TEXT_TAGS, MIXED_TAGS, IGNORED_TAGS, TEXT_TAGS_IGNORING_ASSOCIATION, STYLESETS, TextOnlyPropTypes } from './HTMLUtils';
5-
import { cssStringToRNStyle, _getElementClassStyles, cssStringToObject, cssObjectToString } from './HTMLStyles';
4+
import { cssStringToRNStyle, _getElementClassStyles, cssStringToObject, cssObjectToString, computeTextStyles } from './HTMLStyles';
5+
import {
6+
BLOCK_TAGS,
7+
TEXT_TAGS,
8+
MIXED_TAGS,
9+
IGNORED_TAGS,
10+
TEXT_TAGS_IGNORING_ASSOCIATION,
11+
STYLESETS,
12+
TextOnlyPropTypes
13+
} from './HTMLUtils';
614
import { generateDefaultBlockStyles, generateDefaultTextStyles } from './HTMLDefaultStyles';
715
import htmlparser2 from 'htmlparser2';
816
import * as HTMLRenderers from './HTMLRenderers';
@@ -37,7 +45,8 @@ export default class HTML extends PureComponent {
3745
emSize: PropTypes.number.isRequired,
3846
ptSize: PropTypes.number.isRequired,
3947
baseFontStyle: PropTypes.object.isRequired,
40-
textSelectable: PropTypes.bool
48+
textSelectable: PropTypes.bool,
49+
renderersProps: PropTypes.object
4150
}
4251

4352
static defaultProps = {
@@ -149,36 +158,6 @@ export default class HTML extends PureComponent {
149158
this.defaultTextStyles = generateDefaultTextStyles(baseFontStyle.fontSize || 14);
150159
}
151160

152-
filterBaseFontStyles (element, classStyles, props = this.props) {
153-
const { tagsStyles, baseFontStyle } = props;
154-
const { tagName, parentTag, parent, attribs } = element;
155-
const styles = Object.keys(baseFontStyle);
156-
let appliedStyles = {};
157-
158-
for (let i = 0; i < styles.length; i++) {
159-
const styleAttribute = styles[i];
160-
const tagToCheck = tagName === 'rawtext' ? parentTag : tagName;
161-
const styleAttributeWithCSSDashes = styleAttribute.replace(/[A-Z]/, (match) => { return `-${match.toLowerCase()}`; });
162-
const overridenFromStyle = attribs && attribs.style && attribs.style.search(styleAttributeWithCSSDashes) !== -1;
163-
const overridenFromParentStyle = parent && parent.attribs && parent.attribs.style && parent.attribs.style.search(styleAttributeWithCSSDashes) !== -1;
164-
165-
const overridenFromTagStyle = tagToCheck && tagsStyles[tagToCheck] && tagsStyles[tagToCheck][styleAttribute];
166-
const overridenFromParentTagStyle = parentTag && tagsStyles[parentTag] && tagsStyles[parentTag][styleAttribute];
167-
168-
const overridenFromClassStyles = classStyles && classStyles[styleAttribute];
169-
const overridenFromDefaultStyles = this.defaultTextStyles[tagToCheck] && this.defaultTextStyles[tagToCheck][styleAttribute];
170-
171-
const notOverriden = !overridenFromStyle && !overridenFromParentStyle &&
172-
!overridenFromTagStyle && !overridenFromParentTagStyle &&
173-
!overridenFromClassStyles && !overridenFromDefaultStyles;
174-
175-
if (notOverriden) {
176-
appliedStyles[styleAttribute] = baseFontStyle[styleAttribute];
177-
}
178-
}
179-
return appliedStyles;
180-
}
181-
182161
/**
183162
* Loop on children and return whether if their parent needs to be a <View>
184163
* @param {any} children
@@ -216,7 +195,11 @@ export default class HTML extends PureComponent {
216195
associateRawTexts (children) {
217196
for (let i = 0; i < children.length; i++) {
218197
const child = children[i];
219-
if ((child.wrapper === 'Text' && TEXT_TAGS_IGNORING_ASSOCIATION.indexOf(child.tagName) === -1) && children.length > 1 && (!child.parent || child.parent.name !== 'p')) {
198+
if (
199+
(child.wrapper === 'Text' && TEXT_TAGS_IGNORING_ASSOCIATION.indexOf(child.tagName) === -1) &&
200+
children.length > 1 &&
201+
(!child.parent || TEXT_TAGS_IGNORING_ASSOCIATION.indexOf(child.parent.name) === -1)
202+
) {
220203
// Texts outside <p> or not <p> themselves (with siblings)
221204
let wrappedTexts = [];
222205
for (let j = i; j < children.length; j++) {
@@ -238,7 +221,7 @@ export default class HTML extends PureComponent {
238221
nodeIndex: i,
239222
parent: child.parent,
240223
parentTag: child.parentTag,
241-
tagName: child.parent && child.parent.name === 'li' ? 'textwrapper' : 'p',
224+
tagName: 'textwrapper',
242225
wrapper: 'Text'
243226
};
244227
}
@@ -399,7 +382,7 @@ export default class HTML extends PureComponent {
399382
* @memberof HTML
400383
*/
401384
renderRNElements (RNElements, parentWrapper = 'root', parentIndex = 0, props = this.props) {
402-
const { tagsStyles, classesStyles, emSize, ptSize, ignoredStyles, allowedStyles } = props;
385+
const { tagsStyles, classesStyles, emSize, ptSize, ignoredStyles, allowedStyles, baseFontStyle } = props;
403386
return RNElements && RNElements.length ? RNElements.map((element, index) => {
404387
const { attribs, data, tagName, parentTag, children, nodeIndex, wrapper } = element;
405388
const Wrapper = wrapper === 'Text' ? Text : View;
@@ -445,9 +428,23 @@ export default class HTML extends PureComponent {
445428
}
446429

447430
const classStyles = _getElementClassStyles(attribs, classesStyles);
448-
const textElementStyles = this.filterBaseFontStyles(element, classStyles, props);
449431
const textElement = data ?
450-
<Text style={textElementStyles}>{ data }</Text> :
432+
<Text
433+
style={computeTextStyles(
434+
element,
435+
{
436+
defaultTextStyles: this.defaultTextStyles,
437+
tagsStyles,
438+
classesStyles,
439+
baseFontStyle,
440+
emSize,
441+
ptSize,
442+
ignoredStyles,
443+
allowedStyles
444+
})}
445+
>
446+
{ data }
447+
</Text> :
451448
false;
452449

453450
const style = [
@@ -458,10 +455,12 @@ export default class HTML extends PureComponent {
458455
]
459456
.filter((s) => s !== undefined);
460457

461-
const extraProps = {};
462-
if (Wrapper === Text) extraProps.selectable = this.props.textSelectable;
458+
const renderersProps = {};
459+
if (Wrapper === Text) {
460+
renderersProps.selectable = this.props.textSelectable;
461+
}
463462
return (
464-
<Wrapper key={key} style={style} {...extraProps}>
463+
<Wrapper key={key} style={style} {...renderersProps}>
465464
{ textElement }
466465
{ childElements }
467466
</Wrapper>

src/HTMLStyles.js

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,66 @@ export function _constructStyles ({ tagName, htmlAttribs, passProps, additionalS
6363
return style.filter((style) => style !== undefined);
6464
}
6565

66+
/**
67+
* Computes the styles of a text node
68+
* @export
69+
* @param {any} element parsed DOM node of text
70+
* @param {any} passProps set of props from the HTML component
71+
* @returns {object} react-native styles
72+
*/
73+
export function computeTextStyles (element, passProps) {
74+
let finalStyle = {};
75+
76+
// Construct an array with the styles of each level of the text node, ie :
77+
// [element, parent1, parent2, parent3...]
78+
const parentStyles = _recursivelyComputeParentTextStyles(element, passProps);
79+
80+
// Only merge the keys that aren't yet applied to the final object. ie:
81+
// if fontSize is already set in the first iteration, ignore the fontSize that
82+
// we got from the 3rd iteration because of a class for instance, hence
83+
// respecting the proper style inheritance
84+
parentStyles.forEach((styles) => {
85+
Object.keys(styles).forEach((styleKey) => {
86+
const styleValue = styles[styleKey];
87+
if (!finalStyle[styleKey]) {
88+
finalStyle[styleKey] = styleValue;
89+
}
90+
});
91+
});
92+
93+
// Finally, try to add the baseFontStyle values to add pontentially missing
94+
// styles to each text node
95+
return { ...passProps.baseFontStyle, ...finalStyle };
96+
}
97+
98+
function _recursivelyComputeParentTextStyles (element, passProps, styles = []) {
99+
const { attribs, name } = element;
100+
const { classesStyles, tagsStyles, defaultTextStyles } = passProps;
101+
102+
// Construct every style for this node
103+
const HTMLAttribsStyles = attribs && attribs.style ? cssStringToRNStyle(attribs.style, STYLESETS.TEXT, passProps) : {};
104+
const classStyles = _getElementClassStyles(attribs, classesStyles);
105+
const userTagStyles = tagsStyles[name];
106+
const defaultTagStyles = defaultTextStyles[name];
107+
108+
// Merge those according to their priority level
109+
const mergedStyles = {
110+
...defaultTagStyles,
111+
...userTagStyles,
112+
...classStyles,
113+
...HTMLAttribsStyles
114+
};
115+
116+
styles.push(mergedStyles);
117+
118+
if (element.parent) {
119+
// Keep looping recursively if this node has parents
120+
return _recursivelyComputeParentTextStyles(element.parent, passProps, styles);
121+
} else {
122+
return styles;
123+
}
124+
}
125+
66126
/**
67127
* Creates a set of style from an array of classes asosciated to a node.
68128
* @export
@@ -101,7 +161,7 @@ export function _getElementCSSClasses (htmlAttribs) {
101161
* @param {object} { parentTag, emSize, ignoredStyles }
102162
* @returns {object}
103163
*/
104-
function cssToRNStyle (css, styleset, { parentTag, emSize, ptSize, ignoredStyles, allowedStyles }) {
164+
function cssToRNStyle (css, styleset, { emSize, ptSize, ignoredStyles, allowedStyles }) {
105165
const styleProps = stylePropTypes[styleset];
106166
return Object.keys(css)
107167
.filter((key) => allowedStyles ? allowedStyles.indexOf(key) !== -1 : true)

0 commit comments

Comments
 (0)