├── .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 |
514 | );
515 | }
516 |
517 | renderLine(props) {
518 | return (
519 |
520 | );
521 | }
522 |
523 | renderAccessory(prop) {
524 | let { [prop]: renderAccessory } = this.props;
525 |
526 | return 'function' === typeof renderAccessory?
527 | renderAccessory():
528 | null;
529 | }
530 |
531 | renderAffix(type) {
532 | let { labelAnimation } = this.state;
533 | let {
534 | [type]: affix,
535 | fontSize,
536 | baseColor: color,
537 | affixTextStyle: style,
538 | } = this.props;
539 |
540 | if (null == affix) {
541 | return null;
542 | }
543 |
544 | let props = {
545 | type,
546 | style,
547 | color,
548 | fontSize,
549 | labelAnimation,
550 | };
551 |
552 | return (
553 | {affix}
554 | );
555 | }
556 |
557 | renderHelper() {
558 | let { focusAnimation, error } = this.state;
559 |
560 | let {
561 | title,
562 | disabled,
563 | baseColor,
564 | errorColor,
565 | titleTextStyle: style,
566 | characterRestriction: limit,
567 | } = this.props;
568 |
569 | let { length: count } = this.value();
570 | let contentInset = this.contentInset();
571 |
572 | let containerStyle = {
573 | paddingLeft: contentInset.left,
574 | paddingRight: contentInset.right,
575 | minHeight: contentInset.bottom,
576 | };
577 |
578 | let styleProps = {
579 | style,
580 | baseColor,
581 | errorColor,
582 | };
583 |
584 | let counterProps = {
585 | ...styleProps,
586 | limit,
587 | count,
588 | };
589 |
590 | let helperProps = {
591 | ...styleProps,
592 | title,
593 | error,
594 | disabled,
595 | focusAnimation,
596 | };
597 |
598 | return (
599 |
600 |
601 |
602 |
603 | );
604 | }
605 |
606 | renderInput() {
607 | let {
608 | disabled,
609 | editable,
610 | tintColor,
611 | style: inputStyleOverrides,
612 | } = this.props;
613 |
614 | let props = this.inputProps();
615 | let inputStyle = this.inputStyle();
616 |
617 | return (
618 |
633 | );
634 | }
635 |
636 | render() {
637 | let { labelAnimation, focusAnimation } = this.state;
638 | let {
639 | editable,
640 | disabled,
641 | lineType,
642 | disabledLineType,
643 | lineWidth,
644 | activeLineWidth,
645 | disabledLineWidth,
646 | tintColor,
647 | baseColor,
648 | errorColor,
649 | containerStyle,
650 | inputContainerStyle: inputContainerStyleOverrides,
651 | } = this.props;
652 |
653 | let restricted = this.isRestricted();
654 | let contentInset = this.contentInset();
655 |
656 | let inputContainerStyle = {
657 | paddingTop: contentInset.top,
658 | paddingRight: contentInset.right,
659 | paddingBottom: contentInset.input,
660 | paddingLeft: contentInset.left,
661 | height: this.inputContainerHeight(),
662 | };
663 |
664 | let containerProps = {
665 | style: containerStyle,
666 | onStartShouldSetResponder: () => true,
667 | onResponderRelease: this.onPress,
668 | pointerEvents: !disabled && editable?
669 | 'auto':
670 | 'none',
671 | };
672 |
673 | let inputContainerProps = {
674 | style: [
675 | this.constructor.inputContainerStyle,
676 | inputContainerStyle,
677 | inputContainerStyleOverrides,
678 | ],
679 | };
680 |
681 | let styleProps = {
682 | disabled,
683 | restricted,
684 | baseColor,
685 | tintColor,
686 | errorColor,
687 |
688 | contentInset,
689 |
690 | focusAnimation,
691 | labelAnimation,
692 | };
693 |
694 | let lineProps = {
695 | ...styleProps,
696 |
697 | lineWidth,
698 | activeLineWidth,
699 | disabledLineWidth,
700 |
701 | lineType,
702 | disabledLineType,
703 | };
704 |
705 | return (
706 |
707 |
708 | {this.renderLine(lineProps)}
709 | {this.renderAccessory('renderLeftAccessory')}
710 |
711 |
712 | {this.renderLabel(styleProps)}
713 |
714 |
715 | {this.renderAffix('prefix')}
716 | {this.renderInput()}
717 | {this.renderAffix('suffix')}
718 |
719 |
720 |
721 | {this.renderAccessory('renderRightAccessory')}
722 |
723 |
724 | {this.renderHelper()}
725 |
726 | );
727 | }
728 | }
729 |
--------------------------------------------------------------------------------
/src/components/field/styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet, I18nManager } from 'react-native';
2 |
3 | export default StyleSheet.create({
4 | inputContainer: {
5 | flexDirection: 'row',
6 | alignItems: 'flex-end',
7 | },
8 |
9 | input: {
10 | top: 2,
11 | padding: 0,
12 | paddingTop: 0, /* XXX: iOS has paddingTop set for multiline input */
13 | margin: 0,
14 | flex: 1,
15 |
16 | textAlign: I18nManager.isRTL?
17 | 'right':
18 | 'left',
19 |
20 | includeFontPadding: false,
21 | textAlignVertical: 'top',
22 | },
23 |
24 | helperContainer: {
25 | flexDirection: 'row',
26 | justifyContent: 'flex-end',
27 | alignItems: 'flex-start',
28 | },
29 |
30 | row: {
31 | flex: 1,
32 | flexDirection: 'row',
33 | alignItems: 'flex-end',
34 | },
35 |
36 | stack: {
37 | flex: 1,
38 | alignSelf: 'stretch',
39 | },
40 |
41 | flex: {
42 | flex: 1,
43 | },
44 | });
45 |
--------------------------------------------------------------------------------
/src/components/field/test.js:
--------------------------------------------------------------------------------
1 | import { Image } from 'react-native';
2 | import React from 'react';
3 | import renderer from 'react-test-renderer';
4 |
5 | import TextField 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 default value', () => {
41 | let field = renderer
42 | .create()
43 | .toJSON();
44 |
45 | expect(field)
46 | .toMatchSnapshot();
47 | });
48 |
49 | it('renders multiline value', () => {
50 | let field = renderer
51 | .create()
52 | .toJSON();
53 |
54 | expect(field)
55 | .toMatchSnapshot();
56 | });
57 |
58 | it('renders title', () => {
59 | let field = renderer
60 | .create()
61 | .toJSON();
62 |
63 | expect(field)
64 | .toMatchSnapshot();
65 | });
66 |
67 | it('renders error', () => {
68 | let field = renderer
69 | .create()
70 | .toJSON();
71 |
72 | expect(field)
73 | .toMatchSnapshot();
74 | });
75 |
76 | it('renders counter', () => {
77 | let field = renderer
78 | .create()
79 | .toJSON();
80 |
81 | expect(field)
82 | .toMatchSnapshot();
83 | });
84 |
85 | it('renders restriction', () => {
86 | let field = renderer
87 | .create()
88 | .toJSON();
89 |
90 | expect(field)
91 | .toMatchSnapshot();
92 | });
93 |
94 | it('renders prefix', () => {
95 | let field = renderer
96 | .create()
97 | .toJSON();
98 |
99 | expect(field)
100 | .toMatchSnapshot();
101 | });
102 |
103 | it('renders suffix', () => {
104 | let field = renderer
105 | .create()
106 | .toJSON();
107 |
108 | expect(field)
109 | .toMatchSnapshot();
110 | });
111 |
112 | it('renders left accessory', () => {
113 | let render = () => (
114 |
115 | );
116 |
117 | let field = renderer
118 | .create()
119 | .toJSON();
120 |
121 | expect(field)
122 | .toMatchSnapshot();
123 | });
124 |
125 | it('renders right accessory', () => {
126 | let render = () => (
127 |
128 | );
129 |
130 | let field = renderer
131 | .create()
132 | .toJSON();
133 |
134 | expect(field)
135 | .toMatchSnapshot();
136 | });
137 |
--------------------------------------------------------------------------------
/src/components/helper/__snapshots__/test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`renders disabled helper 1`] = `
4 |
18 | helper
19 |
20 | `;
21 |
22 | exports[`renders helper 1`] = `
23 |
37 | helper
38 |
39 | `;
40 |
41 | exports[`renders helper with error 1`] = `
42 |
56 | helper
57 |
58 | `;
59 |
--------------------------------------------------------------------------------
/src/components/helper/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 Helper extends PureComponent {
8 | static propTypes = {
9 | title: PropTypes.string,
10 | error: PropTypes.string,
11 |
12 | disabled: PropTypes.bool,
13 |
14 | style: Animated.Text.propTypes.style,
15 |
16 | baseColor: PropTypes.string,
17 | errorColor: PropTypes.string,
18 |
19 | focusAnimation: PropTypes.instanceOf(Animated.Value),
20 | };
21 |
22 | constructor(props) {
23 | super(props);
24 |
25 | let { error, focusAnimation } = this.props;
26 |
27 | let opacity = focusAnimation.interpolate({
28 | inputRange: [-1, -0.5, 0],
29 | outputRange: [1, 0, 1],
30 | extrapolate: 'clamp',
31 | });
32 |
33 | this.state = {
34 | errored: !!error,
35 | opacity,
36 | };
37 | }
38 |
39 | componentDidMount() {
40 | let { focusAnimation } = this.props;
41 |
42 | this.listener = focusAnimation
43 | .addListener(this.onAnimation.bind(this));
44 | }
45 |
46 | componentWillUnmount() {
47 | let { focusAnimation } = this.props;
48 |
49 | focusAnimation.removeListener(this.listener);
50 | }
51 |
52 | onAnimation({ value }) {
53 | if (this.animationValue > -0.5 && value <= -0.5) {
54 | this.setState({ errored: true });
55 | }
56 |
57 | if (this.animationValue < -0.5 && value >= -0.5) {
58 | this.setState({ errored: false });
59 | }
60 |
61 | this.animationValue = value;
62 | }
63 |
64 | render() {
65 | let { errored, opacity } = this.state;
66 | let {
67 | style,
68 | title,
69 | error,
70 | disabled,
71 | baseColor,
72 | errorColor,
73 | } = this.props;
74 |
75 | let text = errored?
76 | error:
77 | title;
78 |
79 | if (null == text) {
80 | return null;
81 | }
82 |
83 | let textStyle = {
84 | opacity,
85 |
86 | color: !disabled && errored?
87 | errorColor:
88 | baseColor,
89 | };
90 |
91 | return (
92 |
93 | {text}
94 |
95 | );
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/components/helper/styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 |
3 | export default StyleSheet.create({
4 | text: {
5 | flex: 1,
6 | fontSize: 12,
7 | lineHeight: 16,
8 | backgroundColor: 'transparent',
9 | paddingVertical: 2,
10 | textAlign: 'left',
11 | },
12 | });
13 |
--------------------------------------------------------------------------------
/src/components/helper/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 Helper from '.';
7 |
8 | /* eslint-env jest */
9 |
10 | const text = 'helper';
11 | const props = {
12 | title: text,
13 | fontSize: 16,
14 |
15 | baseColor: 'black',
16 | errorColor: 'red',
17 |
18 | focusAnimation: new Animated.Value(0),
19 | };
20 |
21 | it('renders helper', () => {
22 | let helper = renderer
23 | .create()
24 | .toJSON();
25 |
26 | expect(helper)
27 | .toMatchSnapshot();
28 | });
29 |
30 | it('renders disabled helper', () => {
31 | let helper = renderer
32 | .create(
33 |
34 | )
35 | .toJSON();
36 |
37 | expect(helper)
38 | .toMatchSnapshot();
39 | });
40 |
41 | it('renders helper with error', () => {
42 | let helper = renderer
43 | .create(
44 |
45 | )
46 | .toJSON();
47 |
48 | expect(helper)
49 | .toMatchSnapshot();
50 | });
51 |
--------------------------------------------------------------------------------
/src/components/label/__snapshots__/test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`renders active errored label 1`] = `
4 |
26 |
39 | test
40 |
41 |
42 | `;
43 |
44 | exports[`renders active focused label 1`] = `
45 |
67 |
80 | test
81 |
82 |
83 | `;
84 |
85 | exports[`renders active label 1`] = `
86 |
108 |
121 | test
122 |
123 |
124 | `;
125 |
126 | exports[`renders empty label 1`] = `null`;
127 |
128 | exports[`renders errored label 1`] = `
129 |
151 |
164 | test
165 |
166 |
167 | `;
168 |
169 | exports[`renders label 1`] = `
170 |
192 |
205 | test
206 |
207 |
208 | `;
209 |
210 | exports[`renders restricted label 1`] = `
211 |
233 |
246 | test
247 |
248 |
249 | `;
250 |
251 | exports[`renders styled label 1`] = `
252 |
274 |
288 | test
289 |
290 |
291 | `;
292 |
--------------------------------------------------------------------------------
/src/components/label/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 Label extends PureComponent {
8 | static defaultProps = {
9 | numberOfLines: 1,
10 | disabled: false,
11 | restricted: false,
12 | };
13 |
14 | static propTypes = {
15 | numberOfLines: PropTypes.number,
16 |
17 | disabled: PropTypes.bool,
18 | restricted: PropTypes.bool,
19 |
20 | fontSize: PropTypes.number.isRequired,
21 | activeFontSize: PropTypes.number.isRequired,
22 |
23 | baseColor: PropTypes.string.isRequired,
24 | tintColor: PropTypes.string.isRequired,
25 | errorColor: PropTypes.string.isRequired,
26 |
27 | focusAnimation: PropTypes
28 | .instanceOf(Animated.Value)
29 | .isRequired,
30 |
31 | labelAnimation: PropTypes
32 | .instanceOf(Animated.Value)
33 | .isRequired,
34 |
35 | contentInset: PropTypes.shape({
36 | label: PropTypes.number,
37 | }),
38 |
39 | offset: PropTypes.shape({
40 | x0: PropTypes.number,
41 | y0: PropTypes.number,
42 | x1: PropTypes.number,
43 | y1: PropTypes.number,
44 | }),
45 |
46 | style: Animated.Text.propTypes.style,
47 | label: PropTypes.string,
48 | };
49 |
50 | render() {
51 | let {
52 | label,
53 | offset,
54 | disabled,
55 | restricted,
56 | fontSize,
57 | activeFontSize,
58 | contentInset,
59 | errorColor,
60 | baseColor,
61 | tintColor,
62 | style,
63 | focusAnimation,
64 | labelAnimation,
65 | ...props
66 | } = this.props;
67 |
68 | if (null == label) {
69 | return null;
70 | }
71 |
72 | let color = disabled?
73 | baseColor:
74 | restricted?
75 | errorColor:
76 | focusAnimation.interpolate({
77 | inputRange: [-1, 0, 1],
78 | outputRange: [errorColor, baseColor, tintColor],
79 | });
80 |
81 | let textStyle = {
82 | lineHeight: fontSize,
83 | fontSize,
84 | color,
85 | };
86 |
87 | let { x0, y0, x1, y1 } = offset;
88 |
89 | y0 += activeFontSize;
90 | y0 += contentInset.label;
91 | y0 += fontSize * 0.25;
92 |
93 | let containerStyle = {
94 | transform: [{
95 | scale: labelAnimation.interpolate({
96 | inputRange: [0, 1],
97 | outputRange: [1, activeFontSize / fontSize],
98 | }),
99 | }, {
100 | translateY: labelAnimation.interpolate({
101 | inputRange: [0, 1],
102 | outputRange: [y0, y1],
103 | }),
104 | }, {
105 | translateX: labelAnimation.interpolate({
106 | inputRange: [0, 1],
107 | outputRange: [x0, x1],
108 | }),
109 | }],
110 | };
111 |
112 | return (
113 |
114 |
115 | {label}
116 |
117 |
118 | );
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/components/label/styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 |
3 | export default StyleSheet.create({
4 | container: {
5 | position: 'absolute',
6 | top: 0,
7 | left: '-100%',
8 | width: '200%',
9 | paddingLeft: '50%',
10 | },
11 |
12 | text: {
13 | textAlign: 'left',
14 | includeFontPadding: false,
15 | textAlignVertical: 'top',
16 | },
17 | });
18 |
--------------------------------------------------------------------------------
/src/components/label/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 Label from '.';
7 |
8 | /* eslint-env jest */
9 |
10 | const props = {
11 | fontSize: 16,
12 | activeFontSize: 12,
13 |
14 | contentInset: { label: 4 },
15 |
16 | baseColor: 'black',
17 | tintColor: 'blue',
18 | errorColor: 'red',
19 |
20 | offset: { x0: 0, y0: 0, x1: 0, y1: 0 },
21 |
22 | focusAnimation: new Animated.Value(0),
23 | labelAnimation: new Animated.Value(0),
24 | label: 'test',
25 | };
26 |
27 | it('renders label', () => {
28 | let label = renderer
29 | .create();
30 |
31 | expect(label.toJSON())
32 | .toMatchSnapshot();
33 | });
34 |
35 | it('renders empty label', () => {
36 | let label = renderer
37 | .create();
38 |
39 | expect(label.toJSON())
40 | .toMatchSnapshot();
41 | });
42 |
43 | it('renders active label', () => {
44 | let label = renderer
45 | .create(
46 |
47 | );
48 |
49 | expect(label.toJSON())
50 | .toMatchSnapshot();
51 | });
52 |
53 | it('renders active focused label', () => {
54 | let label = renderer
55 | .create(
56 |
61 | );
62 |
63 | expect(label.toJSON())
64 | .toMatchSnapshot();
65 | });
66 |
67 | it('renders errored label', () => {
68 | let label = renderer
69 | .create(
70 |
75 | );
76 |
77 | expect(label.toJSON())
78 | .toMatchSnapshot();
79 | });
80 |
81 | it('renders active errored label', () => {
82 | let label = renderer
83 | .create(
84 |
89 | );
90 |
91 | expect(label.toJSON())
92 | .toMatchSnapshot();
93 | });
94 |
95 | it('renders restricted label', () => {
96 | let label = renderer
97 | .create(
98 |
99 | );
100 |
101 | expect(label.toJSON())
102 | .toMatchSnapshot();
103 | });
104 |
105 | it('renders styled label', () => {
106 | let style = { textTransform: 'uppercase' };
107 | let label = renderer
108 | .create(
109 |
110 | );
111 |
112 | expect(label.toJSON())
113 | .toMatchSnapshot();
114 | });
115 |
--------------------------------------------------------------------------------
/src/components/line/__snapshots__/test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`renders active line 1`] = `
4 |
17 |
31 |
32 | `;
33 |
34 | exports[`renders disabled line 1`] = `
35 |
48 |
62 |
63 | `;
64 |
65 | exports[`renders line 1`] = `
66 |
79 |
93 |
94 | `;
95 |
96 | exports[`renders restricted line 1`] = `
97 |
110 |
124 |
125 | `;
126 |
--------------------------------------------------------------------------------
/src/components/line/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React, { PureComponent } from 'react';
3 | import { View, Animated } from 'react-native';
4 |
5 | import styles from './styles';
6 |
7 | const lineTypes = PropTypes
8 | .oneOf(['solid', 'dotted', 'dashed', 'none']);
9 |
10 | export default class Line extends PureComponent {
11 | static defaultProps = {
12 | lineType: 'solid',
13 | disabledLineType: 'dotted',
14 |
15 | disabled: false,
16 | restricted: false,
17 | };
18 |
19 | static propTypes = {
20 | lineType: lineTypes,
21 | disabledLineType: lineTypes,
22 |
23 | disabled: PropTypes.bool,
24 | restricted: PropTypes.bool,
25 |
26 | tintColor: PropTypes.string,
27 | baseColor: PropTypes.string,
28 | errorColor: PropTypes.string,
29 |
30 | lineWidth: PropTypes.number,
31 | activeLineWidth: PropTypes.number,
32 | disabledLineWidth: PropTypes.number,
33 |
34 | focusAnimation: PropTypes.instanceOf(Animated.Value),
35 | };
36 |
37 | static getDerivedStateFromProps(props, state) {
38 | let { lineWidth, activeLineWidth, disabledLineWidth } = props;
39 |
40 | let maxLineWidth = Math.max(
41 | lineWidth,
42 | activeLineWidth,
43 | disabledLineWidth,
44 | 1,
45 | );
46 |
47 | if (maxLineWidth !== state.maxLineWidth) {
48 | return { maxLineWidth };
49 | }
50 |
51 | return null;
52 | }
53 |
54 | state = { maxLineWidth: 1 };
55 |
56 | borderProps() {
57 | let {
58 | disabled,
59 | restricted,
60 | lineWidth,
61 | activeLineWidth,
62 | disabledLineWidth,
63 | baseColor,
64 | tintColor,
65 | errorColor,
66 | focusAnimation,
67 | } = this.props;
68 |
69 | if (disabled) {
70 | return {
71 | borderColor: baseColor,
72 | borderWidth: disabledLineWidth,
73 | };
74 | }
75 |
76 | if (restricted) {
77 | return {
78 | borderColor: errorColor,
79 | borderWidth: activeLineWidth,
80 | };
81 | }
82 |
83 | return {
84 | borderColor: focusAnimation.interpolate({
85 | inputRange: [-1, 0, 1],
86 | outputRange: [errorColor, baseColor, tintColor],
87 | }),
88 |
89 | borderWidth: focusAnimation.interpolate({
90 | inputRange: [-1, 0, 1],
91 | outputRange: [activeLineWidth, lineWidth, activeLineWidth],
92 | }),
93 | };
94 | }
95 |
96 | render() {
97 | let { maxLineWidth } = this.state;
98 | let { disabled, lineType, disabledLineType } = this.props;
99 |
100 | let borderStyle = disabled?
101 | disabledLineType:
102 | lineType;
103 |
104 | if ('none' === borderStyle) {
105 | return null;
106 | }
107 |
108 | let [top, right, left] = Array
109 | .from(new Array(3), () => -1.5 * maxLineWidth);
110 |
111 | let lineStyle = {
112 | ...this.borderProps(),
113 |
114 | borderStyle,
115 | top,
116 | right,
117 | left,
118 | };
119 |
120 | return (
121 |
122 |
123 |
124 | );
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/components/line/styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet, Platform } from 'react-native';
2 |
3 | export default StyleSheet.create({
4 | line: {
5 | position: 'absolute',
6 | bottom: 0,
7 |
8 | ...Platform.select({
9 | android: { borderRadius: Number.EPSILON },
10 | }),
11 | },
12 |
13 | container: {
14 | ...StyleSheet.absoluteFillObject,
15 |
16 | overflow: 'hidden',
17 | },
18 | });
19 |
--------------------------------------------------------------------------------
/src/components/line/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 Line from '.';
7 |
8 | /* eslint-env jest */
9 |
10 | const props = {
11 | disabled: false,
12 | restricted: false,
13 |
14 | baseColor: 'black',
15 | tintColor: 'blue',
16 | errorColor: 'red',
17 |
18 | lineWidth: 0.5,
19 | activeLineWidth: 2,
20 | disabledLineWidth: 1,
21 |
22 | focusAnimation: new Animated.Value(0),
23 | };
24 |
25 | it('renders line', () => {
26 | let line = renderer
27 | .create()
28 | .toJSON();
29 |
30 | expect(line)
31 | .toMatchSnapshot();
32 | });
33 |
34 | it('renders disabled line', () => {
35 | let line = renderer
36 | .create()
37 | .toJSON();
38 |
39 | expect(line)
40 | .toMatchSnapshot();
41 | });
42 |
43 | it('renders restricted line', () => {
44 | let line = renderer
45 | .create()
46 | .toJSON();
47 |
48 | expect(line)
49 | .toMatchSnapshot();
50 | });
51 |
52 | it('renders active line', () => {
53 | let line = renderer
54 | .create()
55 | .toJSON();
56 |
57 | expect(line)
58 | .toMatchSnapshot();
59 | });
60 |
--------------------------------------------------------------------------------
/src/components/outline/__snapshots__/test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`renders active outline 1`] = `
4 | Array [
5 |
22 |
42 |
57 |
58 | ,
59 |
76 |
91 | ,
92 |
105 |
120 | ,
121 |
139 |
154 | ,
155 | ]
156 | `;
157 |
158 | exports[`renders disabled outline 1`] = `
159 | Array [
160 |
177 |
197 |
211 |
212 | ,
213 |
230 |
244 | ,
245 |
258 |
272 | ,
273 |
291 |
305 | ,
306 | ]
307 | `;
308 |
309 | exports[`renders outline 1`] = `
310 | Array [
311 |
328 |
348 |
363 |
364 | ,
365 |
382 |
397 | ,
398 |
411 |
426 | ,
427 |
445 |
460 | ,
461 | ]
462 | `;
463 |
464 | exports[`renders restricted outline 1`] = `
465 | Array [
466 |
483 |
503 |
517 |
518 | ,
519 |
536 |
550 | ,
551 |
564 |
578 | ,
579 |
597 |
611 | ,
612 | ]
613 | `;
614 |
--------------------------------------------------------------------------------
/src/components/outline/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React, { Fragment, PureComponent } from 'react';
3 | import { View, Animated, I18nManager } from 'react-native';
4 |
5 | import styles, { borderRadius } from './styles';
6 |
7 | export default class Line extends PureComponent {
8 | static defaultProps = {
9 | lineType: 'solid',
10 | disabled: false,
11 | restricted: false,
12 | };
13 |
14 | static propTypes = {
15 | lineType: PropTypes.oneOf(['solid', 'none']),
16 |
17 | disabled: PropTypes.bool,
18 | restricted: PropTypes.bool,
19 |
20 | tintColor: PropTypes.string,
21 | baseColor: PropTypes.string,
22 | errorColor: PropTypes.string,
23 |
24 | lineWidth: PropTypes.number,
25 | activeLineWidth: PropTypes.number,
26 | disabledLineWidth: PropTypes.number,
27 |
28 | focusAnimation: PropTypes.instanceOf(Animated.Value),
29 | labelAnimation: PropTypes.instanceOf(Animated.Value),
30 | labelWidth: PropTypes.instanceOf(Animated.Value),
31 |
32 | contentInset: PropTypes.shape({
33 | left: PropTypes.number,
34 | right: PropTypes.number,
35 | }),
36 | };
37 |
38 | borderProps() {
39 | let {
40 | disabled,
41 | restricted,
42 | lineType,
43 | lineWidth,
44 | activeLineWidth,
45 | disabledLineWidth,
46 | baseColor,
47 | tintColor,
48 | errorColor,
49 | focusAnimation,
50 | } = this.props;
51 |
52 | if (disabled) {
53 | return {
54 | borderColor: baseColor,
55 | borderWidth: disabledLineWidth,
56 | };
57 | }
58 |
59 | if (restricted) {
60 | return {
61 | borderColor: errorColor,
62 | borderWidth: activeLineWidth,
63 | };
64 | }
65 |
66 | return {
67 | borderColor: focusAnimation.interpolate({
68 | inputRange: [-1, 0, 1],
69 | outputRange: [errorColor, baseColor, tintColor],
70 | }),
71 |
72 | borderWidth: focusAnimation.interpolate({
73 | inputRange: [-1, 0, 1],
74 | outputRange: [activeLineWidth, lineWidth, activeLineWidth],
75 | }),
76 |
77 | borderStyle: lineType,
78 | };
79 | }
80 |
81 | render() {
82 | let { lineType, labelWidth, labelAnimation, contentInset } = this.props;
83 |
84 | if ('none' === lineType) {
85 | return null;
86 | }
87 |
88 | let labelOffset = 2 * (contentInset.left - 2 * borderRadius);
89 | let lineOffset = Animated.add(labelWidth, labelOffset);
90 |
91 | let topLineContainerStyle = {
92 | transform: [{
93 | scaleX: I18nManager.isRTL? -1 : 1,
94 | }, {
95 | translateX: Animated.multiply(labelAnimation, lineOffset),
96 | }],
97 | };
98 |
99 | let leftContainerStyle = {
100 | width: contentInset.left - borderRadius,
101 | };
102 |
103 | let rightContainerStyle = {
104 | width: contentInset.right - borderRadius,
105 | };
106 |
107 | let topContainerStyle = {
108 | left: leftContainerStyle.width,
109 | right: rightContainerStyle.width,
110 | };
111 |
112 | let lineStyle = this.borderProps();
113 |
114 | return (
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 | );
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/src/components/outline/styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet, Platform } from 'react-native';
2 |
3 | export const borderRadius = 4;
4 |
5 | let containerStyle = {
6 | position: 'absolute',
7 | overflow: 'hidden',
8 | };
9 |
10 | export default StyleSheet.create({
11 | borderLeft: {
12 | ...StyleSheet.absoluteFillObject,
13 | borderRadius,
14 |
15 | right: -borderRadius,
16 | bottom: -borderRadius,
17 | },
18 |
19 | borderRight: {
20 | ...StyleSheet.absoluteFillObject,
21 | borderRadius,
22 |
23 | left: -borderRadius,
24 | bottom: -borderRadius,
25 | },
26 |
27 | borderBottom: {
28 | ...StyleSheet.absoluteFillObject,
29 | borderRadius,
30 |
31 | top: -borderRadius,
32 |
33 | /* XXX: Android positioning error workaround */
34 | bottom: StyleSheet.hairlineWidth,
35 | },
36 |
37 | borderTop: {
38 | ...StyleSheet.absoluteFillObject,
39 | borderRadius,
40 |
41 | left: -borderRadius,
42 | right: -borderRadius,
43 | bottom: -borderRadius,
44 | },
45 |
46 | leftContainer: {
47 | ...containerStyle,
48 |
49 | top: 0,
50 | left: 0,
51 | width: borderRadius,
52 | height: borderRadius,
53 | },
54 |
55 | rightContainer: {
56 | ...containerStyle,
57 |
58 | top: 0,
59 | right: 0,
60 | height: borderRadius,
61 | },
62 |
63 | bottomContainer: {
64 | ...containerStyle,
65 |
66 | top: borderRadius - ('android' === Platform.OS? 0.25 : 0),
67 | left: 0,
68 | right: 0,
69 | bottom: 0,
70 | },
71 |
72 | topContainer: {
73 | ...containerStyle,
74 |
75 | top: 0,
76 | height: borderRadius,
77 | },
78 |
79 | topLineContainer: {
80 | ...StyleSheet.absoluteFillObject,
81 | overflow: 'hidden',
82 | },
83 | });
84 |
--------------------------------------------------------------------------------
/src/components/outline/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 Outline from '.';
7 |
8 | /* eslint-env jest */
9 |
10 | const props = {
11 | disabled: false,
12 | restricted: false,
13 |
14 | baseColor: 'black',
15 | tintColor: 'blue',
16 | errorColor: 'red',
17 |
18 | lineWidth: 0.5,
19 | activeLineWidth: 2,
20 | disabledLineWidth: 1,
21 |
22 | focusAnimation: new Animated.Value(0),
23 | labelAnimation: new Animated.Value(0),
24 | labelWidth: new Animated.Value(72),
25 |
26 | contentInset: { left: 12, right: 12 },
27 | };
28 |
29 | it('renders outline', () => {
30 | let line = renderer
31 | .create()
32 | .toJSON();
33 |
34 | expect(line)
35 | .toMatchSnapshot();
36 | });
37 |
38 | it('renders disabled outline', () => {
39 | let line = renderer
40 | .create()
41 | .toJSON();
42 |
43 | expect(line)
44 | .toMatchSnapshot();
45 | });
46 |
47 | it('renders restricted outline', () => {
48 | let line = renderer
49 | .create()
50 | .toJSON();
51 |
52 | expect(line)
53 | .toMatchSnapshot();
54 | });
55 |
56 | it('renders active outline', () => {
57 | let line = renderer
58 | .create()
59 | .toJSON();
60 |
61 | expect(line)
62 | .toMatchSnapshot();
63 | });
64 |
--------------------------------------------------------------------------------