├── .editorconfig ├── .gitattributes ├── .github ├── FUNDING.yml ├── actions │ └── setup │ │ └── action.yml ├── images │ ├── modes-screenshot.png │ ├── react-native-ui-datepicker-example.gif │ └── rnui-datepicker.png └── workflows │ └── ci.yml ├── .gitignore ├── .nvmrc ├── .prettierrc.json ├── .watchmanconfig ├── .yarnrc ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── babel.config.js ├── demo ├── .gitignore ├── README.md ├── app.json ├── app │ ├── +html.tsx │ ├── +not-found.tsx │ ├── _layout.tsx │ ├── examples.tsx │ ├── index.tsx │ └── playground.tsx ├── assets │ ├── fonts │ │ └── SpaceMono-Regular.ttf │ └── images │ │ ├── adaptive-icon.png │ │ ├── calendar.png │ │ ├── favicon.png │ │ ├── github-logo.png │ │ ├── icon.png │ │ ├── partial-react-logo.png │ │ ├── react-logo.png │ │ ├── react-logo@2x.png │ │ ├── react-logo@3x.png │ │ └── splash-icon.png ├── babel.config.js ├── components │ ├── date-input.tsx │ ├── examples │ │ ├── custom-datepicker-1.tsx │ │ ├── custom-datepicker-2.tsx │ │ ├── custom-datepicker-3.tsx │ │ ├── index.ts │ │ ├── multiple-datepicker.tsx │ │ ├── range-datepicker.tsx │ │ └── single-datepicker.tsx │ ├── feature-card.tsx │ ├── github-link.tsx │ ├── locale-selector.tsx │ ├── package-manager.tsx │ ├── theme-selector.tsx │ └── ui │ │ ├── button.tsx │ │ ├── calendar.tsx │ │ ├── separator.tsx │ │ ├── tabs.tsx │ │ └── text.tsx ├── constants │ └── Colors.ts ├── global.css ├── hooks │ ├── use-active-link.ts │ ├── useColorScheme.ts │ ├── useColorScheme.web.ts │ └── useThemeColor.ts ├── layouts │ ├── common │ │ ├── github-stats.tsx │ │ └── theme-toggle.tsx │ ├── header.tsx │ └── nav │ │ ├── config-navigation.ts │ │ ├── nav-item.tsx │ │ ├── nav-menu.tsx │ │ └── types.ts ├── lib │ ├── constants.ts │ ├── generate-dates.ts │ ├── useColorScheme.tsx │ └── utils.ts ├── metro.config.js ├── nativewind-env.d.ts ├── package.json ├── scripts │ └── reset-project.js ├── tailwind.config.js ├── tsconfig.json └── yarn.lock ├── eslint.config.js ├── example ├── .gitignore ├── README.md ├── app.json ├── app │ ├── +not-found.tsx │ ├── _layout.tsx │ ├── bottom-sheet.tsx │ └── index.tsx ├── assets │ ├── fonts │ │ └── SpaceMono-Regular.ttf │ └── images │ │ ├── adaptive-icon.png │ │ ├── favicon.png │ │ ├── icon.png │ │ ├── partial-react-logo.png │ │ ├── react-logo.png │ │ ├── react-logo@2x.png │ │ ├── react-logo@3x.png │ │ └── splash-icon.png ├── components │ ├── Collapsible.tsx │ ├── ExternalLink.tsx │ ├── HapticTab.tsx │ ├── HelloWave.tsx │ ├── ParallaxScrollView.tsx │ ├── ThemedText.tsx │ ├── ThemedView.tsx │ └── ui │ │ ├── IconSymbol.ios.tsx │ │ ├── IconSymbol.tsx │ │ ├── TabBarBackground.ios.tsx │ │ └── TabBarBackground.tsx ├── constants │ └── Colors.ts ├── hooks │ ├── useColorScheme.ts │ ├── useColorScheme.web.ts │ └── useThemeColor.ts ├── metro.config.js ├── package.json ├── scripts │ └── reset-project.js ├── tsconfig.json └── yarn.lock ├── jest.config.ts ├── jest.setup.ts ├── lefthook.yml ├── package.json ├── scripts └── bootstrap.js ├── src ├── __tests__ │ ├── __snapshots__ │ │ └── common.test.tsx.snap │ ├── api.test.tsx │ └── common.test.tsx ├── assets │ └── images │ │ ├── arrow_left.png │ │ └── arrow_right.png ├── calendar-context.ts ├── components │ ├── calendar.tsx │ ├── day.tsx │ ├── days.tsx │ ├── header │ │ ├── index.tsx │ │ ├── month-button.tsx │ │ ├── next-button.tsx │ │ ├── prev-button.tsx │ │ ├── selectors.tsx │ │ ├── time-button.tsx │ │ ├── types.ts │ │ └── year-button.tsx │ ├── months.tsx │ ├── time-picker.tsx │ ├── time-picker │ │ ├── animated-math.ts │ │ ├── period-native.tsx │ │ ├── period-picker.tsx │ │ ├── period-web.tsx │ │ ├── wheel-native.tsx │ │ ├── wheel-picker │ │ │ ├── index.ts │ │ │ ├── wheel-picker-item.tsx │ │ │ ├── wheel-picker.style.ts │ │ │ └── wheel-picker.tsx │ │ ├── wheel-web.tsx │ │ └── wheel.tsx │ ├── weekdays.tsx │ └── years.tsx ├── datetime-picker.tsx ├── enums.ts ├── hooks │ └── use-previous.ts ├── index.ts ├── locales.ts ├── numerals.ts ├── polyfill.ts ├── react-native-extended.d.ts ├── theme.ts ├── types.ts ├── ui.ts └── utils.ts ├── tsconfig.build.json ├── tsconfig.json └── yarn.lock /.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 -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [farhoudshapouran] 4 | buy_me_a_coffee: farhoudshapouran 5 | -------------------------------------------------------------------------------- /.github/actions/setup/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup 2 | description: Setup Node.js and install dependencies 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - name: Setup Node.js 8 | uses: actions/setup-node@v3 9 | with: 10 | node-version: 18 # Force Node.js 18 instead of using .nvmrc 11 | 12 | - name: Cache dependencies 13 | id: yarn-cache 14 | uses: actions/cache@v3 15 | with: 16 | path: | 17 | **/node_modules 18 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 19 | restore-keys: | 20 | ${{ runner.os }}-yarn- 21 | 22 | - name: Install dependencies 23 | if: steps.yarn-cache.outputs.cache-hit != 'true' 24 | run: | 25 | yarn install --cwd example --frozen-lockfile 26 | yarn install --frozen-lockfile 27 | shell: bash 28 | -------------------------------------------------------------------------------- /.github/images/modes-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farhoudshapouran/react-native-ui-datepicker/9b317eba399144944ac5235e2d151036456c5786/.github/images/modes-screenshot.png -------------------------------------------------------------------------------- /.github/images/react-native-ui-datepicker-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farhoudshapouran/react-native-ui-datepicker/9b317eba399144944ac5235e2d151036456c5786/.github/images/react-native-ui-datepicker-example.gif -------------------------------------------------------------------------------- /.github/images/rnui-datepicker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farhoudshapouran/react-native-ui-datepicker/9b317eba399144944ac5235e2d151036456c5786/.github/images/rnui-datepicker.png -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | # disabled because the latest eslint version "ignores" option does not work as expected 12 | # lint: 13 | # runs-on: ubuntu-latest 14 | # steps: 15 | # - name: Checkout repository 16 | # uses: actions/checkout@v3 17 | 18 | # - name: Setup project 19 | # uses: ./.github/actions/setup 20 | 21 | # - name: Lint files 22 | # run: yarn lint 23 | 24 | # - name: Typecheck files 25 | # run: yarn typecheck 26 | 27 | test: 28 | runs-on: ubuntu-latest 29 | steps: 30 | - name: Checkout repository 31 | uses: actions/checkout@v3 32 | 33 | - name: Setup project 34 | uses: ./.github/actions/setup 35 | 36 | - name: Run unit tests 37 | run: yarn test 38 | 39 | # build: 40 | # runs-on: ubuntu-latest 41 | # steps: 42 | # - name: Checkout repository 43 | # uses: actions/checkout@v3 44 | 45 | # - name: Setup project 46 | # uses: ./.github/actions/setup 47 | 48 | # - name: Build package 49 | # run: yarn prepack 50 | -------------------------------------------------------------------------------- /.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 | demo/ios/Pods 46 | example/ios/Pods 47 | 48 | # Ruby 49 | demo/vendor/ 50 | example/vendor/ 51 | 52 | # node.js 53 | # 54 | node_modules/ 55 | npm-debug.log 56 | yarn-debug.log 57 | yarn-error.log 58 | 59 | # BUCK 60 | buck-out/ 61 | \.buckd/ 62 | android/app/libs 63 | android/keystores/debug.keystore 64 | 65 | # Expo 66 | .expo/ 67 | 68 | # Turborepo 69 | .turbo/ 70 | 71 | # generated by bob 72 | /lib/ 73 | 74 | .env 75 | web-build -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 16.18.1 2 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "quoteProps": "consistent", 3 | "trailingComma": "es5", 4 | "tabWidth": 2, 5 | "semi": true, 6 | "singleQuote": true, 7 | "useTabs": false, 8 | "plugins": ["prettier-plugin-tailwindcss"] 9 | } 10 | -------------------------------------------------------------------------------- /.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 typecheck 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 | 63 | ### Commit message convention 64 | 65 | We follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages: 66 | 67 | - `fix`: bug fixes, e.g. fix crash due to deprecated method. 68 | - `feat`: new features, e.g. add new method to the module. 69 | - `refactor`: code refactor, e.g. migrate from class components to hooks. 70 | - `docs`: changes into documentation, e.g. add usage example for the module.. 71 | - `test`: adding or updating tests, e.g. add integration tests using detox. 72 | - `chore`: tooling changes, e.g. change CI config. 73 | 74 | Our pre-commit hooks verify that your commit message matches this format when committing. 75 | 76 | ### Linting and tests 77 | 78 | [ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/) 79 | 80 | 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. 81 | 82 | Our pre-commit hooks verify that the linter and tests pass when committing. 83 | 84 | ### Publishing to npm 85 | 86 | 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. 87 | 88 | To publish new versions, run the following: 89 | 90 | ```sh 91 | yarn release 92 | ``` 93 | 94 | ### Scripts 95 | 96 | The `package.json` file contains various scripts for common tasks: 97 | 98 | - `yarn bootstrap`: setup project by installing all dependencies and pods. 99 | - `yarn typecheck`: type-check files with TypeScript. 100 | - `yarn lint`: lint files with ESLint. 101 | - `yarn test`: run unit tests with Jest. 102 | - `yarn example start`: start the Metro server for the example app. 103 | - `yarn example android`: run the example app on Android. 104 | - `yarn example ios`: run the example app on iOS. 105 | 106 | ### Sending a pull request 107 | 108 | > **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). 109 | 110 | When you're sending a pull request: 111 | 112 | - Prefer small pull requests focused on one change. 113 | - Verify that linters and tests are passing. 114 | - Review the documentation to make sure it looks good. 115 | - Follow the pull request template when opening a pull request. 116 | - For pull requests that change the API or implementation, discuss with maintainers first by opening an issue. 117 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Farhoud Shapouran 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | # Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files 2 | 3 | # dependencies 4 | node_modules/ 5 | 6 | # Expo 7 | .expo/ 8 | dist/ 9 | web-build/ 10 | expo-env.d.ts 11 | 12 | # Native 13 | *.orig.* 14 | *.jks 15 | *.p8 16 | *.p12 17 | *.key 18 | *.mobileprovision 19 | 20 | # Metro 21 | .metro-health-check* 22 | 23 | # debug 24 | npm-debug.* 25 | yarn-debug.* 26 | yarn-error.* 27 | 28 | # macOS 29 | .DS_Store 30 | *.pem 31 | 32 | # local env files 33 | .env*.local 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | 38 | app-example 39 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to your Expo app 👋 2 | 3 | This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app). 4 | 5 | ## Get started 6 | 7 | 1. Install dependencies 8 | 9 | ```bash 10 | npm install 11 | ``` 12 | 13 | 2. Start the app 14 | 15 | ```bash 16 | npx expo start 17 | ``` 18 | 19 | In the output, you'll find options to open the app in a 20 | 21 | - [development build](https://docs.expo.dev/develop/development-builds/introduction/) 22 | - [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/) 23 | - [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/) 24 | - [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo 25 | 26 | You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction). 27 | 28 | ## Get a fresh project 29 | 30 | When you're ready, run: 31 | 32 | ```bash 33 | npm run reset-project 34 | ``` 35 | 36 | This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing. 37 | 38 | ## Learn more 39 | 40 | To learn more about developing your project with Expo, look at the following resources: 41 | 42 | - [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides). 43 | - [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web. 44 | 45 | ## Join the community 46 | 47 | Join our community of developers creating universal apps. 48 | 49 | - [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute. 50 | - [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions. 51 | -------------------------------------------------------------------------------- /demo/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "React Native UI DatePicker Demo", 4 | "slug": "react-native-ui-datepicker-demo", 5 | "description": "Customizable React Native DateTime Picker component for Android, iOS, and Web. It includes date, time, and datetime modes and supports different locales.", 6 | "version": "1.0.0", 7 | "githubUrl": "https://github.com/farhoudshapouran/react-native-ui-datepicker", 8 | "orientation": "portrait", 9 | "icon": "./assets/images/icon.png", 10 | "scheme": "myapp", 11 | "userInterfaceStyle": "automatic", 12 | "newArchEnabled": true, 13 | "ios": { 14 | "userInterfaceStyle": "automatic", 15 | "supportsTablet": true 16 | }, 17 | "android": { 18 | "userInterfaceStyle": "automatic", 19 | "adaptiveIcon": { 20 | "foregroundImage": "./assets/images/adaptive-icon.png", 21 | "backgroundColor": "#ffffff" 22 | } 23 | }, 24 | "web": { 25 | "bundler": "metro", 26 | "output": "static", 27 | "favicon": "./assets/images/favicon.png" 28 | }, 29 | "plugins": [ 30 | "expo-router", 31 | [ 32 | "expo-splash-screen", 33 | { 34 | "image": "./assets/images/splash-icon.png", 35 | "imageWidth": 200, 36 | "resizeMode": "contain", 37 | "backgroundColor": "#ffffff" 38 | } 39 | ], 40 | "expo-font" 41 | ], 42 | "experiments": { 43 | "typedRoutes": true, 44 | "baseUrl": "/react-native-ui-datepicker" 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /demo/app/+html.tsx: -------------------------------------------------------------------------------- 1 | import { ScrollViewStyleReset } from 'expo-router/html'; 2 | import type { PropsWithChildren } from 'react'; 3 | 4 | // This file is web-only and used to configure the root HTML for every 5 | // web page during static rendering. 6 | // The contents of this function only run in Node.js environments and 7 | // do not have access to the DOM or browser APIs. 8 | export default function Root({ children }: PropsWithChildren) { 9 | return ( 10 | 11 | 12 | 13 | 14 | 18 | 19 | {/* 20 | Disable body scrolling on web. This makes ScrollView components work closer to how they do on native. 21 | However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line. 22 | */} 23 | 24 | 25 | {/* Add any additional elements that you want globally available on web... */} 26 | 27 | {children} 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /demo/app/+not-found.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link, Stack } from 'expo-router'; 3 | import { StyleSheet, View, Text } from 'react-native'; 4 | 5 | export default function NotFoundScreen() { 6 | return ( 7 | <> 8 | 9 | 10 | This screen doesn't exist. 11 | 12 | Go to home screen! 13 | 14 | 15 | 16 | ); 17 | } 18 | 19 | const styles = StyleSheet.create({ 20 | container: { 21 | flex: 1, 22 | alignItems: 'center', 23 | justifyContent: 'center', 24 | padding: 20, 25 | }, 26 | link: { 27 | marginTop: 15, 28 | paddingVertical: 15, 29 | }, 30 | }); 31 | -------------------------------------------------------------------------------- /demo/app/_layout.tsx: -------------------------------------------------------------------------------- 1 | import '@/global.css'; 2 | 3 | import React, { useEffect } from 'react'; 4 | import { View, SafeAreaView, ScrollView, Platform } from 'react-native'; 5 | import { ThemeProvider } from '@react-navigation/native'; 6 | import { Slot } from 'expo-router'; 7 | import * as SplashScreen from 'expo-splash-screen'; 8 | import { StatusBar } from 'expo-status-bar'; 9 | import { GestureHandlerRootView } from 'react-native-gesture-handler'; 10 | import { useColorScheme } from '@/lib/useColorScheme'; 11 | import { LIGHT_THEME, DARK_THEME } from '@/lib/constants'; 12 | import { Header } from '@/layouts/header'; 13 | import 'react-native-gesture-handler'; 14 | import 'react-native-reanimated'; 15 | import { 16 | useFonts, 17 | Inter_300Light, 18 | Inter_400Regular, 19 | Inter_500Medium, 20 | Inter_600SemiBold, 21 | Inter_700Bold, 22 | } from '@expo-google-fonts/inter'; 23 | import { 24 | Archivo_300Light, 25 | Archivo_400Regular, 26 | Archivo_500Medium, 27 | Archivo_600SemiBold, 28 | Archivo_700Bold, 29 | } from '@expo-google-fonts/archivo'; 30 | import { 31 | Poppins_300Light, 32 | Poppins_400Regular, 33 | Poppins_500Medium, 34 | Poppins_600SemiBold, 35 | Poppins_700Bold, 36 | } from '@expo-google-fonts/poppins'; 37 | 38 | SplashScreen.preventAutoHideAsync(); 39 | 40 | export default function RootLayout() { 41 | const { isDarkColorScheme } = useColorScheme(); 42 | 43 | const [fontsLoaded] = useFonts({ 44 | Inter_300Light, 45 | Inter_400Regular, 46 | Inter_500Medium, 47 | Inter_600SemiBold, 48 | Inter_700Bold, 49 | Archivo_300Light, 50 | Archivo_400Regular, 51 | Archivo_500Medium, 52 | Archivo_600SemiBold, 53 | Archivo_700Bold, 54 | Poppins_300Light, 55 | Poppins_400Regular, 56 | Poppins_500Medium, 57 | Poppins_600SemiBold, 58 | Poppins_700Bold, 59 | }); 60 | 61 | useEffect(() => { 62 | if (fontsLoaded) { 63 | SplashScreen.hideAsync(); 64 | } 65 | }, [fontsLoaded]); 66 | 67 | if (!fontsLoaded) { 68 | return null; 69 | } 70 | 71 | return ( 72 | 73 | 74 | 75 | 76 |
77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | ); 86 | } 87 | -------------------------------------------------------------------------------- /demo/app/examples.tsx: -------------------------------------------------------------------------------- 1 | import { View } from 'react-native'; 2 | import { Text } from '@/components/ui/text'; 3 | import { 4 | CustomDatePicker1, 5 | CustomDatePicker2, 6 | CustomDatePicker3, 7 | } from '@/components/examples'; 8 | import { Separator } from '@/components/ui/separator'; 9 | 10 | export default function ExamplesPage() { 11 | return ( 12 | 13 | 14 | 15 | Examples 16 | 17 | Inspired by the stunning designs found on Dribbble. 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /demo/app/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { View } from 'react-native'; 3 | import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; 4 | import { Text } from '@/components/ui/text'; 5 | import { 6 | MultipleDatePicker, 7 | RangeDatePicker, 8 | SingleDatePicker, 9 | } from '@/components/examples'; 10 | import { PackageManager } from '@/components/package-manager'; 11 | import { FeatureCard } from '@/components/feature-card'; 12 | import { GithubLink } from '@/components/github-link'; 13 | 14 | export default function MainPage() { 15 | const [exampleTab, setExampleTab] = useState('single'); 16 | const [packageTab, setPackageTab] = useState('npm'); 17 | 18 | return ( 19 | 20 | 21 | 22 | 23 | React Native UI DatePicker 24 | 25 | 26 | Customizable date picker for React Native 27 | 28 | 29 | 30 | 31 | 36 | 37 | 38 | Single 39 | 40 | 41 | Range 42 | 43 | 44 | Multiple 45 | 46 | 47 | 51 | 52 | 53 | 54 | 55 | 56 | 60 | 61 | 62 | 63 | 64 | 65 | Installation 66 | 71 | 72 | 73 | npm 74 | 75 | 76 | yarn 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | Features 89 | 90 | 91 | 95 | 99 | 100 | 101 | 105 | 109 | 110 | 111 | 115 | 119 | 120 | 121 | 125 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | ); 137 | } 138 | -------------------------------------------------------------------------------- /demo/app/playground.tsx: -------------------------------------------------------------------------------- 1 | import { View } from 'react-native'; 2 | import { Text } from '@/components/ui/text'; 3 | 4 | export default function PlaygroundPage() { 5 | return ( 6 | 7 | 8 | This section will be completed soon... 9 | 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /demo/assets/fonts/SpaceMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farhoudshapouran/react-native-ui-datepicker/9b317eba399144944ac5235e2d151036456c5786/demo/assets/fonts/SpaceMono-Regular.ttf -------------------------------------------------------------------------------- /demo/assets/images/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farhoudshapouran/react-native-ui-datepicker/9b317eba399144944ac5235e2d151036456c5786/demo/assets/images/adaptive-icon.png -------------------------------------------------------------------------------- /demo/assets/images/calendar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farhoudshapouran/react-native-ui-datepicker/9b317eba399144944ac5235e2d151036456c5786/demo/assets/images/calendar.png -------------------------------------------------------------------------------- /demo/assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farhoudshapouran/react-native-ui-datepicker/9b317eba399144944ac5235e2d151036456c5786/demo/assets/images/favicon.png -------------------------------------------------------------------------------- /demo/assets/images/github-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farhoudshapouran/react-native-ui-datepicker/9b317eba399144944ac5235e2d151036456c5786/demo/assets/images/github-logo.png -------------------------------------------------------------------------------- /demo/assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farhoudshapouran/react-native-ui-datepicker/9b317eba399144944ac5235e2d151036456c5786/demo/assets/images/icon.png -------------------------------------------------------------------------------- /demo/assets/images/partial-react-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farhoudshapouran/react-native-ui-datepicker/9b317eba399144944ac5235e2d151036456c5786/demo/assets/images/partial-react-logo.png -------------------------------------------------------------------------------- /demo/assets/images/react-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farhoudshapouran/react-native-ui-datepicker/9b317eba399144944ac5235e2d151036456c5786/demo/assets/images/react-logo.png -------------------------------------------------------------------------------- /demo/assets/images/react-logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farhoudshapouran/react-native-ui-datepicker/9b317eba399144944ac5235e2d151036456c5786/demo/assets/images/react-logo@2x.png -------------------------------------------------------------------------------- /demo/assets/images/react-logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farhoudshapouran/react-native-ui-datepicker/9b317eba399144944ac5235e2d151036456c5786/demo/assets/images/react-logo@3x.png -------------------------------------------------------------------------------- /demo/assets/images/splash-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/farhoudshapouran/react-native-ui-datepicker/9b317eba399144944ac5235e2d151036456c5786/demo/assets/images/splash-icon.png -------------------------------------------------------------------------------- /demo/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | return { 4 | presets: [ 5 | ['babel-preset-expo', { jsxImportSource: 'nativewind' }], 6 | 'nativewind/babel', 7 | ], 8 | plugins: ['react-native-reanimated/plugin'], 9 | }; 10 | }; 11 | -------------------------------------------------------------------------------- /demo/components/date-input.tsx: -------------------------------------------------------------------------------- 1 | import { View } from 'react-native'; 2 | import Feather from '@expo/vector-icons/Feather'; 3 | import { cssInterop } from 'nativewind'; 4 | import { Text } from './ui/text'; 5 | import { cn } from '@/lib/utils'; 6 | 7 | cssInterop(Feather, { 8 | className: { 9 | target: 'style', 10 | }, 11 | }); 12 | 13 | type Props = { 14 | value: string | null; 15 | placeholder: string; 16 | }; 17 | 18 | export function DateInput({ value, placeholder }: Props) { 19 | return ( 20 | 21 | 22 | 29 | {value || placeholder} 30 | 31 | 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /demo/components/examples/custom-datepicker-1.tsx: -------------------------------------------------------------------------------- 1 | import { useMemo, useState } from 'react'; 2 | import { View } from 'react-native'; 3 | import { Text } from '../ui/text'; 4 | import { Calendar } from '../ui/calendar'; 5 | import { 6 | DateType, 7 | CalendarDay, 8 | CalendarComponents, 9 | } from 'react-native-ui-datepicker'; 10 | import { cn } from '@/lib/utils'; 11 | import dayjs from 'dayjs'; 12 | import Feather from '@expo/vector-icons/Feather'; 13 | import { cssInterop } from 'nativewind'; 14 | import { 15 | currentMonthDates, 16 | nextMonthDates, 17 | previousMonthDates, 18 | } from '@/lib/generate-dates'; 19 | import { Link } from 'expo-router'; 20 | 21 | cssInterop(Feather, { 22 | className: { 23 | target: 'style', 24 | }, 25 | }); 26 | 27 | const components: CalendarComponents = { 28 | IconPrev: ( 29 | 30 | ), 31 | IconNext: ( 32 | 33 | ), 34 | Day: (day: CalendarDay) => , 35 | }; 36 | 37 | export default function CustomDatePicker1() { 38 | const [dates, setDates] = useState(); 39 | 40 | return ( 41 | 42 | setDates(dates)} 46 | containerHeight={320} 47 | className="bg-card border-muted mb-4 w-[450px] rounded-3xl px-4 pb-4 shadow-xl dark:border-slate-900 dark:bg-slate-950" 48 | firstDayOfWeek={1} 49 | weekdaysFormat="short" 50 | weekdaysHeight={36} 51 | multiRangeMode 52 | classNames={{ 53 | day_cell: 'p-[1px]', 54 | header: 'px-2 mb-2', 55 | weekdays: 'pb-2', 56 | month_selector_label: 'font-archivoSemiBold text-foreground text-lg', 57 | year_selector_label: 'font-archivoSemiBold text-foreground text-lg', 58 | weekday_label: 'font-archivoLight text-foreground', 59 | range_fill: 'my-[1px] bg-slate-200 dark:bg-slate-700/60', 60 | range_fill_weekstart: 'rounded-s-2xl', 61 | range_fill_weekend: 'rounded-e-2xl', 62 | outside_label: 'opacity-70', 63 | month: 64 | 'rounded-2xl web:hover:bg-muted web:dark:hover:bg-slate-700/60', 65 | month_label: 'text-foreground', 66 | selected_month: 'bg-slate-500 web:hover:bg-slate-500', 67 | selected_month_label: 'text-slate-100', 68 | year: 'rounded-2xl web:hover:bg-muted web:dark:hover:bg-slate-700/60', 69 | year_label: 'text-foreground', 70 | active_year: 'bg-slate-700/60', 71 | selected_year: 'bg-slate-500 web:hover:bg-slate-500', 72 | selected_year_label: 'text-slate-100', 73 | }} 74 | components={components} 75 | /> 76 | 77 | 81 | 82 | 83 | 84 | Designed by Roman Kamushken 85 | 86 | 87 | 88 | 89 | 90 | ); 91 | } 92 | 93 | const markedDates = [ 94 | ...previousMonthDates(6), 95 | ...currentMonthDates(6), 96 | ...nextMonthDates(6), 97 | ]; 98 | 99 | type DayProps = { 100 | day: CalendarDay; 101 | }; 102 | 103 | const Day = ({ day }: DayProps) => { 104 | const { isSelected, isToday, isCurrentMonth } = day; 105 | const formatedDate = dayjs(day.date).format('YYYY-MM-DD'); 106 | const isMarked = useMemo( 107 | () => markedDates.includes(formatedDate), 108 | [formatedDate] 109 | ); 110 | const isGreen = day.number % 2 === 0; 111 | 112 | return ( 113 | 119 | 126 | {day.text} 127 | 128 | {isToday ? ( 129 | 130 | ) : isMarked ? ( 131 | 137 | {isGreen ? '1141' : '1610'} 138 | 139 | ) : null} 140 | 141 | ); 142 | }; 143 | -------------------------------------------------------------------------------- /demo/components/examples/custom-datepicker-2.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { View } from 'react-native'; 3 | import { Text } from '../ui/text'; 4 | import { Calendar } from '../ui/calendar'; 5 | import { 6 | DateType, 7 | CalendarWeek, 8 | CalendarComponents, 9 | } from 'react-native-ui-datepicker'; 10 | import dayjs from 'dayjs'; 11 | import Feather from '@expo/vector-icons/Feather'; 12 | import { cssInterop } from 'nativewind'; 13 | import { Link } from 'expo-router'; 14 | import { Button } from '../ui/button'; 15 | 16 | cssInterop(Feather, { 17 | className: { 18 | target: 'style', 19 | }, 20 | }); 21 | 22 | const components: CalendarComponents = { 23 | IconPrev: ( 24 | 25 | ), 26 | IconNext: ( 27 | 28 | ), 29 | Weekday: (weekday: CalendarWeek) => , 30 | }; 31 | 32 | export default function CustomDatePicker2() { 33 | const [date, setDate] = useState(); 34 | 35 | return ( 36 | 37 | 38 | setDate(date)} 42 | containerHeight={305} 43 | className="w-full border-none bg-transparent px-0 shadow-none" 44 | firstDayOfWeek={1} 45 | weekdaysHeight={36} 46 | showOutsideDays={false} 47 | navigationPosition="right" 48 | classNames={{ 49 | day_cell: 'p-1.5', 50 | day: 'rounded-full', 51 | day_label: 'font-poppinsMedium text-foreground', 52 | today: 'bg-blue-500/10', 53 | today_label: 'text-blue-500', 54 | selected: 'bg-blue-500 shadow-lg shadow-blue-500', 55 | selected_label: 'text-white', 56 | header: 'px-2.5 -me-3', 57 | weekdays: 'pb-2', 58 | month_selector_label: 'font-poppinsMedium text-foreground text-xl', 59 | year_selector_label: 'font-poppinsMedium text-foreground text-xl', 60 | range_fill: 'my-[1px] bg-slate-700/60', 61 | range_fill_weekstart: 'rounded-s-2xl', 62 | range_fill_weekend: 'rounded-e-2xl', 63 | outside_label: 'opacity-70', 64 | month: 'rounded-full web:hover:bg-slate-400/60', 65 | month_label: 'font-poppinsMedium text-foreground', 66 | selected_month: 'bg-blue-500 web:hover:bg-blue-500/80', 67 | selected_month_label: 'text-white', 68 | year: 'rounded-full web:hover:bg-slate-400/60', 69 | year_label: 'font-poppinsMedium text-foreground', 70 | selected_year: 'bg-blue-500 web:hover:bg-blue-500/80', 71 | selected_year_label: 'text-white', 72 | }} 73 | components={components} 74 | /> 75 | 76 | 82 | 88 | 89 | 90 | 91 | 95 | 96 | 97 | 98 | Designed by Michał Masiak 99 | 100 | 101 | 102 | 103 | 104 | ); 105 | } 106 | 107 | type WeekdayProps = { 108 | weekday: CalendarWeek; 109 | }; 110 | 111 | const Weekday = ({ weekday }: WeekdayProps) => { 112 | return ( 113 | {weekday.name.min[0]} 114 | ); 115 | }; 116 | -------------------------------------------------------------------------------- /demo/components/examples/custom-datepicker-3.tsx: -------------------------------------------------------------------------------- 1 | import { useMemo, useState } from 'react'; 2 | import { View } from 'react-native'; 3 | import { Text } from '../ui/text'; 4 | import { Calendar } from '../ui/calendar'; 5 | import { 6 | DateType, 7 | CalendarDay, 8 | CalendarWeek, 9 | CalendarComponents, 10 | } from 'react-native-ui-datepicker'; 11 | import { cn } from '@/lib/utils'; 12 | import Feather from '@expo/vector-icons/Feather'; 13 | import Entypo from '@expo/vector-icons/Entypo'; 14 | import { cssInterop } from 'nativewind'; 15 | import { Link } from 'expo-router'; 16 | 17 | cssInterop(Feather, { 18 | className: { 19 | target: 'style', 20 | }, 21 | }); 22 | 23 | cssInterop(Entypo, { 24 | className: { 25 | target: 'style', 26 | }, 27 | }); 28 | 29 | const components: CalendarComponents = { 30 | IconPrev: ( 31 | 32 | ), 33 | IconNext: ( 34 | 35 | ), 36 | Weekday: (weekday: CalendarWeek) => , 37 | Day: (day: CalendarDay) => , 38 | }; 39 | 40 | export default function CustomDatePicker3() { 41 | const [date, setDate] = useState(); 42 | 43 | return ( 44 | 45 | 46 | 47 | 48 | Calendar 49 | 50 | 55 | 56 | 57 | setDate(date)} 61 | containerHeight={400} 62 | weekdaysHeight={44} 63 | className="w-full border-none bg-transparent px-0 pb-2 pt-0 shadow-none" 64 | firstDayOfWeek={1} 65 | monthCaptionFormat="short" 66 | multiRangeMode 67 | classNames={{ 68 | day_cell: 'p-1', 69 | header: 'px-[65px] mb-2', 70 | weekdays: 'py-2 mx-1 bg-blue-500/10 rounded dark:bg-slate-800', 71 | month_selector_label: 72 | 'font-archivoMedium text-muted-foreground text-lg', 73 | year_selector_label: 74 | 'font-archivoMedium text-muted-foreground text-lg', 75 | month: 'rounded web:hover:bg-muted web:dark:hover:bg-slate-700/60', 76 | month_label: 'font-archivo text-lg text-foreground', 77 | selected_month: 'bg-slate-500 web:hover:bg-slate-500', 78 | selected_month_label: 'text-slate-100', 79 | year: 'rounded web:hover:bg-muted web:dark:hover:bg-slate-700/60', 80 | year_label: 'font-archivo text-lg text-foreground', 81 | active_year: 'bg-slate-700/60', 82 | selected_year: 'bg-slate-500 web:hover:bg-slate-500', 83 | selected_year_label: 'text-slate-100', 84 | }} 85 | components={components} 86 | /> 87 | 88 | 89 | 93 | 94 | 95 | 96 | Designed by Roman Kamushken 97 | 98 | 99 | 100 | 101 | 102 | ); 103 | } 104 | 105 | type WeekdayProps = { 106 | weekday: CalendarWeek; 107 | }; 108 | 109 | const Weekday = ({ weekday }: WeekdayProps) => { 110 | return ( 111 | {weekday.name.min[0]} 112 | ); 113 | }; 114 | 115 | type DayProps = { 116 | day: CalendarDay; 117 | }; 118 | 119 | const Day = ({ day }: DayProps) => { 120 | const { isSelected, isToday, isCurrentMonth } = day; 121 | const length = 122 | day.number % 3 === 0 123 | ? 1 124 | : day.number % 4 === 2 125 | ? 2 126 | : day.number % 5 === 0 127 | ? 3 128 | : 0; 129 | 130 | const dots = useMemo(() => , [length]); 131 | 132 | return ( 133 | 140 | 147 | {day.text} 148 | 149 | {dots} 150 | 151 | ); 152 | }; 153 | 154 | const colors = ['bg-green-500', 'bg-red-500', 'bg-yellow-500']; 155 | 156 | const Dots = ({ length }: { length: number }) => { 157 | const shuffledColors = shuffleArray(colors); 158 | 159 | return ( 160 | 161 | {Array.from({ length }, (_, index) => ( 162 | 166 | ))} 167 | 168 | ); 169 | }; 170 | 171 | function shuffleArray(array: string[]) { 172 | for (let i = array.length - 1; i > 0; i--) { 173 | const j = Math.floor(Math.random() * (i + 1)); 174 | [array[i], array[j]] = [array[j], array[i]]; 175 | } 176 | return array; 177 | } 178 | -------------------------------------------------------------------------------- /demo/components/examples/index.ts: -------------------------------------------------------------------------------- 1 | export { default as SingleDatePicker } from './single-datepicker'; 2 | export { default as RangeDatePicker } from './range-datepicker'; 3 | export { default as MultipleDatePicker } from './multiple-datepicker'; 4 | export { default as CustomDatePicker1 } from './custom-datepicker-1'; 5 | export { default as CustomDatePicker2 } from './custom-datepicker-2'; 6 | export { default as CustomDatePicker3 } from './custom-datepicker-3'; 7 | -------------------------------------------------------------------------------- /demo/components/examples/multiple-datepicker.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { View } from 'react-native'; 3 | import { Calendar, DateType } from '../ui/calendar'; 4 | import { DateInput } from '../date-input'; 5 | import dayjs from 'dayjs'; 6 | 7 | export default function MultipleDatePicker() { 8 | const [dates, setDates] = useState(); 9 | 10 | const selectedDates = dates 11 | ?.map((date) => dayjs(date).format('MMM DD, YYYY')) 12 | .join(' - '); 13 | 14 | return ( 15 | 16 | setDates(dates)} 20 | /> 21 | 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /demo/components/examples/range-datepicker.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { View } from 'react-native'; 3 | import { Calendar, DateType } from '../ui/calendar'; 4 | import dayjs from 'dayjs'; 5 | import { DateInput } from '../date-input'; 6 | 7 | export default function RangeDatePicker() { 8 | const [range, setRange] = useState<{ 9 | startDate: DateType; 10 | endDate: DateType; 11 | }>({ startDate: undefined, endDate: undefined }); 12 | 13 | const from = range.startDate 14 | ? dayjs(range.startDate).format('MMM DD, YYYY') 15 | : ''; 16 | const to = range.endDate ? dayjs(range.endDate).format('MMM DD, YYYY') : ''; 17 | 18 | return ( 19 | 20 | setRange(params)} 25 | /> 26 | 30 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /demo/components/examples/single-datepicker.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { View } from 'react-native'; 3 | import { Calendar, DateType } from '../ui/calendar'; 4 | import dayjs from 'dayjs'; 5 | import { DateInput } from '../date-input'; 6 | 7 | export default function SingleDatePicker() { 8 | const [date, setDate] = useState(); 9 | 10 | return ( 11 | 12 | setDate(date)} 16 | timePicker 17 | //use12Hours 18 | //minDate={new Date()} 19 | //maxDate={new Date(new Date().getFullYear(), 11, 31)} // end of the year 20 | /> 21 | 25 | 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /demo/components/feature-card.tsx: -------------------------------------------------------------------------------- 1 | import { View } from 'react-native'; 2 | import { Text } from './ui/text'; 3 | 4 | type Props = { 5 | title: string; 6 | description: string; 7 | }; 8 | 9 | export function FeatureCard({ title, description }: Props) { 10 | return ( 11 | 12 | {title} 13 | {description} 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /demo/components/github-link.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View } from 'react-native'; 3 | import AntDesign from '@expo/vector-icons/AntDesign'; 4 | import { cssInterop } from 'nativewind'; 5 | import { Text } from './ui/text'; 6 | import { Link } from 'expo-router'; 7 | 8 | cssInterop(AntDesign, { 9 | className: { 10 | target: 'style', 11 | }, 12 | }); 13 | 14 | export function GithubLink() { 15 | return ( 16 | 17 | 21 | 22 | 23 | Check repository on GitHub 24 | 25 | 26 | 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /demo/components/locale-selector.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import { StyleSheet, View, Text, Pressable } from 'react-native'; 3 | 4 | const Locales = ['en', 'de', 'es', 'fr', 'tr', 'fa']; 5 | 6 | type Props = { 7 | locale: string; 8 | setLocale: (locale: string) => void; 9 | mainColor?: string; 10 | activeTextColor?: string; 11 | }; 12 | 13 | export const LocaleSelector = ({ 14 | locale, 15 | setLocale, 16 | mainColor, 17 | activeTextColor, 18 | }: Props) => { 19 | return ( 20 | 21 | 27 | Locale: 28 | 29 | {Locales.map((item, index) => ( 30 | setLocale(item)} 39 | accessibilityRole="button" 40 | accessibilityLabel={item.toUpperCase()} 41 | > 42 | 52 | {item.toUpperCase()} 53 | 54 | 55 | ))} 56 | 57 | ); 58 | }; 59 | 60 | const styles = StyleSheet.create({ 61 | localeContainer: { 62 | flexDirection: 'row', 63 | alignItems: 'center', 64 | marginBottom: 20, 65 | }, 66 | localeButton: { 67 | alignItems: 'center', 68 | justifyContent: 'center', 69 | width: 36, 70 | height: 36, 71 | borderRadius: 36, 72 | margin: 2, 73 | }, 74 | localeButtonText: { 75 | fontSize: 15, 76 | }, 77 | }); 78 | -------------------------------------------------------------------------------- /demo/components/package-manager.tsx: -------------------------------------------------------------------------------- 1 | import { View } from 'react-native'; 2 | import { Text } from './ui/text'; 3 | import { Button } from './ui/button'; 4 | import Feather from '@expo/vector-icons/Feather'; 5 | import { cssInterop } from 'nativewind'; 6 | import * as Clipboard from 'expo-clipboard'; 7 | 8 | cssInterop(Feather, { 9 | className: { 10 | target: 'style', 11 | }, 12 | }); 13 | 14 | type Props = { 15 | command: string; 16 | }; 17 | 18 | export function PackageManager({ command }: Props) { 19 | const copyToClipboard = async () => { 20 | await Clipboard.setStringAsync(command); 21 | }; 22 | 23 | return ( 24 | 25 | {command} 26 | 34 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /demo/components/theme-selector.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import { StyleSheet, View, Pressable } from 'react-native'; 3 | 4 | export interface ITheme { 5 | mainColor: string; 6 | activeTextColor: string; 7 | } 8 | 9 | type Props = { 10 | themes: ITheme[]; 11 | setTheme: (theme: ITheme) => void; 12 | }; 13 | 14 | export const ThemeSelector = ({ themes = [], setTheme }: Props) => { 15 | return ( 16 | 17 | {themes.map((item, index) => ( 18 | setTheme(item)} 28 | accessibilityRole="button" 29 | accessibilityLabel="Set Active Theme" 30 | /> 31 | ))} 32 | 33 | ); 34 | }; 35 | 36 | const styles = StyleSheet.create({ 37 | themeContainer: { 38 | flexDirection: 'row', 39 | alignItems: 'center', 40 | justifyContent: 'space-around', 41 | marginBottom: 10, 42 | width: 330, 43 | }, 44 | themeButton: { 45 | borderWidth: 4, 46 | width: 32, 47 | height: 32, 48 | borderRadius: 32, 49 | margin: 5, 50 | shadowRadius: 20, 51 | shadowColor: '#000', 52 | shadowOpacity: 0.1, 53 | shadowOffset: { width: 0, height: 0 }, 54 | }, 55 | }); 56 | -------------------------------------------------------------------------------- /demo/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import { cva, type VariantProps } from 'class-variance-authority'; 2 | import * as React from 'react'; 3 | import { Pressable } from 'react-native'; 4 | import { cn } from '@/lib/utils'; 5 | import { TextClassContext } from './text'; 6 | 7 | const buttonVariants = cva( 8 | 'group flex items-center justify-center rounded-md web:ring-offset-background web:transition-colors web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2', 9 | { 10 | variants: { 11 | variant: { 12 | default: 'bg-primary web:hover:opacity-90 active:opacity-90', 13 | destructive: 'bg-destructive web:hover:opacity-90 active:opacity-90', 14 | outline: 15 | 'border border-input bg-background web:hover:bg-accent web:hover:text-accent-foreground active:bg-accent', 16 | secondary: 'bg-secondary web:hover:opacity-80 active:opacity-80', 17 | ghost: 18 | 'web:hover:bg-accent web:hover:text-accent-foreground active:bg-accent', 19 | link: 'web:underline-offset-4 web:hover:underline web:focus:underline', 20 | }, 21 | size: { 22 | default: 'h-10 px-4 py-2 native:h-12 native:px-5 native:py-3', 23 | sm: 'h-9 rounded-md px-3', 24 | lg: 'h-11 rounded-md px-8 native:h-14', 25 | icon: 'h-10 w-10', 26 | }, 27 | }, 28 | defaultVariants: { 29 | variant: 'default', 30 | size: 'default', 31 | }, 32 | } 33 | ); 34 | 35 | const buttonTextVariants = cva( 36 | 'web:whitespace-nowrap text-sm native:text-base font-medium text-foreground web:transition-colors', 37 | { 38 | variants: { 39 | variant: { 40 | default: 'text-primary-foreground', 41 | destructive: 'text-destructive-foreground', 42 | outline: 'group-active:text-accent-foreground', 43 | secondary: 44 | 'text-secondary-foreground group-active:text-secondary-foreground', 45 | ghost: 'group-active:text-accent-foreground', 46 | link: 'text-primary group-active:underline', 47 | }, 48 | size: { 49 | default: '', 50 | sm: '', 51 | lg: 'native:text-lg', 52 | icon: '', 53 | }, 54 | }, 55 | defaultVariants: { 56 | variant: 'default', 57 | size: 'default', 58 | }, 59 | } 60 | ); 61 | 62 | type ButtonProps = React.ComponentPropsWithoutRef & 63 | VariantProps; 64 | 65 | const Button = React.forwardRef< 66 | React.ElementRef, 67 | ButtonProps 68 | >(({ className, variant, size, ...props }, ref) => { 69 | return ( 70 | 77 | 86 | 87 | ); 88 | }); 89 | Button.displayName = 'Button'; 90 | 91 | export { Button, buttonTextVariants, buttonVariants }; 92 | export type { ButtonProps }; 93 | -------------------------------------------------------------------------------- /demo/components/ui/calendar.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from '@/lib/utils'; 2 | import DateTimePicker, { 3 | DateType, 4 | CalendarDay, 5 | CalendarComponents, 6 | useDefaultClassNames, 7 | } from 'react-native-ui-datepicker'; 8 | import Feather from '@expo/vector-icons/Feather'; 9 | import { cssInterop } from 'nativewind'; 10 | 11 | cssInterop(Feather, { 12 | className: { 13 | target: 'style', 14 | }, 15 | }); 16 | 17 | const icons: CalendarComponents = { 18 | IconPrev: ( 19 | 20 | ), 21 | IconNext: ( 22 | 23 | ), 24 | }; 25 | 26 | export type CalendarProps = React.ComponentProps; 27 | 28 | function Calendar({ 29 | className, 30 | classNames, 31 | showOutsideDays = true, 32 | containerHeight = 280, 33 | components, 34 | ...props 35 | }: React.ComponentProps) { 36 | const defaultClassNames = useDefaultClassNames(); 37 | 38 | return ( 39 | 86 | ); 87 | } 88 | 89 | export { Calendar, DateType, CalendarDay }; 90 | -------------------------------------------------------------------------------- /demo/components/ui/separator.tsx: -------------------------------------------------------------------------------- 1 | import * as SeparatorPrimitive from '@rn-primitives/separator'; 2 | import * as React from 'react'; 3 | import { cn } from '@/lib/utils'; 4 | 5 | const Separator = React.forwardRef< 6 | SeparatorPrimitive.RootRef, 7 | SeparatorPrimitive.RootProps 8 | >( 9 | ( 10 | { className, orientation = 'horizontal', decorative = true, ...props }, 11 | ref 12 | ) => ( 13 | 24 | ) 25 | ); 26 | Separator.displayName = SeparatorPrimitive.Root.displayName; 27 | 28 | export { Separator }; 29 | -------------------------------------------------------------------------------- /demo/components/ui/tabs.tsx: -------------------------------------------------------------------------------- 1 | import * as TabsPrimitive from '@rn-primitives/tabs'; 2 | import * as React from 'react'; 3 | import { cn } from '@/lib/utils'; 4 | import { TextClassContext } from './text'; 5 | 6 | const Tabs = TabsPrimitive.Root; 7 | 8 | const TabsList = React.forwardRef< 9 | TabsPrimitive.ListRef, 10 | TabsPrimitive.ListProps 11 | >(({ className, ...props }, ref) => ( 12 | 20 | )); 21 | TabsList.displayName = TabsPrimitive.List.displayName; 22 | 23 | const TabsTrigger = React.forwardRef< 24 | TabsPrimitive.TriggerRef, 25 | TabsPrimitive.TriggerProps 26 | >(({ className, ...props }, ref) => { 27 | const { value } = TabsPrimitive.useRootContext(); 28 | return ( 29 | 35 | 46 | 47 | ); 48 | }); 49 | TabsTrigger.displayName = TabsPrimitive.Trigger.displayName; 50 | 51 | const TabsContent = React.forwardRef< 52 | TabsPrimitive.ContentRef, 53 | TabsPrimitive.ContentProps 54 | >(({ className, ...props }, ref) => ( 55 | 63 | )); 64 | TabsContent.displayName = TabsPrimitive.Content.displayName; 65 | 66 | export { Tabs, TabsContent, TabsList, TabsTrigger }; 67 | -------------------------------------------------------------------------------- /demo/components/ui/text.tsx: -------------------------------------------------------------------------------- 1 | import * as Slot from '@rn-primitives/slot'; 2 | import type { SlottableTextProps, TextRef } from '@rn-primitives/types'; 3 | import * as React from 'react'; 4 | import { Text as RNText } from 'react-native'; 5 | import { cn } from '@/lib/utils'; 6 | 7 | const TextClassContext = React.createContext(undefined); 8 | 9 | const Text = React.forwardRef( 10 | ({ className, asChild = false, ...props }, ref) => { 11 | const textClass = React.useContext(TextClassContext); 12 | const Component = asChild ? Slot.Text : RNText; 13 | return ( 14 | 23 | ); 24 | } 25 | ); 26 | Text.displayName = 'Text'; 27 | 28 | export { Text, TextClassContext }; 29 | -------------------------------------------------------------------------------- /demo/constants/Colors.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Below are the colors that are used in the app. The colors are defined in the light and dark mode. 3 | * There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc. 4 | */ 5 | 6 | const tintColorLight = '#0a7ea4'; 7 | const tintColorDark = '#fff'; 8 | 9 | export const Colors = { 10 | light: { 11 | text: '#11181C', 12 | background: '#fff', 13 | tint: tintColorLight, 14 | icon: '#687076', 15 | tabIconDefault: '#687076', 16 | tabIconSelected: tintColorLight, 17 | }, 18 | dark: { 19 | text: '#ECEDEE', 20 | background: '#151718', 21 | tint: tintColorDark, 22 | icon: '#9BA1A6', 23 | tabIconDefault: '#9BA1A6', 24 | tabIconSelected: tintColorDark, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /demo/global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --background: 0 0% 100%; 8 | --foreground: 240 10% 3.9%; 9 | --card: 0 0% 100%; 10 | --card-foreground: 240 10% 3.9%; 11 | --popover: 0 0% 100%; 12 | --popover-foreground: 240 10% 3.9%; 13 | --primary: 240 5.9% 10%; 14 | --primary-foreground: 0 0% 98%; 15 | --secondary: 240 4.8% 95.9%; 16 | --secondary-foreground: 240 5.9% 10%; 17 | --muted: 240 4.8% 95.9%; 18 | --muted-foreground: 240 3.8% 46.1%; 19 | --accent: 240 4.8% 95.9%; 20 | --accent-foreground: 240 5.9% 10%; 21 | --destructive: 0 84.2% 60.2%; 22 | --destructive-foreground: 0 0% 98%; 23 | --border: 240 5.9% 90%; 24 | --input: 240 5.9% 90%; 25 | --ring: 240 5.9% 10%; 26 | --radius: 0.5rem; 27 | } 28 | 29 | .dark:root { 30 | --background: 240 10% 3.9%; 31 | --foreground: 0 0% 98%; 32 | --card: 240 10% 3.9%; 33 | --card-foreground: 0 0% 98%; 34 | --popover: 240 10% 3.9%; 35 | --popover-foreground: 0 0% 98%; 36 | --primary: 0 0% 98%; 37 | --primary-foreground: 240 5.9% 10%; 38 | --secondary: 240 3.7% 15.9%; 39 | --secondary-foreground: 0 0% 98%; 40 | --muted: 240 3.7% 15.9%; 41 | --muted-foreground: 240 5% 64.9%; 42 | --accent: 240 3.7% 15.9%; 43 | --accent-foreground: 0 0% 98%; 44 | --destructive: 0 62.8% 30.6%; 45 | --destructive-foreground: 0 0% 98%; 46 | --border: 240 3.7% 15.9%; 47 | --input: 240 3.7% 15.9%; 48 | --ring: 240 4.9% 83.9%; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /demo/hooks/use-active-link.ts: -------------------------------------------------------------------------------- 1 | import { usePathname } from 'expo-router'; 2 | 3 | // ---------------------------------------------------------------------- 4 | 5 | type ReturnType = boolean; 6 | 7 | export function useActiveLink(path: string, deep = false): ReturnType { 8 | const pathname = usePathname(); 9 | 10 | const checkPath = path.startsWith('#'); 11 | 12 | const currentPath = path === '/' ? '/' : `${path}`; 13 | 14 | const normalActive = !checkPath && pathname === currentPath; 15 | 16 | const rootPath = pathname.split('/')[1] || '/'; 17 | const currentPathPath = currentPath.split('/')[1] || '/'; 18 | 19 | const deepActive = !checkPath && currentPathPath === rootPath; 20 | 21 | return deep ? deepActive : normalActive; 22 | } 23 | -------------------------------------------------------------------------------- /demo/hooks/useColorScheme.ts: -------------------------------------------------------------------------------- 1 | export { useColorScheme } from 'react-native'; 2 | -------------------------------------------------------------------------------- /demo/hooks/useColorScheme.web.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { useColorScheme as useRNColorScheme } from 'react-native'; 3 | 4 | /** 5 | * To support static rendering, this value needs to be re-calculated on the client side for web 6 | */ 7 | export function useColorScheme() { 8 | const [hasHydrated, setHasHydrated] = useState(false); 9 | 10 | useEffect(() => { 11 | setHasHydrated(true); 12 | }, []); 13 | 14 | const colorScheme = useRNColorScheme(); 15 | 16 | if (hasHydrated) { 17 | return colorScheme; 18 | } 19 | 20 | return 'light'; 21 | } 22 | -------------------------------------------------------------------------------- /demo/hooks/useThemeColor.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Learn more about light and dark modes: 3 | * https://docs.expo.dev/guides/color-schemes/ 4 | */ 5 | 6 | import { Colors } from '@/constants/Colors'; 7 | import { useColorScheme } from '@/hooks/useColorScheme'; 8 | 9 | export function useThemeColor( 10 | props: { light?: string; dark?: string }, 11 | colorName: keyof typeof Colors.light & keyof typeof Colors.dark 12 | ) { 13 | const theme = useColorScheme() ?? 'light'; 14 | const colorFromProps = props[theme]; 15 | 16 | if (colorFromProps) { 17 | return colorFromProps; 18 | } else { 19 | return Colors[theme][colorName]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /demo/layouts/common/github-stats.tsx: -------------------------------------------------------------------------------- 1 | import { View } from 'react-native'; 2 | import { Link } from 'expo-router'; 3 | import AntDesign from '@expo/vector-icons/AntDesign'; 4 | import Feather from '@expo/vector-icons/Feather'; 5 | import { cssInterop } from 'nativewind'; 6 | import { Text } from '@/components/ui/text'; 7 | import { Button } from '@/components/ui/button'; 8 | import { useEffect, useState } from 'react'; 9 | import { formatNumber } from '@/lib/utils'; 10 | 11 | cssInterop(AntDesign, { 12 | className: { 13 | target: 'style', 14 | }, 15 | }); 16 | 17 | cssInterop(Feather, { 18 | className: { 19 | target: 'style', 20 | }, 21 | }); 22 | 23 | type DataType = { 24 | downloads: number; 25 | start: string; 26 | end: string; 27 | package: string; 28 | }; 29 | 30 | export function GithubStats() { 31 | const [loading, setLoading] = useState(true); 32 | const [data, setData] = useState(); 33 | 34 | const getStats = async () => { 35 | try { 36 | const response = await fetch( 37 | 'https://api.npmjs.org/downloads/point/last-month/react-native-ui-datepicker' 38 | ); 39 | const json = await response.json(); 40 | setData(json); 41 | } catch (error) { 42 | console.error(error); 43 | } finally { 44 | setLoading(false); 45 | } 46 | }; 47 | 48 | useEffect(() => { 49 | getStats(); 50 | }, []); 51 | 52 | return ( 53 | 57 | 69 | 70 | ); 71 | } 72 | -------------------------------------------------------------------------------- /demo/layouts/common/theme-toggle.tsx: -------------------------------------------------------------------------------- 1 | import { cssInterop } from 'nativewind'; 2 | import { Button } from '@/components/ui/button'; 3 | import { useColorScheme } from '@/lib/useColorScheme'; 4 | import Feather from '@expo/vector-icons/Feather'; 5 | 6 | cssInterop(Feather, { 7 | className: { 8 | target: 'style', 9 | }, 10 | }); 11 | 12 | export const ThemeToggle = () => { 13 | const { toggleColorScheme, isDarkColorScheme } = useColorScheme(); 14 | 15 | return ( 16 | 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /demo/layouts/header.tsx: -------------------------------------------------------------------------------- 1 | import { View } from 'react-native'; 2 | import { NavMenu } from './nav/nav-menu'; 3 | import { ThemeToggle } from './common/theme-toggle'; 4 | import { GithubStats } from './common/github-stats'; 5 | 6 | export const Header = () => { 7 | return ( 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /demo/layouts/nav/config-navigation.ts: -------------------------------------------------------------------------------- 1 | import { NavItemProps } from './types'; 2 | 3 | export const navConfig: NavItemProps[] = [ 4 | { title: 'Home', path: '/' }, 5 | { title: 'Examples', path: '/examples' }, 6 | { title: 'Playground', path: '/playground' }, 7 | ]; 8 | -------------------------------------------------------------------------------- /demo/layouts/nav/nav-item.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from 'expo-router'; 2 | import { useActiveLink } from '@/hooks/use-active-link'; 3 | import { NavItemProps } from './types'; 4 | import { Button } from '@/components/ui/button'; 5 | import { Text } from '@/components/ui/text'; 6 | import { cn } from '@/lib/utils'; 7 | 8 | type Props = { 9 | item: NavItemProps; 10 | }; 11 | 12 | export function NavItem({ item }: Props) { 13 | const active = useActiveLink(item.path); 14 | 15 | return ( 16 | 17 | 23 | {item.title} 24 | 25 | 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /demo/layouts/nav/nav-menu.tsx: -------------------------------------------------------------------------------- 1 | import { View } from 'react-native'; 2 | import { navConfig } from './config-navigation'; 3 | import { NavItem } from './nav-item'; 4 | 5 | export function NavMenu() { 6 | return ( 7 | 8 | {navConfig.map((item, index) => ( 9 | 10 | ))} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /demo/layouts/nav/types.ts: -------------------------------------------------------------------------------- 1 | export type NavItemProps = { 2 | title: string; 3 | path: string; 4 | }; 5 | -------------------------------------------------------------------------------- /demo/lib/constants.ts: -------------------------------------------------------------------------------- 1 | import { DarkTheme, DefaultTheme } from '@react-navigation/native'; 2 | 3 | const LIGHT_THEME = { 4 | ...DefaultTheme, 5 | colors: { 6 | ...DefaultTheme.colors, 7 | background: 'hsl(0 0% 100%)', // background 8 | border: 'hsl(240 5.9% 90%)', // border 9 | card: 'hsl(0 0% 100%)', // card 10 | notification: 'hsl(0 84.2% 60.2%)', // destructive 11 | primary: 'hsl(240 5.9% 10%)', // primary 12 | text: 'hsl(240 10% 3.9%)', // foreground 13 | backdrop: 'rgba(91, 91, 91, 0.8)', 14 | }, 15 | }; 16 | 17 | const DARK_THEME = { 18 | ...DarkTheme, 19 | colors: { 20 | ...DarkTheme.colors, 21 | background: 'hsl(240 10% 3.9%)', // background 22 | border: 'hsl(240 3.7% 15.9%)', // border 23 | card: 'hsl(240 10% 3.9%)', // card 24 | notification: 'hsl(0 72% 51%)', // destructive 25 | primary: 'hsl(0 0% 98%)', // primary 26 | text: 'hsl(0 0% 98%)', // foreground 27 | backdrop: 'rgba(91, 91, 91, 0.8)', 28 | }, 29 | }; 30 | 31 | export { LIGHT_THEME, DARK_THEME }; 32 | -------------------------------------------------------------------------------- /demo/lib/generate-dates.ts: -------------------------------------------------------------------------------- 1 | import dayjs from 'dayjs'; 2 | 3 | const currentDate = dayjs(); 4 | const currentYear = currentDate.year(); 5 | const currentMonth = currentDate.month(); 6 | 7 | export function getRandomDateInMonth(year: number, month: number) { 8 | const startOfMonth = dayjs().year(year).month(month).startOf('month'); 9 | const endOfMonth = dayjs().year(year).month(month).endOf('month'); 10 | const randomDays = Math.floor( 11 | Math.random() * endOfMonth.diff(startOfMonth, 'day') 12 | ); 13 | return startOfMonth.add(randomDays, 'day'); 14 | } 15 | 16 | export const previousMonthDates = (length: number) => { 17 | return Array.from({ length }, () => 18 | getRandomDateInMonth(currentYear, currentMonth - 1).format('YYYY-MM-DD') 19 | ); 20 | }; 21 | 22 | export const currentMonthDates = (length: number) => { 23 | return Array.from({ length }, () => 24 | getRandomDateInMonth(currentYear, currentMonth).format('YYYY-MM-DD') 25 | ); 26 | }; 27 | 28 | export const nextMonthDates = (length: number) => { 29 | return Array.from({ length }, () => 30 | getRandomDateInMonth(currentYear, currentMonth + 1).format('YYYY-MM-DD') 31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /demo/lib/useColorScheme.tsx: -------------------------------------------------------------------------------- 1 | import { useColorScheme as useNativewindColorScheme } from 'nativewind'; 2 | 3 | export function useColorScheme() { 4 | const { colorScheme, setColorScheme, toggleColorScheme } = 5 | useNativewindColorScheme(); 6 | 7 | return { 8 | colorScheme: colorScheme ?? 'dark', 9 | isDarkColorScheme: colorScheme === 'dark', 10 | setColorScheme, 11 | toggleColorScheme, 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /demo/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from 'clsx'; 2 | import { twMerge } from 'tailwind-merge'; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | 8 | export function formatNumber(num: number) { 9 | if (num >= 1e9) { 10 | return (num / 1e9).toFixed(1) + 'B'; // Billions 11 | } else if (num >= 1e6) { 12 | return (num / 1e6).toFixed(1) + 'M'; // Millions 13 | } else if (num >= 1e3) { 14 | return (num / 1e3).toFixed(1) + 'K'; // Thousands 15 | } else { 16 | return num.toString(); // Less than 1000 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /demo/metro.config.js: -------------------------------------------------------------------------------- 1 | const { getDefaultConfig } = require('expo/metro-config'); 2 | const { withNativeWind } = require('nativewind/metro'); 3 | const path = require('path'); 4 | 5 | const projectRoot = __dirname; 6 | const libraryRoot = path.resolve(projectRoot, '..'); 7 | 8 | const config = getDefaultConfig(projectRoot); 9 | 10 | if (config.resolver) { 11 | // 1. Watch all files within the Repository 12 | config.watchFolders = [libraryRoot]; 13 | // 2. Let Metro know where to resolve packages, and in what order 14 | config.resolver.nodeModulesPaths = [ 15 | path.resolve(projectRoot, 'node_modules'), 16 | path.resolve(libraryRoot, 'node_modules'), 17 | ]; 18 | // 3. Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths` 19 | config.resolver.disableHierarchicalLookup = true; 20 | } 21 | 22 | module.exports = withNativeWind(config, { input: './global.css' }); 23 | -------------------------------------------------------------------------------- /demo/nativewind-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | // NOTE: This file should not be edited and should be committed with your source code. It is generated by NativeWind.åå 4 | -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-ui-datepicker-demo", 3 | "main": "expo-router/entry", 4 | "version": "1.0.0", 5 | "homepage": "https://farhoudshapouran.github.io/react-native-ui-datepicker", 6 | "scripts": { 7 | "start": "expo start -c", 8 | "reset-project": "node ./scripts/reset-project.js", 9 | "android": "expo start --android", 10 | "ios": "expo start --ios", 11 | "web": "expo start --web", 12 | "test": "jest --watchAll", 13 | "lint": "expo lint", 14 | "predeploy": "expo export -p web", 15 | "deploy": "gh-pages --nojekyll -d dist" 16 | }, 17 | "jest": { 18 | "preset": "jest-expo" 19 | }, 20 | "dependencies": { 21 | "@expo-google-fonts/archivo": "^0.2.3", 22 | "@expo-google-fonts/inter": "^0.2.3", 23 | "@expo-google-fonts/poppins": "^0.2.3", 24 | "@expo/vector-icons": "^14.0.2", 25 | "@react-navigation/bottom-tabs": "^7.2.0", 26 | "@react-navigation/native": "^7.0.14", 27 | "@rn-primitives/separator": "^1.1.0", 28 | "@rn-primitives/slot": "^1.1.0", 29 | "@rn-primitives/tabs": "^1.1.0", 30 | "@rn-primitives/types": "^1.1.0", 31 | "@shopify/react-native-skia": "1.5.0", 32 | "class-variance-authority": "^0.7.1", 33 | "clsx": "^2.1.1", 34 | "dayjs": "^1.11.13", 35 | "expo": "^52.0.35", 36 | "expo-blur": "~14.0.3", 37 | "expo-clipboard": "~7.0.1", 38 | "expo-constants": "~17.0.6", 39 | "expo-font": "~13.0.3", 40 | "expo-haptics": "~14.0.1", 41 | "expo-linking": "~7.0.5", 42 | "expo-router": "~4.0.17", 43 | "expo-splash-screen": "~0.29.22", 44 | "expo-status-bar": "~2.0.1", 45 | "expo-symbols": "~0.2.2", 46 | "expo-system-ui": "~4.0.8", 47 | "expo-web-browser": "~14.0.2", 48 | "nativewind": "^4.1.23", 49 | "react": "18.3.1", 50 | "react-dom": "18.3.1", 51 | "react-native": "0.76.7", 52 | "react-native-bouncy-checkbox": "^4.1.2", 53 | "react-native-gesture-handler": "~2.20.2", 54 | "react-native-reanimated": "~3.16.1", 55 | "react-native-safe-area-context": "4.12.0", 56 | "react-native-screens": "~4.4.0", 57 | "react-native-web": "~0.19.13", 58 | "react-native-webview": "13.12.5", 59 | "react-scan": "^0.0.36-native", 60 | "tailwind-merge": "^3.0.1", 61 | "tailwindcss": "^3.4.3" 62 | }, 63 | "devDependencies": { 64 | "@babel/core": "^7.25.2", 65 | "@types/jest": "^29.5.12", 66 | "@types/react": "~18.3.12", 67 | "@types/react-test-renderer": "^18.3.0", 68 | "gh-pages": "^6.3.0", 69 | "jest": "^29.2.1", 70 | "jest-expo": "~52.0.4", 71 | "prettier": "^3.4.2", 72 | "react-test-renderer": "18.3.1", 73 | "typescript": "^5.3.3" 74 | }, 75 | "private": true 76 | } 77 | -------------------------------------------------------------------------------- /demo/scripts/reset-project.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * This script is used to reset the project to a blank state. 5 | * It moves the /app, /components, /hooks, /scripts, and /constants directories to /app-example and creates a new /app directory with an index.tsx and _layout.tsx file. 6 | * You can remove the `reset-project` script from package.json and safely delete this file after running it. 7 | */ 8 | 9 | const fs = require("fs"); 10 | const path = require("path"); 11 | 12 | const root = process.cwd(); 13 | const oldDirs = ["app", "components", "hooks", "constants", "scripts"]; 14 | const newDir = "app-example"; 15 | const newAppDir = "app"; 16 | const newDirPath = path.join(root, newDir); 17 | 18 | const indexContent = `import { Text, View } from "react-native"; 19 | 20 | export default function Index() { 21 | return ( 22 | 29 | Edit app/index.tsx to edit this screen. 30 | 31 | ); 32 | } 33 | `; 34 | 35 | const layoutContent = `import { Stack } from "expo-router"; 36 | 37 | export default function RootLayout() { 38 | return ; 39 | } 40 | `; 41 | 42 | const moveDirectories = async () => { 43 | try { 44 | // Create the app-example directory 45 | await fs.promises.mkdir(newDirPath, { recursive: true }); 46 | console.log(`📁 /${newDir} directory created.`); 47 | 48 | // Move old directories to new app-example directory 49 | for (const dir of oldDirs) { 50 | const oldDirPath = path.join(root, dir); 51 | const newDirPath = path.join(root, newDir, dir); 52 | if (fs.existsSync(oldDirPath)) { 53 | await fs.promises.rename(oldDirPath, newDirPath); 54 | console.log(`➡️ /${dir} moved to /${newDir}/${dir}.`); 55 | } else { 56 | console.log(`➡️ /${dir} does not exist, skipping.`); 57 | } 58 | } 59 | 60 | // Create new /app directory 61 | const newAppDirPath = path.join(root, newAppDir); 62 | await fs.promises.mkdir(newAppDirPath, { recursive: true }); 63 | console.log("\n📁 New /app directory created."); 64 | 65 | // Create index.tsx 66 | const indexPath = path.join(newAppDirPath, "index.tsx"); 67 | await fs.promises.writeFile(indexPath, indexContent); 68 | console.log("📄 app/index.tsx created."); 69 | 70 | // Create _layout.tsx 71 | const layoutPath = path.join(newAppDirPath, "_layout.tsx"); 72 | await fs.promises.writeFile(layoutPath, layoutContent); 73 | console.log("📄 app/_layout.tsx created."); 74 | 75 | console.log("\n✅ Project reset complete. Next steps:"); 76 | console.log( 77 | "1. Run `npx expo start` to start a development server.\n2. Edit app/index.tsx to edit the main screen.\n3. Delete the /app-example directory when you're done referencing it." 78 | ); 79 | } catch (error) { 80 | console.error(`Error during script execution: ${error}`); 81 | } 82 | }; 83 | 84 | moveDirectories(); 85 | -------------------------------------------------------------------------------- /demo/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const { hairlineWidth } = require('nativewind/theme'); 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | module.exports = { 5 | darkMode: 'class', 6 | content: [ 7 | './app/**/*.{js,jsx,ts,tsx}', 8 | './components/**/*.{ts,tsx}', 9 | './layouts/**/*.{ts,tsx}', 10 | ], 11 | presets: [require('nativewind/preset')], 12 | theme: { 13 | extend: { 14 | fontFamily: { 15 | interLight: ['Inter_300Light'], 16 | inter: ['Inter_400Regular'], 17 | interMedium: ['Inter_500Medium'], 18 | interSemiBold: ['Inter_600SemiBold'], 19 | interBold: ['Inter_700Bold'], 20 | archivoLight: ['Archivo_300Light'], 21 | archivo: ['Archivo_400Regular'], 22 | archivoMedium: ['Archivo_500Medium'], 23 | archivoSemiBold: ['Archivo_600SemiBold'], 24 | archivoBold: ['Archivo_700Bold'], 25 | poppinsLight: ['Poppins_300Light'], 26 | poppins: ['Poppins_400Regular'], 27 | poppinsMedium: ['Poppins_500Medium'], 28 | poppinsSemiBold: ['Poppins_600SemiBold'], 29 | poppinsBold: ['Poppins_700Bold'], 30 | }, 31 | colors: { 32 | border: 'hsl(var(--border))', 33 | input: 'hsl(var(--input))', 34 | ring: 'hsl(var(--ring))', 35 | background: 'hsl(var(--background))', 36 | foreground: 'hsl(var(--foreground))', 37 | primary: { 38 | DEFAULT: 'hsl(var(--primary))', 39 | foreground: 'hsl(var(--primary-foreground))', 40 | }, 41 | secondary: { 42 | DEFAULT: 'hsl(var(--secondary))', 43 | foreground: 'hsl(var(--secondary-foreground))', 44 | }, 45 | destructive: { 46 | DEFAULT: 'hsl(var(--destructive))', 47 | foreground: 'hsl(var(--destructive-foreground))', 48 | }, 49 | success: { 50 | DEFAULT: 'hsl(var(--success))', 51 | foreground: 'hsl(var(--success-foreground))', 52 | }, 53 | warning: { 54 | DEFAULT: 'hsl(var(--warning))', 55 | foreground: 'hsl(var(--warning-foreground))', 56 | }, 57 | muted: { 58 | DEFAULT: 'hsl(var(--muted))', 59 | foreground: 'hsl(var(--muted-foreground))', 60 | }, 61 | accent: { 62 | DEFAULT: 'hsl(var(--accent))', 63 | foreground: 'hsl(var(--accent-foreground))', 64 | }, 65 | popover: { 66 | DEFAULT: 'hsl(var(--popover))', 67 | foreground: 'hsl(var(--popover-foreground))', 68 | }, 69 | card: { 70 | DEFAULT: 'hsl(var(--card))', 71 | foreground: 'hsl(var(--card-foreground))', 72 | }, 73 | }, 74 | borderWidth: { 75 | hairline: hairlineWidth(), 76 | }, 77 | borderRadius: { 78 | '3xl': 'calc(var(--radius) + 16px)', 79 | '2xl': 'calc(var(--radius) + 10px)', 80 | 'xl': 'calc(var(--radius) + 4px)', 81 | 'lg': 'var(--radius)', 82 | 'md': 'calc(var(--radius) - 2px)', 83 | 'sm': 'calc(var(--radius) - 4px)', 84 | }, 85 | }, 86 | }, 87 | plugins: [], 88 | }; 89 | -------------------------------------------------------------------------------- /demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo/tsconfig.base", 3 | "compilerOptions": { 4 | "strict": true, 5 | "paths": { 6 | "@/*": ["./*"], 7 | "react-native-ui-datepicker": ["../src"] 8 | } 9 | }, 10 | "include": [ 11 | "**/*.ts", 12 | "**/*.tsx", 13 | ".expo/types/**/*.ts", 14 | "expo-env.d.ts", 15 | "nativewind-env.d.ts" 16 | ], 17 | "exclude": ["node_modules", "build", "dist", ".expo"] 18 | } 19 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | const prettier = require('eslint-plugin-prettier'); 2 | const reactHooks = require('eslint-plugin-react-hooks'); 3 | const reactNative = require('eslint-plugin-react-native'); 4 | 5 | module.exports = [ 6 | { 7 | files: ['src/**/*.{js,jsx,ts,tsx}'], 8 | ignores: [ 9 | 'node_modules/', 10 | 'lib/', 11 | 'example/', 12 | 'demo/', 13 | '*.config.js', 14 | 'tsconfig.json', 15 | ], 16 | plugins: { 17 | prettier, 18 | 'react-hooks': reactHooks, 19 | 'react-native': reactNative, 20 | }, 21 | rules: { 22 | 'react/react-in-jsx-scope': 'off', 23 | 'prettier/prettier': 'warn', 24 | 'react-hooks/rules-of-hooks': 'error', 25 | 'react-hooks/exhaustive-deps': 'warn', 26 | 'react-native/no-inline-styles': 'warn', 27 | }, 28 | languageOptions: { 29 | parser: require('@typescript-eslint/parser'), 30 | ecmaVersion: 'latest', 31 | sourceType: 'module', 32 | parserOptions: { 33 | ecmaFeatures: { 34 | jsx: true, 35 | }, 36 | project: './tsconfig.json', 37 | }, 38 | }, 39 | }, 40 | ]; 41 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files 2 | 3 | # dependencies 4 | node_modules/ 5 | 6 | # Expo 7 | .expo/ 8 | dist/ 9 | web-build/ 10 | expo-env.d.ts 11 | 12 | # Native 13 | *.orig.* 14 | *.jks 15 | *.p8 16 | *.p12 17 | *.key 18 | *.mobileprovision 19 | 20 | # Metro 21 | .metro-health-check* 22 | 23 | # debug 24 | npm-debug.* 25 | yarn-debug.* 26 | yarn-error.* 27 | 28 | # macOS 29 | .DS_Store 30 | *.pem 31 | 32 | # local env files 33 | .env*.local 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | 38 | app-example 39 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to your Expo app 👋 2 | 3 | This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app). 4 | 5 | ## Get started 6 | 7 | 1. Install dependencies 8 | 9 | ```bash 10 | npm install 11 | ``` 12 | 13 | 2. Start the app 14 | 15 | ```bash 16 | npx expo start 17 | ``` 18 | 19 | In the output, you'll find options to open the app in a 20 | 21 | - [development build](https://docs.expo.dev/develop/development-builds/introduction/) 22 | - [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/) 23 | - [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/) 24 | - [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo 25 | 26 | You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction). 27 | 28 | ## Get a fresh project 29 | 30 | When you're ready, run: 31 | 32 | ```bash 33 | npm run reset-project 34 | ``` 35 | 36 | This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing. 37 | 38 | ## Learn more 39 | 40 | To learn more about developing your project with Expo, look at the following resources: 41 | 42 | - [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides). 43 | - [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web. 44 | 45 | ## Join the community 46 | 47 | Join our community of developers creating universal apps. 48 | 49 | - [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute. 50 | - [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions. 51 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "React Native UI DatePicker Example", 4 | "slug": "react-native-ui-datepicker-example", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/images/icon.png", 8 | "scheme": "myapp", 9 | "userInterfaceStyle": "automatic", 10 | "newArchEnabled": true, 11 | "ios": { 12 | "supportsTablet": true 13 | }, 14 | "android": { 15 | "adaptiveIcon": { 16 | "foregroundImage": "./assets/images/adaptive-icon.png", 17 | "backgroundColor": "#ffffff" 18 | } 19 | }, 20 | "web": { 21 | "bundler": "metro", 22 | "output": "static", 23 | "favicon": "./assets/images/favicon.png" 24 | }, 25 | "plugins": [ 26 | "expo-router", 27 | [ 28 | "expo-splash-screen", 29 | { 30 | "image": "./assets/images/splash-icon.png", 31 | "imageWidth": 200, 32 | "resizeMode": "contain", 33 | "backgroundColor": "#ffffff" 34 | } 35 | ] 36 | ], 37 | "experiments": { 38 | "typedRoutes": true 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /example/app/+not-found.tsx: -------------------------------------------------------------------------------- 1 | import { Link, Stack } from 'expo-router'; 2 | import { StyleSheet } from 'react-native'; 3 | 4 | import { ThemedText } from '@/components/ThemedText'; 5 | import { ThemedView } from '@/components/ThemedView'; 6 | 7 | export default function NotFoundScreen() { 8 | return ( 9 | <> 10 | 11 | 12 | This screen doesn't exist. 13 | 14 | Go to home screen! 15 | 16 | 17 | 18 | ); 19 | } 20 | 21 | const styles = StyleSheet.create({ 22 | container: { 23 | flex: 1, 24 | alignItems: 'center', 25 | justifyContent: 'center', 26 | padding: 20, 27 | }, 28 | link: { 29 | marginTop: 15, 30 | paddingVertical: 15, 31 | }, 32 | }); 33 | -------------------------------------------------------------------------------- /example/app/_layout.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | DarkTheme, 3 | DefaultTheme, 4 | ThemeProvider, 5 | } from '@react-navigation/native'; 6 | import { useFonts } from 'expo-font'; 7 | import { Stack } from 'expo-router'; 8 | import * as SplashScreen from 'expo-splash-screen'; 9 | import { StatusBar } from 'expo-status-bar'; 10 | import { useEffect } from 'react'; 11 | import 'react-native-reanimated'; 12 | 13 | import { useColorScheme } from '@/hooks/useColorScheme'; 14 | import { I18nManager } from 'react-native'; 15 | 16 | // Prevent the splash screen from auto-hiding before asset loading is complete. 17 | SplashScreen.preventAutoHideAsync(); 18 | 19 | //I18nManager.forceRTL(true); 20 | 21 | export default function RootLayout() { 22 | const colorScheme = useColorScheme(); 23 | const [loaded] = useFonts({ 24 | SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'), 25 | }); 26 | 27 | useEffect(() => { 28 | if (loaded) { 29 | SplashScreen.hideAsync(); 30 | } 31 | }, [loaded]); 32 | 33 | if (!loaded) { 34 | return null; 35 | } 36 | 37 | return ( 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /example/app/bottom-sheet.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useRef, useState } from 'react'; 2 | import { Button, StyleSheet } from 'react-native'; 3 | import { GestureHandlerRootView } from 'react-native-gesture-handler'; 4 | import { 5 | BottomSheetModal, 6 | BottomSheetView, 7 | BottomSheetModalProvider, 8 | } from '@gorhom/bottom-sheet'; 9 | import DateTimePicker, { 10 | DateType, 11 | useDefaultStyles, 12 | } from 'react-native-ui-datepicker'; 13 | 14 | export default function BottomSheetScreen() { 15 | const defaultStyles = useDefaultStyles(); 16 | const [date, setDate] = useState(); 17 | const bottomSheetModalRef = useRef(null); 18 | const [dates, setDates] = useState(); 19 | const [range, setRange] = useState<{ 20 | startDate: DateType; 21 | endDate: DateType; 22 | }>({ startDate: undefined, endDate: undefined }); 23 | 24 | const handlePresentModalPress = useCallback(() => { 25 | bottomSheetModalRef.current?.present(); 26 | }, []); 27 | 28 | // const handleSheetChanges = useCallback((index: number) => { 29 | // console.log('handleSheetChanges', index); 30 | // }, []); 31 | 32 | return ( 33 | 34 | 35 |