├── .codeclimate.yml ├── .eslintrc ├── .gitignore ├── .travis.yml ├── babel.config.js ├── changelog.md ├── example ├── .buckconfig ├── .flowconfig ├── .gitattributes ├── .gitignore ├── .watchmanconfig ├── android │ ├── app │ │ ├── _BUCK │ │ ├── build.gradle │ │ ├── build_defs.bzl │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── res │ │ │ ├── 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 │ │ │ └── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── app.js ├── babel.config.js ├── index.js ├── ios │ ├── Podfile │ ├── example-tvOS │ │ └── Info.plist │ ├── example-tvOSTests │ │ └── Info.plist │ ├── example.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── example-tvOS.xcscheme │ │ │ └── example.xcscheme │ ├── example.xcworkspace │ │ └── contents.xcworkspacedata │ ├── example │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Base.lproj │ │ │ └── LaunchScreen.xib │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ └── main.m │ └── exampleTests │ │ ├── Info.plist │ │ └── exampleTests.m ├── metro.config.js └── package.json ├── index.js ├── license.txt ├── package.json ├── readme.md └── src └── components ├── affix ├── __snapshots__ │ └── test.js.snap ├── index.js ├── styles.js └── test.js ├── counter ├── __snapshots__ │ └── test.js.snap ├── index.js ├── styles.js └── test.js ├── field-filled ├── __snapshots__ │ └── test.js.snap ├── index.js ├── styles.js └── test.js ├── field-outlined ├── __snapshots__ │ └── test.js.snap ├── index.js └── test.js ├── field ├── __snapshots__ │ └── test.js.snap ├── index.js ├── styles.js └── test.js ├── helper ├── __snapshots__ │ └── test.js.snap ├── index.js ├── styles.js └── test.js ├── label ├── __snapshots__ │ └── test.js.snap ├── index.js ├── styles.js └── test.js ├── line ├── __snapshots__ │ └── test.js.snap ├── index.js ├── styles.js └── test.js └── outline ├── __snapshots__ └── test.js.snap ├── index.js ├── styles.js └── test.js /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | checks: 3 | file-lines: 4 | config: 5 | threshold: 1000 6 | method-count: 7 | config: 8 | threshold: 50 9 | method-lines: 10 | config: 11 | threshold: 250 12 | plugins: 13 | eslint: 14 | enabled: true 15 | channel: "eslint-5" 16 | exclude_patterns: 17 | - "example/" 18 | - "**/test.js" 19 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | 'parser': 'babel-eslint', 3 | 4 | 'env': { 5 | 'es6': true, 6 | 'react-native/react-native': true, 7 | }, 8 | 9 | 'plugins': [ 10 | 'react', 11 | 'react-native' 12 | ], 13 | 14 | 'extends': [ 15 | 'eslint:recommended', 16 | 'plugin:react/recommended', 17 | ], 18 | 19 | 'settings': { 20 | 'react': { 21 | 'version': '16.9', 22 | }, 23 | }, 24 | 25 | 'rules': { 26 | 'no-unused-vars': ['error', { 27 | 'vars': 'all', 28 | 'args': 'none', 29 | 'ignoreRestSiblings': true, 30 | }], 31 | 32 | 'no-console': ['error'], 33 | 34 | /* 35 | * let foo = [1]; 36 | * let bar = [ 37 | * 1, 38 | * ]; 39 | */ 40 | 'array-bracket-newline': ['error', 'consistent'], 41 | 42 | /* 43 | * let foo = [1, 2, 3]; 44 | */ 45 | 'array-bracket-spacing': ['error', 'never'], 46 | 47 | /* 48 | * if (foo) { bar() } 49 | */ 50 | 'block-spacing': ['error', 'always'], 51 | 52 | /* 53 | * if (foo) { 54 | * bar(); 55 | * } else { 56 | * baz(); 57 | * } 58 | */ 59 | 'brace-style': ['error', '1tbs', { 'allowSingleLine': true }], 60 | 61 | /* 62 | * let foo = [ 63 | * 1, 64 | * ]; 65 | */ 66 | 'comma-dangle': ['error', 'always-multiline'], 67 | 68 | /* 69 | * let foo = [1, 2]; 70 | */ 71 | 'comma-spacing': ['error', { 'before': false, 'after': true }], 72 | 73 | /* 74 | * let foo, 75 | * bar, 76 | * baz; 77 | */ 78 | 'comma-style': ['error', 'last'], 79 | 80 | /* 81 | * let foo = bar[bar]; 82 | */ 83 | 'computed-property-spacing': ['error', 'never'], 84 | 85 | /* 86 | * let that = this; 87 | */ 88 | 'consistent-this': ['error', 'that'], 89 | 90 | /* 91 | * call(); 92 | */ 93 | 'func-call-spacing': ['error', 'never'], 94 | 95 | /* 96 | * function bar() { 1; } 97 | * let bar = () => 1; 98 | */ 99 | 'func-style': ['error', 'declaration', { 'allowArrowFunctions': true }], 100 | 101 | /* 102 | * foo(1); 103 | * bar( 104 | * 2, 3 105 | * ); 106 | */ 107 | 'function-paren-newline': ['error', 'consistent'], 108 | 109 | /* 110 | * () => 1; 111 | */ 112 | 'implicit-arrow-linebreak': ['error', 'beside'], 113 | 114 | /* 115 | * [ 116 | * 1 117 | * ] 118 | */ 119 | 'indent': ['warn', 2, { 'SwitchCase': 1 }], 120 | 121 | /* 122 | * 123 | */ 124 | 'jsx-quotes': ['error', 'prefer-single'], 125 | 126 | /* 127 | * let foo = { bar: true }; 128 | */ 129 | 'key-spacing': ['error', { 'beforeColon': false, 'afterColon': true }], 130 | 131 | /* 132 | * if (foo) { return; } 133 | */ 134 | 'keyword-spacing': ['error', { 'before': true, 'after': true }], 135 | 136 | 'linebreak-style': ['error', 'unix'], 137 | 138 | 'lines-between-class-members': ['error', 'always'], 139 | 140 | 'max-len': ['warn', { 'code': 100, 'ignoreTrailingComments': true }], 141 | 142 | /* 143 | * foo(1, 2, 3, 4, 5); 144 | */ 145 | 'max-params': ['error', { 'max': 6 }], 146 | 147 | 'max-statements-per-line': ['error', { 'max': 1 }], 148 | 149 | 'new-parens': ['error'], 150 | 151 | 'no-trailing-spaces': ['error'], 152 | 153 | 'no-whitespace-before-property': ['error'], 154 | 155 | /* 156 | * let foo = { bar, baz }; 157 | */ 158 | 'object-curly-newline': ['error', { 'consistent': true }], 159 | 'object-curly-spacing': ['error', 'always'], 160 | 'object-property-newline': ['error', { 'allowAllPropertiesOnSameLine': true }], 161 | 162 | /* 163 | * let foo = 'bar' 164 | * + 'baz'; 165 | */ 166 | 'operator-linebreak': ['error', 'before', { 'overrides': { '?': 'after', ':': 'after' } }], 167 | 168 | /* 169 | * 'bar' 170 | */ 171 | 'quotes': ['warn', 'single'], 172 | 173 | /* 174 | * 1; 175 | */ 176 | 'semi': ['warn', 'always'], 177 | 'semi-spacing': ['error', { 'before': false, 'after': true }], 178 | 'semi-style': ['error', 'last'], 179 | 180 | /* 181 | * () => { 182 | * }; 183 | */ 184 | 'space-before-blocks': ['error', 'always'], 185 | 186 | /* 187 | * foo(1, 2, 3); 188 | */ 189 | 'space-before-function-paren': ['error', { 190 | 'anonymous': 'always', 191 | 'named': 'never', 192 | 'asyncArrow': 'always', 193 | }], 194 | 195 | /* 196 | * foo('bar'); 197 | */ 198 | 'space-in-parens': ['error', 'never'], 199 | 200 | /* 201 | * switch (a) { 202 | * case 0: break; 203 | * } 204 | */ 205 | 'switch-colon-spacing': ['error', { 'after': true, 'before': false }], 206 | 207 | 'react-native/no-unused-styles': ['warn'], 208 | 'react-native/no-inline-styles': ['warn'], 209 | }, 210 | } 211 | 212 | /* vim: set ft=javascript : */ 213 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | node_modules 3 | *.lock 4 | 5 | .DS_Store 6 | *.sw[po] 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: osx 2 | language: node_js 3 | 4 | node_js: 5 | - "stable" 6 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ### Added 11 | 12 | - `bottom` key for `contentInset` prop 13 | 14 | ## [0.16.0] - 2019-10-24 15 | 16 | ### Added 17 | 18 | - `left` and `right` keys for `contentInset` prop 19 | - `labelOffset` prop 20 | - `FilledTextField` component 21 | - `OutlinedTextField` component 22 | 23 | ### Changed 24 | 25 | - Accessory view positioning 26 | 27 | ## [0.15.0] - 2019-10-17 28 | 29 | ### Added 30 | 31 | - `contentInset` prop 32 | 33 | ### Changed 34 | 35 | - Refactored helper layout for consistent line height 36 | - Improved RTL support for helper 37 | - Improved label animation 38 | - `label` prop was made optional 39 | 40 | ### Removed 41 | 42 | - `titleFontSize` prop 43 | - `labelHeight` prop 44 | - `labelPadding` prop 45 | - `inputContainerPadding` prop 46 | 47 | ## [0.14.1] - 2019-10-15 48 | 49 | ### Fixed 50 | 51 | - Collapsing layout outside of ScrollView 52 | - Visible sideline on some Android versions 53 | 54 | ## [0.14.0] - 2019-10-14 55 | 56 | ### Added 57 | 58 | - `lineType` prop 59 | - `formatText` prop for masked input support 60 | - `isPlaceholderVisible()` method 61 | 62 | ### Changed 63 | 64 | - Refactored componentDidUpdate and improved animation 65 | 66 | ### Fixed 67 | 68 | - Label and Helper text color in disabled state 69 | - Multiline title and error messages 70 | - Multiline layout on iOS 71 | 72 | ## [0.13.0] - 2019-10-11 73 | 74 | ### Added 75 | 76 | - `renderLeftAccessory` prop 77 | - `renderRightAccessory` prop 78 | - `.isDefaultVisible()` method 79 | - `.isErrored()` method 80 | - `.setValue()` method 81 | 82 | ### Changed 83 | 84 | - `defaultValue` prop becomes current value on focus 85 | - `value` prop provides only initial value 86 | - TextField refactored to improve internal layout 87 | - TextField refactored to be fully uncontrolled component 88 | - Line refactored to render all types of underlines 89 | - Focus and label animations refactored for better performance 90 | 91 | ### Removed 92 | 93 | - `renderAccessory` prop 94 | 95 | ### Fixed 96 | 97 | - Crash on `null` value for `value` prop 98 | - Deprecation warnings for React component lifecycle methods 99 | - Text position for Label and Affix on Android 100 | 101 | ## [0.12.0] - 2018-01-18 102 | 103 | ### Added 104 | 105 | - RTL support 106 | 107 | ### Fixed 108 | 109 | - Multiline input on Web platform 110 | 111 | ## [0.11.0] - 2017-12-05 112 | 113 | ### Added 114 | 115 | - `lineWidth` prop 116 | - `activeLineWidth` prop 117 | - `disabledLineWidth` prop 118 | 119 | ### Fixed 120 | 121 | - Normalized `clearTextOnFocus` behaviour 122 | - Disabled underline on Android 123 | 124 | ## [0.10.0] - 2017-09-15 125 | 126 | ### Added 127 | 128 | - `inputContainerPadding` prop 129 | - `inputContainerStyle` prop 130 | - `disabledLineType` prop 131 | 132 | [Unreleased]: https://github.com/n4kz/react-native-material-textfield/compare/0.16.0...HEAD 133 | [0.16.0]: https://github.com/n4kz/react-native-material-textfield/compare/0.15.0...0.16.0 134 | [0.15.0]: https://github.com/n4kz/react-native-material-textfield/compare/0.14.1...0.15.0 135 | [0.14.1]: https://github.com/n4kz/react-native-material-textfield/compare/0.14.0...0.14.1 136 | [0.14.0]: https://github.com/n4kz/react-native-material-textfield/compare/0.13.0...0.14.0 137 | [0.13.0]: https://github.com/n4kz/react-native-material-textfield/compare/0.12.0...0.13.0 138 | [0.12.0]: https://github.com/n4kz/react-native-material-textfield/compare/0.11.0...0.12.0 139 | [0.11.0]: https://github.com/n4kz/react-native-material-textfield/compare/0.10.0...0.11.0 140 | [0.10.0]: https://github.com/n4kz/react-native-material-textfield/compare/0.9.0...0.10.0 141 | -------------------------------------------------------------------------------- /example/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /example/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore polyfills 9 | node_modules/react-native/Libraries/polyfills/.* 10 | 11 | ; These should not be required directly 12 | ; require from fbjs/lib instead: require('fbjs/lib/warning') 13 | node_modules/warning/.* 14 | 15 | ; Flow doesn't support platforms 16 | .*/Libraries/Utilities/LoadingView.js 17 | 18 | [untyped] 19 | .*/node_modules/@react-native-community/cli/.*/.* 20 | 21 | [include] 22 | 23 | [libs] 24 | node_modules/react-native/Libraries/react-native/react-native-interface.js 25 | node_modules/react-native/flow/ 26 | 27 | [options] 28 | emoji=true 29 | 30 | esproposal.optional_chaining=enable 31 | esproposal.nullish_coalescing=enable 32 | 33 | module.file_ext=.js 34 | module.file_ext=.json 35 | module.file_ext=.ios.js 36 | 37 | munge_underscores=true 38 | 39 | module.name_mapper='^react-native$' -> '/node_modules/react-native/Libraries/react-native/react-native-implementation' 40 | module.name_mapper='^react-native/\(.*\)$' -> '/node_modules/react-native/\1' 41 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '/node_modules/react-native/Libraries/Image/RelativeImageStub' 42 | 43 | suppress_type=$FlowIssue 44 | suppress_type=$FlowFixMe 45 | suppress_type=$FlowFixMeProps 46 | suppress_type=$FlowFixMeState 47 | 48 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\) 49 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+ 50 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 51 | 52 | [lints] 53 | sketchy-null-number=warn 54 | sketchy-null-mixed=warn 55 | sketchy-number=warn 56 | untyped-type-import=warn 57 | nonstrict-import=warn 58 | deprecated-type=warn 59 | unsafe-getters-setters=warn 60 | inexact-spread=warn 61 | unnecessary-invariant=warn 62 | signature-verification-failure=warn 63 | deprecated-utility=error 64 | 65 | [strict] 66 | deprecated-type 67 | nonstrict-import 68 | sketchy-null 69 | unclear-type 70 | unsafe-getters-setters 71 | untyped-import 72 | untyped-type-import 73 | 74 | [version] 75 | ^0.105.0 76 | -------------------------------------------------------------------------------- /example/.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /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 | 24 | # Android/IntelliJ 25 | # 26 | build/ 27 | .idea 28 | .gradle 29 | local.properties 30 | *.iml 31 | 32 | # node.js 33 | # 34 | node_modules/ 35 | npm-debug.log 36 | yarn-error.log 37 | 38 | # BUCK 39 | buck-out/ 40 | \.buckd/ 41 | *.keystore 42 | !debug.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # CocoaPods 59 | /ios/Pods/ 60 | -------------------------------------------------------------------------------- /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/android/app/_BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets") 12 | 13 | lib_deps = [] 14 | 15 | create_aar_targets(glob(["libs/*.aar"])) 16 | 17 | create_jar_targets(glob(["libs/*.jar"])) 18 | 19 | android_library( 20 | name = "all-libs", 21 | exported_deps = lib_deps, 22 | ) 23 | 24 | android_library( 25 | name = "app-code", 26 | srcs = glob([ 27 | "src/main/java/**/*.java", 28 | ]), 29 | deps = [ 30 | ":all-libs", 31 | ":build_config", 32 | ":res", 33 | ], 34 | ) 35 | 36 | android_build_config( 37 | name = "build_config", 38 | package = "com.example", 39 | ) 40 | 41 | android_resource( 42 | name = "res", 43 | package = "com.example", 44 | res = "src/main/res", 45 | ) 46 | 47 | android_binary( 48 | name = "app", 49 | keystore = "//android/keystores:debug", 50 | manifest = "src/main/AndroidManifest.xml", 51 | package_type = "debug", 52 | deps = [ 53 | ":app-code", 54 | ], 55 | ) 56 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | import com.android.build.OutputFile 4 | 5 | /** 6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets 7 | * and bundleReleaseJsAndAssets). 8 | * These basically call `react-native bundle` with the correct arguments during the Android build 9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 10 | * bundle directly from the development server. Below you can see all the possible configurations 11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 12 | * `apply from: "../../node_modules/react-native/react.gradle"` line. 13 | * 14 | * project.ext.react = [ 15 | * // the name of the generated asset file containing your JS bundle 16 | * bundleAssetName: "index.android.bundle", 17 | * 18 | * // the entry file for bundle generation 19 | * entryFile: "index.android.js", 20 | * 21 | * // https://facebook.github.io/react-native/docs/performance#enable-the-ram-format 22 | * bundleCommand: "ram-bundle", 23 | * 24 | * // whether to bundle JS and assets in debug mode 25 | * bundleInDebug: false, 26 | * 27 | * // whether to bundle JS and assets in release mode 28 | * bundleInRelease: true, 29 | * 30 | * // whether to bundle JS and assets in another build variant (if configured). 31 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 32 | * // The configuration property can be in the following formats 33 | * // 'bundleIn${productFlavor}${buildType}' 34 | * // 'bundleIn${buildType}' 35 | * // bundleInFreeDebug: true, 36 | * // bundleInPaidRelease: true, 37 | * // bundleInBeta: true, 38 | * 39 | * // whether to disable dev mode in custom build variants (by default only disabled in release) 40 | * // for example: to disable dev mode in the staging build type (if configured) 41 | * devDisabledInStaging: true, 42 | * // The configuration property can be in the following formats 43 | * // 'devDisabledIn${productFlavor}${buildType}' 44 | * // 'devDisabledIn${buildType}' 45 | * 46 | * // the root of your project, i.e. where "package.json" lives 47 | * root: "../../", 48 | * 49 | * // where to put the JS bundle asset in debug mode 50 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 51 | * 52 | * // where to put the JS bundle asset in release mode 53 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 54 | * 55 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 56 | * // require('./image.png')), in debug mode 57 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 58 | * 59 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 60 | * // require('./image.png')), in release mode 61 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 62 | * 63 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 64 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 65 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 66 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 67 | * // for example, you might want to remove it from here. 68 | * inputExcludes: ["android/**", "ios/**"], 69 | * 70 | * // override which node gets called and with what additional arguments 71 | * nodeExecutableAndArgs: ["node"], 72 | * 73 | * // supply additional arguments to the packager 74 | * extraPackagerArgs: [] 75 | * ] 76 | */ 77 | 78 | project.ext.react = [ 79 | entryFile: "index.js", 80 | enableHermes: false, // clean and rebuild if changing 81 | ] 82 | 83 | apply from: "../../node_modules/react-native/react.gradle" 84 | 85 | /** 86 | * Set this to true to create two separate APKs instead of one: 87 | * - An APK that only works on ARM devices 88 | * - An APK that only works on x86 devices 89 | * The advantage is the size of the APK is reduced by about 4MB. 90 | * Upload all the APKs to the Play Store and people will download 91 | * the correct one based on the CPU architecture of their device. 92 | */ 93 | def enableSeparateBuildPerCPUArchitecture = false 94 | 95 | /** 96 | * Run Proguard to shrink the Java bytecode in release builds. 97 | */ 98 | def enableProguardInReleaseBuilds = false 99 | 100 | /** 101 | * The preferred build flavor of JavaScriptCore. 102 | * 103 | * For example, to use the international variant, you can use: 104 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` 105 | * 106 | * The international variant includes ICU i18n library and necessary data 107 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that 108 | * give correct results when using with locales other than en-US. Note that 109 | * this variant is about 6MiB larger per architecture than default. 110 | */ 111 | def jscFlavor = 'org.webkit:android-jsc:+' 112 | 113 | /** 114 | * Whether to enable the Hermes VM. 115 | * 116 | * This should be set on project.ext.react and mirrored here. If it is not set 117 | * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode 118 | * and the benefits of using Hermes will therefore be sharply reduced. 119 | */ 120 | def enableHermes = project.ext.react.get("enableHermes", false); 121 | 122 | android { 123 | compileSdkVersion rootProject.ext.compileSdkVersion 124 | 125 | compileOptions { 126 | sourceCompatibility JavaVersion.VERSION_1_8 127 | targetCompatibility JavaVersion.VERSION_1_8 128 | } 129 | 130 | defaultConfig { 131 | applicationId "com.example" 132 | minSdkVersion rootProject.ext.minSdkVersion 133 | targetSdkVersion rootProject.ext.targetSdkVersion 134 | versionCode 1 135 | versionName "1.0" 136 | } 137 | splits { 138 | abi { 139 | reset() 140 | enable enableSeparateBuildPerCPUArchitecture 141 | universalApk false // If true, also generate a universal APK 142 | include "armeabi-v7a", "x86", "arm64-v8a", "x86_64" 143 | } 144 | } 145 | signingConfigs { 146 | debug { 147 | storeFile file('debug.keystore') 148 | storePassword 'android' 149 | keyAlias 'androiddebugkey' 150 | keyPassword 'android' 151 | } 152 | } 153 | buildTypes { 154 | debug { 155 | signingConfig signingConfigs.debug 156 | } 157 | release { 158 | // Caution! In production, you need to generate your own keystore file. 159 | // see https://facebook.github.io/react-native/docs/signed-apk-android. 160 | signingConfig signingConfigs.debug 161 | minifyEnabled enableProguardInReleaseBuilds 162 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 163 | } 164 | } 165 | // applicationVariants are e.g. debug, release 166 | applicationVariants.all { variant -> 167 | variant.outputs.each { output -> 168 | // For each separate APK per architecture, set a unique version code as described here: 169 | // https://developer.android.com/studio/build/configure-apk-splits.html 170 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] 171 | def abi = output.getFilter(OutputFile.ABI) 172 | if (abi != null) { // null for the universal-debug, universal-release variants 173 | output.versionCodeOverride = 174 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 175 | } 176 | 177 | } 178 | } 179 | } 180 | 181 | dependencies { 182 | implementation fileTree(dir: "libs", include: ["*.jar"]) 183 | implementation "com.facebook.react:react-native:+" // From node_modules 184 | 185 | if (enableHermes) { 186 | def hermesPath = "../../node_modules/hermes-engine/android/"; 187 | debugImplementation files(hermesPath + "hermes-debug.aar") 188 | releaseImplementation files(hermesPath + "hermes-release.aar") 189 | } else { 190 | implementation jscFlavor 191 | } 192 | } 193 | 194 | // Run this once to be able to run the application with BUCK 195 | // puts all compile dependencies into folder libs for BUCK to use 196 | task copyDownloadableDepsToLibs(type: Copy) { 197 | from configurations.compile 198 | into 'libs' 199 | } 200 | 201 | apply from: file("../../node_modules/react-native-vector-icons/fonts.gradle") 202 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) 203 | -------------------------------------------------------------------------------- /example/android/app/build_defs.bzl: -------------------------------------------------------------------------------- 1 | """Helper definitions to glob .aar and .jar targets""" 2 | 3 | def create_aar_targets(aarfiles): 4 | for aarfile in aarfiles: 5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] 6 | lib_deps.append(":" + name) 7 | android_prebuilt_aar( 8 | name = name, 9 | aar = aarfile, 10 | ) 11 | 12 | def create_jar_targets(jarfiles): 13 | for jarfile in jarfiles: 14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] 15 | lib_deps.append(":" + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | -------------------------------------------------------------------------------- /example/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n4kz/react-native-material-textfield/8df15566ce3635946a5e6d702c9a33bfda8b769f/example/android/app/debug.keystore -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. This is used to schedule 9 | * rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "example"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import com.facebook.react.PackageList; 6 | import com.facebook.react.ReactApplication; 7 | import com.facebook.react.ReactNativeHost; 8 | import com.facebook.react.ReactPackage; 9 | import com.facebook.soloader.SoLoader; 10 | import java.lang.reflect.InvocationTargetException; 11 | import java.util.List; 12 | 13 | public class MainApplication extends Application implements ReactApplication { 14 | 15 | private final ReactNativeHost mReactNativeHost = 16 | new ReactNativeHost(this) { 17 | @Override 18 | public boolean getUseDeveloperSupport() { 19 | return BuildConfig.DEBUG; 20 | } 21 | 22 | @Override 23 | protected List getPackages() { 24 | @SuppressWarnings("UnnecessaryLocalVariable") 25 | List packages = new PackageList(this).getPackages(); 26 | // Packages that cannot be autolinked yet can be added manually here, for example: 27 | // packages.add(new MyReactNativePackage()); 28 | return packages; 29 | } 30 | 31 | @Override 32 | protected String getJSMainModuleName() { 33 | return "index"; 34 | } 35 | }; 36 | 37 | @Override 38 | public ReactNativeHost getReactNativeHost() { 39 | return mReactNativeHost; 40 | } 41 | 42 | @Override 43 | public void onCreate() { 44 | super.onCreate(); 45 | SoLoader.init(this, /* native exopackage */ false); 46 | initializeFlipper(this); // Remove this line if you don't want Flipper enabled 47 | } 48 | 49 | /** 50 | * Loads Flipper in React Native templates. 51 | * 52 | * @param context 53 | */ 54 | private static void initializeFlipper(Context context) { 55 | if (BuildConfig.DEBUG) { 56 | try { 57 | /* 58 | We use reflection here to pick up the class that initializes Flipper, 59 | since Flipper library is not available in release mode 60 | */ 61 | Class aClass = Class.forName("com.facebook.flipper.ReactNativeFlipper"); 62 | aClass.getMethod("initializeFlipper", Context.class).invoke(null, context); 63 | } catch (ClassNotFoundException e) { 64 | e.printStackTrace(); 65 | } catch (NoSuchMethodException e) { 66 | e.printStackTrace(); 67 | } catch (IllegalAccessException e) { 68 | e.printStackTrace(); 69 | } catch (InvocationTargetException e) { 70 | e.printStackTrace(); 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n4kz/react-native-material-textfield/8df15566ce3635946a5e6d702c9a33bfda8b769f/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n4kz/react-native-material-textfield/8df15566ce3635946a5e6d702c9a33bfda8b769f/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n4kz/react-native-material-textfield/8df15566ce3635946a5e6d702c9a33bfda8b769f/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n4kz/react-native-material-textfield/8df15566ce3635946a5e6d702c9a33bfda8b769f/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n4kz/react-native-material-textfield/8df15566ce3635946a5e6d702c9a33bfda8b769f/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n4kz/react-native-material-textfield/8df15566ce3635946a5e6d702c9a33bfda8b769f/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n4kz/react-native-material-textfield/8df15566ce3635946a5e6d702c9a33bfda8b769f/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n4kz/react-native-material-textfield/8df15566ce3635946a5e6d702c9a33bfda8b769f/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n4kz/react-native-material-textfield/8df15566ce3635946a5e6d702c9a33bfda8b769f/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n4kz/react-native-material-textfield/8df15566ce3635946a5e6d702c9a33bfda8b769f/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Hello App Display Name 3 | 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "28.0.3" 6 | minSdkVersion = 16 7 | compileSdkVersion = 28 8 | targetSdkVersion = 28 9 | } 10 | repositories { 11 | google() 12 | jcenter() 13 | } 14 | dependencies { 15 | classpath("com.android.tools.build:gradle:3.4.2") 16 | 17 | // NOTE: Do not place your application dependencies here; they belong 18 | // in the individual module build.gradle files 19 | } 20 | } 21 | 22 | allprojects { 23 | repositories { 24 | mavenLocal() 25 | maven { 26 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 27 | url("$rootDir/../node_modules/react-native/android") 28 | } 29 | maven { 30 | // Android JSC is installed from npm 31 | url("$rootDir/../node_modules/jsc-android/dist") 32 | } 33 | 34 | google() 35 | jcenter() 36 | maven { url 'https://jitpack.io' } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /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: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 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 | android.useAndroidX=true 21 | android.enableJetifier=true 22 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n4kz/react-native-material-textfield/8df15566ce3635946a5e6d702c9a33bfda8b769f/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.5-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or 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 | # http://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 UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin, switch paths to Windows format before running java 129 | if $cygwin ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=$((i+1)) 158 | done 159 | case $i in 160 | (0) set -- ;; 161 | (1) set -- "$args0" ;; 162 | (2) set -- "$args0" "$args1" ;; 163 | (3) set -- "$args0" "$args1" "$args2" ;; 164 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=$(save "$@") 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 184 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 185 | cd "$(dirname "$0")" 186 | fi 187 | 188 | exec "$JAVACMD" "$@" 189 | -------------------------------------------------------------------------------- /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 http://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 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'example' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | -------------------------------------------------------------------------------- /example/app.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { 3 | AppRegistry, 4 | ScrollView, 5 | View, 6 | SafeAreaView, 7 | Platform, 8 | } from 'react-native'; 9 | import { RaisedTextButton } from 'react-native-material-buttons'; 10 | import { TextField } from 'react-native-material-textfield'; 11 | import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; 12 | 13 | let styles = { 14 | scroll: { 15 | backgroundColor: 'transparent', 16 | }, 17 | 18 | container: { 19 | margin: 8, 20 | marginTop: Platform.select({ ios: 8, android: 32 }), 21 | flex: 1, 22 | }, 23 | 24 | contentContainer: { 25 | padding: 8, 26 | }, 27 | 28 | buttonContainer: { 29 | paddingTop: 8, 30 | margin: 8, 31 | }, 32 | 33 | safeContainer: { 34 | flex: 1, 35 | backgroundColor: '#E8EAF6', 36 | }, 37 | }; 38 | 39 | let defaults = { 40 | firstname: 'Eddard', 41 | lastname: 'Stark', 42 | about: 'Stoic, dutiful, and honorable man, considered to embody the values of the North', 43 | }; 44 | 45 | export default function init() { 46 | class Example extends Component { 47 | constructor(props) { 48 | super(props); 49 | 50 | this.onFocus = this.onFocus.bind(this); 51 | this.onSubmit = this.onSubmit.bind(this); 52 | this.onChangeText = this.onChangeText.bind(this); 53 | this.onSubmitFirstName = this.onSubmitFirstName.bind(this); 54 | this.onSubmitLastName = this.onSubmitLastName.bind(this); 55 | this.onSubmitAbout = this.onSubmitAbout.bind(this); 56 | this.onSubmitEmail = this.onSubmitEmail.bind(this); 57 | this.onSubmitPassword = this.onSubmitPassword.bind(this); 58 | this.onAccessoryPress = this.onAccessoryPress.bind(this); 59 | 60 | this.firstnameRef = this.updateRef.bind(this, 'firstname'); 61 | this.lastnameRef = this.updateRef.bind(this, 'lastname'); 62 | this.aboutRef = this.updateRef.bind(this, 'about'); 63 | this.emailRef = this.updateRef.bind(this, 'email'); 64 | this.passwordRef = this.updateRef.bind(this, 'password'); 65 | this.houseRef = this.updateRef.bind(this, 'house'); 66 | 67 | this.renderPasswordAccessory = this.renderPasswordAccessory.bind(this); 68 | 69 | this.state = { 70 | secureTextEntry: true, 71 | ...defaults, 72 | }; 73 | } 74 | 75 | onFocus() { 76 | let { errors = {} } = this.state; 77 | 78 | for (let name in errors) { 79 | let ref = this[name]; 80 | 81 | if (ref && ref.isFocused()) { 82 | delete errors[name]; 83 | } 84 | } 85 | 86 | this.setState({ errors }); 87 | } 88 | 89 | onChangeText(text) { 90 | ['firstname', 'lastname', 'about', 'email', 'password'] 91 | .map((name) => ({ name, ref: this[name] })) 92 | .forEach(({ name, ref }) => { 93 | if (ref.isFocused()) { 94 | this.setState({ [name]: text }); 95 | } 96 | }); 97 | } 98 | 99 | onAccessoryPress() { 100 | this.setState(({ secureTextEntry }) => ({ secureTextEntry: !secureTextEntry })); 101 | } 102 | 103 | onSubmitFirstName() { 104 | this.lastname.focus(); 105 | } 106 | 107 | onSubmitLastName() { 108 | this.about.focus(); 109 | } 110 | 111 | onSubmitAbout() { 112 | this.email.focus(); 113 | } 114 | 115 | onSubmitEmail() { 116 | this.password.focus(); 117 | } 118 | 119 | onSubmitPassword() { 120 | this.password.blur(); 121 | } 122 | 123 | onSubmit() { 124 | let errors = {}; 125 | 126 | ['firstname', 'lastname', 'email', 'password'] 127 | .forEach((name) => { 128 | let value = this[name].value(); 129 | 130 | if (!value) { 131 | errors[name] = 'Should not be empty'; 132 | } else { 133 | if ('password' === name && value.length < 6) { 134 | errors[name] = 'Too short'; 135 | } 136 | } 137 | }); 138 | 139 | this.setState({ errors }); 140 | } 141 | 142 | updateRef(name, ref) { 143 | this[name] = ref; 144 | } 145 | 146 | renderPasswordAccessory() { 147 | let { secureTextEntry } = this.state; 148 | 149 | let name = secureTextEntry? 150 | 'visibility': 151 | 'visibility-off'; 152 | 153 | return ( 154 | 161 | ); 162 | } 163 | 164 | render() { 165 | let { errors = {}, secureTextEntry, ...data } = this.state; 166 | let { firstname, lastname } = data; 167 | 168 | let defaultEmail = `${firstname || 'name'}@${lastname || 'house'}.com` 169 | .replace(/\s+/g, '_') 170 | .toLowerCase(); 171 | 172 | return ( 173 | 174 | 179 | 180 | 192 | 193 | 205 | 206 | 218 | 219 | 233 | 234 | 252 | 253 | 260 | 261 | 262 | 263 | 269 | 270 | 271 | 272 | ); 273 | } 274 | } 275 | 276 | AppRegistry.registerComponent('example', () => Example); 277 | } 278 | -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import init from './app'; 2 | 3 | init(); 4 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '9.0' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | target 'example' do 5 | # Pods for example 6 | pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector" 7 | pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec" 8 | pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired" 9 | pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety" 10 | pod 'React', :path => '../node_modules/react-native/' 11 | pod 'React-Core', :path => '../node_modules/react-native/' 12 | pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules' 13 | pod 'React-Core/DevSupport', :path => '../node_modules/react-native/' 14 | pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS' 15 | pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation' 16 | pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob' 17 | pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image' 18 | pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS' 19 | pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network' 20 | pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings' 21 | pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text' 22 | pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration' 23 | pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/' 24 | 25 | pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact' 26 | pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi' 27 | pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor' 28 | pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector' 29 | pod 'ReactCommon/jscallinvoker', :path => "../node_modules/react-native/ReactCommon" 30 | pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon" 31 | pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga' 32 | 33 | pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec' 34 | pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec' 35 | pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec' 36 | 37 | target 'exampleTests' do 38 | inherit! :search_paths 39 | # Pods for testing 40 | end 41 | 42 | use_native_modules! 43 | end 44 | 45 | target 'example-tvOS' do 46 | # Pods for example-tvOS 47 | 48 | target 'example-tvOSTests' do 49 | inherit! :search_paths 50 | # Pods for testing 51 | end 52 | 53 | end 54 | -------------------------------------------------------------------------------- /example/ios/example-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 | 1.0 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 | armv7 43 | 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | UIViewControllerBasedStatusBarAppearance 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /example/ios/example-tvOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 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 | -------------------------------------------------------------------------------- /example/ios/example.xcodeproj/xcshareddata/xcschemes/example-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 82 | 92 | 94 | 100 | 101 | 102 | 103 | 104 | 105 | 111 | 113 | 119 | 120 | 121 | 122 | 124 | 125 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /example/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 82 | 92 | 94 | 100 | 101 | 102 | 103 | 104 | 105 | 111 | 113 | 119 | 120 | 121 | 122 | 124 | 125 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /example/ios/example.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (nonatomic, strong) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /example/ios/example/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import "AppDelegate.h" 9 | 10 | #import 11 | #import 12 | #import 13 | 14 | @implementation AppDelegate 15 | 16 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 17 | { 18 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; 19 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge 20 | moduleName:@"example" 21 | initialProperties:nil]; 22 | 23 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 24 | 25 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 26 | UIViewController *rootViewController = [UIViewController new]; 27 | rootViewController.view = rootView; 28 | self.window.rootViewController = rootViewController; 29 | [self.window makeKeyAndVisible]; 30 | return YES; 31 | } 32 | 33 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 34 | { 35 | #if DEBUG 36 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 37 | #else 38 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 39 | #endif 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /example/ios/example/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/ios/example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Hello App Display Name 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 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | NSExceptionDomains 32 | 33 | localhost 34 | 35 | NSExceptionAllowsInsecureHTTPLoads 36 | 37 | 38 | 39 | 40 | NSLocationWhenInUseUsageDescription 41 | 42 | UIAppFonts 43 | 44 | Entypo.ttf 45 | EvilIcons.ttf 46 | FontAwesome.ttf 47 | Foundation.ttf 48 | Ionicons.ttf 49 | MaterialCommunityIcons.ttf 50 | MaterialIcons.ttf 51 | Octicons.ttf 52 | SimpleLineIcons.ttf 53 | Zocial.ttf 54 | 55 | UILaunchStoryboardName 56 | LaunchScreen 57 | UIRequiredDeviceCapabilities 58 | 59 | armv7 60 | 61 | UISupportedInterfaceOrientations 62 | 63 | UIInterfaceOrientationPortrait 64 | UIInterfaceOrientationLandscapeLeft 65 | UIInterfaceOrientationLandscapeRight 66 | 67 | UIViewControllerBasedStatusBarAppearance 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /example/ios/example/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /example/ios/exampleTests/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 | -------------------------------------------------------------------------------- /example/ios/exampleTests/exampleTests.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | #import 10 | 11 | #import 12 | #import 13 | 14 | #define TIMEOUT_SECONDS 600 15 | #define TEXT_TO_LOOK_FOR @"Welcome to React" 16 | 17 | @interface exampleTests : XCTestCase 18 | 19 | @end 20 | 21 | @implementation exampleTests 22 | 23 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 24 | { 25 | if (test(view)) { 26 | return YES; 27 | } 28 | for (UIView *subview in [view subviews]) { 29 | if ([self findSubviewInView:subview matching:test]) { 30 | return YES; 31 | } 32 | } 33 | return NO; 34 | } 35 | 36 | - (void)testRendersWelcomeScreen 37 | { 38 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 39 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 40 | BOOL foundElement = NO; 41 | 42 | __block NSString *redboxError = nil; 43 | #ifdef DEBUG 44 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 45 | if (level >= RCTLogLevelError) { 46 | redboxError = message; 47 | } 48 | }); 49 | #endif 50 | 51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 54 | 55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 57 | return YES; 58 | } 59 | return NO; 60 | }]; 61 | } 62 | 63 | #ifdef DEBUG 64 | RCTSetLogFunction(RCTDefaultLogFunction); 65 | #endif 66 | 67 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 68 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 69 | } 70 | 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Metro configuration for React Native 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | */ 7 | 8 | module.exports = { 9 | transformer: { 10 | getTransformOptions: async () => ({ 11 | transform: { 12 | experimentalImportSupport: false, 13 | inlineRequires: false, 14 | }, 15 | }), 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "react-native run-android", 7 | "ios": "react-native run-ios", 8 | "start": "react-native start" 9 | }, 10 | "dependencies": { 11 | "react": "16.8.1", 12 | "react-native": "0.61.2", 13 | "react-native-material-buttons": "^0.6.0", 14 | "react-native-material-textfield": "*", 15 | "react-native-vector-icons": "^6.6.0" 16 | }, 17 | "devDependencies": { 18 | "@babel/core": "^7.5.0", 19 | "@babel/runtime": "^7.5.0", 20 | "metro-react-native-babel-preset": "^0.51.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import TextField from './src/components/field'; 2 | import FilledTextField from './src/components/field-filled'; 3 | import OutlinedTextField from './src/components/field-outlined'; 4 | 5 | export { TextField, FilledTextField, OutlinedTextField }; 6 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | BSD License 2 | 3 | Copyright 2017 Alexander Nazarov 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | * Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software 17 | without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 23 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-material-textfield", 3 | "version": "0.16.1", 4 | "license": "BSD-3-Clause", 5 | "author": "Alexander Nazarov ", 6 | 7 | "description": "Material textfield", 8 | "keywords": [ 9 | "react", 10 | "react-component", 11 | "react-native", 12 | "ios", 13 | "android", 14 | "material", 15 | "input", 16 | "textinput", 17 | "field", 18 | "textfield", 19 | "floating", 20 | "label" 21 | ], 22 | 23 | "main": "index.js", 24 | "files": ["index.js", "src/*", "license.txt", "readme.md", "changelog.md"], 25 | 26 | "repository": { 27 | "type": "git", 28 | "url": "git://github.com/n4kz/react-native-material-textfield.git" 29 | }, 30 | 31 | "dependencies": { 32 | "prop-types": "^15.5.9" 33 | }, 34 | 35 | "peerDependencies": { 36 | "react": ">=16.3.0", 37 | "react-native": ">=0.55.0" 38 | }, 39 | 40 | "devDependencies": { 41 | "@babel/core": "^7.5.5", 42 | "@babel/runtime": "^7.5.5", 43 | "babel-eslint": "^10.0.0", 44 | "babel-jest": "^24.9.0", 45 | "eslint": "^6.5.0", 46 | "eslint-plugin-react": "^7.16.0", 47 | "eslint-plugin-react-native": "^3.7.0", 48 | "jest": "^24.9.0", 49 | "metro-react-native-babel-preset": "^0.56.0", 50 | "react": "16.10.2", 51 | "react-native": "0.61.2", 52 | "react-test-renderer": "16.10.2" 53 | }, 54 | 55 | "scripts": { 56 | "test": "eslint index.js src && jest --silent", 57 | "lint": "eslint index.js src example/app.js", 58 | "jest": "jest" 59 | }, 60 | 61 | "jest": { 62 | "preset": "react-native", 63 | "modulePathIgnorePatterns": ["/example"] 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | [npm-badge]: https://img.shields.io/npm/v/react-native-material-textfield.svg?colorB=ff6d00 2 | [npm-url]: https://npmjs.com/package/react-native-material-textfield 3 | [license-badge]: https://img.shields.io/npm/l/react-native-material-textfield.svg?colorB=448aff 4 | [license-url]: https://raw.githubusercontent.com/n4kz/react-native-material-textfield/master/license.txt 5 | [travis-badge]: https://api.travis-ci.org/n4kz/react-native-material-textfield.svg?branch=master 6 | [travis-url]: https://travis-ci.org/n4kz/react-native-material-textfield?branch=master 7 | [codeclimate-badge]: https://img.shields.io/codeclimate/maintainability/n4kz/react-native-material-textfield.svg 8 | [codeclimate-url]: https://codeclimate.com/github/n4kz/react-native-material-textfield 9 | [example-url]: https://cloud.githubusercontent.com/assets/2055622/24325711/eaa4ff08-11af-11e7-8550-2504c1580979.gif 10 | [rn-textinput]: https://facebook.github.io/react-native/docs/textinput.html#props 11 | [md-textfield]: https://material.io/guidelines/components/text-fields.html 12 | 13 | # react-native-material-textfield 14 | 15 | [![npm][npm-badge]][npm-url] 16 | [![license][license-badge]][license-url] 17 | [![travis][travis-badge]][travis-url] 18 | [![codeclimate][codeclimate-badge]][codeclimate-url] 19 | 20 | Material texfield with consistent behaviour on iOS and Android 21 | 22 | ![example][example-url] 23 | 24 | ## Features 25 | 26 | * Material design [guidelines][md-textfield] compliance 27 | * Consistent look and feel on iOS and Android 28 | * Animated state transitions (normal, focused and errored) 29 | * Customizable font size, colors and animation duration 30 | * Disabled state (with dotted underline) 31 | * Outlined and filled fields 32 | * Masked input support 33 | * Multiline text input 34 | * Character counter 35 | * Prefix and suffix 36 | * Accessory views 37 | * Helper text 38 | * RTL support 39 | * Pure javascript implementation 40 | 41 | ## Installation 42 | 43 | ```bash 44 | npm install --save react-native-material-textfield 45 | ``` 46 | 47 | ## Usage 48 | 49 | ```javascript 50 | import React, { Component } from 'react'; 51 | import { 52 | TextField, 53 | FilledTextField, 54 | OutlinedTextField, 55 | } from 'react-native-material-textfield'; 56 | 57 | class Example extends Component { 58 | fieldRef = React.createRef(); 59 | 60 | onSubmit = () => { 61 | let { current: field } = this.fieldRef; 62 | 63 | console.log(field.value()); 64 | }; 65 | 66 | formatText = (text) => { 67 | return text.replace(/[^+\d]/g, ''); 68 | }; 69 | 70 | render() { 71 | return ( 72 | 79 | ); 80 | } 81 | } 82 | ``` 83 | 84 | ## Properties 85 | 86 | name | description | type | default 87 | :--------------------- |:------------------------------------------- | --------:|:------------------ 88 | textColor | Text input color | String | rgba(0, 0, 0, .87) 89 | fontSize | Text input font size | Number | 16 90 | labelFontSize | Text field label font size | Number | 12 91 | lineWidth | Text field underline width | Number | 0.5 92 | activeLineWidth | Text field active underline width | Number | 2 93 | disabledLineWidth | Text field disabled underline width | Number | 1 94 | tintColor | Text field accent color | String | rgb(0, 145, 234) 95 | baseColor | Text field base color | String | rgba(0, 0, 0, .38) 96 | label | Text field label text | String | - 97 | title | Text field helper text | String | - 98 | prefix | Text field prefix text | String | - 99 | suffix | Text field suffix text | String | - 100 | error | Text field error text | String | - 101 | errorColor | Text field color for errored state | String | rgb(213, 0, 0) 102 | lineType | Text field line type | String | solid 103 | disabledLineType | Text field line type in disabled state | String | dotted 104 | animationDuration | Text field animation duration in ms | Number | 225 105 | characterRestriction | Text field soft limit for character counter | Number | - 106 | disabled | Text field availability | Boolean | false 107 | editable | Text field text can be edited | Boolean | true 108 | multiline | Text filed multiline input | Boolean | false 109 | contentInset | Layout configuration object | Object | [{...}](#content-inset) 110 | labelOffset | Label position adjustment | Object | [{...}](#label-offset) 111 | inputContainerStyle | Style for input container view | Object | - 112 | containerStyle | Style for container view | Object | - 113 | labelTextStyle | Style for label inner Text component | Object | - 114 | titleTextStyle | Style for title inner Text component | Object | - 115 | affixTextStyle | Style for affix inner Text component | Object | - 116 | formatText | Input mask callback | Function | - 117 | renderLeftAccessory | Render left input accessory view | Function | - 118 | renderRightAccessory | Render right input accessory view | Function | - 119 | onChangeText | Change text callback | Function | - 120 | onFocus | Focus callback | Function | - 121 | onBlur | Blur callback | Function | - 122 | 123 | Other [TextInput][rn-textinput] properties will also work. 124 | 125 | ### Content Inset 126 | 127 | name | description | Normal | Filled | Outlined 128 | :----- |:--------------------------------- | ------:| ------:| --------: 129 | top | Inset on the top side | 16 | 8 | 0 130 | left | Inset on the left side | 0 | 12 | 12 131 | right | Inset on the right side | 0 | 12 | 12 132 | label | Space between label and TextInput | 4 | 4 | 4 133 | input | Space between line and TextInput | 8 | 8 | 16 134 | 135 | ### Label Offset 136 | 137 | name | description | Normal | Filled | Outlined 138 | :---- |:------------------------------------ | ------:| ------:| --------: 139 | x0 | Horizontal offset for inactive state | 0 | 0 | 0 140 | y0 | Vertical offset for inactive state | 0 | -10 | 0 141 | x1 | Horizontal offset for active state | 0 | 0 | 0 142 | y1 | Vertical offset for active state | 0 | -2 | -10 143 | 144 | ## Methods 145 | 146 | name | description | returns 147 | :---------------------- |:----------------------------- | -------: 148 | focus() | Acquire focus | - 149 | blur() | Release focus | - 150 | clear() | Clear text field | - 151 | value() | Get current value | String 152 | isFocused() | Get current focus state | Boolean 153 | isErrored() | Get current error state | Boolean 154 | isRestricted() | Get current restriction state | Boolean 155 | isDefaultVisible() | Get default value visibility | Boolean 156 | isPlaceholderVisible() | Get placeholder visibility | Boolean 157 | setValue() | Set current value | - 158 | 159 | ## Example 160 | 161 | ```bash 162 | git clone https://github.com/n4kz/react-native-material-textfield 163 | cd react-native-material-textfield/example 164 | npm install 165 | npm run ios # or npm run android 166 | ``` 167 | 168 | ## Copyright and License 169 | 170 | BSD License 171 | 172 | Copyright 2017-2019 Alexander Nazarov. All rights reserved. 173 | -------------------------------------------------------------------------------- /src/components/affix/__snapshots__/test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`renders inactive prefix 1`] = ` 4 | 15 | 26 | a 27 | 28 | 29 | `; 30 | 31 | exports[`renders inactive suffix 1`] = ` 32 | 43 | 54 | z 55 | 56 | 57 | `; 58 | 59 | exports[`renders prefix 1`] = ` 60 | 71 | 82 | a 83 | 84 | 85 | `; 86 | 87 | exports[`renders suffix 1`] = ` 88 | 99 | 110 | z 111 | 112 | 113 | `; 114 | -------------------------------------------------------------------------------- /src/components/affix/index.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React, { PureComponent } from 'react'; 3 | import { Animated } from 'react-native'; 4 | 5 | import styles from './styles'; 6 | 7 | export default class Affix extends PureComponent { 8 | static defaultProps = { 9 | numberOfLines: 1, 10 | }; 11 | 12 | static propTypes = { 13 | numberOfLines: PropTypes.number, 14 | style: Animated.Text.propTypes.style, 15 | 16 | color: PropTypes.string.isRequired, 17 | fontSize: PropTypes.number.isRequired, 18 | 19 | type: PropTypes 20 | .oneOf(['prefix', 'suffix']) 21 | .isRequired, 22 | 23 | labelAnimation: PropTypes 24 | .instanceOf(Animated.Value) 25 | .isRequired, 26 | 27 | children: PropTypes.oneOfType([ 28 | PropTypes.arrayOf(PropTypes.node), 29 | PropTypes.node, 30 | ]), 31 | }; 32 | 33 | render() { 34 | let { labelAnimation, style, children, type, fontSize, color } = this.props; 35 | 36 | let containerStyle = { 37 | height: fontSize * 1.5, 38 | opacity: labelAnimation, 39 | }; 40 | 41 | let textStyle = { 42 | includeFontPadding: false, 43 | textAlignVertical: 'top', 44 | 45 | fontSize, 46 | color, 47 | }; 48 | 49 | switch (type) { 50 | case 'prefix': 51 | containerStyle.paddingRight = 8; 52 | textStyle.textAlign = 'left'; 53 | break; 54 | 55 | case 'suffix': 56 | containerStyle.paddingLeft = 8; 57 | textStyle.textAlign = 'right'; 58 | break; 59 | } 60 | 61 | return ( 62 | 63 | {children} 64 | 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/components/affix/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export default StyleSheet.create({ 4 | container: { 5 | top: 2, 6 | justifyContent: 'center', 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /src/components/affix/test.js: -------------------------------------------------------------------------------- 1 | import 'react-native'; 2 | import React from 'react'; 3 | import { Animated } from 'react-native'; 4 | import renderer from 'react-test-renderer'; 5 | 6 | import Affix from '.'; 7 | 8 | /* eslint-env jest */ 9 | 10 | const props = { 11 | color: 'black', 12 | fontSize: 16, 13 | 14 | labelAnimation: new Animated.Value(1), 15 | }; 16 | 17 | const prefix = 'a'; 18 | const suffix = 'z'; 19 | 20 | it('renders prefix', () => { 21 | let affix = renderer 22 | .create({prefix}) 23 | .toJSON(); 24 | 25 | expect(affix) 26 | .toMatchSnapshot(); 27 | }); 28 | 29 | it('renders inactive prefix', () => { 30 | let affix = renderer 31 | .create( 32 | 33 | {prefix} 34 | 35 | ) 36 | .toJSON(); 37 | 38 | expect(affix) 39 | .toMatchSnapshot(); 40 | }); 41 | 42 | it('renders suffix', () => { 43 | let affix = renderer 44 | .create({suffix}) 45 | .toJSON(); 46 | 47 | expect(affix) 48 | .toMatchSnapshot(); 49 | }); 50 | 51 | it('renders inactive suffix', () => { 52 | let affix = renderer 53 | .create( 54 | 55 | {suffix} 56 | 57 | ) 58 | .toJSON(); 59 | 60 | expect(affix) 61 | .toMatchSnapshot(); 62 | }); 63 | -------------------------------------------------------------------------------- /src/components/counter/__snapshots__/test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`renders when limit is exceeded 1`] = ` 4 | 22 | 2 23 | / 24 | 1 25 | 26 | `; 27 | 28 | exports[`renders when limit is set 1`] = ` 29 | 47 | 1 48 | / 49 | 1 50 | 51 | `; 52 | -------------------------------------------------------------------------------- /src/components/counter/index.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React, { PureComponent } from 'react'; 3 | import { Text } from 'react-native'; 4 | 5 | import styles from './styles'; 6 | 7 | export default class Counter extends PureComponent { 8 | static propTypes = { 9 | count: PropTypes.number.isRequired, 10 | limit: PropTypes.number, 11 | 12 | baseColor: PropTypes.string.isRequired, 13 | errorColor: PropTypes.string.isRequired, 14 | 15 | style: Text.propTypes.style, 16 | }; 17 | 18 | render() { 19 | let { count, limit, baseColor, errorColor, style } = this.props; 20 | 21 | if (!limit) { 22 | return null; 23 | } 24 | 25 | let textStyle = { 26 | color: count > limit? 27 | errorColor: 28 | baseColor, 29 | }; 30 | 31 | return ( 32 | 33 | {count} / {limit} 34 | 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/components/counter/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export default StyleSheet.create({ 4 | text: { 5 | fontSize: 12, 6 | lineHeight: 16, 7 | textAlign: 'right', 8 | backgroundColor: 'transparent', 9 | paddingVertical: 2, 10 | marginLeft: 8, 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /src/components/counter/test.js: -------------------------------------------------------------------------------- 1 | import 'react-native'; 2 | import React from 'react'; 3 | import renderer from 'react-test-renderer'; 4 | 5 | import Counter from '.'; 6 | 7 | /* eslint-env jest */ 8 | 9 | const props = { 10 | baseColor: 'blue', 11 | errorColor: 'red', 12 | fontSize: 12, 13 | }; 14 | 15 | it('renders null when limit is not set', () => { 16 | let counter = renderer 17 | .create() 18 | .toJSON(); 19 | 20 | expect(counter) 21 | .toBeNull(); 22 | }); 23 | 24 | it('renders when limit is set', () => { 25 | let counter = renderer 26 | .create() 27 | .toJSON(); 28 | 29 | expect(counter) 30 | .toMatchSnapshot(); 31 | }); 32 | 33 | it('renders when limit is exceeded', () => { 34 | let counter = renderer 35 | .create() 36 | .toJSON(); 37 | 38 | expect(counter) 39 | .toMatchSnapshot(); 40 | }); 41 | -------------------------------------------------------------------------------- /src/components/field-filled/__snapshots__/test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`renders 1`] = ` 4 | 9 | 25 | 38 | 52 | 53 | 61 | 83 | 96 | test 97 | 98 | 99 | 108 | 143 | 144 | 145 | 146 | 162 | 163 | `; 164 | 165 | exports[`renders accessory 1`] = ` 166 | 171 | 187 | 200 | 214 | 215 | 216 | 224 | 246 | 259 | test 260 | 261 | 262 | 271 | 306 | 307 | 308 | 309 | 325 | 326 | `; 327 | 328 | exports[`renders counter 1`] = ` 329 | 334 | 350 | 363 | 377 | 378 | 386 | 408 | 421 | test 422 | 423 | 424 | 433 | 468 | 469 | 470 | 471 | 487 | 505 | 4 506 | / 507 | 10 508 | 509 | 510 | 511 | `; 512 | 513 | exports[`renders disabled value 1`] = ` 514 | 519 | 535 | 548 | 562 | 563 | 571 | 593 | 606 | test 607 | 608 | 609 | 618 | 653 | 654 | 655 | 656 | 672 | 673 | `; 674 | 675 | exports[`renders title 1`] = ` 676 | 681 | 697 | 710 | 724 | 725 | 733 | 755 | 768 | test 769 | 770 | 771 | 780 | 815 | 816 | 817 | 818 | 834 | 848 | field 849 | 850 | 851 | 852 | `; 853 | 854 | exports[`renders value 1`] = ` 855 | 860 | 876 | 889 | 903 | 904 | 912 | 934 | 947 | test 948 | 949 | 950 | 959 | 994 | 995 | 996 | 997 | 1013 | 1014 | `; 1015 | -------------------------------------------------------------------------------- /src/components/field-filled/index.js: -------------------------------------------------------------------------------- 1 | import TextField from '../field'; 2 | import styles from './styles'; 3 | 4 | export default class FilledTextField extends TextField { 5 | static contentInset = { 6 | ...TextField.contentInset, 7 | 8 | top: 8, 9 | left: 12, 10 | right: 12, 11 | }; 12 | 13 | static labelOffset = { 14 | ...TextField.labelOffset, 15 | 16 | y0: -10, 17 | y1: -2, 18 | }; 19 | 20 | static inputContainerStyle = [ 21 | TextField.inputContainerStyle, 22 | styles.inputContainer, 23 | ]; 24 | } 25 | -------------------------------------------------------------------------------- /src/components/field-filled/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export default StyleSheet.create({ 4 | inputContainer: { 5 | borderTopLeftRadius: 4, 6 | borderTopRightRadius: 4, 7 | 8 | backgroundColor: 'rgb(204, 204, 204)', 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/components/field-filled/test.js: -------------------------------------------------------------------------------- 1 | import { Image } from 'react-native'; 2 | import React from 'react'; 3 | import renderer from 'react-test-renderer'; 4 | 5 | import FilledTextField from '.'; 6 | 7 | const props = { 8 | label: 'test', 9 | }; 10 | 11 | /* eslint-env jest */ 12 | 13 | it('renders', () => { 14 | let field = renderer 15 | .create() 16 | .toJSON(); 17 | 18 | expect(field) 19 | .toMatchSnapshot(); 20 | }); 21 | 22 | it('renders value', () => { 23 | let field = renderer 24 | .create() 25 | .toJSON(); 26 | 27 | expect(field) 28 | .toMatchSnapshot(); 29 | }); 30 | 31 | it('renders disabled value', () => { 32 | let field = renderer 33 | .create() 34 | .toJSON(); 35 | 36 | expect(field) 37 | .toMatchSnapshot(); 38 | }); 39 | 40 | it('renders title', () => { 41 | let field = renderer 42 | .create() 43 | .toJSON(); 44 | 45 | expect(field) 46 | .toMatchSnapshot(); 47 | }); 48 | 49 | it('renders counter', () => { 50 | let field = renderer 51 | .create() 52 | .toJSON(); 53 | 54 | expect(field) 55 | .toMatchSnapshot(); 56 | }); 57 | 58 | it('renders accessory', () => { 59 | let render = () => ( 60 | 61 | ); 62 | 63 | let field = renderer 64 | .create() 65 | .toJSON(); 66 | 67 | expect(field) 68 | .toMatchSnapshot(); 69 | }); 70 | -------------------------------------------------------------------------------- /src/components/field-outlined/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Animated, StyleSheet } from 'react-native'; 3 | 4 | import TextField from '../field'; 5 | import Outline from '../outline'; 6 | 7 | export default class OutlinedTextField extends TextField { 8 | static contentInset = { 9 | ...TextField.contentInset, 10 | 11 | input: 16, 12 | 13 | top: 0, 14 | left: 12, 15 | right: 12, 16 | }; 17 | 18 | static labelOffset = { 19 | ...TextField.labelOffset, 20 | 21 | y0: 0, 22 | y1: -10, 23 | }; 24 | 25 | static defaultProps = { 26 | ...TextField.defaultProps, 27 | 28 | lineWidth: 1, 29 | disabledLineWidth: StyleSheet.hairlineWidth, 30 | }; 31 | 32 | constructor(props) { 33 | super(props); 34 | 35 | this.onTextLayout = this.onTextLayout.bind(this); 36 | this.state.labelWidth = new Animated.Value(0); 37 | } 38 | 39 | onTextLayout({ nativeEvent: { lines } }) { 40 | let { fontSize, labelFontSize } = this.props; 41 | let { labelWidth } = this.state; 42 | 43 | let scale = labelFontSize / fontSize; 44 | 45 | labelWidth.setValue(lines[0].width * scale); 46 | } 47 | 48 | renderLabel(props) { 49 | let { onTextLayout } = this; 50 | 51 | return super.renderLabel({ ...props, onTextLayout }); 52 | } 53 | 54 | renderLine(props) { 55 | let { labelWidth } = this.state; 56 | 57 | return ( 58 | 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/components/field-outlined/test.js: -------------------------------------------------------------------------------- 1 | import { Image } from 'react-native'; 2 | import React from 'react'; 3 | import renderer from 'react-test-renderer'; 4 | 5 | import OutlinedTextField from '.'; 6 | 7 | const props = { 8 | label: 'test', 9 | }; 10 | 11 | /* eslint-env jest */ 12 | 13 | it('renders', () => { 14 | let field = renderer 15 | .create() 16 | .toJSON(); 17 | 18 | expect(field) 19 | .toMatchSnapshot(); 20 | }); 21 | 22 | it('renders value', () => { 23 | let field = renderer 24 | .create() 25 | .toJSON(); 26 | 27 | expect(field) 28 | .toMatchSnapshot(); 29 | }); 30 | 31 | it('renders disabled value', () => { 32 | let field = renderer 33 | .create() 34 | .toJSON(); 35 | 36 | expect(field) 37 | .toMatchSnapshot(); 38 | }); 39 | 40 | it('renders title', () => { 41 | let field = renderer 42 | .create() 43 | .toJSON(); 44 | 45 | expect(field) 46 | .toMatchSnapshot(); 47 | }); 48 | 49 | it('renders counter', () => { 50 | let field = renderer 51 | .create() 52 | .toJSON(); 53 | 54 | expect(field) 55 | .toMatchSnapshot(); 56 | }); 57 | 58 | it('renders accessory', () => { 59 | let render = () => ( 60 | 61 | ); 62 | 63 | let field = renderer 64 | .create() 65 | .toJSON(); 66 | 67 | expect(field) 68 | .toMatchSnapshot(); 69 | }); 70 | -------------------------------------------------------------------------------- /src/components/field/index.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React, { PureComponent } from 'react'; 3 | import { 4 | View, 5 | Text, 6 | TextInput, 7 | Animated, 8 | StyleSheet, 9 | Platform, 10 | ViewPropTypes, 11 | } from 'react-native'; 12 | 13 | import Line from '../line'; 14 | import Label from '../label'; 15 | import Affix from '../affix'; 16 | import Helper from '../helper'; 17 | import Counter from '../counter'; 18 | 19 | import styles from './styles'; 20 | 21 | function startAnimation(animation, options, callback) { 22 | Animated 23 | .timing(animation, options) 24 | .start(callback); 25 | } 26 | 27 | function labelStateFromProps(props, state) { 28 | let { placeholder, defaultValue } = props; 29 | let { text, receivedFocus } = state; 30 | 31 | return !!(placeholder || text || (!receivedFocus && defaultValue)); 32 | } 33 | 34 | function errorStateFromProps(props, state) { 35 | let { error } = props; 36 | 37 | return !!error; 38 | } 39 | 40 | export default class TextField extends PureComponent { 41 | static defaultProps = { 42 | underlineColorAndroid: 'transparent', 43 | disableFullscreenUI: true, 44 | autoCapitalize: 'sentences', 45 | editable: true, 46 | 47 | animationDuration: 225, 48 | 49 | fontSize: 16, 50 | labelFontSize: 12, 51 | 52 | tintColor: 'rgb(0, 145, 234)', 53 | textColor: 'rgba(0, 0, 0, .87)', 54 | baseColor: 'rgba(0, 0, 0, .38)', 55 | 56 | errorColor: 'rgb(213, 0, 0)', 57 | 58 | lineWidth: StyleSheet.hairlineWidth, 59 | activeLineWidth: 2, 60 | disabledLineWidth: 1, 61 | 62 | lineType: 'solid', 63 | disabledLineType: 'dotted', 64 | 65 | disabled: false, 66 | }; 67 | 68 | static propTypes = { 69 | ...TextInput.propTypes, 70 | 71 | animationDuration: PropTypes.number, 72 | 73 | fontSize: PropTypes.number, 74 | labelFontSize: PropTypes.number, 75 | 76 | contentInset: PropTypes.shape({ 77 | top: PropTypes.number, 78 | label: PropTypes.number, 79 | input: PropTypes.number, 80 | left: PropTypes.number, 81 | right: PropTypes.number, 82 | bottom: PropTypes.number, 83 | }), 84 | 85 | labelOffset: Label.propTypes.offset, 86 | 87 | labelTextStyle: Text.propTypes.style, 88 | titleTextStyle: Text.propTypes.style, 89 | affixTextStyle: Text.propTypes.style, 90 | 91 | tintColor: PropTypes.string, 92 | textColor: PropTypes.string, 93 | baseColor: PropTypes.string, 94 | 95 | label: PropTypes.string, 96 | title: PropTypes.string, 97 | 98 | characterRestriction: PropTypes.number, 99 | 100 | error: PropTypes.string, 101 | errorColor: PropTypes.string, 102 | 103 | lineWidth: PropTypes.number, 104 | activeLineWidth: PropTypes.number, 105 | disabledLineWidth: PropTypes.number, 106 | 107 | lineType: Line.propTypes.lineType, 108 | disabledLineType: Line.propTypes.lineType, 109 | 110 | disabled: PropTypes.bool, 111 | 112 | formatText: PropTypes.func, 113 | 114 | renderLeftAccessory: PropTypes.func, 115 | renderRightAccessory: PropTypes.func, 116 | 117 | prefix: PropTypes.string, 118 | suffix: PropTypes.string, 119 | 120 | containerStyle: (ViewPropTypes || View.propTypes).style, 121 | inputContainerStyle: (ViewPropTypes || View.propTypes).style, 122 | }; 123 | 124 | static inputContainerStyle = styles.inputContainer; 125 | 126 | static contentInset = { 127 | top: 16, 128 | label: 4, 129 | input: 8, 130 | left: 0, 131 | right: 0, 132 | bottom: 8, 133 | }; 134 | 135 | static labelOffset = { 136 | x0: 0, 137 | y0: 0, 138 | x1: 0, 139 | y1: 0, 140 | }; 141 | 142 | static getDerivedStateFromProps({ error }, state) { 143 | /* Keep last received error in state */ 144 | if (error && error !== state.error) { 145 | return { error }; 146 | } 147 | 148 | return null; 149 | } 150 | 151 | constructor(props) { 152 | super(props); 153 | 154 | this.onBlur = this.onBlur.bind(this); 155 | this.onFocus = this.onFocus.bind(this); 156 | this.onPress = this.focus.bind(this); 157 | this.onChange = this.onChange.bind(this); 158 | this.onChangeText = this.onChangeText.bind(this); 159 | this.onContentSizeChange = this.onContentSizeChange.bind(this); 160 | this.onFocusAnimationEnd = this.onFocusAnimationEnd.bind(this); 161 | 162 | this.createGetter('contentInset'); 163 | this.createGetter('labelOffset'); 164 | 165 | this.inputRef = React.createRef(); 166 | this.mounted = false; 167 | this.focused = false; 168 | 169 | let { value: text, error, fontSize } = this.props; 170 | 171 | let labelState = labelStateFromProps(this.props, { text })? 1 : 0; 172 | let focusState = errorStateFromProps(this.props)? -1 : 0; 173 | 174 | this.state = { 175 | text, 176 | error, 177 | 178 | focusAnimation: new Animated.Value(focusState), 179 | labelAnimation: new Animated.Value(labelState), 180 | 181 | receivedFocus: false, 182 | 183 | height: fontSize * 1.5, 184 | }; 185 | } 186 | 187 | createGetter(name) { 188 | this[name] = () => { 189 | let { [name]: value } = this.props; 190 | let { [name]: defaultValue } = this.constructor; 191 | 192 | return { ...defaultValue, ...value }; 193 | }; 194 | } 195 | 196 | componentDidMount() { 197 | this.mounted = true; 198 | } 199 | 200 | componentWillUnmount() { 201 | this.mounted = false; 202 | } 203 | 204 | componentDidUpdate(prevProps, prevState) { 205 | let errorState = errorStateFromProps(this.props); 206 | let prevErrorState = errorStateFromProps(prevProps); 207 | 208 | if (errorState ^ prevErrorState) { 209 | this.startFocusAnimation(); 210 | } 211 | 212 | let labelState = labelStateFromProps(this.props, this.state); 213 | let prevLabelState = labelStateFromProps(prevProps, prevState); 214 | 215 | if (labelState ^ prevLabelState) { 216 | this.startLabelAnimation(); 217 | } 218 | } 219 | 220 | startFocusAnimation() { 221 | let { focusAnimation } = this.state; 222 | let { animationDuration: duration } = this.props; 223 | 224 | let options = { 225 | toValue: this.focusState(), 226 | duration, 227 | }; 228 | 229 | startAnimation(focusAnimation, options, this.onFocusAnimationEnd); 230 | } 231 | 232 | startLabelAnimation() { 233 | let { labelAnimation } = this.state; 234 | let { animationDuration: duration } = this.props; 235 | 236 | let options = { 237 | toValue: this.labelState(), 238 | useNativeDriver: true, 239 | duration, 240 | }; 241 | 242 | startAnimation(labelAnimation, options); 243 | } 244 | 245 | setNativeProps(props) { 246 | let { current: input } = this.inputRef; 247 | 248 | input.setNativeProps(props); 249 | } 250 | 251 | focusState() { 252 | if (errorStateFromProps(this.props)) { 253 | return -1; 254 | } 255 | 256 | return this.focused? 1 : 0; 257 | } 258 | 259 | labelState() { 260 | if (labelStateFromProps(this.props, this.state)) { 261 | return 1; 262 | } 263 | 264 | return this.focused? 1 : 0; 265 | } 266 | 267 | focus() { 268 | let { disabled, editable } = this.props; 269 | let { current: input } = this.inputRef; 270 | 271 | if (!disabled && editable) { 272 | input.focus(); 273 | } 274 | } 275 | 276 | blur() { 277 | let { current: input } = this.inputRef; 278 | 279 | input.blur(); 280 | } 281 | 282 | clear() { 283 | let { current: input } = this.inputRef; 284 | 285 | input.clear(); 286 | 287 | /* onChangeText is not triggered by .clear() */ 288 | this.onChangeText(''); 289 | } 290 | 291 | value() { 292 | let { text } = this.state; 293 | let { defaultValue } = this.props; 294 | 295 | let value = this.isDefaultVisible()? 296 | defaultValue: 297 | text; 298 | 299 | if (null == value) { 300 | return ''; 301 | } 302 | 303 | return 'string' === typeof value? 304 | value: 305 | String(value); 306 | } 307 | 308 | setValue(text) { 309 | this.setState({ text }); 310 | } 311 | 312 | isFocused() { 313 | let { current: input } = this.inputRef; 314 | 315 | return input.isFocused(); 316 | } 317 | 318 | isRestricted() { 319 | let { characterRestriction: limit } = this.props; 320 | let { length: count } = this.value(); 321 | 322 | return limit < count; 323 | } 324 | 325 | isErrored() { 326 | return errorStateFromProps(this.props); 327 | } 328 | 329 | isDefaultVisible() { 330 | let { text, receivedFocus } = this.state; 331 | let { defaultValue } = this.props; 332 | 333 | return !receivedFocus && null == text && null != defaultValue; 334 | } 335 | 336 | isPlaceholderVisible() { 337 | let { placeholder } = this.props; 338 | 339 | return placeholder && !this.focused && !this.value(); 340 | } 341 | 342 | isLabelActive() { 343 | return 1 === this.labelState(); 344 | } 345 | 346 | onFocus(event) { 347 | let { onFocus, clearTextOnFocus } = this.props; 348 | let { receivedFocus } = this.state; 349 | 350 | if ('function' === typeof onFocus) { 351 | onFocus(event); 352 | } 353 | 354 | if (clearTextOnFocus) { 355 | this.clear(); 356 | } 357 | 358 | this.focused = true; 359 | 360 | this.startFocusAnimation(); 361 | this.startLabelAnimation(); 362 | 363 | if (!receivedFocus) { 364 | this.setState({ receivedFocus: true, text: this.value() }); 365 | } 366 | } 367 | 368 | onBlur(event) { 369 | let { onBlur } = this.props; 370 | 371 | if ('function' === typeof onBlur) { 372 | onBlur(event); 373 | } 374 | 375 | this.focused = false; 376 | 377 | this.startFocusAnimation(); 378 | this.startLabelAnimation(); 379 | } 380 | 381 | onChange(event) { 382 | let { onChange } = this.props; 383 | 384 | if ('function' === typeof onChange) { 385 | onChange(event); 386 | } 387 | } 388 | 389 | onChangeText(text) { 390 | let { onChangeText, formatText } = this.props; 391 | 392 | if ('function' === typeof formatText) { 393 | text = formatText(text); 394 | } 395 | 396 | this.setState({ text }); 397 | 398 | if ('function' === typeof onChangeText) { 399 | onChangeText(text); 400 | } 401 | } 402 | 403 | onContentSizeChange(event) { 404 | let { onContentSizeChange, fontSize } = this.props; 405 | let { height } = event.nativeEvent.contentSize; 406 | 407 | if ('function' === typeof onContentSizeChange) { 408 | onContentSizeChange(event); 409 | } 410 | 411 | this.setState({ 412 | height: Math.max( 413 | fontSize * 1.5, 414 | Math.ceil(height) + Platform.select({ ios: 4, android: 1 }) 415 | ), 416 | }); 417 | } 418 | 419 | onFocusAnimationEnd() { 420 | let { error } = this.props; 421 | let { error: retainedError } = this.state; 422 | 423 | if (this.mounted && !error && retainedError) { 424 | this.setState({ error: null }); 425 | } 426 | } 427 | 428 | inputHeight() { 429 | let { height: computedHeight } = this.state; 430 | let { multiline, fontSize, height = computedHeight } = this.props; 431 | 432 | return multiline? 433 | height: 434 | fontSize * 1.5; 435 | } 436 | 437 | inputContainerHeight() { 438 | let { labelFontSize, multiline } = this.props; 439 | let contentInset = this.contentInset(); 440 | 441 | if ('web' === Platform.OS && multiline) { 442 | return 'auto'; 443 | } 444 | 445 | return contentInset.top 446 | + labelFontSize 447 | + contentInset.label 448 | + this.inputHeight() 449 | + contentInset.input; 450 | } 451 | 452 | inputProps() { 453 | let store = {}; 454 | 455 | for (let key in TextInput.propTypes) { 456 | if ('defaultValue' === key) { 457 | continue; 458 | } 459 | 460 | if (key in this.props) { 461 | store[key] = this.props[key]; 462 | } 463 | } 464 | 465 | return store; 466 | } 467 | 468 | inputStyle() { 469 | let { fontSize, baseColor, textColor, disabled, multiline } = this.props; 470 | 471 | let color = disabled || this.isDefaultVisible()? 472 | baseColor: 473 | textColor; 474 | 475 | let style = { 476 | fontSize, 477 | color, 478 | 479 | height: this.inputHeight(), 480 | }; 481 | 482 | if (multiline) { 483 | let lineHeight = fontSize * 1.5; 484 | let offset = 'ios' === Platform.OS? 2 : 0; 485 | 486 | style.height += lineHeight; 487 | style.transform = [{ 488 | translateY: lineHeight + offset, 489 | }]; 490 | } 491 | 492 | return style; 493 | } 494 | 495 | renderLabel(props) { 496 | let offset = this.labelOffset(); 497 | 498 | let { 499 | label, 500 | fontSize, 501 | labelFontSize, 502 | labelTextStyle, 503 | } = this.props; 504 | 505 | return ( 506 |