diff --git a/example/react_native_renderer_patch.sh b/example/react_native_renderer_patch.sh deleted file mode 100755 index 8f069bd..0000000 --- a/example/react_native_renderer_patch.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -rm ../react-native/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js -cp ./ReactNativeRenderer-dev.js ../react-native/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js diff --git a/example/src/AndroidPatch.js b/example/src/AndroidPatch.js new file mode 100644 index 0000000..73a05b3 --- /dev/null +++ b/example/src/AndroidPatch.js @@ -0,0 +1,39 @@ +import * as React from 'react'; +import { View, StyleSheet, Text, TextInput } from 'react-native'; +export function AndroidPatch() { + const email = + 'From vincenzoddragon+five@gmail.com From vincenzoddrlxagon+five@gmail.com'; + return ( + <> + + + + {email} + + + + Normal Text + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: 'center', + padding: 8, + backgroundColor: 'yellow', + }, + flexBrokenStyle: { + flexDirection: 'row', + }, + parentText: { + backgroundColor: 'red', + }, + input: { borderWidth: 1 }, +}); diff --git a/example/src/App.tsx b/example/src/App.tsx index 49edd71..e91ed65 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,42 +1,16 @@ import * as React from 'react'; -import { View, StyleSheet, Text, TextInput } from 'react-native'; -import { TextImproved } from 'react-native-improved'; +import { ScrollView, View, StyleSheet, Text, TextInput } from 'react-native'; +import { TextScreen } from './TextScreen'; export default function App() { const email = 'From vincenzoddragon+five@gmail.com From vincenzoddrlxagon+five@gmail.com'; - return ( - <> - - - - {email} - - - - Normal Text - - - ); + return ; } -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - padding: 8, - backgroundColor: 'yellow', - }, - flexBrokenStyle: { - flexDirection: 'row', - }, - parentText: { - backgroundColor: 'red', - }, - input: { borderWidth: 1 }, -}); +function CurrentScreen() { + return ; +} + +const styles = StyleSheet.create({}); diff --git a/example/src/RNTOption.js b/example/src/RNTOption.js new file mode 100644 index 0000000..515de94 --- /dev/null +++ b/example/src/RNTOption.js @@ -0,0 +1,86 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow strict-local + */ + +'use strict'; + +import * as React from 'react'; +import {Text, Pressable, StyleSheet, View} from 'react-native'; +import type {PressEvent} from 'react-native/Libraries/Types/CoreEventTypes'; +import type {ViewStyleProp} from 'react-native/Libraries/StyleSheet/StyleSheet'; + +type Props = $ReadOnly<{| + testID?: ?string, + label: string, + onPress?: ?(event: PressEvent) => mixed, + selected?: ?boolean, + multiSelect?: ?boolean, + disabled?: ?boolean, + style?: ViewStyleProp, +|}>; + +/** + * A reusable toggle button component for RNTester. Highlights when selected. + */ +export default function RNTOption(props: Props): React.Node { + const [pressed, setPressed] = React.useState(false); + + return ( + setPressed(true)} + onPressOut={() => setPressed(false)} + testID={props.testID}> + + {props.label} + + + ); +} + +const styles = StyleSheet.create({ + pressed: { + backgroundColor: 'rgba(100,215,255,.3)', + }, + label: { + color: 'black', + }, + selected: { + backgroundColor: 'rgba(100,215,255,.3)', + borderColor: 'rgba(100,215,255,.3)', + }, + disabled: {borderWidth: 0}, + container: { + borderColor: 'rgba(0,0,0, 0.1)', + borderWidth: 1, + borderRadius: 16, + padding: 6, + paddingHorizontal: 10, + alignItems: 'center', + }, +}); diff --git a/example/src/TextLegend.js b/example/src/TextLegend.js new file mode 100644 index 0000000..52b09a2 --- /dev/null +++ b/example/src/TextLegend.js @@ -0,0 +1,254 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + +import * as React from 'react'; +import {Text, View, StyleSheet} from 'react-native'; +import RNTOption from './RNTOption'; + +const PANGRAMS = { + arabic: + 'صِف خَلقَ خَودِ كَمِثلِ الشَمسِ إِذ بَزَغَت — يَحظى الضَجيعُ بِها نَجلاءَ مِعطارِ', + chinese: 'Innovation in China 中国智造,慧及全球 0123456789', + english: 'The quick brown fox jumps over the lazy dog.', + emoji: '🙏🏾🚗💩😍🤯👩🏽‍🔧🇨🇦💯', + german: 'Falsches Üben von Xylophonmusik quält jeden größeren Zwerg', + greek: 'Ταχίστη αλώπηξ βαφής ψημένη γη, δρασκελίζει υπέρ νωθρού κυνός', + hebrew: 'דג סקרן שט בים מאוכזב ולפתע מצא חברה', + hindi: + 'ऋषियों को सताने वाले दुष्ट राक्षसों के राजा रावण का सर्वनाश करने वाले विष्णुवतार भगवान श्रीराम, अयोध्या के महाराज दशरथ के बड़े सपुत्र थे।', + igbo: 'Nne, nna, wepụ he’l’ụjọ dum n’ime ọzụzụ ụmụ, vufesi obi nye Chukwu, ṅụrịanụ, gbakọọnụ kpaa, kwee ya ka o guzoshie ike; ọ ghaghị ito, nwapụta ezi agwa', + irish: 'D’fhuascail Íosa Úrmhac na hÓighe Beannaithe pór Éava agus Ádhaimh', + japanese: + '色は匂へど 散りぬるを 我が世誰ぞ 常ならむ 有為の奥山 今日越えて 浅き夢見じ 酔ひもせず', + korean: '키스의 고유조건은 입술끼리 만나야 하고 특별한 기술은 필요치 않다', + norwegian: + 'Vår sære Zulu fra badeøya spilte jo whist og quickstep i min taxi.', + polish: 'Jeżu klątw, spłódź Finom część gry hańb!', + romanian: 'Muzicologă în bej vând whisky și tequila, preț fix.', + russian: 'Эх, чужак, общий съём цен шляп (юфть) – вдрызг!', + swedish: 'Yxskaftbud, ge vår WC-zonmö IQ-hjälp.', + thai: 'เป็นมนุษย์สุดประเสริฐเลิศคุณค่า กว่าบรรดาฝูงสัตว์เดรัจฉาน จงฝ่าฟันพัฒนาวิชาการ อย่าล้างผลาญฤๅเข่นฆ่าบีฑาใคร ไม่ถือโทษโกรธแช่งซัดฮึดฮัดด่า หัดอภัยเหมือนกีฬาอัชฌาสัย ปฏิบัติประพฤติกฎกำหนดใจ พูดจาให้จ๊ะๆ จ๋าๆ น่าฟังเอยฯ', +}; + +export default function TextLegend(): React.Node { + const [language, setLanguage] = React.useState('english'); + const [alignment, setAlignment] = React.useState('left'); + // $FlowFixMe[missing-empty-array-annot] + const [textMetrics, setTextMetrics] = React.useState([]); + const [fontSize, setFontSize] = React.useState(50); + return ( + + setFontSize(fontSize + 3)}>Increase size + setFontSize(fontSize - 3)}>Decrease size + + Language + + {Object.keys(PANGRAMS).map(lang => ( + setLanguage(lang)} + selected={lang === language} + style={styles.option} + /> + ))} + + + + {textMetrics.map( + ({x, y, width, height, capHeight, ascender, descender, xHeight}) => { + return [ + , + + Baseline + , + , + + Capheight + , + , + + X-height + , + , + + Descender + , + , + + End of text + , + , + + Start of text + , + ]; + }, + )} + { + setTextMetrics(event.nativeEvent.lines); + }} + style={{ + fontSize: fontSize, + textAlign: alignment, + }}> + {PANGRAMS[language]} + + + + Alignment: + setAlignment('left')} + selected={alignment === 'left'} + style={styles.option} + /> + setAlignment('center')} + selected={alignment === 'center'} + style={styles.option} + /> + setAlignment('right')} + selected={alignment === 'right'} + style={styles.option} + /> + + + ); +} + +const styles = StyleSheet.create({ + row: { + flexDirection: 'row', + flexWrap: 'wrap', + margin: 6, + alignItems: 'center', + }, + title: { + fontWeight: 'bold', + }, + block: { + borderColor: 'rgba(0,0,0, 0.1)', + borderBottomWidth: 1, + padding: 6, + }, + option: { + margin: 4, + }, +}); diff --git a/example/src/TextScreen.tsx b/example/src/TextScreen.tsx new file mode 100644 index 0000000..a038d64 --- /dev/null +++ b/example/src/TextScreen.tsx @@ -0,0 +1,802 @@ +import * as React from 'react'; +import { View, StyleSheet, Text, TextInput, ScrollView } from 'react-native'; +import {TextLegend} from './TextLegend'; +import {AndroidPatch} from './AndroidPatch'; + +export function TextScreen() { + return ( + + + + + + Text `alignItems: 'baseline'` style + + 'Selectable Text' + + Text element is selectable + + 'Text alignment' + + + Text element aligned to the top via textAlignVertical + + + Text element aligned to the top via verticalAlign + + + Text element aligned to the middle via textAlignVertical + + + Text element aligned to the middle via verticalAlign + + + + ); +} + +function RNTesterBlock(props) { + return( + + {props.title} + {props.children} + + ) +} + +class TextExample extends React.Component { + render() { + return ( + <> + + + The text should wrap if it goes on multiple lines. See, this is + going to the next line. + + + + + Normal: + WillHaveAHyphenWhenBreakingForNewLine + + + None: + WillNotHaveAHyphenWhenBreakingForNewLine + + + Full: + WillHaveAHyphenWhenBreakingForNewLine + + + + + This text is indented by 10px padding on all sides. + + + + Sans-Serif + + Sans-Serif Bold + + Serif + + Serif Bold + + Monospace + + Monospace Bold (After 5.0) + + + Unknown Font Family + + + + + + Roboto Regular + + Roboto Italic + + + Roboto Bold + + + Roboto Bold Italic + + Roboto Light + + Roboto Light Italic + + + Roboto Thin (After 4.2) + + + Roboto Thin Italic (After 4.2) + + + Roboto Condensed + + + Roboto Condensed Italic + + + Roboto Condensed Bold + + + Roboto Condensed Bold Italic + + + Roboto Medium (After 5.0) + + + Roboto Medium Italic (After 5.0) + + + + + + + + NotoSerif Regular + + NotoSerif Bold Italic + + + NotoSerif Italic (Missing Font file) + + + Rubik Regular + + + Rubik Light + + + Rubik Bold + + + Rubik Medium + + + Rubik Medium Italic + + + + + + + Size 23 + Size 8 + + + Red color + Blue color + + + Move fast and be bold + Move fast and be normal + FONT WEIGHT 900 + FONT WEIGHT 800 + FONT WEIGHT 700 + FONT WEIGHT 600 + FONT WEIGHT 500 + FONT WEIGHT 400 + FONT WEIGHT 300 + FONT WEIGHT 200 + FONT WEIGHT 100 + FONT WEIGHT 900 + FONT WEIGHT 800 + FONT WEIGHT 700 + FONT WEIGHT 600 + FONT WEIGHT 500 + FONT WEIGHT 400 + FONT WEIGHT 300 + FONT WEIGHT 200 + FONT WEIGHT 100 + + + Move fast and be italic + Move fast and be normal + + + + Move fast and be both bold and italic + + + + Solid underline + None textDecoration + + Solid line-through + + + Both underline and line-through + + + Mixed text with{' '} + underline and{' '} + + line-through + {' '} + text nodes + + + + console.log('1st')}> + (Normal text, + + (R)red + + (G)green + + (B)blue + + (C)cyan + + (M)magenta + + (Y)yellow + + (K)black + + + + + + + + console.log('2nd')}> + (and bold + console.log('3rd')}> + (and tiny bold italic blue + console.log('4th')}> + (and tiny normal blue) + + ) + + ) + + ) + + console.log('1st')}> + (Serif + console.log('2nd')}> + (Serif Bold Italic + console.log('3rd')}> + (Monospace Normal + console.log('4th')}> + (Sans-Serif Bold + console.log('5th')}> + (and Sans-Serif Normal) + + ) + + ) + + ) + + ) + + + Nested text with size 8,{' '} + size 23, + and size 8 again + + + Nested text with red color,{' '} + blue color, + and red color again + + + + auto (default) - english LTR + أحب اللغة العربية auto (default) - arabic RTL + + left left left left left left left left left left left left left + left left + + + center center center center center center center center center + center center + + + right right right right right right right right right right right + right right + + + justify (works when api level >= 26 otherwise fallbacks to "left"): + this text component{"'"}s contents are laid out with "textAlign: + justify" and as you can see all of the lines except the last one + span the available width of the parent container. + + + + + + + 星际争霸是世界上最好的游戏。 + + + + + 星际争霸是世界上最好的游戏。 + + + + + 星际争霸是世界上最好的游戏。 + + + + + 星际争霸是世界上最好的游戏。星际争霸是世界上最好的游戏。星际争霸是世界上最好的游戏。星际争霸是世界上最好的游戏。 + + + + + + + A {'generated'} {'string'} and some     spaces + + + + + Holisticly formulate inexpensive ideas before best-of-breed + benefits. Continually expedite + magnetic potentialities rather than client-focused interfaces. + + + + + letterSpacing = 0 + + letterSpacing = 2 + + + letterSpacing = 9 + + + + With size and background color + + + + letterSpacing = -1 + + + [letterSpacing = 3] + + [Nested letterSpacing = 0] + + + [Nested letterSpacing = 6] + + + + + + + + + + Red background, + + {' '} + blue background, + + {' '} + inherited blue background, + + {' '} + nested green background. + + + + + + Same alpha as background, + + Inherited alpha from background, + + Reapply alpha + + + + + + + + + + + Default containerBackgroundColor (inherited) + backgroundColor wash + + + {"containerBackgroundColor: 'transparent' + backgroundColor wash"} + + + + + Maximum of one line no matter now much I write here. If I keep + writing it{"'"}ll just truncate after one line + + + Maximum of one line no matter now much I write here. If I keep + writing it{"'"}ll just truncate after one line + + + Maximum of two lines no matter now much I write here. If I keep + writing it{"'"}ll just truncate after two lines + + + No maximum lines specified no matter now much I write here. If I + keep writing it{"'"}ll just keep going and going + + + + + By default, text will respect Text Size accessibility setting on + Android. It means that all font sizes will be increased or decreased + depending on the value of the Text Size setting in the OS's Settings + app. + + + You can disable scaling for your Text component by passing {'"'} + allowFontScaling={'{'}false{'}"'} prop. + + + This text will not scale.{' '} + + This text also won't scale because it inherits "allowFontScaling" + from its parent. + + + + + + This text is selectable if you click-and-hold, and will offer the + native Android selection menus. + + + + + This text will have a orange highlight on selection. + + + + + Demo text shadow + + + + + This very long text should be truncated with dots in the end. + + + This very long text should be truncated with dots in the middle. + + + This very long text should be truncated with dots in the beginning. + + + This very long text should be clipped and this will not be visible. + + + + Small Caps{'\n'} + + Old Style nums 0123456789{'\n'} + + + Lining nums 0123456789{'\n'} + + + Tabular nums{'\n'} + 1111{'\n'} + 2222{'\n'} + + + Proportional nums{'\n'} + 1111{'\n'} + 2222{'\n'} + + + + + + Ey + Default + + + + Ey + + includeFontPadding: false + + + + By default Android will put extra space above text to allow for + upper-case accents or other ascenders. With some fonts, this can + make text look slightly misaligned when centered vertically. + + + + + This text should be uppercased. + + + This TEXT SHOULD be lowercased. + + + This text should be CAPITALIZED. + + + Capitalize a date: + + the 9th of november, 1998 + + + + Capitalize a 2 digit date: + + the 25th of december + + + + Mixed: uppercase + LoWeRcAsE + + capitalize each word + + + + Should be "ABC": + + abc + + + + Should be "AbC": + + abc + + + + { + '.aa\tbb\t\tcc dd EE \r\nZZ I like to eat apples. \n中文éé 我喜欢吃苹果。awdawd ' + } + + + { + '.aa\tbb\t\tcc dd EE \r\nZZ I like to eat apples. \n中文éé 我喜欢吃苹果。awdawd ' + } + + + { + '.aa\tbb\t\tcc dd EE \r\nZZ I like to eat apples. \n中文éé 我喜欢吃苹果。awdawd ' + } + + + { + '.aa\tbb\t\tcc dd EE \r\nZZ I like to eat apples. \n中文éé 我喜欢吃苹果。awdawd ' + } + + + Works with other text styles + + + + {'test🙃'.substring(0, 5)} + + + Phone number: 123-123-1234 + Link: https://www.facebook.com + Email: employee@facebook.com + + Phone number: 123-123-1234 Link: https://www.facebook.com Email: + employee@facebook.com + + + Phone number: 123-123-1234 Link: https://www.facebook.com Email: + employee@facebook.com + + + + ); + } +} + +function TextBaseLineLayoutExample(props: {}): React.Node { + const texts = []; + for (let i = 9; i >= 0; i--) { + texts.push( + + {i} + , + ); + } + + const marker = ( + + ); + const subtitleStyle = {fontSize: 16, marginTop: 8, fontWeight: 'bold'}; + + return ( + + {'Nested s:'} + + {marker} + {texts} + {marker} + + + {'Array of s in :'} + + {marker} + {texts} + {marker} + + + {'Interleaving and :'} + + {marker} + + Some text. + + {marker} + Text inside View. + {marker} + + + {marker} + + + ); +} + +const styles = StyleSheet.create({ + header: { + fontSize: 25, + borderWidth: 1, + textAlign: "center", + }, + backgroundColorText: { + left: 5, + backgroundColor: 'rgba(100, 100, 100, 0.3)', + }, + includeFontPaddingText: { + fontSize: 120, + fontFamily: 'sans-serif', + backgroundColor: '#EEEEEE', + color: '#000000', + textAlignVertical: 'center', + alignSelf: 'center', + }, +}); + diff --git a/package.json b/package.json index 7d1c141..204c9de 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "!**/.*" ], "bin": { - "rn-networking-patch": "patch.js" + "react-native-patch": "patch.js" }, "scripts": { "example": "yarn workspace react-native-improved-example", @@ -37,7 +37,8 @@ "prepare": "bob build", "release": "release-it", "patch": "node patch.js", - "postinstall": "yarn run patch" + "patch-dev": "node patch-dev.js", + "postinstall": "yarn run patch-dev" }, "keywords": [ "react-native", diff --git a/patch-dev.js b/patch-dev.js new file mode 100755 index 0000000..f8484ab --- /dev/null +++ b/patch-dev.js @@ -0,0 +1,83 @@ +#!/usr/bin/env node + +// Credits to ifsnow/react-native-networking-patch +// https://github.com/ifsnow/react-native-networking-patch/blob/master/patch.js + +const fs = require('fs'); +const path = require('path'); + +const rootPath = path.join(__dirname, '.'); + +// ============================================================================ +// Get RN version +// ============================================================================ +const RNRootPath = path.join( + __dirname, + './example/node_modules', + 'react-native' +); + +const RNPackageFile = `${RNRootPath}/package.json`; +if (!fs.existsSync(RNPackageFile)) { + console.log('[!] Not exists react-native'); + process.exit(1); +} + +const packageFile = fs.readFileSync(RNPackageFile); +let packageJSON = null; + +try { + packageJSON = JSON.parse(packageFile); +} catch (e) { + console.error(e); +} + +if (!packageJSON) { + console.log('[!] Failed to get version of react-native'); + process.exit(1); +} + +const patchDir = packageJSON.version; +const isPatchExists = + patchDir !== '' && fs.existsSync(`${rootPath}/patches/${patchDir}`); +if (!isPatchExists) { + const supportVersions = fs + .readdirSync(`${rootPath}/patches`) + .filter((dir) => dir.match(/^\d/)) + .join(', '); + console.log(`[!] Unsupported react-native version! (${patchDir})`); + console.log(`[!] Supported react-native versions: ${supportVersions}`); + process.exit(1); +} + +function getAllFiles(dirPath, arrayOfFiles) { + const files = fs.readdirSync(dirPath); + + arrayOfFiles = arrayOfFiles || []; + + files.forEach(function (file) { + if (file === '.DS_Store') { + return; + } + + const filePath = `${dirPath}/${file}`; + if (fs.statSync(filePath).isDirectory()) { + arrayOfFiles = getAllFiles(filePath, arrayOfFiles); + } else { + arrayOfFiles.push(filePath); + } + }); + + return arrayOfFiles; +} + +// ============================================================================ +// Copy files +// ============================================================================ +const targetFilesDir = `${rootPath}/patches/${patchDir}/files`; +getAllFiles(targetFilesDir).forEach(function (sourceFile) { + const destFile = `${RNRootPath}${sourceFile.replace(targetFilesDir, '')}`; + fs.copyFileSync(sourceFile, destFile); +}); + +console.log('[!] React Native was patched!'); diff --git a/patch.js b/patch.js index c3a827e..94c0e72 100755 --- a/patch.js +++ b/patch.js @@ -11,12 +11,8 @@ const rootPath = path.join(__dirname, '.'); // ============================================================================ // Get RN version // ============================================================================ -// const RNRootPath = path.join(__dirname, '..', 'react-native'); -const RNRootPath = path.join( - __dirname, - './example/node_modules', - 'react-native' -); +const RNRootPath = path.join(__dirname, '..', 'react-native'); + const RNPackageFile = `${RNRootPath}/package.json`; if (!fs.existsSync(RNPackageFile)) { console.log('[!] Not exists react-native'); diff --git a/src/TextViewNativeComponent.ts b/src/TextViewNativeComponent.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/__tests__/index.test.tsx b/src/__tests__/index.test.tsx deleted file mode 100644 index bf84291..0000000 --- a/src/__tests__/index.test.tsx +++ /dev/null @@ -1 +0,0 @@ -it.todo('write a test'); diff --git a/src/index.tsx b/src/index.tsx deleted file mode 100644 index dce43e5..0000000 --- a/src/index.tsx +++ /dev/null @@ -1 +0,0 @@ -// no implementation