Description
import React, { useState, useRef, useEffect } from 'react';
import PropTypes from 'prop-types';
import { Animated, Dimensions, Text, View, StyleSheet } from 'react-native';
import get from 'lodash.get';
import {I18nManager} from 'react-native'
// import { getFormattedCurrency } from '~/utils/units'
const ComparisonTableRow = (props) => {
const {
data = [],
comparing,
getBestBy,
fixed,
cellTextAlign,
headerCellContent,
headerCellStyle,
cellContent,
cellStyle,
rowStyle,
bestCellStyle,
bestCellTextStyle,
mergeCells,
isHighlightBest,
headerCellWidth,
cellWidth,
innerScrollX,
scrollY
} = props;
let bestValueIndices = [];
// Only sort them if user provide getBestBy order string/comparison function to sort the values
if (comparing && getBestBy) {
const valuesOriginal =
typeof comparing === 'function'
? comparing(data)
: data.map((item) => getComparingValue(item, comparing));
const valuesSorted = getSortedValues(valuesOriginal, getBestBy);
// if bestValueIndices is -1, either there is no best or getBestBy is null
bestValueIndices = getBestValueIndices(valuesOriginal, valuesSorted);
}
const isAllValuesEqual = bestValueIndices.length === data.length;
const mergedBestCellStyle = {
...styles.bestCell,
...bestCellStyle
};
const mergedBestCellTextStyle = {
...styles.bestCellText,
...bestCellTextStyle
};
function getComparingValue(item, comparing) {
return get(item, comparing);
}
function getSortedValues(valuesOriginal, getBestBy) {
let valuesSorted = [...valuesOriginal];
if (typeof getBestBy === 'function') {
valuesSorted.sort(getBestBy);
} else if (getBestBy === 'asc') {
valuesSorted.sort((a, b) => a - b);
} else if (getBestBy === 'desc') {
valuesSorted.sort((a, b) => b - a);
}
return valuesSorted;
}
function getBestValueIndices(valuesOriginal, valuesSorted) {
const [bestValue] = valuesSorted;
let bestValueIndices = [];
if (bestValue !== undefined) {
valuesOriginal.forEach((value, i) => {
if (value === bestValue) {
bestValueIndices.push(i);
}
});
} else {
bestValueIndices = [-1];
}
return bestValueIndices;
}
function renderHeaderCellContent() {
return (
<Animated.View
style={[
styles.headerCell,
{ width: headerCellWidth },
headerCellStyle,
innerScrollX && {
transform: [
{
translateX: innerScrollX.interpolate({
inputRange: [0, I18nManager.isRTL ? 10: 3000],
outputRange: [0, I18nManager.isRTL ? 10: 3000],
extrapolate: 'clamp'
})
}
]
}
]}
>
{/* {typeof headerCellContent === 'function' ? (
headerCellContent()
) : ( */}
<Text style={styles.headerCellText}>{headerCellContent}</Text>
{/* )} */}
</Animated.View>
);
}
function renderTemplateCellContent(cellContent, item) {
const comparingValue = getComparingValue(item, comparing);
if (comparing && !comparingValue) {
return '-';
}
}
function renderCustomCellContent(
cellContent,
index,
item,
isBest,
mergedBestCellTextStyle
) {
let customCellContent = cellContent({
item,
index,
isBest,
cellTextStyle: styles.cellText,
bestCellTextStyle: mergedBestCellTextStyle
});
if (React.isValidElement(customCellContent)) {
return customCellContent;
}
return (
<Text
style={[
styles.cellText,
cellTextAlign && { textAlign: cellTextAlign },
isBest && mergedBestCellTextStyle
]}
>
{renderTemplateCellContent(customCellContent, item)}
</Text>
);
}
return (
<Animated.View
style={[
styles.row,
rowStyle,
fixed &&
scrollY && {
...styles.fixedRow,
transform: [
{
translateY: scrollY.interpolate({
inputRange: [0, 10000],
outputRange: [0, 10000],
extrapolate: 'clamp'
})
}
]
}
]}
>
{headerCellContent !== undefined && renderHeaderCellContent()}
{mergeCells ? (
<Animated.View
style={[
styles.mergedCell,
{ width: Dimensions.get('window').width - headerCellWidth },
cellStyle,
innerScrollX && {
transform: [
{
translateX: innerScrollX.interpolate({
inputRange: [0, 1000],
outputRange: [0, 1000],
extrapolate: 'clamp'
})
}
]
}
]}
>
{cellContent && typeof cellContent === 'function'
? cellContent({ data })
: null}
</Animated.View>
) : (
data.map((item, index) => {
const isBest =
bestValueIndices.includes(index) &&
isHighlightBest &&
!isAllValuesEqual;
return (
<View
style={[
styles.cell,
cellStyle,
{ width: cellWidth },
isBest && mergedBestCellStyle
]}
key={index}
>
{typeof cellContent === 'function' ? (
renderCustomCellContent(
cellContent,
index,
item,
isBest,
mergedBestCellTextStyle
)
) : (
<Text
style={[
styles.cellText,
cellTextAlign && { textAlign: cellTextAlign },
isBest && mergedBestCellTextStyle
]}
>
{renderTemplateCellContent(cellContent, item)}
)}
);
})
)}
</Animated.View>
);
};
const styles = StyleSheet.create({
row: {
borderTopWidth: 1,
borderColor: '#E5E5E5',
flexDirection: 'row'
},
fixedRow: {
position: 'relative',
zIndex: 100
},
headerCell: {
position: 'relative',
zIndex: 10,
backgroundColor: '#F9F9FB',
paddingVertical: 11,
paddingHorizontal: 12,
borderRightWidth: 1,
borderColor: '#E5E5E5'
},
cell: {
backgroundColor: '#FFF',
paddingVertical: 10,
paddingHorizontal: 14,
borderRightWidth: 1,
borderColor: '#E5E5E5'
},
mergedCell: {
justifyContent: 'center',
paddingVertical: 10,
paddingHorizontal: 12
},
headerCellText: {
fontSize: 20
},
cellText: {
fontSize: 20
},
bestCell: {
backgroundColor: 'rgba(255,236,170,0.2)'
},
bestCellText: {
color: '#F6571A',
fontWeight: 'bold'
}
});
ComparisonTableRow.propTypes = {
data: PropTypes.array,
comparing: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
getBestBy: PropTypes.oneOfType([
PropTypes.oneOf(['asc', 'desc']),
PropTypes.func
]),
headerCellContent: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
headerCellStyle: PropTypes.object,
cellContent: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
cellStyle: PropTypes.object,
rowStyle: PropTypes.object,
bestCellStyle: PropTypes.object,
bestCellTextStyle: PropTypes.object,
isHighlightBest: PropTypes.bool,
innerScrollX: PropTypes.object
};
ComparisonTableRow.defaultProps = {};
export default ComparisonTableRow;
code responsible for this:
<Animated.View
style={[
styles.headerCell,
{ width: headerCellWidth },
headerCellStyle,
innerScrollX && {
transform: [
{
translateX: innerScrollX.interpolate({
inputRange: [0, I18nManager.isRTL ? 10: 3000],
outputRange: [0, I18nManager.isRTL ? 10: 3000],
extrapolate: 'clamp'
})
}
]
}
]}
>
<Text style={styles.headerCellText}>{headerCellContent}</Text>
</Animated.View>
Steps to reproduce
unable to reproduce
React Native Version
0.76.5
Affected Platforms
Runtime - Android, Runtime - iOS
Output of npx react-native info
System:
OS: macOS 14.6.1
CPU: (8) x64 Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz
Memory: 56.37 MB / 16.00 GB
Shell:
version: "5.9"
path: /bin/zsh
Binaries:
Node:
version: 22.12.0
path: ~/.nvm/versions/node/v22.12.0/bin/node
Yarn:
version: 1.22.22
path: /usr/local/bin/yarn
npm:
version: 10.9.0
path: ~/.nvm/versions/node/v22.12.0/bin/npm
Watchman:
version: 2024.12.02.00
path: /usr/local/bin/watchman
Managers:
CocoaPods:
version: 1.16.2
path: /usr/local/lib/ruby/gems/3.3.0/bin/pod
SDKs:
iOS SDK:
Platforms:
- DriverKit 24.2
- iOS 18.2
- macOS 15.2
- tvOS 18.2
- visionOS 2.2
- watchOS 11.2
Android SDK: Not Found
IDEs:
Android Studio: 2024.2 AI-242.23339.11.2421.12700392
Xcode:
version: 16.2/16C5032a
path: /usr/bin/xcodebuild
Languages:
Java:
version: 17.0.13
path: /usr/bin/javac
Ruby:
version: 3.3.6
path: /usr/local/opt/ruby/bin/ruby
npmPackages:
"@react-native-community/cli":
installed: 15.0.1
wanted: 15.0.1
react:
installed: 18.3.1
wanted: 18.3.1
react-native:
installed: 0.76.5
wanted: 0.76.5
react-native-macos: Not Found
npmGlobalPackages:
"*react-native*": Not Found
Android:
hermesEnabled: true
newArchEnabled: false
iOS:
hermesEnabled: true
newArchEnabled: false
Stacktrace or Logs
Reproducer
no reproducer
Screenshots and Videos
No response
Description
import React, { useState, useRef, useEffect } from 'react';
import PropTypes from 'prop-types';
import { Animated, Dimensions, Text, View, StyleSheet } from 'react-native';
import get from 'lodash.get';
import {I18nManager} from 'react-native'
// import { getFormattedCurrency } from '~/utils/units'
const ComparisonTableRow = (props) => {
const {
data = [],
comparing,
getBestBy,
fixed,
cellTextAlign,
headerCellContent,
headerCellStyle,
cellContent,
cellStyle,
rowStyle,
bestCellStyle,
bestCellTextStyle,
mergeCells,
} = props;
let bestValueIndices = [];
// Only sort them if user provide getBestBy order string/comparison function to sort the values
if (comparing && getBestBy) {
const valuesOriginal =
typeof comparing === 'function'
? comparing(data)
: data.map((item) => getComparingValue(item, comparing));
}
const isAllValuesEqual = bestValueIndices.length === data.length;
const mergedBestCellStyle = {
...styles.bestCell,
...bestCellStyle
};
const mergedBestCellTextStyle = {
...styles.bestCellText,
...bestCellTextStyle
};
function getComparingValue(item, comparing) {
return get(item, comparing);
}
function getSortedValues(valuesOriginal, getBestBy) {
let valuesSorted = [...valuesOriginal];
if (typeof getBestBy === 'function') {
valuesSorted.sort(getBestBy);
} else if (getBestBy === 'asc') {
valuesSorted.sort((a, b) => a - b);
} else if (getBestBy === 'desc') {
valuesSorted.sort((a, b) => b - a);
}
return valuesSorted;
}
function getBestValueIndices(valuesOriginal, valuesSorted) {
const [bestValue] = valuesSorted;
let bestValueIndices = [];
if (bestValue !== undefined) {
valuesOriginal.forEach((value, i) => {
if (value === bestValue) {
bestValueIndices.push(i);
}
});
} else {
bestValueIndices = [-1];
}
return bestValueIndices;
}
function renderHeaderCellContent() {
}
function renderTemplateCellContent(cellContent, item) {
const comparingValue = getComparingValue(item, comparing);
if (comparing && !comparingValue) {
return '-';
}
}
function renderCustomCellContent(
cellContent,
index,
item,
isBest,
mergedBestCellTextStyle
) {
let customCellContent = cellContent({
item,
index,
isBest,
cellTextStyle: styles.cellText,
bestCellTextStyle: mergedBestCellTextStyle
});
}
return (
<Animated.View
style={[
styles.row,
rowStyle,
fixed &&
scrollY && {
...styles.fixedRow,
transform: [
{
translateY: scrollY.interpolate({
inputRange: [0, 10000],
outputRange: [0, 10000],
extrapolate: 'clamp'
})
}
]
}
]}
>
{headerCellContent !== undefined && renderHeaderCellContent()}
{mergeCells ? (
<Animated.View
style={[
styles.mergedCell,
{ width: Dimensions.get('window').width - headerCellWidth },
cellStyle,
innerScrollX && {
transform: [
{
translateX: innerScrollX.interpolate({
inputRange: [0, 1000],
outputRange: [0, 1000],
extrapolate: 'clamp'
})
}
]
}
]}
>
{cellContent && typeof cellContent === 'function'
? cellContent({ data })
: null}
</Animated.View>
) : (
data.map((item, index) => {
const isBest =
bestValueIndices.includes(index) &&
isHighlightBest &&
!isAllValuesEqual;
return (
<View
style={[
styles.cell,
cellStyle,
{ width: cellWidth },
isBest && mergedBestCellStyle
]}
key={index}
>
{typeof cellContent === 'function' ? (
renderCustomCellContent(
cellContent,
index,
item,
isBest,
mergedBestCellTextStyle
)
) : (
<Text
style={[
styles.cellText,
cellTextAlign && { textAlign: cellTextAlign },
isBest && mergedBestCellTextStyle
]}
>
{renderTemplateCellContent(cellContent, item)}
)}
);
})
)}
</Animated.View>
);
};
const styles = StyleSheet.create({
row: {
borderTopWidth: 1,
borderColor: '#E5E5E5',
flexDirection: 'row'
},
fixedRow: {
position: 'relative',
zIndex: 100
},
headerCell: {
position: 'relative',
zIndex: 10,
backgroundColor: '#F9F9FB',
paddingVertical: 11,
paddingHorizontal: 12,
borderRightWidth: 1,
borderColor: '#E5E5E5'
},
cell: {
backgroundColor: '#FFF',
paddingVertical: 10,
paddingHorizontal: 14,
borderRightWidth: 1,
borderColor: '#E5E5E5'
},
mergedCell: {
justifyContent: 'center',
paddingVertical: 10,
paddingHorizontal: 12
},
headerCellText: {
fontSize: 20
},
cellText: {
fontSize: 20
},
bestCell: {
backgroundColor: 'rgba(255,236,170,0.2)'
},
bestCellText: {
color: '#F6571A',
fontWeight: 'bold'
}
});
ComparisonTableRow.propTypes = {
data: PropTypes.array,
comparing: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
getBestBy: PropTypes.oneOfType([
PropTypes.oneOf(['asc', 'desc']),
PropTypes.func
]),
headerCellContent: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
headerCellStyle: PropTypes.object,
cellContent: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
cellStyle: PropTypes.object,
rowStyle: PropTypes.object,
bestCellStyle: PropTypes.object,
bestCellTextStyle: PropTypes.object,
isHighlightBest: PropTypes.bool,
innerScrollX: PropTypes.object
};
ComparisonTableRow.defaultProps = {};
export default ComparisonTableRow;
code responsible for this:
<Animated.View
style={[
styles.headerCell,
{ width: headerCellWidth },
headerCellStyle,
innerScrollX && {
transform: [
{
translateX: innerScrollX.interpolate({
inputRange: [0, I18nManager.isRTL ? 10: 3000],
outputRange: [0, I18nManager.isRTL ? 10: 3000],
extrapolate: 'clamp'
})
}
]
}
]}
>
Steps to reproduce
unable to reproduce
React Native Version
0.76.5
Affected Platforms
Runtime - Android, Runtime - iOS
Output of
npx react-native infoStacktrace or Logs
Reproducer
no reproducer
Screenshots and Videos
No response