├── .nvmrc ├── .watchmanconfig ├── tv-example ├── .watchmanconfig ├── tsconfig.json ├── jest.config.js ├── .bundle │ └── config ├── app.json ├── babel.config.js ├── android │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── values │ │ │ │ │ │ ├── strings.xml │ │ │ │ │ │ └── styles.xml │ │ │ │ │ ├── drawable │ │ │ │ │ │ ├── tv_banner.png │ │ │ │ │ │ └── rn_edit_text_material.xml │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ └── mipmap-xxxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── java │ │ │ │ │ └── com │ │ │ │ │ │ └── tvexample │ │ │ │ │ │ ├── MainActivity.kt │ │ │ │ │ │ └── MainApplication.kt │ │ │ │ └── AndroidManifest.xml │ │ │ └── debug │ │ │ │ └── AndroidManifest.xml │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ ├── build.gradle │ ├── gradle.properties │ ├── gradlew.bat │ └── gradlew ├── ios │ ├── tvexample │ │ ├── Images.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── AppDelegate.h │ │ ├── main.m │ │ ├── AppDelegate.mm │ │ ├── Info.plist │ │ └── LaunchScreen.storyboard │ ├── tvexample.xcworkspace │ │ └── contents.xcworkspacedata │ ├── .xcode.env │ ├── tvexample-tvOSTests │ │ └── Info.plist │ ├── tvexampleTests │ │ ├── Info.plist │ │ └── tvexampleTests.m │ ├── tvexample-tvOS │ │ └── Info.plist │ ├── Podfile │ └── tvexample.xcodeproj │ │ └── xcshareddata │ │ └── xcschemes │ │ ├── tvexample.xcscheme │ │ └── tvexample-tvOS.xcscheme ├── index.js ├── react-native.config.js ├── Gemfile ├── src │ ├── ClassApp │ │ ├── MediaQuery.tsx │ │ └── index.tsx │ ├── App.tsx │ └── HooksApp │ │ ├── RStyleApp.tsx │ │ └── ResponsiveMethodsApp.tsx ├── package.json ├── .gitignore ├── metro.config.js ├── Gemfile.lock └── README.md ├── macos-example ├── .watchmanconfig ├── macos │ ├── .gitignore │ ├── .xcode.env │ ├── .xcode.env.local │ ├── macosexample-macOS │ │ ├── Assets.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── main.m │ │ ├── AppDelegate.h │ │ ├── macosexample.entitlements │ │ ├── AppDelegate.mm │ │ └── Info.plist │ ├── macosexample.xcworkspace │ │ └── contents.xcworkspacedata │ ├── Podfile │ └── macosexample.xcodeproj │ │ └── xcshareddata │ │ └── xcschemes │ │ └── macosexample-macOS.xcscheme ├── tsconfig.json ├── jest.config.js ├── .bundle │ └── config ├── app.json ├── index.js ├── babel.config.js ├── Gemfile ├── README.md ├── src │ ├── ClassApp │ │ ├── MediaQuery.tsx │ │ └── index.tsx │ ├── App.tsx │ └── HooksApp │ │ ├── RStyleApp.tsx │ │ └── ResponsiveMethodsApp.tsx ├── package.json ├── .gitignore ├── metro.config.js └── Gemfile.lock ├── example ├── App.js ├── assets │ ├── icon.png │ ├── favicon.png │ ├── splash.png │ └── adaptive-icon.png ├── tsconfig.json ├── babel.config.js ├── app.json ├── package.json ├── webpack.config.js ├── src │ ├── ClassApp │ │ ├── MediaQuery.tsx │ │ └── index.tsx │ ├── App.tsx │ └── HooksApp │ │ ├── RStyleApp.tsx │ │ └── ResponsiveMethodsApp.tsx └── metro.config.js ├── .gitattributes ├── banner.png ├── sim-1x.png ├── sim-2x.png ├── sim-3x.png ├── sim-4x.png ├── .husky ├── pre-push ├── pre-commit └── commit-msg ├── src ├── layout │ ├── index.ts │ ├── getBaseWidth.ts │ ├── createRStyle │ │ ├── parseValue.ts │ │ ├── index.ts │ │ └── mapping │ │ │ └── recursiveMapping.ts │ └── responsiveMethods.ts ├── HOC │ ├── index.tsx │ ├── withMediaQuery.tsx │ └── withResponsiveMethods.tsx ├── utils │ ├── constants.ts │ ├── index.ts │ └── performanceMeasure.ts ├── index.tsx ├── hooks │ ├── index.ts │ ├── useDevice.ts │ ├── useResponsiveDim.ts │ ├── useResponsiveWidth.ts │ ├── useResponsiveHeight.ts │ ├── useResponsiveScale.ts │ ├── useMediaQuery.ts │ ├── useRStyle.ts │ └── useResponsiveMethods.ts ├── constants.ts ├── context │ └── FRProvider.tsx └── types.ts ├── babel.config.js ├── tsconfig.build.json ├── .yarnrc ├── lint-staged.config.js ├── .editorconfig ├── lefthook.yml ├── commitlint.config.js ├── .github ├── actions │ └── setup │ │ └── action.yml └── workflows │ └── ci.yml ├── tsconfig.json ├── scripts └── bootstrap.js ├── .gitignore ├── LICENSE ├── jest └── index.ts ├── __tests__ ├── mapping.test.ts └── parseValue.test.ts ├── CONTRIBUTING.md ├── package.json ├── CODE_OF_CONDUCT.md ├── README.md └── USAGE.md /.nvmrc: -------------------------------------------------------------------------------- 1 | 22.12.0 -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /tv-example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /macos-example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /macos-example/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # CocoaPods 2 | Pods/ 3 | -------------------------------------------------------------------------------- /example/App.js: -------------------------------------------------------------------------------- 1 | export { default } from './src/HooksApp/RStyleApp'; 2 | -------------------------------------------------------------------------------- /macos-example/macos/.xcode.env: -------------------------------------------------------------------------------- 1 | export NODE_BINARY=$(command -v node) 2 | -------------------------------------------------------------------------------- /tv-example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /macos-example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /macos-example/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'react-native', 3 | }; 4 | -------------------------------------------------------------------------------- /tv-example/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'react-native', 3 | }; 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | # specific for windows script files 3 | *.bat text eol=crlf -------------------------------------------------------------------------------- /banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/banner.png -------------------------------------------------------------------------------- /sim-1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/sim-1x.png -------------------------------------------------------------------------------- /sim-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/sim-2x.png -------------------------------------------------------------------------------- /sim-3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/sim-3x.png -------------------------------------------------------------------------------- /sim-4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/sim-4x.png -------------------------------------------------------------------------------- /tv-example/.bundle/config: -------------------------------------------------------------------------------- 1 | BUNDLE_PATH: "vendor/bundle" 2 | BUNDLE_FORCE_RUBY_PLATFORM: 1 3 | -------------------------------------------------------------------------------- /tv-example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tvexample", 3 | "displayName": "tvexample" 4 | } 5 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | yarn run test 5 | -------------------------------------------------------------------------------- /macos-example/.bundle/config: -------------------------------------------------------------------------------- 1 | BUNDLE_PATH: "vendor/bundle" 2 | BUNDLE_FORCE_RUBY_PLATFORM: 1 3 | -------------------------------------------------------------------------------- /src/layout/index.ts: -------------------------------------------------------------------------------- 1 | export * from './responsiveMethods'; 2 | export * from './createRStyle'; 3 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:@react-native/babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /macos-example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "macosexample", 3 | "displayName": "macosexample" 4 | } 5 | -------------------------------------------------------------------------------- /src/HOC/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './withMediaQuery'; 2 | export * from './withResponsiveMethods'; 3 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | yarn run pre-commit 5 | -------------------------------------------------------------------------------- /src/utils/constants.ts: -------------------------------------------------------------------------------- 1 | export const PATTERN_REGEX = /^(undefined|[+-]?\d+(\.\d+)?|(\.\d+))(rs|rw|rh)$/; 2 | -------------------------------------------------------------------------------- /macos-example/macos/.xcode.env.local: -------------------------------------------------------------------------------- 1 | export NODE_BINARY=/Users/mhp/.nvm/versions/node/v20.10.0/bin/node 2 | 3 | -------------------------------------------------------------------------------- /tv-example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:@react-native/babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /example/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/example/assets/icon.png -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx --no -- commitlint --edit ${1} 5 | -------------------------------------------------------------------------------- /example/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/example/assets/favicon.png -------------------------------------------------------------------------------- /example/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/example/assets/splash.png -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "exclude": ["example", "macos-example", "tv-example"] 4 | } 5 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | # Override Yarn command so we can automatically setup the repo on running `yarn` 2 | 3 | yarn-path "scripts/bootstrap.js" 4 | -------------------------------------------------------------------------------- /example/assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/example/assets/adaptive-icon.png -------------------------------------------------------------------------------- /tv-example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | tvexample 3 | 4 | -------------------------------------------------------------------------------- /tv-example/ios/tvexample/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "version": 1, 4 | "author": "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tv-example/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/tv-example/android/app/debug.keystore -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig", 3 | "compilerOptions": { 4 | // Avoid expo-cli auto-generating a tsconfig 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /macos-example/macos/macosexample-macOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "author": "xcode", 4 | "version": 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tv-example/ios/tvexample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : RCTAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /macos-example/macos/macosexample-macOS/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | int main(int argc, const char *argv[]) { 4 | return NSApplicationMain(argc, argv); 5 | } 6 | -------------------------------------------------------------------------------- /macos-example/macos/macosexample-macOS/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : RCTAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /tv-example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/tv-example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /tv-example/android/app/src/main/res/drawable/tv_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/tv-example/android/app/src/main/res/drawable/tv_banner.png -------------------------------------------------------------------------------- /lint-staged.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | '*.{js,jsx,ts,tsx,md,json}': 'prettier --write --ignore-unknown', 5 | 'src/**/*.+(js|ts|tsx)': ['eslint'], 6 | }; 7 | -------------------------------------------------------------------------------- /tv-example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/tv-example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /tv-example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/tv-example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /tv-example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/tv-example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /tv-example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/tv-example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /tv-example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/tv-example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /tv-example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/tv-example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /tv-example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/tv-example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /tv-example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/tv-example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /tv-example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/tv-example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /tv-example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mhp23/react-native-full-responsive/HEAD/tv-example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /tv-example/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import { AppRegistry } from 'react-native'; 6 | import App from './src/HooksApp/RStyleApp'; 7 | import { name as appName } from './app.json'; 8 | 9 | AppRegistry.registerComponent(appName, () => App); 10 | -------------------------------------------------------------------------------- /macos-example/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import { AppRegistry } from 'react-native'; 6 | import App from './src/HooksApp/RStyleApp'; 7 | import { name as appName } from './app.json'; 8 | 9 | AppRegistry.registerComponent(appName, () => App); 10 | -------------------------------------------------------------------------------- /tv-example/ios/tvexample/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | @autoreleasepool { 8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { Dimensions } from 'react-native'; 2 | 3 | const { width: screenWidth, height: screenHeight } = Dimensions.get('window'); 4 | 5 | const getDimensions = () => { 6 | return { screenWidth, screenHeight }; 7 | }; 8 | 9 | export { getDimensions }; 10 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './HOC'; 2 | export * from './hooks'; 3 | export * from './utils'; 4 | export * from './layout'; 5 | export * from './constants'; 6 | export * from './context/FRProvider'; 7 | export * from './layout/createRStyle'; 8 | 9 | export * from './types'; 10 | -------------------------------------------------------------------------------- /tv-example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'tvexample' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | includeBuild('../node_modules/@react-native/gradle-plugin') 5 | -------------------------------------------------------------------------------- /src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useRStyle'; 2 | export * from './useDevice'; 3 | export * from './useMediaQuery'; 4 | export * from './useResponsiveDim'; 5 | export * from './useResponsiveScale'; 6 | export * from './useResponsiveWidth'; 7 | export * from './useResponsiveHeight'; 8 | export * from './useResponsiveMethods'; 9 | -------------------------------------------------------------------------------- /tv-example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /tv-example/ios/tvexample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /macos-example/macos/macosexample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/layout/getBaseWidth.ts: -------------------------------------------------------------------------------- 1 | import { DefaultBases } from '../constants'; 2 | import type { DeviceType, MappedDeviceType } from '../types'; 3 | 4 | export const getBaseWidth = ( 5 | deviceType: DeviceType = 'sm', 6 | baseSizes: Partial = DefaultBases 7 | ) => { 8 | return baseSizes[deviceType] || DefaultBases.sm; 9 | }; 10 | -------------------------------------------------------------------------------- /tv-example/react-native.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | dependencies: { 3 | // Required for Expo CLI to be used with platforms (such as Apple TV) that are not supported in Expo SDK 4 | expo: { 5 | platforms: { 6 | android: null, 7 | ios: null, 8 | macos: null, 9 | }, 10 | }, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | indent_style = space 10 | indent_size = 2 11 | 12 | end_of_line = lf 13 | charset = utf-8 14 | trim_trailing_whitespace = true 15 | insert_final_newline = true 16 | -------------------------------------------------------------------------------- /macos-example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:@react-native/babel-preset'], 3 | plugins: [ 4 | [ 5 | 'module-resolver', 6 | { 7 | extensions: ['.js', '.ts', '.tsx'], 8 | alias: { 9 | 'react-native': './node_modules/react-native-macos', 10 | }, 11 | }, 12 | ], 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /tv-example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /tv-example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | -------------------------------------------------------------------------------- /macos-example/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version 4 | ruby ">= 2.6.10" 5 | 6 | # Cocoapods 1.15 introduced a bug which break the build. We will remove the upper 7 | # bound in the template on Cocoapods with next React Native release. 8 | gem 'cocoapods', '>= 1.13', '< 1.15' 9 | gem 'activesupport', '>= 6.1.7.5', '< 7.1.0' 10 | -------------------------------------------------------------------------------- /tv-example/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version 4 | ruby ">= 2.6.10" 5 | 6 | # Cocoapods 1.15 introduced a bug which break the build. We will remove the upper 7 | # bound in the template on Cocoapods with next React Native release. 8 | gem 'cocoapods', '>= 1.13', '< 1.15' 9 | gem 'activesupport', '>= 6.1.7.5', '< 7.1.0' 10 | -------------------------------------------------------------------------------- /lefthook.yml: -------------------------------------------------------------------------------- 1 | pre-commit: 2 | parallel: true 3 | commands: 4 | lint: 5 | files: git diff --name-only @{push} 6 | glob: "*.{js,ts,jsx,tsx}" 7 | run: npx eslint {files} 8 | types: 9 | files: git diff --name-only @{push} 10 | glob: "*.{js,ts, jsx, tsx}" 11 | run: npx tsc --noEmit 12 | commit-msg: 13 | parallel: true 14 | commands: 15 | commitlint: 16 | run: npx commitlint --edit 17 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | import { BASE_SIZES, MappedDeviceType } from './types'; 2 | 3 | export const DefaultBases: MappedDeviceType = { 4 | 'xs': BASE_SIZES.xs, 5 | 'sm': BASE_SIZES.sm, 6 | 'md': BASE_SIZES.md, 7 | 'lg': BASE_SIZES.lg, 8 | 'xl': BASE_SIZES.xl, 9 | '2xl': BASE_SIZES?.['2xl'], 10 | }; 11 | export const DefaultThresholds = { 12 | 'xs': 320, 13 | 'sm': 576, 14 | 'md': 768, 15 | 'lg': 992, 16 | 'xl': 1200, 17 | '2xl': 1440, 18 | }; 19 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | rules: { 4 | 'type-enum': [ 5 | 2, 6 | 'always', 7 | [ 8 | 'build', 9 | 'ci', 10 | 'chore', 11 | 'docs', 12 | 'feat', 13 | 'fix', 14 | 'perf', 15 | 'refactor', 16 | 'revert', 17 | 'style', 18 | 'test', 19 | 'deps', 20 | ], 21 | ], 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /macos-example/macos/macosexample-macOS/macosexample.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | com.apple.security.network.client 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/hooks/useDevice.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import type { ContextProps } from '../types'; 3 | import { FRContext } from '../context/FRProvider'; 4 | 5 | export const useDevice = () => { 6 | const deviceContext = useContext(FRContext); 7 | if (!deviceContext) { 8 | throw new Error( 9 | 'the device context is not found, please use FRProvider in your root component' 10 | ); 11 | } 12 | return deviceContext; 13 | }; 14 | -------------------------------------------------------------------------------- /tv-example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /src/hooks/useResponsiveDim.ts: -------------------------------------------------------------------------------- 1 | import { type MaybeNumber } from 'src/types'; 2 | import { useResponsiveWidth } from './useResponsiveWidth'; 3 | import { useResponsiveHeight } from './useResponsiveHeight'; 4 | 5 | const useResponsiveDim = ( 6 | widthPercentage: MaybeNumber, 7 | heightPercentage: MaybeNumber 8 | ) => { 9 | const width = useResponsiveWidth(widthPercentage); 10 | const height = useResponsiveHeight(heightPercentage); 11 | return { width, height }; 12 | }; 13 | 14 | export { useResponsiveDim, useResponsiveDim as useRD }; 15 | -------------------------------------------------------------------------------- /tv-example/ios/.xcode.env: -------------------------------------------------------------------------------- 1 | # This `.xcode.env` file is versioned and is used to source the environment 2 | # used when running script phases inside Xcode. 3 | # To customize your local environment, you can create an `.xcode.env.local` 4 | # file that is not versioned. 5 | 6 | # NODE_BINARY variable contains the PATH to the node executable. 7 | # 8 | # Customize the NODE_BINARY variable here. 9 | # For example, to use nvm with brew, add the following line 10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use 11 | export NODE_BINARY=$(command -v node) 12 | -------------------------------------------------------------------------------- /src/hooks/useResponsiveWidth.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | import { rw } from '../layout'; 3 | import { MaybeNumber } from 'src/types'; 4 | import { useWindowDimensions } from 'react-native'; 5 | 6 | const useResponsiveWidth = (widthPercentage: MaybeNumber) => { 7 | const { width: screenWidth } = useWindowDimensions(); 8 | 9 | const width = useMemo(() => { 10 | return rw(widthPercentage, screenWidth); 11 | }, [screenWidth, widthPercentage]); 12 | 13 | return width; 14 | }; 15 | 16 | export { useResponsiveWidth, useResponsiveWidth as useRW }; 17 | -------------------------------------------------------------------------------- /src/hooks/useResponsiveHeight.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | import { rh } from '../layout'; 3 | import { type MaybeNumber } from 'src/types'; 4 | import { useWindowDimensions } from 'react-native'; 5 | 6 | const useResponsiveHeight = (heightPercentage: MaybeNumber) => { 7 | const { height: screenHeight } = useWindowDimensions(); 8 | 9 | const height = useMemo(() => { 10 | return rh(heightPercentage, screenHeight); 11 | }, [heightPercentage, screenHeight]); 12 | 13 | return height; 14 | }; 15 | 16 | export { useResponsiveHeight, useResponsiveHeight as useRH }; 17 | -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const pak = require('../package.json'); 3 | 4 | module.exports = function (api) { 5 | api.cache(true); 6 | 7 | return { 8 | presets: ['babel-preset-expo'], 9 | plugins: [ 10 | [ 11 | 'module-resolver', 12 | { 13 | extensions: ['.tsx', '.ts', '.js', '.json'], 14 | alias: { 15 | // For development, we want to alias the library to the source 16 | [pak.name]: path.join(__dirname, '..', pak.source), 17 | }, 18 | }, 19 | ], 20 | ], 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /src/hooks/useResponsiveScale.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | import { rs } from '../layout'; 3 | import { useDevice } from './useDevice'; 4 | import { type MaybeNumber } from 'src/types'; 5 | import { useWindowDimensions } from 'react-native'; 6 | 7 | const useResponsiveScale = (size: MaybeNumber) => { 8 | const device = useDevice(); 9 | 10 | const { width, height } = useWindowDimensions(); 11 | 12 | const scaledSize = useMemo(() => { 13 | return rs(size, width, height, device); 14 | }, [size, height, width, device]); 15 | 16 | return scaledSize; 17 | }; 18 | 19 | export { useResponsiveScale, useResponsiveScale as useRS }; 20 | -------------------------------------------------------------------------------- /src/utils/performanceMeasure.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @param label performance execution time label 3 | * @param func the execution time of the function is supposed to be measured 4 | * @param count the function should be called a specific number of times, default is `100` 5 | */ 6 | export const performanceMeasure = ( 7 | label: string, 8 | func: () => void, 9 | count = 100 10 | ) => { 11 | if (__DEV__) { 12 | console.log(`Running the function for ${count} times`); 13 | console.time(label); 14 | for (let i = 0; i < count; ++i) { 15 | func(); 16 | } 17 | console.timeEnd(label); 18 | } else { 19 | func(); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /tv-example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | buildToolsVersion = "34.0.0" 4 | minSdkVersion = 21 5 | compileSdkVersion = 34 6 | targetSdkVersion = 34 7 | ndkVersion = "25.1.8937393" 8 | kotlinVersion = "1.8.0" 9 | } 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | dependencies { 15 | classpath("com.android.tools.build:gradle") 16 | classpath("com.facebook.react:react-native-gradle-plugin") 17 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") 18 | } 19 | } 20 | 21 | apply plugin: "com.facebook.react.rootproject" 22 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "example", 4 | "slug": "example", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "userInterfaceStyle": "light", 9 | "splash": { 10 | "image": "./assets/splash.png", 11 | "resizeMode": "contain", 12 | "backgroundColor": "#ffffff" 13 | }, 14 | "assetBundlePatterns": [ 15 | "**/*" 16 | ], 17 | "ios": { 18 | "supportsTablet": true 19 | }, 20 | "android": { 21 | "adaptiveIcon": { 22 | "foregroundImage": "./assets/adaptive-icon.png", 23 | "backgroundColor": "#ffffff" 24 | } 25 | }, 26 | "web": { 27 | "favicon": "./assets/favicon.png" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/context/FRProvider.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { DefaultBases } from '../constants'; 3 | import type { ContextProps, PropsWithChildren } from '../types'; 4 | 5 | export const FRContext = React.createContext( 6 | undefined 7 | ); 8 | 9 | export const FRProvider: React.FC>> = ({ 10 | bases, 11 | children, 12 | type = 'sm', 13 | }) => { 14 | const contextValue = React.useMemo(() => { 15 | const newContext = { 16 | type, 17 | bases: { 18 | ...DefaultBases, 19 | ...bases, 20 | }, 21 | }; 22 | return newContext; 23 | }, [type, bases]); 24 | 25 | return ( 26 | {children} 27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "main": "node_modules/expo/AppEntry.js", 5 | "scripts": { 6 | "start": "expo start", 7 | "android": "expo start --android", 8 | "ios": "expo start --ios", 9 | "web": "expo start --web" 10 | }, 11 | "dependencies": { 12 | "expo": "^50.0.7", 13 | "expo-status-bar": "~1.11.1", 14 | "react": "18.2.0", 15 | "react-dom": "18.2.0", 16 | "react-native": "0.73.4", 17 | "react-native-full-responsive": "link:../", 18 | "react-native-web": "~0.19.6" 19 | }, 20 | "devDependencies": { 21 | "@babel/core": "^7.20.0", 22 | "@expo/webpack-config": "~19.0.1", 23 | "babel-loader": "^8.1.0", 24 | "babel-plugin-module-resolver": "^4.1.0" 25 | }, 26 | "private": true 27 | } 28 | -------------------------------------------------------------------------------- /.github/actions/setup/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup 2 | description: Setup Node.js and install dependencies 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - name: Setup Node.js 8 | uses: actions/setup-node@v3 9 | with: 10 | node-version-file: .nvmrc 11 | 12 | - name: Cache dependencies 13 | id: yarn-cache 14 | uses: actions/cache@v3 15 | with: 16 | path: | 17 | **/node_modules 18 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 19 | restore-keys: | 20 | ${{ runner.os }}-yarn- 21 | 22 | - name: Install dependencies 23 | if: steps.yarn-cache.outputs.cache-hit != 'true' 24 | run: | 25 | yarn install --cwd example --frozen-lockfile 26 | yarn install --frozen-lockfile 27 | shell: bash 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "paths": { 5 | "react-native-full-responsive": ["./src/index"] 6 | }, 7 | "allowUnreachableCode": false, 8 | "allowUnusedLabels": false, 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "jsx": "react", 12 | "lib": ["esnext"], 13 | "module": "esnext", 14 | "moduleResolution": "node", 15 | "noFallthroughCasesInSwitch": true, 16 | "noImplicitReturns": true, 17 | "noImplicitUseStrict": false, 18 | "noStrictGenericChecks": false, 19 | "noUncheckedIndexedAccess": true, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | "resolveJsonModule": true, 23 | "skipLibCheck": true, 24 | "strict": true, 25 | "target": "esnext" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tv-example/ios/tvexample-tvOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | -------------------------------------------------------------------------------- /tv-example/ios/tvexampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /scripts/bootstrap.js: -------------------------------------------------------------------------------- 1 | const os = require('os'); 2 | const path = require('path'); 3 | const child_process = require('child_process'); 4 | 5 | const root = path.resolve(__dirname, '..'); 6 | const args = process.argv.slice(2); 7 | const options = { 8 | cwd: process.cwd(), 9 | env: process.env, 10 | stdio: 'inherit', 11 | encoding: 'utf-8', 12 | }; 13 | 14 | if (os.type() === 'Windows_NT') { 15 | options.shell = true; 16 | } 17 | 18 | let result; 19 | 20 | if (process.cwd() !== root || args.length) { 21 | // We're not in the root of the project, or additional arguments were passed 22 | // In this case, forward the command to `yarn` 23 | result = child_process.spawnSync('yarn', args, options); 24 | } else { 25 | // If `yarn` is run without arguments, perform bootstrap 26 | result = child_process.spawnSync('yarn', ['bootstrap'], options); 27 | } 28 | 29 | process.exitCode = result.status; 30 | -------------------------------------------------------------------------------- /src/HOC/withMediaQuery.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useMediaQuery } from '../hooks'; 3 | import type { DeviceType, MappedDeviceType } from '../types'; 4 | /** 5 | * A HOC for when you want to use full responsive methods in your class components without additional steps as props. 6 | */ 7 | const withMediaQuery = ( 8 | Component: React.ComponentType, 9 | thresholds?: Partial 10 | ) => { 11 | const displayName = Component.displayName || Component.name || 'Component'; 12 | 13 | const ComponentWithMethods = (props: { type?: DeviceType }) => { 14 | const media = useMediaQuery(thresholds); 15 | return ; 16 | }; 17 | 18 | ComponentWithMethods.displayName = `withMediaQuery(${displayName})`; 19 | 20 | return ComponentWithMethods; 21 | }; 22 | 23 | export { withMediaQuery, withMediaQuery as withMQ }; 24 | -------------------------------------------------------------------------------- /example/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const createExpoWebpackConfigAsync = require('@expo/webpack-config'); 3 | const { resolver } = require('./metro.config'); 4 | 5 | const root = path.resolve(__dirname, '..'); 6 | const node_modules = path.join(__dirname, 'node_modules'); 7 | 8 | module.exports = async function (env, argv) { 9 | const config = await createExpoWebpackConfigAsync(env, argv); 10 | 11 | config.module.rules.push({ 12 | test: /\.(js|jsx|ts|tsx)$/, 13 | include: path.resolve(root, 'src'), 14 | use: 'babel-loader', 15 | }); 16 | 17 | // We need to make sure that only one version is loaded for peerDependencies 18 | // So we alias them to the versions in example's node_modules 19 | Object.assign(config.resolve.alias, { 20 | ...resolver.extraNodeModules, 21 | 'react-native-web': path.join(node_modules, 'react-native-web'), 22 | }); 23 | 24 | return config; 25 | }; 26 | -------------------------------------------------------------------------------- /macos-example/macos/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native-macos/scripts/react_native_pods' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | prepare_react_native_project! 5 | 6 | target 'macosexample-macOS' do 7 | platform :macos, '10.15' 8 | use_native_modules! 9 | 10 | # Flags change depending on the env values. 11 | flags = get_default_flags() 12 | 13 | use_react_native!( 14 | :path => '../node_modules/react-native-macos', 15 | :hermes_enabled => false, 16 | :fabric_enabled => ENV['RCT_NEW_ARCH_ENABLED'] == '1', 17 | # Flipper is not compatible w/ macOS 18 | :flipper_configuration => FlipperConfiguration.disabled, 19 | # An absolute path to your application root. 20 | :app_path => "#{Pod::Config.instance.installation_root}/.." 21 | ) 22 | 23 | post_install do |installer| 24 | react_native_post_install(installer) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /src/HOC/withResponsiveMethods.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useResponsiveMethods } from '../hooks'; 3 | import type { ResponsiveMethodsProps } from '../types'; 4 | /** 5 | * A HOC for when you want to use full responsive methods in your class components without additional steps as props. 6 | */ 7 | const withResponsiveMethods = ( 8 | Component: React.ComponentType 9 | ) => { 10 | const displayName = Component.displayName || Component.name || 'Component'; 11 | 12 | const ComponentWithMethods = ( 13 | props: Omit 14 | ) => { 15 | const responsiveMethods = useResponsiveMethods(); 16 | return ; 17 | }; 18 | 19 | ComponentWithMethods.displayName = `withResponsiveMethods(${displayName})`; 20 | 21 | return ComponentWithMethods; 22 | }; 23 | 24 | export { withResponsiveMethods, withResponsiveMethods as withRM }; 25 | -------------------------------------------------------------------------------- /tv-example/ios/tvexample/AppDelegate.mm: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | 5 | @implementation AppDelegate 6 | 7 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 8 | { 9 | self.moduleName = @"tvexample"; 10 | // You can add your custom initial props in the dictionary below. 11 | // They will be passed down to the ViewController used by React Native. 12 | self.initialProps = @{}; 13 | 14 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 15 | } 16 | 17 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 18 | { 19 | return [self getBundleURL]; 20 | } 21 | 22 | - (NSURL *)getBundleURL 23 | { 24 | #if DEBUG 25 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; 26 | #else 27 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 28 | #endif 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # XDE 6 | .expo/ 7 | 8 | # VSCode 9 | .vscode/ 10 | jsconfig.json 11 | 12 | # Xcode 13 | # 14 | build/ 15 | *.pbxuser 16 | !default.pbxuser 17 | *.mode1v3 18 | !default.mode1v3 19 | *.mode2v3 20 | !default.mode2v3 21 | *.perspectivev3 22 | !default.perspectivev3 23 | xcuserdata 24 | *.xccheckout 25 | *.moved-aside 26 | DerivedData 27 | *.hmap 28 | *.ipa 29 | *.xcuserstate 30 | project.xcworkspace 31 | 32 | # Android/IJ 33 | # 34 | .classpath 35 | .cxx 36 | .gradle 37 | .idea 38 | .project 39 | .settings 40 | local.properties 41 | android.iml 42 | 43 | # Cocoapods 44 | # 45 | example/ios/Pods 46 | 47 | # Ruby 48 | example/vendor/ 49 | 50 | # node.js 51 | # 52 | node_modules/ 53 | npm-debug.log 54 | yarn-debug.log 55 | yarn-error.log 56 | 57 | # BUCK 58 | buck-out/ 59 | \.buckd/ 60 | android/app/libs 61 | android/keystores/debug.keystore 62 | 63 | # Expo 64 | .expo/ 65 | 66 | # Turborepo 67 | .turbo/ 68 | 69 | # generated by bob 70 | lib/ 71 | -------------------------------------------------------------------------------- /tv-example/android/app/src/main/java/com/tvexample/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.tvexample 2 | 3 | import com.facebook.react.ReactActivity 4 | import com.facebook.react.ReactActivityDelegate 5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled 6 | import com.facebook.react.defaults.DefaultReactActivityDelegate 7 | 8 | class MainActivity : ReactActivity() { 9 | 10 | /** 11 | * Returns the name of the main component registered from JavaScript. This is used to schedule 12 | * rendering of the component. 13 | */ 14 | override fun getMainComponentName(): String = "tvexample" 15 | 16 | /** 17 | * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] 18 | * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] 19 | */ 20 | override fun createReactActivityDelegate(): ReactActivityDelegate = 21 | DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) 22 | } 23 | -------------------------------------------------------------------------------- /macos-example/README.md: -------------------------------------------------------------------------------- 1 | This is a new [**React Native**](https://reactnative.dev) project, bootstrapped using [`@react-native-community/cli`](https://github.com/react-native-community/cli). 2 | 3 | # Getting Started 4 | 5 | > **Note**: Make sure you have completed the [React Native - Environment Setup](https://reactnative.dev/docs/environment-setup) instructions till "Creating a new application" step, before proceeding. 6 | 7 | ## Step 1: Start the Metro Server 8 | 9 | First, you will need to start **Metro**, the JavaScript _bundler_ that ships _with_ React Native. 10 | 11 | To start Metro, run the following command from the _root_ of your React Native project: 12 | 13 | ```bash 14 | # using npm 15 | npm start 16 | 17 | # OR using Yarn 18 | yarn start 19 | ``` 20 | 21 | ## Step 2: Start your Application 22 | 23 | If cocoapods aren't installed: 24 | 25 | ```bash 26 | # using npm 27 | npm run pod 28 | 29 | # OR using Yarn 30 | yarn pod 31 | ``` 32 | 33 | ### For MacOS 34 | 35 | ```bash 36 | # using npm 37 | npm run macos 38 | 39 | # OR using Yarn 40 | yarn macos 41 | ``` 42 | -------------------------------------------------------------------------------- /src/layout/createRStyle/parseValue.ts: -------------------------------------------------------------------------------- 1 | import { rs, rh, rw } from '../responsiveMethods'; 2 | import { PATTERN_REGEX } from '../../utils/constants'; 3 | import type { ValuePattern, CreateStyleConfig } from '../../types'; 4 | 5 | export const parseValue = ( 6 | value: ValuePattern | undefined, 7 | styleConfig?: Partial 8 | ) => { 9 | if (typeof value !== 'string') { 10 | return value; 11 | } else { 12 | const { width, height, scaleConfig } = styleConfig ?? {}; 13 | const executed = value.match(PATTERN_REGEX); 14 | if (!executed) { 15 | return value; 16 | } 17 | if (executed[1] === 'undefined') { 18 | return undefined; 19 | } 20 | const styleValue = parseFloat(executed[1]!); 21 | const suffix = executed[4]; 22 | switch (suffix) { 23 | case 'rs': 24 | return rs(styleValue, width, height, scaleConfig); 25 | case 'rw': 26 | return rw(styleValue, width); 27 | case 'rh': 28 | return rh(styleValue, height); 29 | default: 30 | return value; 31 | } 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | lint: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v3 16 | 17 | - name: Setup 18 | uses: ./.github/actions/setup 19 | 20 | - name: Lint files 21 | run: yarn lint 22 | 23 | - name: Typecheck files 24 | run: yarn typecheck 25 | 26 | - name: Test files 27 | run: yarn test 28 | 29 | test: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v3 34 | 35 | - name: Setup 36 | uses: ./.github/actions/setup 37 | 38 | - name: Run unit tests 39 | run: yarn test --maxWorkers=2 --coverage 40 | 41 | build: 42 | runs-on: ubuntu-latest 43 | steps: 44 | - name: Checkout 45 | uses: actions/checkout@v3 46 | 47 | - name: Setup 48 | uses: ./.github/actions/setup 49 | 50 | - name: Build package 51 | run: yarn prepack 52 | -------------------------------------------------------------------------------- /tv-example/ios/tvexample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "idiom": "iphone", 5 | "scale": "2x", 6 | "size": "20x20" 7 | }, 8 | { 9 | "idiom": "iphone", 10 | "scale": "3x", 11 | "size": "20x20" 12 | }, 13 | { 14 | "idiom": "iphone", 15 | "scale": "2x", 16 | "size": "29x29" 17 | }, 18 | { 19 | "idiom": "iphone", 20 | "scale": "3x", 21 | "size": "29x29" 22 | }, 23 | { 24 | "idiom": "iphone", 25 | "scale": "2x", 26 | "size": "40x40" 27 | }, 28 | { 29 | "idiom": "iphone", 30 | "scale": "3x", 31 | "size": "40x40" 32 | }, 33 | { 34 | "idiom": "iphone", 35 | "scale": "2x", 36 | "size": "60x60" 37 | }, 38 | { 39 | "idiom": "iphone", 40 | "scale": "3x", 41 | "size": "60x60" 42 | }, 43 | { 44 | "idiom": "ios-marketing", 45 | "scale": "1x", 46 | "size": "1024x1024" 47 | } 48 | ], 49 | "info": { 50 | "author": "xcode", 51 | "version": 1 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /example/src/ClassApp/MediaQuery.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { 3 | FRProvider, 4 | withMediaQuery, 5 | type DeviceType, 6 | } from 'react-native-full-responsive'; 7 | import { StyleSheet, View, Text } from 'react-native'; 8 | 9 | class Component extends React.Component<{ 10 | type: DeviceType; 11 | }> { 12 | render(): React.ReactNode { 13 | const { type } = this.props; 14 | return ( 15 | 16 | Dimensions type is: "{type}" 17 | 18 | ); 19 | } 20 | } 21 | 22 | const MyComponent = withMediaQuery(Component); 23 | 24 | class App extends React.Component { 25 | render(): React.ReactNode { 26 | return ( 27 | 28 | 29 | 30 | ); 31 | } 32 | } 33 | 34 | export default App; 35 | 36 | const styles = StyleSheet.create({ 37 | container: { 38 | flex: 1, 39 | alignItems: 'center', 40 | justifyContent: 'center', 41 | }, 42 | textBold: { 43 | fontSize: 20, 44 | fontWeight: 'bold', 45 | }, 46 | }); 47 | -------------------------------------------------------------------------------- /tv-example/src/ClassApp/MediaQuery.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { 3 | FRProvider, 4 | withMediaQuery, 5 | type DeviceType, 6 | } from 'react-native-full-responsive'; 7 | import { StyleSheet, View, Text } from 'react-native'; 8 | 9 | class Component extends React.Component<{ 10 | type: DeviceType; 11 | }> { 12 | render(): React.ReactNode { 13 | const { type } = this.props; 14 | return ( 15 | 16 | Dimensions type is: "{type}" 17 | 18 | ); 19 | } 20 | } 21 | 22 | const MyComponent = withMediaQuery(Component); 23 | 24 | class App extends React.Component { 25 | render(): React.ReactNode { 26 | return ( 27 | 28 | 29 | 30 | ); 31 | } 32 | } 33 | 34 | export default App; 35 | 36 | const styles = StyleSheet.create({ 37 | container: { 38 | flex: 1, 39 | alignItems: 'center', 40 | justifyContent: 'center', 41 | }, 42 | textBold: { 43 | fontSize: 20, 44 | fontWeight: 'bold', 45 | }, 46 | }); 47 | -------------------------------------------------------------------------------- /macos-example/src/ClassApp/MediaQuery.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { 3 | FRProvider, 4 | withMediaQuery, 5 | type DeviceType, 6 | } from 'react-native-full-responsive'; 7 | import { StyleSheet, View, Text } from 'react-native'; 8 | 9 | class Component extends React.Component<{ 10 | type: DeviceType; 11 | }> { 12 | render(): React.ReactNode { 13 | const { type } = this.props; 14 | return ( 15 | 16 | Dimensions type is: "{type}" 17 | 18 | ); 19 | } 20 | } 21 | 22 | const MyComponent = withMediaQuery(Component); 23 | 24 | class App extends React.Component { 25 | render(): React.ReactNode { 26 | return ( 27 | 28 | 29 | 30 | ); 31 | } 32 | } 33 | 34 | export default App; 35 | 36 | const styles = StyleSheet.create({ 37 | container: { 38 | flex: 1, 39 | alignItems: 'center', 40 | justifyContent: 'center', 41 | }, 42 | textBold: { 43 | fontSize: 20, 44 | fontWeight: 'bold', 45 | }, 46 | }); 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 mhp23 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /macos-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "macosexample", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "lint": "eslint .", 7 | "pod": "cd macos && pod install", 8 | "start": "react-native start", 9 | "macos": "react-native run-macos", 10 | "test": "jest" 11 | }, 12 | "dependencies": { 13 | "react": "18.2.0", 14 | "react-native": "0.73.4", 15 | "react-native-full-responsive": "link:../", 16 | "react-native-macos": "^0.73.0-0" 17 | }, 18 | "devDependencies": { 19 | "@babel/core": "^7.20.0", 20 | "@babel/preset-env": "^7.20.0", 21 | "@babel/runtime": "^7.20.0", 22 | "@react-native/babel-preset": "0.73.21", 23 | "@react-native/eslint-config": "0.73.2", 24 | "@react-native/metro-config": "0.73.5", 25 | "@react-native/typescript-config": "0.73.1", 26 | "babel-jest": "^29.6.3", 27 | "babel-plugin-module-resolver": "^5.0.0", 28 | "eslint": "^8.19.0", 29 | "jest": "^29.6.3", 30 | "prettier": "2.8.8", 31 | "react-test-renderer": "18.2.0", 32 | "typescript": "5.0.4" 33 | }, 34 | "engines": { 35 | "node": ">=18" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tv-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tvexample", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "expo run:android", 7 | "ios": "expo run:ios", 8 | "pod": "cd ios && pod install", 9 | "tvos": "expo run:ios --scheme tvexample-tvOS --device \"Apple TV\"", 10 | "lint": "eslint .", 11 | "start": "expo start", 12 | "test": "jest" 13 | }, 14 | "dependencies": { 15 | "expo": "^50.0.2", 16 | "react": "18.2.0", 17 | "react-native": "npm:react-native-tvos@0.73.4-0", 18 | "react-native-full-responsive": "link:../" 19 | }, 20 | "devDependencies": { 21 | "@babel/core": "^7.20.0", 22 | "@babel/preset-env": "^7.20.0", 23 | "@babel/runtime": "^7.20.0", 24 | "@react-native/babel-preset": "0.73.21", 25 | "@react-native/eslint-config": "0.73.2", 26 | "@react-native/metro-config": "0.73.5", 27 | "@react-native/typescript-config": "0.73.1", 28 | "babel-jest": "^29.6.3", 29 | "eslint": "^8.19.0", 30 | "jest": "^29.6.3", 31 | "prettier": "2.8.8", 32 | "react-test-renderer": "18.2.0", 33 | "typescript": "5.0.4" 34 | }, 35 | "engines": { 36 | "node": ">=18" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const escape = require('escape-string-regexp'); 3 | const { getDefaultConfig } = require('@expo/metro-config'); 4 | const exclusionList = require('metro-config/src/defaults/exclusionList'); 5 | const pak = require('../package.json'); 6 | 7 | const root = path.resolve(__dirname, '..'); 8 | 9 | const modules = Object.keys({ 10 | ...pak.peerDependencies, 11 | }); 12 | 13 | const defaultConfig = getDefaultConfig(__dirname); 14 | 15 | module.exports = { 16 | ...defaultConfig, 17 | 18 | projectRoot: __dirname, 19 | watchFolders: [root], 20 | 21 | // We need to make sure that only one version is loaded for peerDependencies 22 | // So we block them at the root, and alias them to the versions in example's node_modules 23 | resolver: { 24 | ...defaultConfig.resolver, 25 | 26 | blacklistRE: exclusionList( 27 | modules.map( 28 | (m) => 29 | new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`) 30 | ) 31 | ), 32 | 33 | extraNodeModules: modules.reduce((acc, name) => { 34 | acc[name] = path.join(__dirname, 'node_modules', name); 35 | return acc; 36 | }, {}), 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /macos-example/macos/macosexample-macOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "idiom": "mac", 5 | "scale": "1x", 6 | "size": "16x16" 7 | }, 8 | { 9 | "idiom": "mac", 10 | "scale": "2x", 11 | "size": "16x16" 12 | }, 13 | { 14 | "idiom": "mac", 15 | "scale": "1x", 16 | "size": "32x32" 17 | }, 18 | { 19 | "idiom": "mac", 20 | "scale": "2x", 21 | "size": "32x32" 22 | }, 23 | { 24 | "idiom": "mac", 25 | "scale": "1x", 26 | "size": "128x128" 27 | }, 28 | { 29 | "idiom": "mac", 30 | "scale": "2x", 31 | "size": "128x128" 32 | }, 33 | { 34 | "idiom": "mac", 35 | "scale": "1x", 36 | "size": "256x256" 37 | }, 38 | { 39 | "idiom": "mac", 40 | "scale": "2x", 41 | "size": "256x256" 42 | }, 43 | { 44 | "idiom": "mac", 45 | "scale": "1x", 46 | "size": "512x512" 47 | }, 48 | { 49 | "idiom": "mac", 50 | "scale": "2x", 51 | "size": "512x512" 52 | } 53 | ], 54 | "info": { 55 | "author": "xcode", 56 | "version": 1 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/layout/createRStyle/index.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | import { recursiveMapping } from './mapping/recursiveMapping'; 3 | import type { StyleType, NamedStyles, CreateStyleConfig } from '../../types'; 4 | /** 5 | * Creating responsive styles, instead of using `StyleSheet.create({})`, you can use `createRStyle` (create responsive style) 6 | * to generate your responsive styles for your components. 7 | * ```ts 8 | * const styles = createRStyle({ 9 | * container: { 10 | * flex: 1, 11 | * }, 12 | * box: { 13 | * width: '10rw', //instead of using responsiveWidth method 14 | * height: '20rh', //instead of using responsiveHeight method 15 | * }, 16 | * text: { 17 | * fontSize: '10rs', //instead of using responsiveScale method 18 | * }, 19 | * //... 20 | * }) 21 | * ``` 22 | * @param styles 23 | * @param styleConfig 24 | */ 25 | export const createRStyle = | NamedStyles>( 26 | style: T, 27 | styleConfig?: Partial 28 | ): StyleType => { 29 | const responsivedStyles = recursiveMapping( 30 | style, 31 | styleConfig 32 | ) as StyleSheet.NamedStyles; 33 | return StyleSheet.create(responsivedStyles) as StyleType; 34 | }; 35 | -------------------------------------------------------------------------------- /tv-example/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | ios/.xcode.env.local 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | *.hprof 33 | .cxx/ 34 | *.keystore 35 | !debug.keystore 36 | 37 | # node.js 38 | # 39 | node_modules/ 40 | npm-debug.log 41 | yarn-error.log 42 | 43 | # fastlane 44 | # 45 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 46 | # screenshots whenever they are needed. 47 | # For more information about the recommended setup visit: 48 | # https://docs.fastlane.tools/best-practices/source-control/ 49 | 50 | **/fastlane/report.xml 51 | **/fastlane/Preview.html 52 | **/fastlane/screenshots 53 | **/fastlane/test_output 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # Ruby / CocoaPods 59 | /ios/Pods/ 60 | /vendor/bundle/ 61 | 62 | # Temporary files created by Metro to check the health of the file watcher 63 | .metro-health-check* 64 | 65 | # testing 66 | /coverage 67 | -------------------------------------------------------------------------------- /macos-example/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | ios/.xcode.env.local 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | *.hprof 33 | .cxx/ 34 | *.keystore 35 | !debug.keystore 36 | 37 | # node.js 38 | # 39 | node_modules/ 40 | npm-debug.log 41 | yarn-error.log 42 | 43 | # fastlane 44 | # 45 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 46 | # screenshots whenever they are needed. 47 | # For more information about the recommended setup visit: 48 | # https://docs.fastlane.tools/best-practices/source-control/ 49 | 50 | **/fastlane/report.xml 51 | **/fastlane/Preview.html 52 | **/fastlane/screenshots 53 | **/fastlane/test_output 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # Ruby / CocoaPods 59 | /ios/Pods/ 60 | /vendor/bundle/ 61 | 62 | # Temporary files created by Metro to check the health of the file watcher 63 | .metro-health-check* 64 | 65 | # testing 66 | /coverage 67 | -------------------------------------------------------------------------------- /src/hooks/useMediaQuery.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | import { DefaultThresholds } from '../constants'; 3 | import { useWindowDimensions } from 'react-native'; 4 | import type { DeviceType, MappedDeviceType } from '../types'; 5 | 6 | const useMediaQuery = ( 7 | thresholds: Partial = DefaultThresholds 8 | ) => { 9 | const { width, height } = useWindowDimensions(); 10 | 11 | const mergedThresholds = useMemo(() => { 12 | return { 13 | ...DefaultThresholds, 14 | ...thresholds, 15 | }; 16 | }, [thresholds]); 17 | 18 | const deviceSize = useMemo(() => { 19 | let type: DeviceType; 20 | const candidatedSize = width < height ? width : height; 21 | if (candidatedSize <= mergedThresholds.xs) { 22 | type = 'xs'; 23 | } else if (candidatedSize <= mergedThresholds.sm) { 24 | type = 'sm'; 25 | } else if (candidatedSize <= mergedThresholds.md) { 26 | type = 'md'; 27 | } else if (candidatedSize <= mergedThresholds.lg) { 28 | type = 'lg'; 29 | } else if (candidatedSize <= mergedThresholds.xl) { 30 | type = 'xl'; 31 | } else { 32 | type = '2xl'; 33 | } 34 | return type; 35 | }, [mergedThresholds, height, width]); 36 | 37 | return deviceSize; 38 | }; 39 | 40 | export { useMediaQuery, useMediaQuery as useMQ }; 41 | -------------------------------------------------------------------------------- /macos-example/macos/macosexample-macOS/AppDelegate.mm: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | 5 | @implementation AppDelegate 6 | 7 | - (void)applicationDidFinishLaunching:(NSNotification *)notification 8 | { 9 | self.moduleName = @"macosexample"; 10 | // You can add your custom initial props in the dictionary below. 11 | // They will be passed down to the ViewController used by React Native. 12 | self.initialProps = @{}; 13 | 14 | return [super applicationDidFinishLaunching:notification]; 15 | } 16 | 17 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 18 | { 19 | #if DEBUG 20 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; 21 | #else 22 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 23 | #endif 24 | } 25 | 26 | /// This method controls whether the `concurrentRoot`feature of React18 is turned on or off. 27 | /// 28 | /// @see: https://reactjs.org/blog/2022/03/29/react-v18.html 29 | /// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). 30 | /// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`. 31 | - (BOOL)concurrentRootEnabled 32 | { 33 | #ifdef RN_FABRIC_ENABLED 34 | return true; 35 | #else 36 | return false; 37 | #endif 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /src/hooks/useRStyle.ts: -------------------------------------------------------------------------------- 1 | import { type DependencyList, useMemo } from 'react'; 2 | import { useDevice } from './useDevice'; 3 | import { createRStyle } from '../layout'; 4 | import { useWindowDimensions } from 'react-native'; 5 | import type { NamedStyles } from '../types'; 6 | /** 7 | * A hook is provided for createRStyle to create a dynamic responsive scale. This hook generates a new style when there are changes in dimensions, the parsing method, type, or bases. 8 | * @param styles 9 | * @param deps dependency list to regenerate styles after changing them, and default is an empty array 10 | * @returns a responsive styles `object` 11 | */ 12 | export const useRStyle = | NamedStyles>( 13 | styles: T | (() => T), 14 | deps: DependencyList = [] 15 | ) => { 16 | const device = useDevice(); 17 | 18 | const { width, height } = useWindowDimensions(); 19 | 20 | const dependencies = [width, height, device, ...deps]; 21 | 22 | const responsivedStyles = useMemo(() => { 23 | const passedStyles = typeof styles === 'function' ? styles() : styles; 24 | return createRStyle(passedStyles, { 25 | width, 26 | height, 27 | scaleConfig: device, 28 | }); 29 | // eslint-disable-next-line react-hooks/exhaustive-deps 30 | }, dependencies); 31 | 32 | return responsivedStyles; 33 | }; 34 | -------------------------------------------------------------------------------- /macos-example/metro.config.js: -------------------------------------------------------------------------------- 1 | const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config'); 2 | 3 | const path = require('path'); 4 | const exclusionList = require('metro-config/src/defaults/exclusionList'); 5 | const escape = require('escape-string-regexp'); 6 | const pack = require('../package.json'); 7 | 8 | const root = path.resolve(__dirname, '..'); 9 | 10 | const modules = [...Object.keys(pack.peerDependencies), 'react-native-macos']; 11 | 12 | /** 13 | * Metro configuration 14 | * https://facebook.github.io/metro/docs/configuration 15 | * 16 | * @type {import('metro-config').MetroConfig} 17 | */ 18 | const config = { 19 | projectRoot: __dirname, 20 | watchFolders: [root], 21 | 22 | // We need to make sure that only one version is loaded for peerDependencies 23 | // So we exclude them at the root, and alias them to the versions in example's node_modules 24 | resolver: { 25 | blacklistRE: exclusionList( 26 | modules.map( 27 | (m) => 28 | new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`) 29 | ) 30 | ), 31 | 32 | extraNodeModules: modules.reduce((acc, name) => { 33 | acc[name] = path.join(__dirname, 'node_modules', name); 34 | return acc; 35 | }, {}), 36 | }, 37 | }; 38 | 39 | module.exports = mergeConfig(getDefaultConfig(__dirname), config); 40 | -------------------------------------------------------------------------------- /tv-example/metro.config.js: -------------------------------------------------------------------------------- 1 | const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config'); 2 | 3 | const path = require('path'); 4 | const exclusionList = require('metro-config/src/defaults/exclusionList'); 5 | const escape = require('escape-string-regexp'); 6 | const pack = require('../package.json'); 7 | 8 | const root = path.resolve(__dirname, '..'); 9 | 10 | const modules = [...Object.keys(pack.peerDependencies), 'react-native-macos']; 11 | 12 | /** 13 | * Metro configuration 14 | * https://facebook.github.io/metro/docs/configuration 15 | * 16 | * @type {import('metro-config').MetroConfig} 17 | */ 18 | const config = { 19 | projectRoot: __dirname, 20 | watchFolders: [root], 21 | 22 | // We need to make sure that only one version is loaded for peerDependencies 23 | // So we exclude them at the root, and alias them to the versions in example's node_modules 24 | resolver: { 25 | blacklistRE: exclusionList( 26 | modules.map( 27 | (m) => 28 | new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`) 29 | ) 30 | ), 31 | 32 | extraNodeModules: modules.reduce((acc, name) => { 33 | acc[name] = path.join(__dirname, 'node_modules', name); 34 | return acc; 35 | }, {}), 36 | }, 37 | }; 38 | 39 | module.exports = mergeConfig(getDefaultConfig(__dirname), config); 40 | -------------------------------------------------------------------------------- /src/layout/createRStyle/mapping/recursiveMapping.ts: -------------------------------------------------------------------------------- 1 | import { parseValue } from '../parseValue'; 2 | import type { ValuePattern, CreateStyleConfig } from '../../../types'; 3 | 4 | export const recursiveMapping = ( 5 | style: T, 6 | styleConfig?: Partial 7 | ): T => { 8 | /** 9 | * WeakMap to store references to already processed objects and arrays 10 | * to avoid redundant processing and improve performance (caching). 11 | */ 12 | const cache = new WeakMap(); 13 | const mapper = (currentStyle: unknown): unknown => { 14 | if (currentStyle == null || typeof currentStyle !== 'object') { 15 | return parseValue(currentStyle as ValuePattern, styleConfig); 16 | } 17 | const cachedResult = cache.get(currentStyle); 18 | if (cachedResult) return cachedResult; 19 | /** 20 | * Create an empty result structure to hold the mapped values 21 | */ 22 | const result = Array.isArray(currentStyle) 23 | ? currentStyle.map(mapper) 24 | : Object.create(null); 25 | /** 26 | * Recursively map each property in the current object (or array element). 27 | * If the currentStyle is an object, we loop through its properties and apply `mapper` to each one. 28 | */ 29 | for (const property in currentStyle) { 30 | result[property] = mapper( 31 | (currentStyle as Record)[property] 32 | ); 33 | } 34 | cache.set(currentStyle, result); 35 | return result; 36 | }; 37 | return mapper(style) as T; 38 | }; 39 | -------------------------------------------------------------------------------- /tv-example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 17 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /macos-example/macos/macosexample-macOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSAppTransportSecurity 26 | 27 | NSAllowsArbitraryLoads 28 | 29 | NSExceptionDomains 30 | 31 | localhost 32 | 33 | NSExceptionAllowsInsecureHTTPLoads 34 | 35 | 36 | 37 | 38 | NSMainStoryboardFile 39 | Main 40 | NSPrincipalClass 41 | NSApplication 42 | NSSupportsAutomaticTermination 43 | 44 | NSSupportsSuddenTermination 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /example/src/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { StyleSheet, View, Text } from 'react-native'; 3 | import { FRProvider, createRStyle } from 'react-native-full-responsive'; 4 | 5 | const SIZE = 20; 6 | 7 | const Box: React.FC = () => { 8 | return ( 9 | 10 | without react-native-full-responsive 11 | 12 | ); 13 | }; 14 | 15 | const ResponsiveBox: React.FC = () => { 16 | return ( 17 | 18 | with react-native-full-responsive 19 | 20 | ); 21 | }; 22 | 23 | export default function App() { 24 | return ( 25 | 26 | 27 | 28 | 29 | 30 | 31 | ); 32 | } 33 | 34 | const styles = StyleSheet.create({ 35 | container: { 36 | flex: 1, 37 | alignItems: 'center', 38 | justifyContent: 'center', 39 | }, 40 | box: { 41 | height: SIZE * 3, 42 | marginVertical: SIZE, 43 | justifyContent: 'center', 44 | backgroundColor: 'orange', 45 | paddingHorizontal: SIZE / 2, 46 | }, 47 | textBold: { 48 | fontWeight: 'bold', 49 | fontSize: SIZE, 50 | }, 51 | }); 52 | 53 | const rStyles = createRStyle({ 54 | box: { 55 | height: `${SIZE * 3}rs`, 56 | justifyContent: 'center', 57 | backgroundColor: 'yellow', 58 | marginVertical: `${SIZE}rs`, 59 | paddingHorizontal: `${SIZE / 2}rs`, 60 | }, 61 | textBold: { 62 | fontWeight: 'bold', 63 | fontSize: `${SIZE}rs`, 64 | }, 65 | }); 66 | -------------------------------------------------------------------------------- /tv-example/src/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { StyleSheet, View, Text } from 'react-native'; 3 | import { FRProvider, createRStyle } from 'react-native-full-responsive'; 4 | 5 | const SIZE = 20; 6 | 7 | const Box: React.FC = () => { 8 | return ( 9 | 10 | without react-native-full-responsive 11 | 12 | ); 13 | }; 14 | 15 | const ResponsiveBox: React.FC = () => { 16 | return ( 17 | 18 | with react-native-full-responsive 19 | 20 | ); 21 | }; 22 | 23 | export default function App() { 24 | return ( 25 | 26 | 27 | 28 | 29 | 30 | 31 | ); 32 | } 33 | 34 | const styles = StyleSheet.create({ 35 | container: { 36 | flex: 1, 37 | alignItems: 'center', 38 | justifyContent: 'center', 39 | }, 40 | box: { 41 | height: SIZE * 3, 42 | marginVertical: SIZE, 43 | justifyContent: 'center', 44 | backgroundColor: 'orange', 45 | paddingHorizontal: SIZE / 2, 46 | }, 47 | textBold: { 48 | fontWeight: 'bold', 49 | fontSize: SIZE, 50 | }, 51 | }); 52 | 53 | const rStyles = createRStyle({ 54 | box: { 55 | height: `${SIZE * 3}rs`, 56 | justifyContent: 'center', 57 | backgroundColor: 'yellow', 58 | marginVertical: `${SIZE}rs`, 59 | paddingHorizontal: `${SIZE / 2}rs`, 60 | }, 61 | textBold: { 62 | color: '#000000', 63 | fontWeight: 'bold', 64 | fontSize: `${SIZE}rs`, 65 | }, 66 | }); 67 | -------------------------------------------------------------------------------- /example/src/HooksApp/RStyleApp.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { StyleSheet, View, Text } from 'react-native'; 3 | import { FRProvider, useRStyle } from 'react-native-full-responsive'; 4 | 5 | const SIZE = 20; 6 | 7 | const Box: React.FC = () => { 8 | return ( 9 | 10 | without react-native-full-responsive 11 | 12 | ); 13 | }; 14 | 15 | const ResponsiveBox: React.FC = () => { 16 | const rStyles = useRStyle({ 17 | box: { 18 | height: `${SIZE * 3}rs`, 19 | justifyContent: 'center', 20 | backgroundColor: 'yellow', 21 | marginVertical: `${SIZE}rs`, 22 | paddingHorizontal: `${SIZE / 2}rs`, 23 | }, 24 | textBold: { 25 | fontWeight: 'bold', 26 | fontSize: `${SIZE}rs`, 27 | }, 28 | }); 29 | 30 | return ( 31 | 32 | with react-native-full-responsive 33 | 34 | ); 35 | }; 36 | 37 | export default function App() { 38 | return ( 39 | 40 | 41 | 42 | 43 | 44 | 45 | ); 46 | } 47 | 48 | const styles = StyleSheet.create({ 49 | container: { 50 | flex: 1, 51 | alignItems: 'center', 52 | justifyContent: 'center', 53 | }, 54 | box: { 55 | height: SIZE * 3, 56 | marginVertical: SIZE, 57 | justifyContent: 'center', 58 | backgroundColor: 'orange', 59 | paddingHorizontal: SIZE / 2, 60 | }, 61 | textBold: { 62 | fontWeight: 'bold', 63 | fontSize: SIZE, 64 | }, 65 | }); 66 | -------------------------------------------------------------------------------- /macos-example/src/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { StyleSheet, View, Text } from 'react-native'; 3 | import { FRProvider, createRStyle } from 'react-native-full-responsive'; 4 | 5 | const SIZE = 20; 6 | 7 | const Box: React.FC = () => { 8 | return ( 9 | 10 | without react-native-full-responsive 11 | 12 | ); 13 | }; 14 | 15 | const ResponsiveBox: React.FC = () => { 16 | return ( 17 | 18 | with react-native-full-responsive 19 | 20 | ); 21 | }; 22 | 23 | export default function App() { 24 | return ( 25 | 26 | 27 | 28 | 29 | 30 | 31 | ); 32 | } 33 | 34 | const styles = StyleSheet.create({ 35 | container: { 36 | flex: 1, 37 | alignItems: 'center', 38 | justifyContent: 'center', 39 | }, 40 | box: { 41 | height: SIZE * 3, 42 | marginVertical: SIZE, 43 | justifyContent: 'center', 44 | backgroundColor: 'orange', 45 | paddingHorizontal: SIZE / 2, 46 | }, 47 | textBold: { 48 | fontWeight: 'bold', 49 | fontSize: SIZE, 50 | }, 51 | }); 52 | 53 | const rStyles = createRStyle({ 54 | box: { 55 | height: `${SIZE * 3}rs`, 56 | justifyContent: 'center', 57 | backgroundColor: 'yellow', 58 | marginVertical: `${SIZE}rs`, 59 | paddingHorizontal: `${SIZE / 2}rs`, 60 | }, 61 | textBold: { 62 | color: '#000000', 63 | fontWeight: 'bold', 64 | fontSize: `${SIZE}rs`, 65 | }, 66 | }); 67 | -------------------------------------------------------------------------------- /macos-example/src/HooksApp/RStyleApp.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { StyleSheet, View, Text } from 'react-native'; 3 | import { FRProvider, useRStyle } from 'react-native-full-responsive'; 4 | 5 | const SIZE = 20; 6 | 7 | const Box: React.FC = () => { 8 | return ( 9 | 10 | without react-native-full-responsive 11 | 12 | ); 13 | }; 14 | 15 | const ResponsiveBox: React.FC = () => { 16 | const rStyles = useRStyle({ 17 | box: { 18 | height: `${SIZE * 3}rs`, 19 | justifyContent: 'center', 20 | backgroundColor: 'yellow', 21 | marginVertical: `${SIZE}rs`, 22 | paddingHorizontal: `${SIZE / 2}rs`, 23 | }, 24 | textBold: { 25 | color: '#000000', 26 | fontWeight: 'bold', 27 | fontSize: `${SIZE}rs`, 28 | }, 29 | }); 30 | 31 | return ( 32 | 33 | with react-native-full-responsive 34 | 35 | ); 36 | }; 37 | 38 | export default function App() { 39 | return ( 40 | 41 | 42 | 43 | 44 | 45 | 46 | ); 47 | } 48 | 49 | const styles = StyleSheet.create({ 50 | container: { 51 | flex: 1, 52 | alignItems: 'center', 53 | justifyContent: 'center', 54 | }, 55 | box: { 56 | height: SIZE * 3, 57 | marginVertical: SIZE, 58 | justifyContent: 'center', 59 | backgroundColor: 'orange', 60 | paddingHorizontal: SIZE / 2, 61 | }, 62 | textBold: { 63 | fontWeight: 'bold', 64 | fontSize: SIZE, 65 | }, 66 | }); 67 | -------------------------------------------------------------------------------- /tv-example/src/HooksApp/RStyleApp.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { StyleSheet, View, Text } from 'react-native'; 3 | import { FRProvider, useRStyle } from 'react-native-full-responsive'; 4 | 5 | const SIZE = 20; 6 | 7 | const Box: React.FC = () => { 8 | return ( 9 | 10 | without react-native-full-responsive 11 | 12 | ); 13 | }; 14 | 15 | const ResponsiveBox: React.FC = () => { 16 | const rStyles = useRStyle({ 17 | box: { 18 | height: `${SIZE * 3}rs`, 19 | justifyContent: 'center', 20 | backgroundColor: 'yellow', 21 | marginVertical: `${SIZE}rs`, 22 | paddingHorizontal: `${SIZE / 2}rs`, 23 | }, 24 | textBold: { 25 | color: '#000000', 26 | fontWeight: 'bold', 27 | fontSize: `${SIZE}rs`, 28 | }, 29 | }); 30 | 31 | return ( 32 | 33 | with react-native-full-responsive 34 | 35 | ); 36 | }; 37 | 38 | export default function App() { 39 | return ( 40 | 41 | 42 | 43 | 44 | 45 | 46 | ); 47 | } 48 | 49 | const styles = StyleSheet.create({ 50 | container: { 51 | flex: 1, 52 | alignItems: 'center', 53 | justifyContent: 'center', 54 | }, 55 | box: { 56 | height: SIZE * 3, 57 | marginVertical: SIZE, 58 | justifyContent: 'center', 59 | backgroundColor: 'orange', 60 | paddingHorizontal: SIZE / 2, 61 | }, 62 | textBold: { 63 | fontWeight: 'bold', 64 | fontSize: SIZE, 65 | }, 66 | }); 67 | -------------------------------------------------------------------------------- /example/src/HooksApp/ResponsiveMethodsApp.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { StyleSheet, View, Text } from 'react-native'; 3 | import { useRM, FRProvider } from 'react-native-full-responsive'; 4 | 5 | const SIZE = 20; 6 | 7 | const MyComponent: React.FC = () => { 8 | const { rs } = useRM(); 9 | 10 | const scaledValue = rs(SIZE); 11 | 12 | return ( 13 | 14 | 15 | 16 | without react-native-full-responsive 17 | 18 | 19 | 29 | 30 | with react-native-full-responsive 31 | 32 | 33 | 34 | ); 35 | }; 36 | 37 | export default function App() { 38 | return ( 39 | 40 | 41 | 42 | ); 43 | } 44 | 45 | const styles = StyleSheet.create({ 46 | container: { 47 | flex: 1, 48 | alignItems: 'center', 49 | justifyContent: 'center', 50 | }, 51 | box: { 52 | height: SIZE * 3, 53 | marginVertical: SIZE, 54 | justifyContent: 'center', 55 | backgroundColor: 'orange', 56 | paddingHorizontal: SIZE / 2, 57 | }, 58 | responsiveBox: { 59 | justifyContent: 'center', 60 | backgroundColor: 'yellow', 61 | }, 62 | textBold: { 63 | fontWeight: 'bold', 64 | }, 65 | }); 66 | -------------------------------------------------------------------------------- /macos-example/src/HooksApp/ResponsiveMethodsApp.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { StyleSheet, View, Text } from 'react-native'; 3 | import { useRM, FRProvider } from 'react-native-full-responsive'; 4 | 5 | const SIZE = 20; 6 | 7 | const MyComponent: React.FC = () => { 8 | const { rs } = useRM(); 9 | 10 | const scaledValue = rs(SIZE); 11 | 12 | return ( 13 | 14 | 15 | 16 | without react-native-full-responsive 17 | 18 | 19 | 29 | 30 | with react-native-full-responsive 31 | 32 | 33 | 34 | ); 35 | }; 36 | 37 | export default function App() { 38 | return ( 39 | 40 | 41 | 42 | ); 43 | } 44 | 45 | const styles = StyleSheet.create({ 46 | container: { 47 | flex: 1, 48 | alignItems: 'center', 49 | justifyContent: 'center', 50 | }, 51 | box: { 52 | height: SIZE * 3, 53 | marginVertical: SIZE, 54 | justifyContent: 'center', 55 | backgroundColor: 'orange', 56 | paddingHorizontal: SIZE / 2, 57 | }, 58 | responsiveBox: { 59 | justifyContent: 'center', 60 | backgroundColor: 'yellow', 61 | }, 62 | textBold: { 63 | fontWeight: 'bold', 64 | }, 65 | }); 66 | -------------------------------------------------------------------------------- /tv-example/src/HooksApp/ResponsiveMethodsApp.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { StyleSheet, View, Text } from 'react-native'; 3 | import { useRM, FRProvider } from 'react-native-full-responsive'; 4 | 5 | const SIZE = 20; 6 | 7 | const MyComponent: React.FC = () => { 8 | const { rs } = useRM(); 9 | 10 | const scaledValue = rs(SIZE); 11 | 12 | return ( 13 | 14 | 15 | 16 | without react-native-full-responsive 17 | 18 | 19 | 29 | 30 | with react-native-full-responsive 31 | 32 | 33 | 34 | ); 35 | }; 36 | 37 | export default function App() { 38 | return ( 39 | 40 | 41 | 42 | ); 43 | } 44 | 45 | const styles = StyleSheet.create({ 46 | container: { 47 | flex: 1, 48 | alignItems: 'center', 49 | justifyContent: 'center', 50 | }, 51 | box: { 52 | height: SIZE * 3, 53 | marginVertical: SIZE, 54 | justifyContent: 'center', 55 | backgroundColor: 'orange', 56 | paddingHorizontal: SIZE / 2, 57 | }, 58 | responsiveBox: { 59 | justifyContent: 'center', 60 | backgroundColor: 'yellow', 61 | }, 62 | textBold: { 63 | fontWeight: 'bold', 64 | }, 65 | }); 66 | -------------------------------------------------------------------------------- /tv-example/ios/tvexample-tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSExceptionDomains 28 | 29 | localhost 30 | 31 | NSExceptionAllowsInsecureHTTPLoads 32 | 33 | 34 | 35 | 36 | NSLocationWhenInUseUsageDescription 37 | 38 | UILaunchStoryboardName 39 | LaunchScreen 40 | UIRequiredDeviceCapabilities 41 | 42 | arm64 43 | 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | UIViewControllerBasedStatusBarAppearance 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /tv-example/android/app/src/main/java/com/tvexample/MainApplication.kt: -------------------------------------------------------------------------------- 1 | package com.tvexample 2 | 3 | import android.app.Application 4 | import com.facebook.react.PackageList 5 | import com.facebook.react.ReactApplication 6 | import com.facebook.react.ReactHost 7 | import com.facebook.react.ReactNativeHost 8 | import com.facebook.react.ReactPackage 9 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load 10 | import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost 11 | import com.facebook.react.defaults.DefaultReactNativeHost 12 | import com.facebook.soloader.SoLoader 13 | 14 | class MainApplication : Application(), ReactApplication { 15 | 16 | override val reactNativeHost: ReactNativeHost = 17 | object : DefaultReactNativeHost(this) { 18 | override fun getPackages(): List = 19 | PackageList(this).packages.apply { 20 | // Packages that cannot be autolinked yet can be added manually here, for example: 21 | // add(MyReactNativePackage()) 22 | } 23 | 24 | override fun getJSMainModuleName(): String = "index" 25 | 26 | override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG 27 | 28 | override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED 29 | override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED 30 | } 31 | 32 | override val reactHost: ReactHost 33 | get() = getDefaultReactHost(this.applicationContext, reactNativeHost) 34 | 35 | override fun onCreate() { 36 | super.onCreate() 37 | SoLoader.init(this, false) 38 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { 39 | // If you opted-in for the New Architecture, we load the native entry point for this app. 40 | load() 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/hooks/useResponsiveMethods.ts: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react'; 2 | import { useDevice } from './useDevice'; 3 | import { type MaybeNumber } from 'src/types'; 4 | import { useWindowDimensions } from 'react-native'; 5 | import { responsiveScale, responsiveWidth, responsiveHeight } from '../layout'; 6 | 7 | const useResponsiveMethods = () => { 8 | const device = useDevice(); 9 | 10 | const { width, height } = useWindowDimensions(); 11 | /** 12 | * The responsive scaled size will be calculated using the passed size. 13 | * @param size 14 | * @returns scaled size, if the passed size is not a number, it will be the passed value. 15 | */ 16 | const rs = useCallback( 17 | (size: T) => { 18 | return responsiveScale(size, width, height, device); 19 | }, 20 | [device, height, width] 21 | ); 22 | /** 23 | * The responsive width size will be calculated using the passed percentage. 24 | * @param widthPercentage 25 | * @returns calculated size, if the passed percentage is not a number, it will be the passed value. 26 | */ 27 | const rw = useCallback( 28 | (widthPercentage: T) => { 29 | return responsiveWidth(widthPercentage, width); 30 | }, 31 | [width] 32 | ); 33 | /** 34 | * The responsive height size will be calculated using the passed percentage. 35 | * @param heightPercentage 36 | * @returns calculated size, if the passed percentage is not a number, it will be the passed value. 37 | */ 38 | const rh = useCallback( 39 | (heightPercentage: T) => { 40 | return responsiveHeight(heightPercentage, height); 41 | }, 42 | [height] 43 | ); 44 | 45 | return { rs, rw, rh }; 46 | }; 47 | 48 | export { useResponsiveMethods, useResponsiveMethods as useRM }; 49 | -------------------------------------------------------------------------------- /tv-example/ios/tvexample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | tvexample 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(CURRENT_PROJECT_VERSION) 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | 30 | NSAllowsArbitraryLoads 31 | 32 | NSAllowsLocalNetworking 33 | 34 | 35 | NSLocationWhenInUseUsageDescription 36 | 37 | UILaunchStoryboardName 38 | LaunchScreen 39 | UIRequiredDeviceCapabilities 40 | 41 | arm64 42 | 43 | UISupportedInterfaceOrientations 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | UIViewControllerBasedStatusBarAppearance 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /example/src/ClassApp/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { StyleSheet, View, Text } from 'react-native'; 4 | import { 5 | withRM, 6 | FRProvider, 7 | type ResponsiveMethodsProps, 8 | } from 'react-native-full-responsive'; 9 | 10 | const SIZE = 20; 11 | 12 | class Component extends React.Component { 13 | render(): React.ReactNode { 14 | const { rs } = this.props; 15 | 16 | const scaledValue = rs(SIZE); 17 | 18 | return ( 19 | 20 | 21 | 22 | without react-native-full-responsive 23 | 24 | 25 | 35 | 36 | with react-native-full-responsive 37 | 38 | 39 | 40 | ); 41 | } 42 | } 43 | 44 | const MyComponent = withRM(Component); 45 | 46 | class App extends React.Component { 47 | render(): React.ReactNode { 48 | return ( 49 | 50 | 51 | 52 | ); 53 | } 54 | } 55 | 56 | export default App; 57 | 58 | const styles = StyleSheet.create({ 59 | container: { 60 | flex: 1, 61 | alignItems: 'center', 62 | justifyContent: 'center', 63 | }, 64 | box: { 65 | height: SIZE * 3, 66 | marginVertical: SIZE, 67 | justifyContent: 'center', 68 | backgroundColor: 'orange', 69 | }, 70 | responsiveBox: { 71 | justifyContent: 'center', 72 | backgroundColor: 'yellow', 73 | }, 74 | textBold: { 75 | fontWeight: 'bold', 76 | }, 77 | }); 78 | -------------------------------------------------------------------------------- /tv-example/src/ClassApp/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { StyleSheet, View, Text } from 'react-native'; 4 | import { 5 | withRM, 6 | FRProvider, 7 | type ResponsiveMethodsProps, 8 | } from 'react-native-full-responsive'; 9 | 10 | const SIZE = 20; 11 | 12 | class Component extends React.Component { 13 | render(): React.ReactNode { 14 | const { rs } = this.props; 15 | 16 | const scaledValue = rs(SIZE); 17 | 18 | return ( 19 | 20 | 21 | 22 | without react-native-full-responsive 23 | 24 | 25 | 35 | 36 | with react-native-full-responsive 37 | 38 | 39 | 40 | ); 41 | } 42 | } 43 | 44 | const MyComponent = withRM(Component); 45 | 46 | class App extends React.Component { 47 | render(): React.ReactNode { 48 | return ( 49 | 50 | 51 | 52 | ); 53 | } 54 | } 55 | 56 | export default App; 57 | 58 | const styles = StyleSheet.create({ 59 | container: { 60 | flex: 1, 61 | alignItems: 'center', 62 | justifyContent: 'center', 63 | }, 64 | box: { 65 | height: SIZE * 3, 66 | marginVertical: SIZE, 67 | justifyContent: 'center', 68 | backgroundColor: 'orange', 69 | }, 70 | responsiveBox: { 71 | justifyContent: 'center', 72 | backgroundColor: 'yellow', 73 | }, 74 | textBold: { 75 | fontWeight: 'bold', 76 | }, 77 | }); 78 | -------------------------------------------------------------------------------- /macos-example/src/ClassApp/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { StyleSheet, View, Text } from 'react-native'; 4 | import { 5 | withRM, 6 | FRProvider, 7 | type ResponsiveMethodsProps, 8 | } from 'react-native-full-responsive'; 9 | 10 | const SIZE = 20; 11 | 12 | class Component extends React.Component { 13 | render(): React.ReactNode { 14 | const { rs } = this.props; 15 | 16 | const scaledValue = rs(SIZE); 17 | 18 | return ( 19 | 20 | 21 | 22 | without react-native-full-responsive 23 | 24 | 25 | 35 | 36 | with react-native-full-responsive 37 | 38 | 39 | 40 | ); 41 | } 42 | } 43 | 44 | const MyComponent = withRM(Component); 45 | 46 | class App extends React.Component { 47 | render(): React.ReactNode { 48 | return ( 49 | 50 | 51 | 52 | ); 53 | } 54 | } 55 | 56 | export default App; 57 | 58 | const styles = StyleSheet.create({ 59 | container: { 60 | flex: 1, 61 | alignItems: 'center', 62 | justifyContent: 'center', 63 | }, 64 | box: { 65 | height: SIZE * 3, 66 | marginVertical: SIZE, 67 | justifyContent: 'center', 68 | backgroundColor: 'orange', 69 | }, 70 | responsiveBox: { 71 | justifyContent: 'center', 72 | backgroundColor: 'yellow', 73 | }, 74 | textBold: { 75 | fontWeight: 'bold', 76 | }, 77 | }); 78 | -------------------------------------------------------------------------------- /jest/index.ts: -------------------------------------------------------------------------------- 1 | export const getDimensions = jest.fn(() => ({ 2 | screenWidth: 375, 3 | screenHeight: 667, 4 | })); 5 | 6 | export const withMediaQuery = jest.fn( 7 | (Component, _thresholds = {}) => Component 8 | ); 9 | export const withMQ = withMediaQuery; 10 | 11 | export const withResponsiveMethods = jest.fn((Component) => Component); 12 | export const withRM = withResponsiveMethods; 13 | 14 | export const useDevice = jest.fn(() => ({ type: 'sm', bases: {} })); 15 | 16 | export const useMediaQuery = jest.fn(() => 'sm'); 17 | export const useMQ = useMediaQuery; 18 | 19 | export const useResponsiveDim = jest.fn(() => ({ width: 100, height: 100 })); 20 | export const useRD = useResponsiveDim; 21 | 22 | export const useResponsiveScale = jest.fn(() => 1); 23 | export const useRS = useResponsiveScale; 24 | 25 | export const useResponsiveWidth = jest.fn(() => 100); 26 | export const useRW = useResponsiveWidth; 27 | 28 | export const useResponsiveHeight = jest.fn(() => 100); 29 | export const useRH = useResponsiveHeight; 30 | 31 | export const useResponsiveMethods = jest.fn(() => ({ 32 | rs: jest.fn((n) => n), 33 | rw: jest.fn((n) => n), 34 | rh: jest.fn((n) => n), 35 | })); 36 | export const useRM = useResponsiveMethods; 37 | 38 | export const FRContext = { Provider: jest.fn(), Consumer: jest.fn() }; 39 | export const FRProvider = jest.fn(({ children }) => children); 40 | 41 | export const responsiveScale = jest.fn((size) => size); 42 | export const rs = responsiveScale; 43 | 44 | export const responsiveWidth = jest.fn((widthPercentage) => widthPercentage); 45 | export const rw = responsiveWidth; 46 | 47 | export const responsiveHeight = jest.fn((heightPercentage) => heightPercentage); 48 | export const rh = responsiveHeight; 49 | 50 | export const createRStyle = jest.fn((style, _styleConfig = {}) => style); 51 | 52 | export const useRStyle = jest.fn((styles, _deps = []) => { 53 | const passedStyles = typeof styles === 'function' ? styles() : styles; 54 | return createRStyle(passedStyles); 55 | }); 56 | -------------------------------------------------------------------------------- /tv-example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m 13 | org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | # AndroidX package structure to make it clearer which packages are bundled with the 21 | # Android operating system, and which are packaged with your app's APK 22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 23 | android.useAndroidX=true 24 | # Automatically convert third-party libraries to use AndroidX 25 | android.enableJetifier=true 26 | 27 | # Use this property to specify which architecture you want to build. 28 | # You can also override it from the CLI using 29 | # ./gradlew -PreactNativeArchitectures=x86_64 30 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 31 | 32 | # Use this property to enable support to the new architecture. 33 | # This will allow you to use TurboModules and the Fabric render in 34 | # your application. You should enable this flag either if you want 35 | # to write custom TurboModules/Fabric components OR use libraries that 36 | # are providing them. 37 | newArchEnabled=false 38 | 39 | # Use this property to enable or disable the Hermes JS engine. 40 | # If set to false, you will be using JSC instead. 41 | hermesEnabled=true 42 | -------------------------------------------------------------------------------- /tv-example/android/app/src/main/res/drawable/rn_edit_text_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 21 | 22 | 23 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import type { ReactNode } from 'react'; 2 | import type { ImageStyle, TextStyle, ViewStyle } from 'react-native'; 3 | 4 | export enum BASE_SIZES { 5 | xs = 320, 6 | sm = 360, 7 | md = 520, 8 | lg = 680, 9 | xl = 740, 10 | '2xl' = 920, 11 | } 12 | export type MaybeNumber = number | undefined; 13 | export type PropsWithChildren

