├── .circleci └── config.yml ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .watchmanconfig ├── .yarnrc ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── babel.config.js ├── example ├── .expo-shared │ └── assets.json ├── App.js ├── app.json ├── assets │ ├── adaptive-icon.png │ ├── favicon.png │ ├── icon.png │ └── splash.png ├── babel.config.js ├── metro.config.js ├── package.json ├── src │ ├── App.tsx │ └── assets │ │ └── default.jpg ├── webpack.config.js └── yarn.lock ├── lefthook.yml ├── package.json ├── scripts └── bootstrap.js ├── src ├── Avatar │ ├── icons │ │ ├── ic-avatar.png │ │ └── ic-camera.png │ ├── index.tsx │ ├── styles.ts │ └── type.ts ├── Image │ ├── index.tsx │ └── type.ts ├── __tests__ │ └── index.test.tsx └── index.tsx ├── tsconfig.build.json ├── tsconfig.json └── yarn.lock /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | executors: 4 | default: 5 | docker: 6 | - image: circleci/node:16 7 | working_directory: ~/project 8 | 9 | commands: 10 | attach_project: 11 | steps: 12 | - attach_workspace: 13 | at: ~/project 14 | 15 | jobs: 16 | install-dependencies: 17 | executor: default 18 | steps: 19 | - checkout 20 | - attach_project 21 | - restore_cache: 22 | keys: 23 | - dependencies-{{ checksum "package.json" }} 24 | - dependencies- 25 | - restore_cache: 26 | keys: 27 | - dependencies-example-{{ checksum "example/package.json" }} 28 | - dependencies-example- 29 | - run: 30 | name: Install dependencies 31 | command: | 32 | yarn install --cwd example --frozen-lockfile 33 | yarn install --frozen-lockfile 34 | - save_cache: 35 | key: dependencies-{{ checksum "package.json" }} 36 | paths: node_modules 37 | - save_cache: 38 | key: dependencies-example-{{ checksum "example/package.json" }} 39 | paths: example/node_modules 40 | - persist_to_workspace: 41 | root: . 42 | paths: . 43 | 44 | lint: 45 | executor: default 46 | steps: 47 | - attach_project 48 | - run: 49 | name: Lint files 50 | command: | 51 | yarn lint 52 | 53 | typescript: 54 | executor: default 55 | steps: 56 | - attach_project 57 | - run: 58 | name: Typecheck files 59 | command: | 60 | yarn typescript 61 | 62 | unit-tests: 63 | executor: default 64 | steps: 65 | - attach_project 66 | - run: 67 | name: Run unit tests 68 | command: | 69 | yarn test --coverage 70 | - store_artifacts: 71 | path: coverage 72 | destination: coverage 73 | 74 | build-package: 75 | executor: default 76 | steps: 77 | - attach_project 78 | - run: 79 | name: Build package 80 | command: | 81 | yarn prepare 82 | 83 | workflows: 84 | build-and-test: 85 | jobs: 86 | - install-dependencies 87 | - lint: 88 | requires: 89 | - install-dependencies 90 | - typescript: 91 | requires: 92 | - install-dependencies 93 | - unit-tests: 94 | requires: 95 | - install-dependencies 96 | - build-package: 97 | requires: 98 | - install-dependencies 99 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | indent_style = space 10 | indent_size = 2 11 | 12 | end_of_line = lf 13 | charset = utf-8 14 | trim_trailing_whitespace = true 15 | insert_final_newline = true 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | # specific for windows script files 3 | *.bat text eol=crlf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # XDE 6 | .expo/ 7 | 8 | # VSCode 9 | .vscode/ 10 | jsconfig.json 11 | 12 | # Xcode 13 | # 14 | build/ 15 | *.pbxuser 16 | !default.pbxuser 17 | *.mode1v3 18 | !default.mode1v3 19 | *.mode2v3 20 | !default.mode2v3 21 | *.perspectivev3 22 | !default.perspectivev3 23 | xcuserdata 24 | *.xccheckout 25 | *.moved-aside 26 | DerivedData 27 | *.hmap 28 | *.ipa 29 | *.xcuserstate 30 | project.xcworkspace 31 | 32 | # Android/IJ 33 | # 34 | .classpath 35 | .cxx 36 | .gradle 37 | .idea 38 | .project 39 | .settings 40 | local.properties 41 | android.iml 42 | 43 | # Cocoapods 44 | # 45 | example/ios/Pods 46 | 47 | # Ruby 48 | example/vendor/ 49 | 50 | # node.js 51 | # 52 | node_modules/ 53 | npm-debug.log 54 | yarn-debug.log 55 | yarn-error.log 56 | 57 | # BUCK 58 | buck-out/ 59 | \.buckd/ 60 | android/app/libs 61 | android/keystores/debug.keystore 62 | 63 | # Expo 64 | .expo/* 65 | 66 | # generated by bob 67 | lib/ 68 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | # Override Yarn command so we can automatically setup the repo on running `yarn` 2 | 3 | yarn-path "scripts/bootstrap.js" 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, caste, color, religion, or sexual 11 | identity and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the overall 27 | community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or advances of 32 | any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email address, 36 | without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official e-mail address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | [INSERT CONTACT METHOD]. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series of 87 | actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or permanent 94 | ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within the 114 | community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.1, available at 120 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 121 | 122 | Community Impact Guidelines were inspired by 123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 127 | [https://www.contributor-covenant.org/translations][translations]. 128 | 129 | [homepage]: https://www.contributor-covenant.org 130 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 131 | [Mozilla CoC]: https://github.com/mozilla/diversity 132 | [FAQ]: https://www.contributor-covenant.org/faq 133 | [translations]: https://www.contributor-covenant.org/translations 134 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are always welcome, no matter how large or small! 4 | 5 | We want this community to be friendly and respectful to each other. Please follow it in all your interactions with the project. Before contributing, please read the [code of conduct](./CODE_OF_CONDUCT.md). 6 | 7 | ## Development workflow 8 | 9 | To get started with the project, run `yarn` in the root directory to install the required dependencies for each package: 10 | 11 | ```sh 12 | yarn 13 | ``` 14 | 15 | > While it's possible to use [`npm`](https://github.com/npm/cli), the tooling is built around [`yarn`](https://classic.yarnpkg.com/), so you'll have an easier time if you use `yarn` for development. 16 | 17 | While developing, you can run the [example app](/example/) to test your changes. Any changes you make in your library's JavaScript code will be reflected in the example app without a rebuild. If you change any native code, then you'll need to rebuild the example app. 18 | 19 | To start the packager: 20 | 21 | ```sh 22 | yarn example start 23 | ``` 24 | 25 | To run the example app on Android: 26 | 27 | ```sh 28 | yarn example android 29 | ``` 30 | 31 | To run the example app on iOS: 32 | 33 | ```sh 34 | yarn example ios 35 | ``` 36 | 37 | To run the example app on Web: 38 | 39 | ```sh 40 | yarn example web 41 | ``` 42 | 43 | Make sure your code passes TypeScript and ESLint. Run the following to verify: 44 | 45 | ```sh 46 | yarn typescript 47 | yarn lint 48 | ``` 49 | 50 | To fix formatting errors, run the following: 51 | 52 | ```sh 53 | yarn lint --fix 54 | ``` 55 | 56 | Remember to add tests for your change if possible. Run the unit tests by: 57 | 58 | ```sh 59 | yarn test 60 | ``` 61 | 62 | ### Commit message convention 63 | 64 | We follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages: 65 | 66 | - `fix`: bug fixes, e.g. fix crash due to deprecated method. 67 | - `feat`: new features, e.g. add new method to the module. 68 | - `refactor`: code refactor, e.g. migrate from class components to hooks. 69 | - `docs`: changes into documentation, e.g. add usage example for the module.. 70 | - `test`: adding or updating tests, e.g. add integration tests using detox. 71 | - `chore`: tooling changes, e.g. change CI config. 72 | 73 | Our pre-commit hooks verify that your commit message matches this format when committing. 74 | 75 | ### Linting and tests 76 | 77 | [ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/) 78 | 79 | We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing. 80 | 81 | Our pre-commit hooks verify that the linter and tests pass when committing. 82 | 83 | ### Publishing to npm 84 | 85 | We use [release-it](https://github.com/release-it/release-it) to make it easier to publish new versions. It handles common tasks like bumping version based on semver, creating tags and releases etc. 86 | 87 | To publish new versions, run the following: 88 | 89 | ```sh 90 | yarn release 91 | ``` 92 | 93 | ### Scripts 94 | 95 | The `package.json` file contains various scripts for common tasks: 96 | 97 | - `yarn bootstrap`: setup project by installing all dependencies and pods. 98 | - `yarn typescript`: type-check files with TypeScript. 99 | - `yarn lint`: lint files with ESLint. 100 | - `yarn test`: run unit tests with Jest. 101 | - `yarn example start`: start the Metro server for the example app. 102 | - `yarn example android`: run the example app on Android. 103 | - `yarn example ios`: run the example app on iOS. 104 | 105 | ### Sending a pull request 106 | 107 | > **Working on your first pull request?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github). 108 | 109 | When you're sending a pull request: 110 | 111 | - Prefer small pull requests focused on one change. 112 | - Verify that linters and tests are passing. 113 | - Review the documentation to make sure it looks good. 114 | - Follow the pull request template when opening a pull request. 115 | - For pull requests that change the API or implementation, discuss with maintainers first by opening an issue. 116 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Hoa Phan 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## react-native-element-image 2 | 3 | Automatically calculate width or height based on input Image component for React Native. 4 | 5 | #### Source code demo 6 | 7 | - [react-native-template-components](https://github.com/hoaphantn7604/react-native-template-components) A beautiful template for React Native. 8 | 9 | ## Getting started 10 | 11 | ```js 12 | npm install react-native-element-image --save 13 | ``` 14 | 15 | or 16 | 17 | ```js 18 | yarn add npm install react-native-element-image 19 | ``` 20 | 21 | #### Demo 22 | 23 | ![](https://github.com/hoaphantn7604/file-upload/blob/master/document/image/demo.png) 24 | 25 | #### Image Props 26 | 27 | | Props | Params | isRequire | Description | 28 | | ---------- | ------------------- | --------- | ----------------------------- | 29 | | source | ImageSourcePropType | Yes | | 30 | | width | Number | | Fixed width, automatic height | 31 | | height | Number | | Fixed height, automatic width | 32 | | background | Boolean | No | if true is Image Background | 33 | | onSize | (size) => void | No | get Image size | 34 | 35 | #### Avatar Props 36 | 37 | | Props | Params | isRequire | Description | 38 | | -------------- | ------------------- | --------- | ----------------------- | 39 | | containerStyle | ViewStyle | No | | 40 | | size | Number | No | Default is 100px | 41 | | source | ImageSourcePropType | Yes | | 42 | | borderColor | String | No | Default is white | 43 | | name | String | No | | 44 | | nameStyle | TextStyle | No | | 45 | | iconEnable | Boolean | No | Default is true | 46 | | renderIcon | Element | No | Customize icon camera | 47 | | onPressIcon | ()=> void | No | Event click icon camera | 48 | 49 | ## Usage 50 | 51 | ```javascript 52 | import React from 'react'; 53 | import { StyleSheet, View, Text } from 'react-native'; 54 | import { Image, Avatar } from 'react-native-element-image'; 55 | const img = require('./assets/default.png'); 56 | 57 | const ImageScreen = (_props) => { 58 | return ( 59 | 60 | alert('Click')} 66 | nameStyle={{ fontSize: 20 }} 67 | /> 68 | alert('Click')} 74 | nameStyle={{ fontSize: 20 }} 75 | /> 76 | 77 | alert('Click')} 83 | name="User name" 84 | nameStyle={{ fontSize: 20, marginBottom: 20 }} 85 | /> 86 | Width: 200, Height: Automatic 87 | 88 | Width: Automatic, Height: 200 89 | 90 | 91 | ); 92 | }; 93 | 94 | export default ImageScreen; 95 | 96 | const styles = StyleSheet.create({ 97 | container: { 98 | flex: 1, 99 | justifyContent: 'center', 100 | alignItems: 'center', 101 | }, 102 | image: { 103 | margin: 20, 104 | }, 105 | text: { marginTop: 50 }, 106 | avatar: { marginTop: 10 }, 107 | }); 108 | ``` 109 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /example/.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, 3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true 4 | } 5 | -------------------------------------------------------------------------------- /example/App.js: -------------------------------------------------------------------------------- 1 | export { default } from './src/App'; 2 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "example", 4 | "slug": "example", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "userInterfaceStyle": "light", 9 | "splash": { 10 | "image": "./assets/splash.png", 11 | "resizeMode": "contain", 12 | "backgroundColor": "#ffffff" 13 | }, 14 | "updates": { 15 | "fallbackToCacheTimeout": 0 16 | }, 17 | "assetBundlePatterns": [ 18 | "**/*" 19 | ], 20 | "ios": { 21 | "supportsTablet": true 22 | }, 23 | "android": { 24 | "adaptiveIcon": { 25 | "foregroundImage": "./assets/adaptive-icon.png", 26 | "backgroundColor": "#FFFFFF" 27 | } 28 | }, 29 | "web": { 30 | "favicon": "./assets/favicon.png" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /example/assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoaphantn7604/react-native-element-image/89f9432af3f84c9c0ab2cecb17cfda9d80a9519c/example/assets/adaptive-icon.png -------------------------------------------------------------------------------- /example/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoaphantn7604/react-native-element-image/89f9432af3f84c9c0ab2cecb17cfda9d80a9519c/example/assets/favicon.png -------------------------------------------------------------------------------- /example/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoaphantn7604/react-native-element-image/89f9432af3f84c9c0ab2cecb17cfda9d80a9519c/example/assets/icon.png -------------------------------------------------------------------------------- /example/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoaphantn7604/react-native-element-image/89f9432af3f84c9c0ab2cecb17cfda9d80a9519c/example/assets/splash.png -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const pak = require('../package.json'); 3 | 4 | module.exports = function (api) { 5 | api.cache(true); 6 | 7 | return { 8 | presets: ['babel-preset-expo'], 9 | plugins: [ 10 | [ 11 | 'module-resolver', 12 | { 13 | extensions: ['.tsx', '.ts', '.js', '.json'], 14 | alias: { 15 | // For development, we want to alias the library to the source 16 | [pak.name]: path.join(__dirname, '..', pak.source), 17 | }, 18 | }, 19 | ], 20 | ], 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const escape = require('escape-string-regexp'); 3 | const { getDefaultConfig } = require('@expo/metro-config'); 4 | const exclusionList = require('metro-config/src/defaults/exclusionList'); 5 | const pak = require('../package.json'); 6 | 7 | const root = path.resolve(__dirname, '..'); 8 | 9 | const modules = Object.keys({ 10 | ...pak.peerDependencies, 11 | }); 12 | 13 | const defaultConfig = getDefaultConfig(__dirname); 14 | 15 | module.exports = { 16 | ...defaultConfig, 17 | 18 | projectRoot: __dirname, 19 | watchFolders: [root], 20 | 21 | // We need to make sure that only one version is loaded for peerDependencies 22 | // So we block them at the root, and alias them to the versions in example's node_modules 23 | resolver: { 24 | ...defaultConfig.resolver, 25 | 26 | blacklistRE: exclusionList( 27 | modules.map( 28 | (m) => 29 | new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`) 30 | ) 31 | ), 32 | 33 | extraNodeModules: modules.reduce((acc, name) => { 34 | acc[name] = path.join(__dirname, 'node_modules', name); 35 | return acc; 36 | }, {}), 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "main": "node_modules/expo/AppEntry.js", 5 | "scripts": { 6 | "start": "expo start", 7 | "android": "expo start --android", 8 | "ios": "expo start --ios", 9 | "web": "expo start --web" 10 | }, 11 | "dependencies": { 12 | "expo": "~46.0.16", 13 | "expo-status-bar": "~1.4.0", 14 | "react": "18.0.0", 15 | "react-native": "0.69.6" 16 | }, 17 | "devDependencies": { 18 | "@babel/core": "^7.12.9", 19 | "babel-plugin-module-resolver": "^4.1.0" 20 | }, 21 | "private": true 22 | } 23 | -------------------------------------------------------------------------------- /example/src/App.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react-native/no-inline-styles */ 2 | import React from 'react'; 3 | import { StyleSheet, View, Text, Alert, ScrollView } from 'react-native'; 4 | import { Image, Avatar } from 'react-native-element-image'; 5 | const img = require('./assets/default.jpg'); 6 | 7 | const ImageScreen = (_props: any) => { 8 | return ( 9 | 10 | 11 | Alert.alert('Click')} 17 | nameStyle={{ fontSize: 20 }} 18 | /> 19 | Alert.alert('Click')} 25 | nameStyle={{ fontSize: 20 }} 26 | /> 27 | 28 | Alert.alert('Click')} 34 | name="User name" 35 | nameStyle={{ fontSize: 20, marginBottom: 20 }} 36 | /> 37 | Width: 150, Height: Automatic 38 | 39 | Width: Automatic, Height: 200 40 | 41 | 42 | 43 | ); 44 | }; 45 | 46 | export default ImageScreen; 47 | 48 | const styles = StyleSheet.create({ 49 | container: { 50 | flex: 1, 51 | justifyContent: 'center', 52 | alignItems: 'center', 53 | paddingVertical: 20, 54 | }, 55 | image: { 56 | margin: 20, 57 | }, 58 | text: { marginTop: 50 }, 59 | avatar: { marginTop: 10 }, 60 | }); 61 | -------------------------------------------------------------------------------- /example/src/assets/default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoaphantn7604/react-native-element-image/89f9432af3f84c9c0ab2cecb17cfda9d80a9519c/example/src/assets/default.jpg -------------------------------------------------------------------------------- /example/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const createExpoWebpackConfigAsync = require('@expo/webpack-config'); 3 | const { resolver } = require('./metro.config'); 4 | 5 | const root = path.resolve(__dirname, '..'); 6 | const node_modules = path.join(__dirname, 'node_modules'); 7 | 8 | module.exports = async function (env, argv) { 9 | const config = await createExpoWebpackConfigAsync(env, argv); 10 | 11 | config.module.rules.push({ 12 | test: /\.(js|jsx|ts|tsx)$/, 13 | include: path.resolve(root, 'src'), 14 | use: 'babel-loader', 15 | }); 16 | 17 | // We need to make sure that only one version is loaded for peerDependencies 18 | // So we alias them to the versions in example's node_modules 19 | Object.assign(config.resolve.alias, { 20 | ...resolver.extraNodeModules, 21 | 'react-native-web': path.join(node_modules, 'react-native-web'), 22 | }); 23 | 24 | return config; 25 | }; 26 | -------------------------------------------------------------------------------- /lefthook.yml: -------------------------------------------------------------------------------- 1 | pre-commit: 2 | parallel: true 3 | commands: 4 | lint: 5 | files: git diff --name-only @{push} 6 | glob: "*.{js,ts,jsx,tsx}" 7 | run: npx eslint {files} 8 | types: 9 | files: git diff --name-only @{push} 10 | glob: "*.{js,ts, jsx, tsx}" 11 | run: npx tsc --noEmit 12 | commit-msg: 13 | parallel: true 14 | commands: 15 | commitlint: 16 | run: npx commitlint --edit 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-element-image", 3 | "version": "1.1.1", 4 | "description": "Automatically calculate width or height based on input Image component for React Native.", 5 | "main": "lib/commonjs/index", 6 | "module": "lib/module/index", 7 | "types": "lib/typescript/index.d.ts", 8 | "react-native": "src/index", 9 | "source": "src/index", 10 | "files": [ 11 | "src", 12 | "lib", 13 | "android", 14 | "ios", 15 | "cpp", 16 | "*.podspec", 17 | "!lib/typescript/example", 18 | "!ios/build", 19 | "!android/build", 20 | "!android/gradle", 21 | "!android/gradlew", 22 | "!android/gradlew.bat", 23 | "!android/local.properties", 24 | "!**/__tests__", 25 | "!**/__fixtures__", 26 | "!**/__mocks__", 27 | "!**/.*" 28 | ], 29 | "scripts": { 30 | "test": "jest", 31 | "typescript": "tsc --noEmit", 32 | "lint": "eslint \"**/*.{js,ts,tsx}\"", 33 | "prepare": "bob build", 34 | "release": "release-it", 35 | "example": "yarn --cwd example", 36 | "bootstrap": "yarn example && yarn install" 37 | }, 38 | "keywords": [ 39 | "react-native", 40 | "elements", 41 | "components", 42 | "image", 43 | "auto width", 44 | "auto height", 45 | "image scale", 46 | "user avatar" 47 | ], 48 | "repository": "https://github.com/hoaphantn7604/react-native-element-image", 49 | "author": "Hoa Phan (https://github.com/hoaphantn7604)", 50 | "license": "MIT", 51 | "bugs": { 52 | "url": "https://github.com/hoaphantn7604/react-native-element-image/issues" 53 | }, 54 | "homepage": "https://github.com/hoaphantn7604/react-native-element-image#readme", 55 | "publishConfig": { 56 | "registry": "https://registry.npmjs.org/" 57 | }, 58 | "devDependencies": { 59 | "@arkweid/lefthook": "^0.7.7", 60 | "@commitlint/config-conventional": "^17.0.2", 61 | "@react-native-community/eslint-config": "^3.0.2", 62 | "@release-it/conventional-changelog": "^5.0.0", 63 | "@types/jest": "^28.1.2", 64 | "@types/react": "~17.0.21", 65 | "@types/react-native": "0.70.0", 66 | "commitlint": "^17.0.2", 67 | "eslint": "^8.4.1", 68 | "eslint-config-prettier": "^8.5.0", 69 | "eslint-plugin-prettier": "^4.0.0", 70 | "jest": "^28.1.1", 71 | "pod-install": "^0.1.0", 72 | "prettier": "^2.0.5", 73 | "react": "18.0.0", 74 | "react-native": "0.69.6", 75 | "react-native-builder-bob": "^0.20.0", 76 | "release-it": "^15.0.0", 77 | "typescript": "^4.5.2" 78 | }, 79 | "resolutions": { 80 | "@types/react": "17.0.21" 81 | }, 82 | "peerDependencies": { 83 | "react": "*", 84 | "react-native": "*" 85 | }, 86 | "jest": { 87 | "preset": "react-native", 88 | "modulePathIgnorePatterns": [ 89 | "/example/node_modules", 90 | "/lib/" 91 | ] 92 | }, 93 | "commitlint": { 94 | "extends": [ 95 | "@commitlint/config-conventional" 96 | ] 97 | }, 98 | "release-it": { 99 | "git": { 100 | "commitMessage": "chore: release ${version}", 101 | "tagName": "v${version}" 102 | }, 103 | "npm": { 104 | "publish": true 105 | }, 106 | "github": { 107 | "release": true 108 | }, 109 | "plugins": { 110 | "@release-it/conventional-changelog": { 111 | "preset": "angular" 112 | } 113 | } 114 | }, 115 | "eslintConfig": { 116 | "root": true, 117 | "extends": [ 118 | "@react-native-community", 119 | "prettier" 120 | ], 121 | "rules": { 122 | "prettier/prettier": [ 123 | "error", 124 | { 125 | "quoteProps": "consistent", 126 | "singleQuote": true, 127 | "tabWidth": 2, 128 | "trailingComma": "es5", 129 | "useTabs": false 130 | } 131 | ] 132 | } 133 | }, 134 | "eslintIgnore": [ 135 | "node_modules/", 136 | "lib/" 137 | ], 138 | "prettier": { 139 | "quoteProps": "consistent", 140 | "singleQuote": true, 141 | "tabWidth": 2, 142 | "trailingComma": "es5", 143 | "useTabs": false 144 | }, 145 | "react-native-builder-bob": { 146 | "source": "src", 147 | "output": "lib", 148 | "targets": [ 149 | "commonjs", 150 | "module", 151 | [ 152 | "typescript", 153 | { 154 | "project": "tsconfig.build.json" 155 | } 156 | ] 157 | ] 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /scripts/bootstrap.js: -------------------------------------------------------------------------------- 1 | const os = require('os'); 2 | const path = require('path'); 3 | const child_process = require('child_process'); 4 | 5 | const root = path.resolve(__dirname, '..'); 6 | const args = process.argv.slice(2); 7 | const options = { 8 | cwd: process.cwd(), 9 | env: process.env, 10 | stdio: 'inherit', 11 | encoding: 'utf-8', 12 | }; 13 | 14 | if (os.type() === 'Windows_NT') { 15 | options.shell = true; 16 | } 17 | 18 | let result; 19 | 20 | if (process.cwd() !== root || args.length) { 21 | // We're not in the root of the project, or additional arguments were passed 22 | // In this case, forward the command to `yarn` 23 | result = child_process.spawnSync('yarn', args, options); 24 | } else { 25 | // If `yarn` is run without arguments, perform bootstrap 26 | result = child_process.spawnSync('yarn', ['bootstrap'], options); 27 | } 28 | 29 | process.exitCode = result.status; 30 | -------------------------------------------------------------------------------- /src/Avatar/icons/ic-avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoaphantn7604/react-native-element-image/89f9432af3f84c9c0ab2cecb17cfda9d80a9519c/src/Avatar/icons/ic-avatar.png -------------------------------------------------------------------------------- /src/Avatar/icons/ic-camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoaphantn7604/react-native-element-image/89f9432af3f84c9c0ab2cecb17cfda9d80a9519c/src/Avatar/icons/ic-camera.png -------------------------------------------------------------------------------- /src/Avatar/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react-native/no-inline-styles */ 2 | import React, { useMemo } from 'react'; 3 | import { View, TouchableWithoutFeedback } from 'react-native'; 4 | import type { AvatarProps } from './type'; 5 | import { Image, Text } from 'react-native'; 6 | import { styles } from './styles'; 7 | 8 | const img_avatar = require('./icons/ic-avatar.png'); 9 | const img_camera = require('./icons/ic-camera.png'); 10 | 11 | const AvatarComponent: AvatarProps = (props) => { 12 | const { 13 | containerStyle = {}, 14 | size = 100, 15 | borderColor = 'white', 16 | name, 17 | nameStyle, 18 | iconEnable = true, 19 | renderIcon, 20 | onPressIcon, 21 | } = props; 22 | 23 | const _renderIcon = () => { 24 | if (iconEnable) { 25 | if (renderIcon) { 26 | return renderIcon; 27 | } else { 28 | return ( 29 | { 31 | onPressIcon && onPressIcon(); 32 | }} 33 | > 34 | 44 | 52 | 53 | 54 | ); 55 | } 56 | } 57 | return null; 58 | }; 59 | 60 | const uri = useMemo(() => { 61 | if (props.source) { 62 | return props.source; 63 | } 64 | return img_avatar; 65 | }, [props.source]); 66 | 67 | return ( 68 | 69 | 79 | 84 | {_renderIcon()} 85 | 86 | {name ? {name} : null} 87 | 88 | ); 89 | }; 90 | 91 | export default AvatarComponent; 92 | -------------------------------------------------------------------------------- /src/Avatar/styles.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export const styles = StyleSheet.create({ 4 | container: { 5 | justifyContent: 'center', 6 | alignItems: 'center', 7 | }, 8 | name: { 9 | fontSize: 14, 10 | fontWeight: '500', 11 | marginTop: 8, 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /src/Avatar/type.ts: -------------------------------------------------------------------------------- 1 | import type React from 'react'; 2 | import type { ImageProps, TextStyle, ViewStyle } from 'react-native'; 3 | 4 | interface Props extends ImageProps { 5 | containerStyle?: ViewStyle; 6 | size?: number; 7 | borderColor?: string; 8 | name?: string; 9 | nameStyle?: TextStyle; 10 | iconEnable?: boolean; 11 | renderIcon?: JSX.Element | undefined; 12 | onPressIcon?: () => void; 13 | } 14 | 15 | export type AvatarProps = React.FC; 16 | -------------------------------------------------------------------------------- /src/Image/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef, useState } from 'react'; 2 | import { Image, ImageBackground } from 'react-native'; 3 | import type { PropsImage } from './type'; 4 | 5 | const resolveAssetSource = Image.resolveAssetSource; 6 | 7 | const ImageAuto: PropsImage = (props) => { 8 | const { style = {}, onSize = () => {}, background } = props; 9 | const [autoWidth, setAutoWidth] = useState(null); 10 | const [autoHeight, setAutoHeight] = useState(null); 11 | const mounted = useRef(false); 12 | 13 | useEffect(() => { 14 | mounted.current = true; 15 | 16 | return () => { 17 | mounted.current = false; 18 | }; 19 | }, []); 20 | 21 | useEffect(() => { 22 | onProps(props); 23 | }); 24 | 25 | const onProps = (localProps: any) => { 26 | const { source } = localProps; 27 | if (source.uri) { 28 | const sourceToUse = source.uri ? source.uri : source; 29 | 30 | Image.getSize( 31 | sourceToUse, 32 | (width, height) => adjustSize(width, height, props), 33 | console.log 34 | ); 35 | } else { 36 | const sourceToUse = resolveAssetSource(source); 37 | adjustSize(sourceToUse.width, sourceToUse.height, props); 38 | } 39 | }; 40 | 41 | const adjustSize = ( 42 | sourceWidth: number, 43 | sourceHeight: number, 44 | localProps: any 45 | ) => { 46 | const { width, height } = localProps; 47 | 48 | let ratio = 1; 49 | 50 | if (width && height) { 51 | ratio = Math.min(width / sourceWidth, height / sourceHeight); 52 | } else if (width) { 53 | ratio = width / sourceWidth; 54 | } else if (height) { 55 | ratio = height / sourceHeight; 56 | } 57 | 58 | if (mounted.current) { 59 | const ratioWidth = sourceWidth * ratio; 60 | const ratioHeight = sourceHeight * ratio; 61 | 62 | setAutoWidth(ratioWidth); 63 | setAutoHeight(ratioHeight); 64 | if (onSize) { 65 | onSize({ width: ratioWidth, height: ratioHeight }); 66 | } 67 | } 68 | }; 69 | 70 | if (autoWidth && autoHeight) { 71 | if (background) { 72 | return ( 73 | 77 | ); 78 | } 79 | return ( 80 | 84 | ); 85 | } 86 | return null; 87 | }; 88 | 89 | export default ImageAuto; 90 | -------------------------------------------------------------------------------- /src/Image/type.ts: -------------------------------------------------------------------------------- 1 | import type React from 'react'; 2 | import type { ImageBackgroundProps, ImageStyle } from 'react-native'; 3 | 4 | interface onSize { 5 | width: number; 6 | height: number; 7 | } 8 | 9 | interface Props extends ImageBackgroundProps { 10 | style?: ImageStyle; 11 | height?: number; 12 | width?: number; 13 | background?: boolean; 14 | onSize?: (size: onSize) => void; 15 | } 16 | 17 | export type PropsImage = React.FC; 18 | -------------------------------------------------------------------------------- /src/__tests__/index.test.tsx: -------------------------------------------------------------------------------- 1 | it.todo('write a test'); 2 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import Image from './Image'; 2 | import Avatar from './Avatar'; 3 | 4 | export { Image, Avatar }; 5 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "extends": "./tsconfig", 4 | "exclude": ["example"] 5 | } 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "paths": { 5 | "react-native-element-image": ["./src/index"] 6 | }, 7 | "allowUnreachableCode": false, 8 | "allowUnusedLabels": false, 9 | "esModuleInterop": true, 10 | "importsNotUsedAsValues": "error", 11 | "forceConsistentCasingInFileNames": true, 12 | "jsx": "react", 13 | "lib": ["esnext"], 14 | "module": "esnext", 15 | "moduleResolution": "node", 16 | "noFallthroughCasesInSwitch": true, 17 | "noImplicitReturns": true, 18 | "noImplicitUseStrict": false, 19 | "noStrictGenericChecks": false, 20 | "noUncheckedIndexedAccess": true, 21 | "noUnusedLocals": true, 22 | "noUnusedParameters": true, 23 | "resolveJsonModule": true, 24 | "skipLibCheck": true, 25 | "strict": true, 26 | "target": "esnext" 27 | } 28 | } 29 | --------------------------------------------------------------------------------