= P & { children?: ReactNode | undefined }; 14 | export type DeviceType = Uncapitalize; 15 | export type MappedDeviceType = { 16 | [K in DeviceType]: K extends DeviceType ? number : never; 17 | }; 18 | export interface ContextProps { 19 | /** 20 | * Specify the current device type to calculation be based on the related base width. 21 | * @default sm 22 | */ 23 | type: DeviceType; 24 | /** 25 | * The base width sizes for each platform to handle and calculate scaled size based on that. 26 | */ 27 | bases: Partial; 28 | } 29 | export interface ResponsiveMethodsProps { 30 | rs: (size: number) => number; 31 | rw: (widthPercentage: number) => number; 32 | rh: (heightPercentage: number) => number; 33 | } 34 | export type StyleStack = { 35 | style: any; 36 | parentPath: unknown; 37 | }; 38 | export type WithPattern = { 39 | [P in keyof T]: number extends T[P] ? ResponsivePattern | T[P] : T[P]; 40 | }; 41 | export type NamedStyles = { 42 | [P in keyof T]: 43 | | WithPattern 44 | | WithPattern 45 | | WithPattern; 46 | }; 47 | export type Pattern = 'rs' | 'rw' | 'rh'; 48 | export type ResponsivePattern = `${number}${Pattern}`; 49 | export type ValuePattern = string | number | ResponsivePattern; 50 | export type CreateStyleConfig = { 51 | /** 52 | * To use custom dimensions width for the calculation 53 | */ 54 | width: number; 55 | /** 56 | * To use custom dimensions height for the calculation 57 | */ 58 | height: number; 59 | /** 60 | * To use a specific responsive scale method config for applying when using `rs` for style properties 61 | */ 62 | scaleConfig: Partial; 63 | }; 64 | 65 | export type StyleType = { 66 | [P in keyof T]: { 67 | [S in keyof T[P]]: T[P][S] extends ResponsivePattern ? number : T[P][S]; 68 | }; 69 | }; 70 | -------------------------------------------------------------------------------- /tv-example/ios/tvexampleTests/tvexampleTests.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import 5 | #import 6 | 7 | #define TIMEOUT_SECONDS 600 8 | #define TEXT_TO_LOOK_FOR @"Welcome to React" 9 | 10 | @interface tvexampleTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation tvexampleTests 15 | 16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test 17 | { 18 | if (test(view)) { 19 | return YES; 20 | } 21 | for (UIView *subview in [view subviews]) { 22 | if ([self findSubviewInView:subview matching:test]) { 23 | return YES; 24 | } 25 | } 26 | return NO; 27 | } 28 | 29 | - (void)testRendersWelcomeScreen 30 | { 31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 33 | BOOL foundElement = NO; 34 | 35 | __block NSString *redboxError = nil; 36 | #ifdef DEBUG 37 | RCTSetLogFunction( 38 | ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 39 | if (level >= RCTLogLevelError) { 40 | redboxError = message; 41 | } 42 | }); 43 | #endif 44 | 45 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 46 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 47 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 48 | 49 | foundElement = [self findSubviewInView:vc.view 50 | matching:^BOOL(UIView *view) { 51 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 52 | return YES; 53 | } 54 | return NO; 55 | }]; 56 | } 57 | 58 | #ifdef DEBUG 59 | RCTSetLogFunction(RCTDefaultLogFunction); 60 | #endif 61 | 62 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 63 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /src/layout/responsiveMethods.ts: -------------------------------------------------------------------------------- 1 | import { PixelRatio } from 'react-native'; 2 | import { getDimensions } from '../utils'; 3 | import { getBaseWidth } from './getBaseWidth'; 4 | import type { ContextProps, MaybeNumber } from '../types'; 5 | 6 | const { screenWidth, screenHeight } = getDimensions(); 7 | /** 8 | * The responsive width size will be calculated using the passed percentage. 9 | * @param widthPercentage 10 | * @param width screen width of device 11 | * @returns calculated size, if the passed percentage is not a number, it will be the passed value. 12 | */ 13 | const responsiveWidth = ( 14 | widthPercentage: T, 15 | width = screenWidth 16 | ) => { 17 | if (typeof widthPercentage !== 'number') { 18 | return widthPercentage; 19 | } 20 | return PixelRatio.roundToNearestPixel((width * widthPercentage) / 100); 21 | }; 22 | /** 23 | * The responsive height size will be calculated using the passed percentage. 24 | * @param heightPercentage 25 | * @param height screen height of device 26 | * @returns calculated size, if the passed percentage is not a number, it will be the passed value. 27 | */ 28 | const responsiveHeight = ( 29 | heightPercentage: T, 30 | height = screenHeight 31 | ) => { 32 | if (typeof heightPercentage !== 'number') { 33 | return heightPercentage; 34 | } 35 | return PixelRatio.roundToNearestPixel((height * heightPercentage) / 100); 36 | }; 37 | /** 38 | * The responsive scaled size will be calculated using the passed size. 39 | * @param size 40 | * @param width screen width of device 41 | * @param height screen height of device 42 | * @returns scaled size, if the passed size is not a number, it will be the passed value. 43 | */ 44 | const responsiveScale = ( 45 | size: T, 46 | width = screenWidth, 47 | height = screenHeight, 48 | config?: Partial 49 | ) => { 50 | if (typeof size !== 'number') { 51 | return size; 52 | } 53 | const baseWidth = getBaseWidth(config?.type, config?.bases); 54 | const dimension = width < height ? width : height; 55 | return (dimension / baseWidth) * size; 56 | }; 57 | 58 | export { 59 | responsiveScale, 60 | responsiveWidth, 61 | responsiveHeight, 62 | responsiveScale as rs, 63 | responsiveWidth as rw, 64 | responsiveHeight as rh, 65 | }; 66 | -------------------------------------------------------------------------------- /tv-example/ios/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/react-native-tvos/react-native-tvos-podspecs.git' 2 | source 'https://cdn.cocoapods.org/' 3 | 4 | # Resolve react_native_pods.rb with node to allow for hoisting 5 | require Pod::Executable.execute_command('node', ['-p', 6 | 'require.resolve( 7 | "react-native/scripts/react_native_pods.rb", 8 | {paths: [process.argv[1]]}, 9 | )', __dir__]).strip 10 | 11 | prepare_react_native_project! 12 | 13 | flipper_config = FlipperConfiguration.disabled 14 | 15 | linkage = ENV['USE_FRAMEWORKS'] 16 | if linkage != nil 17 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green 18 | use_frameworks! :linkage => linkage.to_sym 19 | end 20 | 21 | ####### For now, only one target at a time can be used in a React Native podfile ######## 22 | ####### https://github.com/react-native-tvos/react-native-tvos/issues/619 ######## 23 | 24 | # target 'tvexample' do 25 | # config = use_native_modules! 26 | # platform :ios, min_ios_version_supported 27 | # 28 | # use_react_native!( 29 | # :path => config[:reactNativePath], 30 | # # Enables Flipper. 31 | # # 32 | # # Note that if you have use_frameworks! enabled, Flipper will not work and 33 | # # you should disable the next line. 34 | # :flipper_configuration => flipper_config, 35 | # # An absolute path to your application root. 36 | # :app_path => "#{Pod::Config.instance.installation_root}/.." 37 | # ) 38 | # 39 | # target 'tvexampleTests' do 40 | # inherit! :complete 41 | # # Pods for testing 42 | # end 43 | # 44 | # end 45 | 46 | target 'tvexample-tvOS' do 47 | config = use_native_modules! 48 | platform :tvos, min_ios_version_supported 49 | 50 | use_react_native!( 51 | :path => config[:reactNativePath], 52 | # Enables Flipper. 53 | # 54 | # Note that if you have use_frameworks! enabled, Flipper will not work and 55 | # you should disable the next line. 56 | :flipper_configuration => flipper_config, 57 | # An absolute path to your application root. 58 | :app_path => "#{Pod::Config.instance.installation_root}/.." 59 | ) 60 | 61 | target 'tvexample-tvOSTests' do 62 | inherit! :complete 63 | # Pods for testing 64 | end 65 | 66 | end 67 | 68 | post_install do |installer| 69 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 70 | config = use_native_modules! 71 | react_native_post_install( 72 | installer, 73 | config[:reactNativePath], 74 | :mac_catalyst_enabled => false 75 | ) 76 | end 77 | -------------------------------------------------------------------------------- /tv-example/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.7) 5 | base64 6 | nkf 7 | rexml 8 | activesupport (6.1.7.6) 9 | concurrent-ruby (~> 1.0, >= 1.0.2) 10 | i18n (>= 1.6, < 2) 11 | minitest (>= 5.1) 12 | tzinfo (~> 2.0) 13 | zeitwerk (~> 2.3) 14 | addressable (2.8.6) 15 | public_suffix (>= 2.0.2, < 6.0) 16 | algoliasearch (1.27.5) 17 | httpclient (~> 2.8, >= 2.8.3) 18 | json (>= 1.5.1) 19 | atomos (0.1.3) 20 | base64 (0.2.0) 21 | claide (1.1.0) 22 | cocoapods (1.14.3) 23 | addressable (~> 2.8) 24 | claide (>= 1.0.2, < 2.0) 25 | cocoapods-core (= 1.14.3) 26 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 27 | cocoapods-downloader (>= 2.1, < 3.0) 28 | cocoapods-plugins (>= 1.0.0, < 2.0) 29 | cocoapods-search (>= 1.0.0, < 2.0) 30 | cocoapods-trunk (>= 1.6.0, < 2.0) 31 | cocoapods-try (>= 1.1.0, < 2.0) 32 | colored2 (~> 3.1) 33 | escape (~> 0.0.4) 34 | fourflusher (>= 2.3.0, < 3.0) 35 | gh_inspector (~> 1.0) 36 | molinillo (~> 0.8.0) 37 | nap (~> 1.0) 38 | ruby-macho (>= 2.3.0, < 3.0) 39 | xcodeproj (>= 1.23.0, < 2.0) 40 | cocoapods-core (1.14.3) 41 | activesupport (>= 5.0, < 8) 42 | addressable (~> 2.8) 43 | algoliasearch (~> 1.0) 44 | concurrent-ruby (~> 1.1) 45 | fuzzy_match (~> 2.0.4) 46 | nap (~> 1.0) 47 | netrc (~> 0.11) 48 | public_suffix (~> 4.0) 49 | typhoeus (~> 1.0) 50 | cocoapods-deintegrate (1.0.5) 51 | cocoapods-downloader (2.1) 52 | cocoapods-plugins (1.0.0) 53 | nap 54 | cocoapods-search (1.0.1) 55 | cocoapods-trunk (1.6.0) 56 | nap (>= 0.8, < 2.0) 57 | netrc (~> 0.11) 58 | cocoapods-try (1.2.0) 59 | colored2 (3.1.2) 60 | concurrent-ruby (1.2.3) 61 | escape (0.0.4) 62 | ethon (0.16.0) 63 | ffi (>= 1.15.0) 64 | ffi (1.16.3) 65 | fourflusher (2.3.1) 66 | fuzzy_match (2.0.4) 67 | gh_inspector (1.1.3) 68 | httpclient (2.8.3) 69 | i18n (1.14.1) 70 | concurrent-ruby (~> 1.0) 71 | json (2.7.1) 72 | minitest (5.22.2) 73 | molinillo (0.8.0) 74 | nanaimo (0.3.0) 75 | nap (1.1.0) 76 | netrc (0.11.0) 77 | nkf (0.2.0) 78 | public_suffix (4.0.7) 79 | rexml (3.2.6) 80 | ruby-macho (2.5.1) 81 | typhoeus (1.4.1) 82 | ethon (>= 0.9.0) 83 | tzinfo (2.0.6) 84 | concurrent-ruby (~> 1.0) 85 | xcodeproj (1.24.0) 86 | CFPropertyList (>= 2.3.3, < 4.0) 87 | atomos (~> 0.1.3) 88 | claide (>= 1.0.2, < 2.0) 89 | colored2 (~> 3.1) 90 | nanaimo (~> 0.3.0) 91 | rexml (~> 3.2.4) 92 | zeitwerk (2.6.13) 93 | 94 | PLATFORMS 95 | ruby 96 | 97 | DEPENDENCIES 98 | activesupport (>= 6.1.7.5, < 7.1.0) 99 | cocoapods (>= 1.13, < 1.15) 100 | 101 | RUBY VERSION 102 | ruby 2.6.10p210 103 | 104 | BUNDLED WITH 105 | 2.3.23 106 | -------------------------------------------------------------------------------- /macos-example/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.7) 5 | base64 6 | nkf 7 | rexml 8 | activesupport (6.1.7.6) 9 | concurrent-ruby (~> 1.0, >= 1.0.2) 10 | i18n (>= 1.6, < 2) 11 | minitest (>= 5.1) 12 | tzinfo (~> 2.0) 13 | zeitwerk (~> 2.3) 14 | addressable (2.8.6) 15 | public_suffix (>= 2.0.2, < 6.0) 16 | algoliasearch (1.27.5) 17 | httpclient (~> 2.8, >= 2.8.3) 18 | json (>= 1.5.1) 19 | atomos (0.1.3) 20 | base64 (0.2.0) 21 | claide (1.1.0) 22 | cocoapods (1.14.3) 23 | addressable (~> 2.8) 24 | claide (>= 1.0.2, < 2.0) 25 | cocoapods-core (= 1.14.3) 26 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 27 | cocoapods-downloader (>= 2.1, < 3.0) 28 | cocoapods-plugins (>= 1.0.0, < 2.0) 29 | cocoapods-search (>= 1.0.0, < 2.0) 30 | cocoapods-trunk (>= 1.6.0, < 2.0) 31 | cocoapods-try (>= 1.1.0, < 2.0) 32 | colored2 (~> 3.1) 33 | escape (~> 0.0.4) 34 | fourflusher (>= 2.3.0, < 3.0) 35 | gh_inspector (~> 1.0) 36 | molinillo (~> 0.8.0) 37 | nap (~> 1.0) 38 | ruby-macho (>= 2.3.0, < 3.0) 39 | xcodeproj (>= 1.23.0, < 2.0) 40 | cocoapods-core (1.14.3) 41 | activesupport (>= 5.0, < 8) 42 | addressable (~> 2.8) 43 | algoliasearch (~> 1.0) 44 | concurrent-ruby (~> 1.1) 45 | fuzzy_match (~> 2.0.4) 46 | nap (~> 1.0) 47 | netrc (~> 0.11) 48 | public_suffix (~> 4.0) 49 | typhoeus (~> 1.0) 50 | cocoapods-deintegrate (1.0.5) 51 | cocoapods-downloader (2.1) 52 | cocoapods-plugins (1.0.0) 53 | nap 54 | cocoapods-search (1.0.1) 55 | cocoapods-trunk (1.6.0) 56 | nap (>= 0.8, < 2.0) 57 | netrc (~> 0.11) 58 | cocoapods-try (1.2.0) 59 | colored2 (3.1.2) 60 | concurrent-ruby (1.2.3) 61 | escape (0.0.4) 62 | ethon (0.16.0) 63 | ffi (>= 1.15.0) 64 | ffi (1.16.3) 65 | fourflusher (2.3.1) 66 | fuzzy_match (2.0.4) 67 | gh_inspector (1.1.3) 68 | httpclient (2.8.3) 69 | i18n (1.14.1) 70 | concurrent-ruby (~> 1.0) 71 | json (2.7.1) 72 | minitest (5.22.2) 73 | molinillo (0.8.0) 74 | nanaimo (0.3.0) 75 | nap (1.1.0) 76 | netrc (0.11.0) 77 | nkf (0.2.0) 78 | public_suffix (4.0.7) 79 | rexml (3.2.6) 80 | ruby-macho (2.5.1) 81 | typhoeus (1.4.1) 82 | ethon (>= 0.9.0) 83 | tzinfo (2.0.6) 84 | concurrent-ruby (~> 1.0) 85 | xcodeproj (1.24.0) 86 | CFPropertyList (>= 2.3.3, < 4.0) 87 | atomos (~> 0.1.3) 88 | claide (>= 1.0.2, < 2.0) 89 | colored2 (~> 3.1) 90 | nanaimo (~> 0.3.0) 91 | rexml (~> 3.2.4) 92 | zeitwerk (2.6.13) 93 | 94 | PLATFORMS 95 | ruby 96 | 97 | DEPENDENCIES 98 | activesupport (>= 6.1.7.5, < 7.1.0) 99 | cocoapods (>= 1.13, < 1.15) 100 | 101 | RUBY VERSION 102 | ruby 2.6.10p210 103 | 104 | BUNDLED WITH 105 | 2.3.23 106 | -------------------------------------------------------------------------------- /__tests__/mapping.test.ts: -------------------------------------------------------------------------------- 1 | import { rh, rs, rw } from '../src'; 2 | import { recursiveMapping } from '../src/layout/createRStyle/mapping/recursiveMapping'; 3 | 4 | describe('recursive mapping tests', () => { 5 | it("should returns input value if it isn't an object or array", () => { 6 | expect(recursiveMapping(undefined)).toEqual(undefined); 7 | expect(recursiveMapping(null)).toEqual(null); 8 | expect(recursiveMapping(123)).toEqual(123); 9 | expect(recursiveMapping(true)).toEqual(true); 10 | expect(recursiveMapping('style')).toEqual('style'); 11 | }); 12 | 13 | it('should map empty object and nested objects correctly', () => { 14 | expect(recursiveMapping({})).toEqual({}); 15 | expect( 16 | recursiveMapping({ 17 | container: {}, 18 | text: {}, 19 | box: { 20 | transoform: [], 21 | }, 22 | }) 23 | ).toEqual({ 24 | container: {}, 25 | text: {}, 26 | box: { 27 | transoform: [], 28 | }, 29 | }); 30 | }); 31 | 32 | it('should map an object correctly', () => { 33 | const input = { 34 | container: { 35 | flex: 1, 36 | padding: '20rs', 37 | }, 38 | box: { 39 | width: '10rw', 40 | height: '10rh', 41 | }, 42 | }; 43 | const expectedResult = { 44 | container: { 45 | flex: 1, 46 | padding: rs(20), 47 | }, 48 | box: { 49 | width: rw(10), 50 | height: rh(10), 51 | }, 52 | }; 53 | expect(recursiveMapping(input)).toEqual(expectedResult); 54 | }); 55 | 56 | it('should map an array correctly', () => { 57 | const input = [{ box: { width: '10rs' } }, { box2: { width: '12.5rs' } }]; 58 | const expectedResult = [ 59 | { box: { width: rs(10) } }, 60 | { box2: { width: rs(12.5) } }, 61 | ]; 62 | expect(recursiveMapping(input)).toEqual(expectedResult); 63 | }); 64 | 65 | it('should map deep nested arrays and objects correctly', () => { 66 | const input = { 67 | box: { 68 | nestedArray: [ 69 | { 70 | width: '10rs', 71 | }, 72 | ], 73 | nestedBox: { 74 | nestedArray: [ 75 | { 76 | height: '10rw', 77 | }, 78 | ], 79 | }, 80 | }, 81 | box2: { 82 | nestedBox: { 83 | nestedBox2: { 84 | padding: '12.5rh', 85 | }, 86 | }, 87 | }, 88 | }; 89 | const expectedResult = { 90 | box: { 91 | nestedArray: [ 92 | { 93 | width: rs(10), 94 | }, 95 | ], 96 | nestedBox: { 97 | nestedArray: [ 98 | { 99 | height: rw(10), 100 | }, 101 | ], 102 | }, 103 | }, 104 | box2: { 105 | nestedBox: { 106 | nestedBox2: { 107 | padding: rh(12.5), 108 | }, 109 | }, 110 | }, 111 | }; 112 | expect(recursiveMapping(input)).toEqual(expectedResult); 113 | }); 114 | }); 115 | -------------------------------------------------------------------------------- /tv-example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /macos-example/macos/macosexample.xcodeproj/xcshareddata/xcschemes/macosexample-macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /tv-example/README.md: -------------------------------------------------------------------------------- 1 | This is a new [**React Native**](https://reactnative.dev) project, bootstrapped using [`@react-native-community/cli`](https://github.com/react-native-community/cli). 2 | 3 | # Getting Started 4 | 5 | > **Note**: Make sure you have completed the [React Native - Environment Setup](https://reactnative.dev/docs/environment-setup) instructions till "Creating a new application" step, before proceeding. 6 | 7 | ## Step 1: Start the Metro Server 8 | 9 | First, you will need to start **Metro**, the JavaScript _bundler_ that ships _with_ React Native. 10 | 11 | To start Metro, run the following command from the _root_ of your React Native project: 12 | 13 | ```bash 14 | # using npm 15 | npm start 16 | 17 | # OR using Yarn 18 | yarn start 19 | ``` 20 | 21 | ## Step 2: Start your Application 22 | 23 | Let Metro Bundler run in its _own_ terminal. Open a _new_ terminal from the _root_ of your React Native project. Run the following command to start your _Android_ or _iOS_ app: 24 | 25 | ### For Android 26 | 27 | ```bash 28 | # using npm 29 | npm run android 30 | 31 | # OR using Yarn 32 | yarn android 33 | ``` 34 | 35 | ### For iOS 36 | 37 | ```bash 38 | # using npm 39 | npm run ios 40 | 41 | # OR using Yarn 42 | yarn ios 43 | ``` 44 | 45 | If everything is set up _correctly_, you should see your new app running in your _Android Emulator_ or _iOS Simulator_ shortly provided you have set up your emulator/simulator correctly. 46 | 47 | This is one way to run your app — you can also run it directly from within Android Studio and Xcode respectively. 48 | 49 | ## Step 3: Modifying your App 50 | 51 | Now that you have successfully run the app, let's modify it. 52 | 53 | 1. Open `App.tsx` in your text editor of choice and edit some lines. 54 | 2. For **Android**: Press the R key twice or select **"Reload"** from the **Developer Menu** (Ctrl + M (on Window and Linux) or Cmd ⌘ + M (on macOS)) to see your changes! 55 | 56 | For **iOS**: Hit Cmd ⌘ + R in your iOS Simulator to reload the app and see your changes! 57 | 58 | ## Congratulations! :tada: 59 | 60 | You've successfully run and modified your React Native App. :partying_face: 61 | 62 | ### Now what? 63 | 64 | - If you want to add this new React Native code to an existing application, check out the [Integration guide](https://reactnative.dev/docs/integration-with-existing-apps). 65 | - If you're curious to learn more about React Native, check out the [Introduction to React Native](https://reactnative.dev/docs/getting-started). 66 | 67 | # Troubleshooting 68 | 69 | If you can't get this to work, see the [Troubleshooting](https://reactnative.dev/docs/troubleshooting) page. 70 | 71 | # Learn More 72 | 73 | To learn more about React Native, take a look at the following resources: 74 | 75 | - [React Native Website](https://reactnative.dev) - learn more about React Native. 76 | - [Getting Started](https://reactnative.dev/docs/environment-setup) - an **overview** of React Native and how setup your environment. 77 | - [Learn the Basics](https://reactnative.dev/docs/getting-started) - a **guided tour** of the React Native **basics**. 78 | - [Blog](https://reactnative.dev/blog) - read the latest official React Native **Blog** posts. 79 | - [`@facebook/react-native`](https://github.com/facebook/react-native) - the Open Source; GitHub **repository** for React Native. 80 | -------------------------------------------------------------------------------- /tv-example/ios/tvexample.xcodeproj/xcshareddata/xcschemes/tvexample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /tv-example/ios/tvexample.xcodeproj/xcshareddata/xcschemes/tvexample-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | -------------------------------------------------------------------------------- /tv-example/ios/tvexample/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are always welcome, no matter how large or small! 4 | 5 | We want this community to be friendly and respectful to each other. Please follow it in all your interactions with the project. Before contributing, please read the [code of conduct](./CODE_OF_CONDUCT.md). 6 | 7 | ## Development workflow 8 | 9 | To get started with the project, run `yarn` in the root directory to install the required dependencies for each package: 10 | 11 | ```sh 12 | yarn 13 | ``` 14 | 15 | > While it's possible to use [`npm`](https://github.com/npm/cli), the tooling is built around [`yarn`](https://classic.yarnpkg.com/), so you'll have an easier time if you use `yarn` for development. 16 | 17 | While developing, you can run the [example app](/example/) to test your changes. Any changes you make in your library's JavaScript code will be reflected in the example app without a rebuild. If you change any native code, then you'll need to rebuild the example app. 18 | 19 | To start the packager: 20 | 21 | ```sh 22 | yarn example start 23 | ``` 24 | 25 | To run the example app on Android: 26 | 27 | ```sh 28 | yarn example android 29 | ``` 30 | 31 | To run the example app on iOS: 32 | 33 | ```sh 34 | yarn example ios 35 | ``` 36 | 37 | To run the example app on Web: 38 | 39 | ```sh 40 | yarn example web 41 | ``` 42 | 43 | To run the example app on MacOS: 44 | 45 | ```sh 46 | yarn macos-example macos 47 | ``` 48 | 49 | To run the example app on MacOS: 50 | 51 | ```sh 52 | yarn tv-example tvos 53 | ``` 54 | 55 | Make sure your code passes TypeScript and ESLint. Run the following to verify: 56 | 57 | ```sh 58 | yarn typecheck 59 | yarn lint 60 | ``` 61 | 62 | To fix formatting errors, run the following: 63 | 64 | ```sh 65 | yarn lint --fix 66 | ``` 67 | 68 | Remember to add tests for your change if possible. Run the unit tests by: 69 | 70 | ```sh 71 | yarn test 72 | ``` 73 | 74 | ### Commit message convention 75 | 76 | We follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages: 77 | 78 | - `fix`: bug fixes, e.g. fix crash due to deprecated method. 79 | - `feat`: new features, e.g. add new method to the module. 80 | - `refactor`: code refactor, e.g. migrate from class components to hooks. 81 | - `docs`: changes into documentation, e.g. add usage example for the module.. 82 | - `test`: adding or updating tests, e.g. add integration tests using detox. 83 | - `chore`: tooling changes, e.g. change CI config. 84 | 85 | Our pre-commit hooks verify that your commit message matches this format when committing. 86 | 87 | ### Linting and tests 88 | 89 | [ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/) 90 | 91 | We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing. 92 | 93 | Our pre-commit hooks verify that the linter and tests pass when committing. 94 | 95 | ### Publishing to npm 96 | 97 | We use [release-it](https://github.com/release-it/release-it) to make it easier to publish new versions. It handles common tasks like bumping version based on semver, creating tags and releases etc. 98 | 99 | To publish new versions, run the following: 100 | 101 | ```sh 102 | yarn release 103 | ``` 104 | 105 | ### Scripts 106 | 107 | The `package.json` file contains various scripts for common tasks: 108 | 109 | - `yarn bootstrap`: setup project by installing all dependencies and pods. 110 | - `yarn typecheck`: type-check files with TypeScript. 111 | - `yarn lint`: lint files with ESLint. 112 | - `yarn test`: run unit tests with Jest. 113 | - `yarn example start`: start the Metro server for the example app. 114 | - `yarn example android`: run the example app on Android. 115 | - `yarn example ios`: run the example app on iOS. 116 | - `yarn macos-example macos`: run the example app on MacOS. 117 | - `yarn tv-example tvos`: run the example app on TvOS. 118 | 119 | ### Sending a pull request 120 | 121 | > **Working on your first pull request?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github). 122 | 123 | When you're sending a pull request: 124 | 125 | - Prefer small pull requests focused on one change. 126 | - Verify that linters and tests are passing. 127 | - Review the documentation to make sure it looks good. 128 | - Follow the pull request template when opening a pull request. 129 | - For pull requests that change the API or implementation, discuss with maintainers first by opening an issue. 130 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-full-responsive", 3 | "version": "2.4.5", 4 | "description": "Create a fully responsive React Native app for all supported platforms", 5 | "main": "lib/commonjs/index.js", 6 | "module": "lib/module/index.js", 7 | "types": "lib/typescript/src/index.d.ts", 8 | "react-native": "src/index.tsx", 9 | "source": "src/index", 10 | "files": [ 11 | "src", 12 | "lib", 13 | "jest", 14 | "!**/__tests__", 15 | "!**/__fixtures__", 16 | "!**/__mocks__", 17 | "android", 18 | "ios", 19 | "cpp", 20 | "*.podspec", 21 | "!lib/typescript/example", 22 | "!lib/typescript/macos-example", 23 | "!lib/typescript/tv-example", 24 | "!ios/build", 25 | "!android/build", 26 | "!android/gradle", 27 | "!android/gradlew", 28 | "!android/gradlew.bat", 29 | "!android/local.properties", 30 | "!**/.*" 31 | ], 32 | "scripts": { 33 | "test": "jest", 34 | "typecheck": "tsc --noEmit", 35 | "lint": "eslint \"**/*.{js,ts,tsx}\"", 36 | "prepack": "bob build", 37 | "release": "release-it --only-version", 38 | "example": "yarn --cwd example", 39 | "macos-example": "yarn --cwd macos-example", 40 | "tv-example": "yarn --cwd tv-example", 41 | "bootstrap": "yarn example && yarn macos-example && yarn tv-example && yarn install", 42 | "prepare": "husky", 43 | "pre-commit": "lint-staged" 44 | }, 45 | "keywords": [ 46 | "react-native", 47 | "responsive", 48 | "responsive-ui", 49 | "responsive-font", 50 | "full-responsive", 51 | "responsive-scale", 52 | "responsive-layout", 53 | "responsive-font-size", 54 | "responsive-dimensions", 55 | "react-native-full-responsive", 56 | "react native media query", 57 | "media query", 58 | "responsive size", 59 | "responsiveness", 60 | "responsiveness react native", 61 | "useStyle", 62 | "react native useStyle" 63 | ], 64 | "repository": "https://github.com/Mhp23/react-native-full-responsive", 65 | "author": "mhp23 (https://github.com/Mhp23)", 66 | "license": "MIT", 67 | "bugs": { 68 | "url": "https://github.com/Mhp23/react-native-full-responsive/issues" 69 | }, 70 | "homepage": "https://github.com/Mhp23/react-native-full-responsive#readme", 71 | "publishConfig": { 72 | "registry": "https://registry.npmjs.org/" 73 | }, 74 | "devDependencies": { 75 | "@commitlint/cli": "18.4.4", 76 | "@commitlint/config-conventional": "18.4.4", 77 | "@evilmartians/lefthook": "^1.2.2", 78 | "@react-native-community/eslint-config": "^3.0.2", 79 | "@release-it/conventional-changelog": "^9.0.2", 80 | "@types/jest": "^28.1.2", 81 | "@types/react": "~17.0.21", 82 | "@types/react-native": "0.70.0", 83 | "commitlint": "^17.0.2", 84 | "del-cli": "^5.0.0", 85 | "eslint": "^8.4.1", 86 | "eslint-config-prettier": "^8.5.0", 87 | "eslint-plugin-prettier": "^4.0.0", 88 | "husky": "^9.0.10", 89 | "jest": "^28.1.1", 90 | "lint-staged": "^15.2.1", 91 | "pod-install": "^0.1.0", 92 | "prettier": "^2.0.5", 93 | "react": "18.2.0", 94 | "react-native": "0.73.4", 95 | "react-native-builder-bob": "^0.20.4", 96 | "release-it": "^17.10.0", 97 | "typescript": "^4.5.2" 98 | }, 99 | "resolutions": { 100 | "@types/react": "17.0.21" 101 | }, 102 | "peerDependencies": { 103 | "react": "*", 104 | "react-native": "*" 105 | }, 106 | "engines": { 107 | "node": ">= 16.0.0" 108 | }, 109 | "packageManager": "yarn@1.22.21", 110 | "jest": { 111 | "preset": "react-native", 112 | "modulePathIgnorePatterns": [ 113 | "/example/node_modules", 114 | "/macos-example/node_modules", 115 | "/tv-example/node_modules", 116 | "/lib/" 117 | ] 118 | }, 119 | "release-it": { 120 | "git": { 121 | "commitMessage": "chore: release ${version}", 122 | "tagName": "v${version}" 123 | }, 124 | "npm": { 125 | "publish": true 126 | }, 127 | "github": { 128 | "release": true 129 | }, 130 | "plugins": { 131 | "@release-it/conventional-changelog": { 132 | "preset": { 133 | "name": "angular" 134 | } 135 | } 136 | } 137 | }, 138 | "eslintConfig": { 139 | "root": true, 140 | "extends": [ 141 | "@react-native-community", 142 | "prettier" 143 | ], 144 | "rules": { 145 | "prettier/prettier": [ 146 | "error", 147 | { 148 | "quoteProps": "consistent", 149 | "singleQuote": true, 150 | "tabWidth": 2, 151 | "trailingComma": "es5", 152 | "useTabs": false 153 | } 154 | ] 155 | } 156 | }, 157 | "eslintIgnore": [ 158 | "node_modules/", 159 | "lib/" 160 | ], 161 | "prettier": { 162 | "quoteProps": "consistent", 163 | "singleQuote": true, 164 | "tabWidth": 2, 165 | "trailingComma": "es5", 166 | "useTabs": false 167 | }, 168 | "react-native-builder-bob": { 169 | "source": "src", 170 | "output": "lib", 171 | "targets": [ 172 | "commonjs", 173 | "module", 174 | "typescript" 175 | ] 176 | }, 177 | "dependencies": {} 178 | } 179 | -------------------------------------------------------------------------------- /__tests__/parseValue.test.ts: -------------------------------------------------------------------------------- 1 | import { DefaultBases, rh, rs, rw } from '../src'; 2 | import { parseValue } from '../src/layout/createRStyle/parseValue'; 3 | 4 | const mockedDimensions = { 5 | width: 1024, 6 | height: 720, 7 | }; 8 | 9 | describe('parse value tests', () => { 10 | it('should not parse undefined and number values', () => { 11 | expect(parseValue(undefined)).toEqual(undefined); 12 | expect(parseValue(10)).toEqual(10); 13 | }); 14 | it('should parse responsive scale correctly', () => { 15 | expect(parseValue('10rs')).toEqual(rs(10)); 16 | }); 17 | it('should parse responsive width correctly', () => { 18 | expect(parseValue('10rw')).toEqual(rw(10)); 19 | }); 20 | it('should parse responsive height correctly', () => { 21 | expect(parseValue('10rh')).toEqual(rh(10)); 22 | }); 23 | it('should parse responsive scale with undefined in the pattern correctly', () => { 24 | expect(parseValue(`${undefined}rs`)).toBe(undefined); 25 | }); 26 | it('should parse responsive width with undefined in the pattern correctly', () => { 27 | expect(parseValue(`${undefined}rw`)).toBe(undefined); 28 | }); 29 | it('should parse responsive height with undefined in the pattern correctly', () => { 30 | expect(parseValue(`${undefined}rh`)).toBe(undefined); 31 | }); 32 | it('should parse values are float or have sign correctly', () => { 33 | expect(parseValue('+10rs')).toEqual(rs(10)); 34 | expect(parseValue('-10rs')).toEqual(rs(-10)); 35 | 36 | expect(parseValue('+10rw')).toEqual(rw(10)); 37 | expect(parseValue('-10rw')).toEqual(rw(-10)); 38 | 39 | expect(parseValue('+10rh')).toEqual(rh(10)); 40 | expect(parseValue('-10rh')).toEqual(rh(-10)); 41 | 42 | expect(parseValue('.5rs')).toEqual(rs(0.5)); 43 | expect(parseValue('-0.5rs')).toEqual(rs(-0.5)); 44 | 45 | expect(parseValue('+12.51221rs')).toEqual(rs(12.51221)); 46 | expect(parseValue('-12.51221rs')).toEqual(rs(-12.51221)); 47 | 48 | expect(parseValue('+12.51221rw')).toEqual(rw(12.51221)); 49 | expect(parseValue('-12.51221rw')).toEqual(rw(-12.51221)); 50 | 51 | expect(parseValue('+12.51221rh')).toEqual(rh(12.51221)); 52 | expect(parseValue('-12.51221rh')).toEqual(rh(-12.51221)); 53 | }); 54 | it('should parse responsive scale float value correctly', () => { 55 | expect(parseValue('12.51221rs')).toEqual(rs(12.51221)); 56 | }); 57 | it('should parse responsive width float value correctly', () => { 58 | expect(parseValue('12.5rw')).toEqual(rw(12.5)); 59 | }); 60 | it('should parse responsive height float value correctly', () => { 61 | expect(parseValue('12.5rh')).toEqual(rh(12.5)); 62 | }); 63 | it('should not parse wrong responsive scale patterns', () => { 64 | expect(parseValue('10 rs')).toEqual('10 rs'); 65 | expect(parseValue('10@rs')).toEqual('10@rs'); 66 | expect(parseValue('rs@10')).toEqual('rs@10'); 67 | expect(parseValue('10r')).toEqual('10r'); 68 | expect(parseValue('10rss')).toEqual('10rss'); 69 | }); 70 | it('should not parse wrong responsive width patterns', () => { 71 | expect(parseValue('10 rw')).toEqual('10 rw'); 72 | expect(parseValue('10@rw')).toEqual('10@rw'); 73 | expect(parseValue('rw@10')).toEqual('rw@10'); 74 | expect(parseValue('10r')).toEqual('10r'); 75 | expect(parseValue('10rww')).toEqual('10rww'); 76 | }); 77 | it('should not parse wrong responsive height patterns', () => { 78 | expect(parseValue('10 rh')).toEqual('10 rh'); 79 | expect(parseValue('10@rh')).toEqual('10@rh'); 80 | expect(parseValue('rh@10')).toEqual('rh@10'); 81 | expect(parseValue('10r')).toEqual('10r'); 82 | expect(parseValue('10rhh')).toEqual('10rhh'); 83 | }); 84 | it('should parse responsive scale with custom dimensions', () => { 85 | const { width, height } = mockedDimensions; 86 | expect(parseValue('10rs', { width, height })).toEqual( 87 | rs(10, width, height) 88 | ); 89 | }); 90 | it('should parse responsive width with custom width', () => { 91 | const { width } = mockedDimensions; 92 | expect(parseValue('10rw', { width })).toEqual(rw(10, width)); 93 | }); 94 | it('should parse responsive height with custom height', () => { 95 | const { height } = mockedDimensions; 96 | expect(parseValue('10rh', { height })).toEqual(rh(10, height)); 97 | }); 98 | it('should parse responsive scale with custom config', () => { 99 | const { width, height } = mockedDimensions; 100 | expect( 101 | parseValue('10rs', { 102 | width, 103 | height, 104 | scaleConfig: { 105 | type: 'md', 106 | bases: DefaultBases, 107 | }, 108 | }) 109 | ).toEqual(rs(10, width, height, { type: 'md', bases: DefaultBases })); 110 | expect( 111 | parseValue('10rs', { 112 | width, 113 | height, 114 | scaleConfig: { 115 | type: 'lg', 116 | }, 117 | }) 118 | ).toEqual(rs(10, width, height, { type: 'lg' })); 119 | expect( 120 | parseValue('10rs', { 121 | width, 122 | height, 123 | scaleConfig: { 124 | bases: { 125 | ...DefaultBases, 126 | sm: 240, 127 | }, 128 | }, 129 | }) 130 | ).toEqual( 131 | rs(10, width, height, { 132 | bases: { 133 | ...DefaultBases, 134 | sm: 240, 135 | }, 136 | }) 137 | ); 138 | }); 139 | }); 140 | -------------------------------------------------------------------------------- /tv-example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | apply plugin: "org.jetbrains.kotlin.android" 3 | apply plugin: "com.facebook.react" 4 | 5 | /** 6 | * This is the configuration block to customize your React Native Android app. 7 | * By default you don't need to apply any configuration, just uncomment the lines you need. 8 | */ 9 | react { 10 | /* Folders */ 11 | // The root of your project, i.e. where "package.json" lives. Default is '..' 12 | // root = file("../") 13 | // The folder where the react-native NPM package is. Default is ../node_modules/react-native 14 | // reactNativeDir = file("../node_modules/react-native") 15 | // The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen 16 | // codegenDir = file("../node_modules/@react-native/codegen") 17 | // The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js 18 | // cliFile = file("../node_modules/react-native/cli.js") 19 | 20 | /* Variants */ 21 | // The list of variants to that are debuggable. For those we're going to 22 | // skip the bundling of the JS bundle and the assets. By default is just 'debug'. 23 | // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. 24 | // debuggableVariants = ["liteDebug", "prodDebug"] 25 | 26 | /* Bundling */ 27 | // A list containing the node command and its flags. Default is just 'node'. 28 | // nodeExecutableAndArgs = ["node"] 29 | // 30 | // The command to run when bundling. By default is 'bundle' 31 | // bundleCommand = "ram-bundle" 32 | // 33 | // The path to the CLI configuration file. Default is empty. 34 | // bundleConfig = file(../rn-cli.config.js) 35 | // 36 | // The name of the generated asset file containing your JS bundle 37 | // bundleAssetName = "MyApplication.android.bundle" 38 | // 39 | // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' 40 | // entryFile = file("../js/MyApplication.android.js") 41 | // 42 | // A list of extra flags to pass to the 'bundle' commands. 43 | // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle 44 | // extraPackagerArgs = [] 45 | 46 | /* Hermes Commands */ 47 | // The hermes compiler command to run. By default it is 'hermesc' 48 | // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" 49 | // 50 | // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" 51 | // hermesFlags = ["-O", "-output-source-map"] 52 | } 53 | 54 | /** 55 | * Set this to true to Run Proguard on Release builds to minify the Java bytecode. 56 | */ 57 | def enableProguardInReleaseBuilds = false 58 | 59 | /** 60 | * The preferred build flavor of JavaScriptCore (JSC) 61 | * 62 | * For example, to use the international variant, you can use: 63 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` 64 | * 65 | * The international variant includes ICU i18n library and necessary data 66 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that 67 | * give correct results when using with locales other than en-US. Note that 68 | * this variant is about 6MiB larger per architecture than default. 69 | */ 70 | def jscFlavor = 'org.webkit:android-jsc:+' 71 | 72 | android { 73 | ndkVersion rootProject.ext.ndkVersion 74 | buildToolsVersion rootProject.ext.buildToolsVersion 75 | compileSdk rootProject.ext.compileSdkVersion 76 | 77 | namespace "com.tvexample" 78 | defaultConfig { 79 | applicationId "com.tvexample" 80 | minSdkVersion rootProject.ext.minSdkVersion 81 | targetSdkVersion rootProject.ext.targetSdkVersion 82 | versionCode 1 83 | versionName "1.0" 84 | } 85 | signingConfigs { 86 | debug { 87 | storeFile file('debug.keystore') 88 | storePassword 'android' 89 | keyAlias 'androiddebugkey' 90 | keyPassword 'android' 91 | } 92 | } 93 | buildTypes { 94 | debug { 95 | signingConfig signingConfigs.debug 96 | } 97 | release { 98 | // Caution! In production, you need to generate your own keystore file. 99 | // see https://reactnative.dev/docs/signed-apk-android. 100 | signingConfig signingConfigs.debug 101 | minifyEnabled enableProguardInReleaseBuilds 102 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 103 | } 104 | } 105 | } 106 | 107 | dependencies { 108 | // The version of react-native is set by the React Native Gradle Plugin 109 | // For the TV repo, 110 | // we use the io.github.react-native-tvos group for the react-android and hermes-android dependencies 111 | 112 | implementation("io.github.react-native-tvos:react-android") 113 | // No Flipper for TV 114 | // implementation("com.facebook.react:flipper-integration") 115 | 116 | if (hermesEnabled.toBoolean()) { 117 | implementation("io.github.react-native-tvos:hermes-android") 118 | } else { 119 | implementation jscFlavor 120 | } 121 | } 122 | 123 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) 124 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, caste, color, religion, or sexual 11 | identity and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the overall 27 | community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or advances of 32 | any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email address, 36 | without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official e-mail address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | [INSERT CONTACT METHOD]. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series of 87 | actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or permanent 94 | ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within the 114 | community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.1, available at 120 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 121 | 122 | Community Impact Guidelines were inspired by 123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 127 | [https://www.contributor-covenant.org/translations][translations]. 128 | 129 | [homepage]: https://www.contributor-covenant.org 130 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 131 | [Mozilla CoC]: https://github.com/mozilla/diversity 132 | [FAQ]: https://www.contributor-covenant.org/faq 133 | [translations]: https://www.contributor-covenant.org/translations 134 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | React Native Full Responsive Banner 4 | 5 |

6 | 7 |

8 | Create a fully responsive React Native app for all supported platforms 9 |

10 | 11 |
12 | Documentation · Examples 13 |
14 | 15 | ## 📢 Introduction 16 | 17 | This package makes it super easy to create apps responsive that work perfectly on all different screen sizes in React Native (like font size, width, height, and more), making sure everything looks great on any device, from extra small to extra large. You can also tweak how things scale and adjust settings to make everything just the way you want it. 18 | 19 | 20 | 21 | 26 | 31 | 36 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
22 | 23 | iPhone 15 Pro Max 24 | 25 | 27 | 28 | iPhone SE (3rd generation) 29 | 30 | 32 | 33 | iPad Pro (12.9-inch) 34 | 35 | 37 | 38 | Web 39 | 40 |
iPhone 15 Pro MaxiPhone SE (3rd gen)iPad Pro (12.9-inch)Web
49 | 50 | ## 💫 Features 51 | 52 | - Easy to use: Effortlessly implement size scaling and responsive design. 53 | - Cross-platform: Works seamlessly across multiple platforms and devices. 54 | - [createRStyle](./USAGE.md#createrstyle) method and [useRStyle](./USAGE.md#userstyle) hook as alternatives to using `StyleSheet.create` for create stylesheets. 55 | - Various responsive hooks provided: Use these hooks based on your specific use cases. 56 | - Customizable scaling: Define base widths for specific dimension types `(xs, sm, ... 2xl)` for precise control. 57 | - Responsive percentage-based sizing: Adjust sizing based on width or height by `PixelRatio`. 58 | - Media query hook: Detect dimension types by using the [useMediaQuery](./USAGE.md#usemediaquery-usemq) hook. You can also override default thresholds as needed. This hook can be used in the provider to automatically detect and respond based on the configurations. 59 | - Various responsive Higher-Order Components (HOCs) provided: Utilize these methods in your class components. 60 | - Written in TypeScript and fully typed. 61 | 62 | ## 📀 Installation 63 | 64 | **Supported for React Native >= 0.60**
65 | 66 | ```sh 67 | yarn add react-native-full-responsive 68 | 69 | //or 70 | 71 | npm install react-native-full-responsive --save 72 | ``` 73 | 74 | ## 🚀 Quick Start 75 | 76 | **_Starting from v2, you can easily create your styles using the `createRStyle` or `useRStyle` hooks_** 77 | 78 | Use [createRStyle](./USAGE.md#createrstyle) in a similar way to when you use `StyleSheet.create`: 79 | 80 | ```tsx 81 | import * as React from 'react'; 82 | import { View, Text } from 'react-native'; 83 | import { createRStyle } from 'react-native-full-responsive'; 84 | 85 | const SIZE = 20; 86 | 87 | export default function App() { 88 | return ( 89 | 90 | 91 | My awesome responsive text! 92 | 93 | 94 | ); 95 | } 96 | 97 | const styles = createRStyle({ 98 | container: { 99 | flex: 1, 100 | alignItems: 'center', 101 | justifyContent: 'center', 102 | }, 103 | box: { 104 | height: `${SIZE * 3}rs`, 105 | justifyContent: 'center', 106 | backgroundColor: 'yellow', 107 | marginVertical: `${SIZE}rs`, 108 | paddingHorizontal: `${SIZE / 2}rs`, 109 | }, 110 | textBold: { 111 | fontWeight: 'bold', 112 | fontSize: `${SIZE}rs`, 113 | }, 114 | }); 115 | ``` 116 | 117 | Alternatively, use [useRStyle](./USAGE.md#userstyle) to create dynamic styles that change when dimensions, bases, or types are modified: 118 | 119 | ```tsx 120 | import * as React from 'react'; 121 | import { View, Text } from 'react-native'; 122 | import { FRProvider, useRStyle } from 'react-native-full-responsive'; 123 | 124 | const SIZE = 20; 125 | 126 | const ResponsiveBox: React.FC = () => { 127 | const styles = useRStyle({ 128 | container: { 129 | flex: 1, 130 | alignItems: 'center', 131 | justifyContent: 'center', 132 | }, 133 | box: { 134 | height: `${SIZE * 3}rs`, 135 | justifyContent: 'center', 136 | backgroundColor: 'yellow', 137 | marginVertical: `${SIZE}rs`, 138 | paddingHorizontal: `${SIZE / 2}rs`, 139 | }, 140 | textBold: { 141 | fontWeight: 'bold', 142 | fontSize: `${SIZE}rs`, 143 | }, 144 | }); 145 | 146 | return ( 147 | 148 | 149 | My awesome responsive text! 150 | 151 | 152 | ); 153 | }; 154 | 155 | export default function App() { 156 | return ( 157 | 158 | 159 | 160 | ); 161 | } 162 | ``` 163 | 164 | Alternatively, make use of the responsive methods and hooks that are available from v1: 165 | 166 | ```tsx 167 | import * as React from 'react'; 168 | import { Text } from 'react-native'; 169 | import { useRM, FRProvider } from 'react-native-full-responsive'; 170 | //... 171 | 172 | const MyComponent = () => { 173 | const { rs } = useRM(); 174 | 175 | const scaledValue = rs(20); 176 | 177 | return ( 178 | My awesome responsive text! 179 | ); 180 | }; 181 | 182 | export default function App() { 183 | return ( 184 | 185 | 186 | 187 | ); 188 | } 189 | ``` 190 | 191 | To become more familiar with how to use methods within your function or class components, check out the provided [examples](./example/src/). 192 | 193 | Also, if you're looking to become more familiar with how to use the package as a universal responsive utility, check out [Todo App](https://github.com/Mhp23/todo-app-client) project for a practical example 194 | 195 | ## 📚 Documentation 196 | 197 | Explore the [usage documentation](./USAGE.md) to discover how to leverage the methods, hooks, and other features. 198 | 199 | ## 🤝 Contribution 200 | 201 | See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow. 202 | 203 | ## 🧪 Test 204 | 205 | To mock the package's methods and components using the default mock configuration provided, follow these steps: 206 | 207 | - Create a file named `react-native-full-responsive.ts` inside your `__mocks__` directory. 208 | 209 | - Copy the following code into that file: 210 | 211 | ```ts 212 | export * from 'react-native-full-responsive/jest'; 213 | ``` 214 | 215 | ## 🛡️ License 216 | 217 | MIT 218 | -------------------------------------------------------------------------------- /tv-example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command; 206 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 207 | # shell script including quotes and variable substitutions, so put them in 208 | # double quotes to make sure that they get re-expanded; and 209 | # * put everything else in single quotes, so that it's not re-expanded. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /USAGE.md: -------------------------------------------------------------------------------- 1 |

📚 React Native Full Responsive Usage Documentation

2 | 3 | - [Full Responsive Provider](#full-responsive-provider) 4 | - [createRStyle](#createrstyle) 5 | - [useRStyle](#userstyle) 6 | - [responsiveScale (rs)](#responsivescale-rs) 7 | - [responsiveWidth (rw)](#responsivewidth-rw) 8 | - [responsiveHeight (rh)](#responsiveheight-rh) 9 | - [useResponsiveMethods (useRM)](#useresponsivemethods-userm) 10 | - [useResponsiveScale (useRS)](#useresponsivescale-users) 11 | - [useResponsiveWidth (useRW)](#useresponsivewidth-userw) 12 | - [useResponsiveHeight (useRH)](#useresponsiveheight-userh) 13 | - [useResponsiveDim (useRD)](#useresponsivedim-userd) 14 | - [useMediaQuery (useMQ)](#usemediaquery-usemq) 15 | - [HOC](#hoc) 16 | 17 | Excpet for `createRStyle` and `useRStyle`, All methods within this package offer two usage options, a longer syntax for those who prefer explicitness, and an abbreviated version for those who prefer conciseness. Choose the approach that best suits your preferred import and usage style. 18 | 19 | ```js 20 | import { 21 | useMediaQuery, 22 | responsiveScale, 23 | responsiveWidth, 24 | responsiveHeight, 25 | useResponsiveDim, 26 | useResponsiveScale, 27 | useResponsiveWidth, 28 | useResponsiveHeight, 29 | useResponsiveMethods, 30 | //HOC 31 | withMediaQuery, 32 | withResponsiveMethods, 33 | } from 'react-native-full-responsive'; 34 | //or 35 | import { 36 | rs, 37 | rw, 38 | rh, 39 | useRD, 40 | useRS, 41 | useRW, 42 | useRH, 43 | useRM, 44 | useMQ, 45 | //HOC 46 | withMQ, 47 | withRM, 48 | } from 'react-native-full-responsive'; 49 | ``` 50 | 51 | ## Full Responsive Provider 52 | 53 | To use the `responsiveScale` hook, first you should use `FRProvider` in the root component. 54 | 55 | ```tsx 56 | import { FRProvider } from 'react-native-full-responsive'; 57 | //... 58 | 59 | export default function App() { 60 | return ( 61 | 62 | { 63 | //... 64 | } 65 | 66 | ); 67 | } 68 | ``` 69 | 70 | The provider accepts `bases` and `type` props; explanations of each follow. 71 | 72 |

type

73 | 74 | Specifies the current device dimension type that size scaling calculations will use, based on specified related bases. 75 | 76 | _Possible values_: `xs` | `sm` | `md` | `lg` | `xl` | `2xl` 77 | 78 | _Default_: `sm` 79 | 80 | _Determination_ (suggestion): 81 | 82 | - Can be based on device specification type (e.g., "sm" for smartphones, "md" for tablets). 83 | - Can be dynamically determined using the [useMediaQuery](<'#useMediaQuery-(useMQ)>) hook, based on device dimensions. 84 | 85 |

bases

86 | 87 | _Description_: Specifies or overrides default experimental base sizes to achieve the ideal point for scaling for each dimension type. 88 | 89 | _Default_: 90 | 91 | ```ts 92 | { 93 | 'xs' = 320, 94 | 'sm' = 360, 95 | 'md' = 520, 96 | 'lg' = 680, 97 | 'xl' = 740, 98 | '2xl' = 920, 99 | } 100 | ``` 101 | 102 | ⚠️ **_Note: While we'll explore the three main methods and their usage below, it's highly recommended to use the provided hooks (and HOCs for class components) for dynamic dimensions and a more streamlined approach._** 103 | 104 | ## createRStyle 105 | 106 | This method is similar to `StyleSheet.create` and can be used in the same way. However, the key difference is that it allows you to easily create responsive styles by utilizing the following responsive patterns (inspired by [react-native-size-matters](https://github.com/nirsky/react-native-size-matters)): 107 | 108 | `number(rs|rw|rh)` 109 | 110 | These patterns accept a number (which can be a signed or unsigned integer or a float number) and a suffix to specify responsive methods. 111 | 112 | This method accepts two arguments: the first argument is your style, and the second argument is optional. The second argument allows for more advanced and flexible usage. 113 | 114 | ```ts 115 | createRStyle(styles, config); 116 | ``` 117 | 118 | The config options includes: 119 | 120 |

width

121 | 122 | To use custom dimensions width for the calculation 123 | 124 |

height

125 | 126 | To use custom dimensions height for the calculation 127 | 128 |

scaleConfig

129 | 130 | To use a specific responsive scale method config for applying when using `rs` for style properties 131 | 132 | ```ts 133 | const styles = createRStyle({ 134 | container: { 135 | flex: 1, 136 | }, 137 | box: { 138 | width: '20rw', 139 | height: '10rh', 140 | marginVertical: `5rs`, 141 | justifyContent: 'center', 142 | backgroundColor: 'yellow', 143 | paddingHorizontal: `7.5rs`, 144 | }, 145 | //... 146 | }); 147 | ``` 148 | 149 | ## useRStyle 150 | 151 | A hook is provided for [createRStyle](#createrstyle) to create a dynamic responsive scale. This hook generates a new style when there are changes in dimensions, the parsing method, type, or bases. It accepts two arguments: 152 | 153 | 1. The first argument is the style and is required (as an object or a function) 154 | 2. The second argument is the dependency list to regenerate styles after changing them, and default is an empty array 155 | 156 | An example: 157 | 158 | ```tsx 159 | import * as React from 'react'; 160 | import { View, Text } from 'react-native'; 161 | import { FRProvider, useRStyle } from 'react-native-full-responsive'; 162 | 163 | const SIZE = 20; 164 | 165 | const ResponsiveBox: React.FC = () => { 166 | const styles = useRStyle( 167 | { 168 | container: { 169 | flex: 1, 170 | alignItems: 'center', 171 | justifyContent: 'center', 172 | }, 173 | box: { 174 | height: `${SIZE * 3}rs`, 175 | justifyContent: 'center', 176 | backgroundColor: 'yellow', 177 | marginVertical: `${SIZE}rs`, 178 | paddingHorizontal: `${SIZE / 2}rs`, 179 | }, 180 | textBold: { 181 | fontWeight: 'bold', 182 | fontSize: `${SIZE}rs`, 183 | }, 184 | }, 185 | [] 186 | ); 187 | 188 | return ( 189 | 190 | 191 | My awesome responsive text! 192 | 193 | 194 | ); 195 | }; 196 | 197 | export default function App() { 198 | return ( 199 | 200 | 201 | 202 | ); 203 | } 204 | ``` 205 | 206 | I highly recommend that if you're using `ESLint`, for better debugging and to reduce re-render mistakes due to missing dependencies in your custom hooks and specifically in the `useRStyle` hook, you use the [eslint-plugin-react-hooks](https://github.com/facebook/react/tree/main/packages/eslint-plugin-react-hooks) plugin. Follow its instructions, and add useRStyle to your ESLint rules like this: 207 | 208 | ```js 209 | { 210 | //... 211 | "rules": { 212 | // ... 213 | "react-hooks/exhaustive-deps": ["warn", { 214 | "additionalHooks": "(useRStyle)" 215 | }] 216 | } 217 | } 218 | ``` 219 | 220 | ## responsiveScale (rs) 221 | 222 | This function scales the passed size based on the user's device dimensions and base width size and returns the scaled value. It accepts the following arguments: 223 | 224 | 1. Required argument: The size to be scaled. 225 | 2. Optional argument (for event listeners): The screen width. 226 | 227 | 3. Optional argument (for event listeners): The screen height. 228 | 229 | 4. Optional argument (for specifying current `type` and custom `bases`) 230 | 231 | The second and third arguments are only needed when adding an event listener to respond to dimension changes (e.g., switching between portrait and landscape modes) and forth arguments to specify current `type` and custom `bases`. 232 | 233 | In function components, you can use the [useResponsiveScale](#useResponsiveScale) or [useResponsiveMethods](#useResponsiveMethods) hooks for a more streamlined approach. It only requires passing the size to be scaled, eliminating the need for additional steps or arguments. 234 | 235 | ```tsx 236 | import { rs } from 'react-native-full-responsive'; 237 | //... 238 | const MyComponent = () => { 239 | //... 240 | return My Scaled Text!; 241 | }; 242 | ``` 243 | 244 | ## responsiveWidth (rw) 245 | 246 | This method calculates a responsive value based on a numeric percentage value, utilizing `PixelRatio` and the provided width size. Its usage is similar to [responsiveScale](#responsivescale-rs), but it differs in that it only takes two arguments: 247 | 248 | 1. Required argument: A numeric **percentage** value. 249 | 250 | 2. Optional argument: A custom screen width for width calculations. 251 | 252 | ```tsx 253 | import { rw } from 'react-native-full-responsive'; 254 | //... 255 | const MyComponent = () => { 256 | //... 257 | return ( 258 | 259 | { 260 | //... 261 | } 262 | 263 | ); 264 | }; 265 | ``` 266 | 267 | ## responsiveHeight (rh) 268 | 269 | This method calculates a responsive value based on a numeric percentage value, utilizing `PixelRatio` and the provided height size. Its usage is similar to [responsiveScale](#responsivescale-rs), but it differs in that it only takes two arguments: 270 | 271 | 1. Required argument: A numeric **percentage** value. 272 | 273 | 2. Optional argument: A custom screen height for height calculations. 274 | 275 | ```tsx 276 | import { rh } from 'react-native-full-responsive'; 277 | //... 278 | const MyComponent = () => { 279 | //... 280 | return ( 281 | 282 | { 283 | //... 284 | } 285 | 286 | ); 287 | }; 288 | ``` 289 | 290 | ## useResponsiveMethods (useRM) 291 | 292 | This hook provides `rs`, `rw`, and `rh` methods, which simplify usage by only requiring the needed size to be passed. It automatically detects dimension changes, including landscape/portrait mode switches and `type` updates for scaling sizes on the provider, ensuring responsiveness without manual intervention. 293 | 294 | ```tsx 295 | import { useResponsiveMethods } from 'react-native-full-responsive'; 296 | //... 297 | const MyComponent = () => { 298 | const { rs, rw, rh } = useResponsiveMethods(); //or useRM 299 | //... 300 | return My Scaled Text!; 301 | }; 302 | ``` 303 | 304 | ## useResponsiveScale (useRS) 305 | 306 | A hook is provided for [responsiveScale](#responsivescale-rs), which only requires passing a size for scaling. 307 | 308 | ```tsx 309 | import { useResponsiveScale } from 'react-native-full-responsive'; 310 | //... 311 | const MyComponent = () => { 312 | const fontSize = useResponsiveScale(16); 313 | //... 314 | return My Scaled Text!; 315 | }; 316 | ``` 317 | 318 | ## useResponsiveWidth (useRW) 319 | 320 | A hook is provided for [responsiveWidth](#responsivewidth-rw), which only requires passing a percentage size. 321 | 322 | ```tsx 323 | import { useResponsiveWidth } from 'react-native-full-responsive'; 324 | //... 325 | const MyComponent = () => { 326 | const width = useResponsiveWidth(5); 327 | //... 328 | return ( 329 | 330 | { 331 | //... 332 | } 333 | 334 | ); 335 | }; 336 | ``` 337 | 338 | ## useResponsiveHeight (useRH) 339 | 340 | A hook is provided for [responsiveHeight](#responsivewheight-rh), which only requires passing a percentage size. 341 | 342 | ```tsx 343 | import { useResponsiveHeight } from 'react-native-full-responsive'; 344 | //... 345 | const MyComponent = () => { 346 | const height = useResponsiveHeight(10); 347 | //... 348 | return ( 349 | 350 | { 351 | //... 352 | } 353 | 354 | ); 355 | }; 356 | ``` 357 | 358 | ## useResponsiveDim (useRD) 359 | 360 | A hook is provided responsive width and height together and could to use it like below: 361 | 362 | ```tsx 363 | import { useResponsiveDim } from 'react-native-full-responsive'; 364 | //... 365 | const MyComponent = () => { 366 | const { width, height } = useResponsiveDim(10); 367 | //... 368 | return ( 369 | 370 | { 371 | //... 372 | } 373 | 374 | ); 375 | }; 376 | ``` 377 | 378 | ## useMediaQuery (useMQ) 379 | 380 | A hook is provided to retrieve the `type` of dimension size (`xs` | `sm` | `md` | `lg` | `xl` | `2xl`) based on specified thresholds. These thresholds can be overridden either entirely or selectively for specific dimensions. This hook offers benefits for use within the provider or when actions need to be performed based on type specifications. 381 | 382 | _Default_: 383 | 384 | ```ts 385 | { 386 | 'xs': 320, 387 | 'sm': 576, 388 | 'md': 768, 389 | 'lg': 992, 390 | 'xl': 1200, 391 | '2xl': 1440, 392 | } 393 | ``` 394 | 395 | ```tsx 396 | import { FRProvider, useMediaQuery } from 'react-native-full-responsive'; 397 | //... 398 | 399 | const customThresholds = { 400 | //... 401 | }; 402 | 403 | export default function App() { 404 | const type = useMediaQuery(customThresholds); 405 | 406 | return ( 407 | 408 | { 409 | //... 410 | } 411 | 412 | ); 413 | } 414 | ``` 415 | 416 | ## HOC 417 | 418 | If you use class components, it's all under control! You can access `rs`, `rw`, and `rh` functions using the `withResponsiveMethods` (or withRM) HOC. For media query, use the `withMediaQuery` (or withMQ) HOC. For detailed guidance on employing these HOCs within your class components, kindly refer to [the class app examples](./example/src/ClassApp/). 419 | --------------------------------------------------------------------------------