[
  {
    "path": ".editorconfig",
    "content": "# EditorConfig helps developers define and maintain consistent\n# coding styles between different editors and IDEs\n# editorconfig.org\n\nroot = true\n\n[*]\n\nindent_style = space\nindent_size = 2\n\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n"
  },
  {
    "path": ".gitattributes",
    "content": "*.pbxproj -text\n# specific for windows script files\n*.bat text eol=crlf"
  },
  {
    "path": ".github/actions/setup/action.yml",
    "content": "name: Setup\ndescription: Setup Node.js and install dependencies\n\nruns:\n  using: composite\n  steps:\n    - name: Setup Node.js\n      uses: actions/setup-node@v3\n      with:\n        node-version-file: .nvmrc\n\n    - name: Cache dependencies\n      id: yarn-cache\n      uses: actions/cache@v3\n      with:\n        path: |\n          **/node_modules\n        key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('**/package.json') }}\n        restore-keys: |\n          ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}\n          ${{ runner.os }}-yarn-\n\n    - name: Install dependencies\n      if: steps.yarn-cache.outputs.cache-hit != 'true'\n      run: |\n        yarn install --cwd example --frozen-lockfile\n        yarn install --frozen-lockfile\n      shell: bash\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    branches:\n      - main\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n\n      - name: Setup\n        uses: ./.github/actions/setup\n\n      - name: Lint files\n        run: yarn lint\n\n      - name: Typecheck files\n        run: yarn typecheck\n\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n\n      - name: Setup\n        uses: ./.github/actions/setup\n\n      - name: Run unit tests\n        run: yarn test --maxWorkers=2 --coverage\n\n  build-library:\n    runs-on: macos-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n\n      - name: Setup\n        uses: ./.github/actions/setup\n\n      - name: Build package\n        run: yarn prepack\n\n  build-web:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n\n      - name: Setup\n        uses: ./.github/actions/setup\n\n      - name: Build example for Web\n        run: |\n          yarn example expo export:web\n"
  },
  {
    "path": ".gitignore",
    "content": "# OSX\n#\n.DS_Store\n\n# XDE\n.expo/\n\n# VSCode\n.vscode/\njsconfig.json\n\n# Xcode\n#\nbuild/\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!default.perspectivev3\nxcuserdata\n*.xccheckout\n*.moved-aside\nDerivedData\n*.hmap\n*.ipa\n*.xcuserstate\nproject.xcworkspace\n\n# Android/IJ\n#\n.classpath\n.cxx\n.gradle\n.idea\n.project\n.settings\nlocal.properties\nandroid.iml\n\n# Cocoapods\n#\nexample/ios/Pods\n\n# Ruby\nexample/vendor/\n\n# node.js\n#\nnode_modules/\nnpm-debug.log\nyarn-debug.log\nyarn-error.log\n.env\n\n# BUCK\nbuck-out/\n\\.buckd/\nandroid/app/libs\nandroid/keystores/debug.keystore\n\n# Expo\n.expo/\n\n# Turborepo\n.turbo/\n\n# generated by bob\nlib/\n\n# user files\nbacklog.md\n"
  },
  {
    "path": ".nvmrc",
    "content": "v18\n"
  },
  {
    "path": ".watchmanconfig",
    "content": "{}"
  },
  {
    "path": ".yarnrc",
    "content": "# Override Yarn command so we can automatically setup the repo on running `yarn`\n\nyarn-path \"scripts/bootstrap.js\"\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "\n# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the overall\n  community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or advances of\n  any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email address,\n  without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n[INSERT CONTACT METHOD].\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series of\nactions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within the\ncommunity.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.1, available at\n[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].\n\nCommunity Impact Guidelines were inspired by\n[Mozilla's code of conduct enforcement ladder][Mozilla CoC].\n\nFor answers to common questions about this code of conduct, see the FAQ at\n[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at\n[https://www.contributor-covenant.org/translations][translations].\n\n[homepage]: https://www.contributor-covenant.org\n[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html\n[Mozilla CoC]: https://github.com/mozilla/diversity\n[FAQ]: https://www.contributor-covenant.org/faq\n[translations]: https://www.contributor-covenant.org/translations\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nContributions are always welcome, no matter how large or small!\n\nWe 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).\n\n## Development workflow\n\nTo get started with the project, run `yarn` in the root directory to install the required dependencies for each package:\n\n```sh\nyarn\n```\n\n> 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.\n\nWhile 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.\n\nTo start the packager:\n\n```sh\nyarn example start\n```\n\nTo run the example app on Android:\n\n```sh\nyarn example android\n```\n\nTo run the example app on iOS:\n\n```sh\nyarn example ios\n```\n\nTo run the example app on Web:\n\n```sh\nyarn example web\n```\n\nMake sure your code passes TypeScript and ESLint. Run the following to verify:\n\n```sh\nyarn typecheck\nyarn lint\n```\n\nTo fix formatting errors, run the following:\n\n```sh\nyarn lint --fix\n```\n\nRemember to add tests for your change if possible. Run the unit tests by:\n\n```sh\nyarn test\n```\n\n\n### Commit message convention\n\nWe follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages:\n\n- `fix`: bug fixes, e.g. fix crash due to deprecated method.\n- `feat`: new features, e.g. add new method to the module.\n- `refactor`: code refactor, e.g. migrate from class components to hooks.\n- `docs`: changes into documentation, e.g. add usage example for the module..\n- `test`: adding or updating tests, e.g. add integration tests using detox.\n- `chore`: tooling changes, e.g. change CI config.\n- `perf`: changes that improve performance\n- `improvement`: changes that improve a current implementation\n- `style`: syntax formatting\n\n\nOur pre-commit hooks verify that your commit message matches this format when committing.\n\n### Linting and tests\n\n[ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/)\n\nWe 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.\n\nOur pre-commit hooks verify that the linter and tests pass when committing.\n\n### Publishing to npm\n\nWe 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.\n\nTo publish new versions, run the following:\n\n```sh\nyarn release\n```\n\n### Scripts\n\nThe `package.json` file contains various scripts for common tasks:\n\n- `yarn bootstrap`: setup project by installing all dependencies and pods.\n- `yarn typecheck`: type-check files with TypeScript.\n- `yarn lint`: lint files with ESLint.\n- `yarn test`: run unit tests with Jest.\n- `yarn example start`: start the Metro server for the example app.\n- `yarn example android`: run the example app on Android.\n- `yarn example ios`: run the example app on iOS.\n\n### Sending a pull request\n\n> **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).\n\nWhen you're sending a pull request:\n\n- Prefer small pull requests focused on one change.\n- Verify that linters and tests are passing.\n- Review the documentation to make sure it looks good.\n- Follow the pull request template when opening a pull request.\n- For pull requests that change the API or implementation, discuss with maintainers first by opening an issue.\n\nThanks for your contributions 💖\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023 Devvie\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# React Native Bottom Sheet 💖\n\n![GitHub](https://img.shields.io/github/license/stanleyugwu/react-native-bottom-sheet?style=plastic&label=License&color=%23fea9f8)\n![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/stanleyugwu/react-native-bottom-sheet/ci.yml?color=%23fea9f8&label=Build)\n[![runs with expo](https://img.shields.io/badge/Expo-Support-fea9f8.svg?style=platic&logo=EXPO&logoColor=fff)](https://expo.io/)\n![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/stanleyugwu/react-native-bottom-sheet?color=%23fea9f8&label=Code%20Size)\n![npm bundle size (scoped)](https://img.shields.io/bundlephobia/minzip/@devvie/bottom-sheet?style=plastic&logo=npm&color=%23fea9f8&label=Bundle%20Size)\n![npm downloads](https://img.shields.io/npm/dm/@devvie/bottom-sheet?style=plastic&logo=npm&color=%23fea9f8&label=Downloads)\n\nThe smart 😎, tiny 📦, and flexible 🎗 bottom sheet your app craves 🚀\n\n---\n👉🏾\n<a href=\"https://www.buymeacoffee.com/devvie\" target=\"_blank\"><img src=\"https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png\" alt=\"Buy Me A Coffee\" style=\"height: 60px !important;width: 217px !important;\" ></a>\n👈🏾\n---\n\n![Preview for Android & iOS](https://i.ibb.co/Y38XsMr/Combined.gif)\n\n#### Web Preview\n\n<p float=\"left\">\n  <img src=\"https://i.ibb.co/sJpsFKD/Web.png\" width=\"400\" />\n</p>\n\n## ✨Features\n\n- 📦 Very tiny and lightweight\n- 0️⃣ No dependency (yeah!, just plug and play 😎)\n- ✨ Modal and standard (non-modal) bottom sheet support\n- ⌨ Smart & automatic keyboard and orientation handling for iOS & Android\n- 💪 Imperative calls\n- 📜 Supports FlatList, SectionList, ScrollView & View scrolling interactions\n- 📟 Handles layout & orientation changes smartly\n- 💯 Compatible with Expo\n- 🔧 Flexible config\n- 🚀 Supports props live update\n- 🎞 Configurable animation\n- 🎨 Follows Material Design principles\n- 🌐 Runs on the web\n- ✅ Written in TypeScript\n\n## 💻 Installation\n\n```sh\nnpm install @devvie/bottom-sheet\n```\n\nor\n\n```sh\nyarn add @devvie/bottom-sheet\n```\n\n## 📱 Minimal Usage\n\nOpening and closing the bottom sheet is done imperatively, so just pass a `ref` to the bottom sheet and call the `open` or `close` methods via the `ref` instance to open and close the bottom sheet respectively.\n\n##### Examples\n\n#### Typescript\n\n```tsx\nimport React, { useRef } from 'react';\nimport BottomSheet, { BottomSheetMethods } from '@devvie/bottom-sheet';\nimport { Button, View } from 'react-native';\n\nconst App = () => {\n  const sheetRef = useRef<BottomSheetMethods>(null);\n  return (\n    <View>\n      <Button title=\"Open\" onPress={() => sheetRef.current?.open()} />\n      <BottomSheet ref={sheetRef}>\n        <Text>\n          The smart 😎, tiny 📦, and flexible 🎗 bottom sheet your app craves 🚀\n        </Text>\n      </BottomSheet>\n    </View>\n  );\n};\n\nexport default App;\n```\n\n#### Javascript\n\n```tsx\nimport React, { useRef } from 'react';\nimport BottomSheet, { BottomSheetMethods } from '@devvie/bottom-sheet';\nimport { Button, View } from 'react-native';\n\nconst App = () => {\n  const sheetRef = useRef(null);\n  return (\n    <View>\n      <Button title=\"Open\" onPress={() => sheetRef.current?.open()} />\n      <BottomSheet ref={sheetRef}>\n        <Text>\n          The smart 😎, tiny 📦, and flexible 🎗 bottom sheet your app craves 🚀\n        </Text>\n      </BottomSheet>\n    </View>\n  );\n};\n```\n\n### ⚠ Warning\n\nThe bottom sheet component uses and handles pan gestures internally, so to avoid scroll/pan misbehavior with its container, **DO NOT** put it inside a container that supports panning e.g `ScrollView`. You can always put it just next to the `ScrollView` and use `React Fragment` or a `View` to wrap them and everything should be okay.\n\n#### ❌ Don't do this\n\n```jsx\n<ScrollView>\n  <BottomSheet>...</BottomSheet>\n</ScrollView>\n```\n\n#### ✅ Do this\n\n```jsx\n<>\n  <ScrollView>...</ScrollView>\n\n  <BottomSheet>...</BottomSheet>\n</>\n```\n\n## 🛠 Props\n\nThe bottom sheet is highly configurable via props. All props works for both `Android` and `iOS` except those prefixed with `android_` and `ios_`, which works for only `Android` and `iOS` respectively.\n\n| Property                          | Type                                                                                    | Default                | Description                                                                                                                                       | Required |\n| --------------------------------- | --------------------------------------------------------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |\n| `android_backdropMaskRippleColor` | `string \\| OpaqueColorValue`                                                            |                        | Color of the ripple effect when backdrop mask is pressed (**Android Only**).                                                                      | No       |\n| `android_closeOnBackPress`        | `boolean`                                                                               | `true`                 | Determines whether the sheet will close when the device back button is pressed (**Android Only**).                                                | No       |\n| `animationType`                   | `'slide' \\| 'spring' \\| 'fade' \\| ANIMATIONS`                                           | `'slide'`              | Animation to use when opening and closing the bottom sheet.                                                                                       | No       |\n| `backdropMaskColor`               | `string \\| OpaqueColorValue`                                                            | `'#00000052'`          | Color of the scrim or backdrop mask.                                                                                                              | No       |\n| `children`                        | `ViewProps['children'] \\| React.FunctionComponent<{_animatedHeight: Animated.Value}>`   | `null`                 | Contents of the bottom sheet.                                                                                                                     | Yes      |\n| `closeDuration`                   | `number`                                                                                | `500`                  | Duration for sheet closing animation.                                                                                                             | No       |\n| `closeOnBackdropPress`            | `boolean`                                                                               | `true`                 | Determines whether the bottom sheet will close when the scrim or backdrop mask is pressed.                                                        | No       |\n| `closeOnDragDown`                 | `boolean`                                                                               | `true`                 | Determines whether the bottom sheet will close when dragged down.                                                                                 | No       |\n| `containerHeight`                 | `ViewStyle['height']`                                                                   | `DEVICE SCREEN HEIGHT` | Height of the bottom sheet's overall container.                                                                                                   | No       |\n| `customBackdropComponent`         | `React.FunctionComponent<{_animatedHeight: Animated.Value}>`                            | `null`                 | Custom component for sheet's scrim or backdrop mask.                                                                                              | No       |\n| `customBackdropPosition`          | `\"top\" \\| \"behind\"`                                                                     | `'behind'`             | Determines the position of the custom scrim or backdrop component. `'behind'` puts it behind the keyboard and `'top'`` puts it atop the keyboard. | No       |\n| `customDragHandleComponent`       | `React.FC<{_animatedHeight: Animated.Value}>`                                           |                        | Custom drag handle component to replace the default bottom sheet's drag handle.                                                                   | No       |\n| `customEasingFunction`            | `AnimationEasingFunction`                                                               | `ANIMATIONS.SLIDE`     | Custom easing function for driving sheet's animation.                                                                                             | No       |\n| `disableBodyPanning`              | `boolean`                                                                               | `false`                | Prevents the bottom sheet from being dragged/panned down on its body.                                                                             | No       |\n| `disableDragHandlePanning`        | `boolean`                                                                               | `false`                | Prevents the bottom sheet from being panned down by dragging its drag handle.                                                                     | No       |\n| `dragHandleStyle`                 | `ViewStyle`                                                                             |                        | Extra styles to apply to the drag handle.                                                                                                         | No       |\n| `height`                          | `number \\| string`                                                                      | `'50%'`                | Height of the bottom sheet when opened. Relative to `containerHeight` prop                                                                        | No       |\n| `hideDragHandle`                  | `boolean`                                                                               | `false`                | When true, hides the sheet's drag handle.                                                                                                         | No       |\n| `modal`                           | `boolean`                                                                               | `true`                 | Determines whether the sheet is a modal. A modal sheet has a scrim or backdrop mask, while a standard (non-modal) sheet doesn't.                  | No       |\n| `openDuration`                    | `number`                                                                                | `500`                  | Duration for sheet opening animation.                                                                                                             | No       |\n| `style`                           | `Omit<ViewStyle, 'height' \\| 'minHeight' \\| 'maxHeight' \\| 'transform:[{translateY}]'>` |                        | Extra styles to apply to the bottom sheet.                                                                                                        | No       |\n\n## Examples\n\nFlexibility is a focus for this bottom sheet, these few examples shows certain behaviors of the bottom sheet and what can be achieved by tweaking its props.\n\n### 1️⃣ Smart response to keyboard pop ups and orientation changes (_automatic behavior_)\n\n|                                          Android                                           |                                          iOS                                           |\n| :----------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------: |\n| ![Preview for keyboard handling (Android)](https://i.ibb.co/0BfLWYK/Keyboard-Response.gif) | ![Preview for keyboard handling (iOS)](https://i.ibb.co/302ZYBL/Keyboard-Response.gif) |\n\n### 2️⃣ Handles deeply nested list and scroll views interactions (_automatic beavior_)\n\n|                                       Android                                        |                                      iOS                                       |\n| :----------------------------------------------------------------------------------: | :----------------------------------------------------------------------------: |\n| ![Preview for scroll handling (Android)](https://i.ibb.co/kgfPM3w/Nested-Scroll.gif) | ![Preview for scroll handling (iOS)](https://i.ibb.co/rcrJVLc/Nested-List.gif) |\n\n### 3️⃣ Auto adjusts layout when `height` and `containerHeight` props change (_automatic behavior_)\n\n<p float=\"left\">\n  <img src=\"https://i.ibb.co/3YGXHht/Detect-Height.gif\" width=\"300\" />\n</p>\n\n### 4️⃣ Extend sheet height when its content is scrolled\n\n<p float=\"left\">\n  <img src=\"https://i.ibb.co/9W5J2t5/Extend-on-scroll.gif\" width=\"300\" />\n</p>\n\n### 5️⃣ Use as `SnackBar`\n\n<p float=\"left\">\n  <img src=\"https://i.ibb.co/LkMJ255/Snack-Bar.gif\" width=\"300\" />\n</p>\n\n### 6️⃣ Custom Drag Handle Animation Interpolation\n\n<p float=\"left\">\n  <img src=\"https://i.ibb.co/0yDPQ0W/Drag-Handle-Animation.gif\" width=\"300\" />\n</p>\n\n### 7️⃣ Custom Scrim/Backdrop Mask\n\n<p float=\"left\">\n  <img src=\"https://i.ibb.co/h9XqBJC/Custom-Scrim.gif\" width=\"300\" />\n</p>\n\n#### _More Examples and code samples coming soon..._\n\n## Contributing\n\nSee the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.\n\n## License\n\nMIT\n\nsee [LICENSE](LICENSE.md)\n\n---\n\n## Support\n\n<a href=\"https://www.buymeacoffee.com/devvie\"><img style=\"height: 50px !important;align:center;width: 217px !important\" src=\"https://img.buymeacoffee.com/button-api/?text=Buy me Okpa&emoji=🍘&slug=devvie&button_colour=Fea9f8&font_colour=000000&font_family=Cookie&outline_colour=000000&coffee_colour=ffffff\" /></a>\n\n</> with 💖 by [Devvie](https://twitter.com/stanleyugwu_) ✌\n"
  },
  {
    "path": "babel.config.js",
    "content": "module.exports = {\n  presets: ['module:metro-react-native-babel-preset'],\n};\n"
  },
  {
    "path": "commitlint.config.ts",
    "content": "import type { UserConfig } from '@commitlint/types';\n\nconst Configuration: UserConfig = {\n  extends: ['@commitlint/config-conventional'],\n  parserPreset: {\n    parserOpts: {\n      headerPattern:\n        /^(?<type>.*\\s\\w*)(?:\\((?<scope>.*)\\))?!?:\\s(?<subject>(?:(?!#).)*(?:(?!\\s).))$/,\n      headerCorrespondence: ['type', 'scope', 'subject'],\n    },\n  },\n  rules: {\n    'type-enum': [\n      2,\n      'always',\n      [\n        'build',\n        'chore',\n        'ci',\n        'docs',\n        'feat',\n        'fix',\n        'perf',\n        'refactor',\n        'revert',\n        'style',\n        'test',\n        '🛠️ build',\n        '🛠️🚀 build', // new version release\n        '♻️ chore',\n        '⚙️ ci',\n        '📃 docs',\n        '✨ feat',\n        '🐞 fix',\n        '🚀 perf',\n        '🦄 refactor',\n        '🗑️ revert',\n        '🌈 style',\n        '🧪 test',\n      ],\n    ],\n  },\n};\n\nmodule.exports = Configuration;\n"
  },
  {
    "path": "example/App.js",
    "content": "export { default } from './src/App';\n"
  },
  {
    "path": "example/app.json",
    "content": "{\n  \"expo\": {\n    \"name\": \"example\",\n    \"slug\": \"example\",\n    \"version\": \"1.0.0\",\n    \"orientation\": \"portrait\",\n    \"icon\": \"./assets/icon.png\",\n    \"userInterfaceStyle\": \"light\",\n    \"splash\": {\n      \"image\": \"./assets/splash.png\",\n      \"resizeMode\": \"contain\",\n      \"backgroundColor\": \"#ffffff\"\n    },\n    \"assetBundlePatterns\": [\n      \"**/*\"\n    ],\n    \"ios\": {\n      \"supportsTablet\": true\n    },\n    \"android\": {\n      \"adaptiveIcon\": {\n        \"foregroundImage\": \"./assets/adaptive-icon.png\",\n        \"backgroundColor\": \"#ffffff\"\n      }\n    },\n    \"web\": {\n      \"favicon\": \"./assets/favicon.png\"\n    }\n  }\n}\n"
  },
  {
    "path": "example/babel.config.js",
    "content": "const path = require('path');\nconst pak = require('../package.json');\n\nmodule.exports = function (api) {\n  api.cache(true);\n\n  return {\n    presets: ['babel-preset-expo'],\n    plugins: [\n      [\n        'module-resolver',\n        {\n          extensions: ['.tsx', '.ts', '.js', '.json'],\n          alias: {\n            // For development, we want to alias the library to the source\n            [pak.name]: path.join(__dirname, '..', pak.source),\n          },\n        },\n      ],\n    ],\n  };\n};\n"
  },
  {
    "path": "example/metro.config.js",
    "content": "const path = require('path');\nconst escape = require('escape-string-regexp');\nconst { getDefaultConfig } = require('@expo/metro-config');\nconst exclusionList = require('metro-config/src/defaults/exclusionList');\nconst pak = require('../package.json');\n\nconst root = path.resolve(__dirname, '..');\nconst modules = Object.keys({ ...pak.peerDependencies });\n\nconst defaultConfig = getDefaultConfig(__dirname);\n\n/**\n * Metro configuration\n * https://facebook.github.io/metro/docs/configuration\n *\n * @type {import('metro-config').MetroConfig}\n */\nconst config = {\n  ...defaultConfig,\n\n  projectRoot: __dirname,\n  watchFolders: [root],\n\n  // We need to make sure that only one version is loaded for peerDependencies\n  // So we block them at the root, and alias them to the versions in example's node_modules\n  resolver: {\n    ...defaultConfig.resolver,\n\n    blacklistRE: exclusionList(\n      modules.map(\n        (m) =>\n          new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\\\/.*$`)\n      )\n    ),\n\n    extraNodeModules: modules.reduce((acc, name) => {\n      acc[name] = path.join(__dirname, 'node_modules', name);\n      return acc;\n    }, {}),\n  },\n};\n\nmodule.exports = config;\n"
  },
  {
    "path": "example/package.json",
    "content": "{\n  \"name\": \"example\",\n  \"version\": \"1.0.0\",\n  \"main\": \"node_modules/expo/AppEntry.js\",\n  \"scripts\": {\n    \"start\": \"expo start\",\n    \"android\": \"expo start --android\",\n    \"ios\": \"expo start --ios\",\n    \"web\": \"expo start --web\"\n  },\n  \"dependencies\": {\n    \"@devvie/bottom-sheet\": \"^0.1.2\",\n    \"expo\": \"~49.0.11\",\n    \"expo-status-bar\": \"~1.6.0\",\n    \"react\": \"18.2.0\",\n    \"react-dom\": \"18.2.0\",\n    \"react-native\": \"0.72.4\",\n    \"react-native-web\": \"~0.19.6\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.20.0\",\n    \"@expo/webpack-config\": \"^18.0.1\",\n    \"babel-loader\": \"^8.1.0\",\n    \"babel-plugin-module-resolver\": \"^5.0.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "example/src/App.tsx",
    "content": "import * as React from 'react';\nimport { StyleSheet, View, Text, Button } from 'react-native';\n// @ts-ignore ts can't find module '@devvie/bottom-sheet' on github runner because it's aliased\nimport BottomSheet, { type BottomSheetMethods } from '@devvie/bottom-sheet';\n\nexport default function App() {\n  const sheetRef = React.useRef<BottomSheetMethods>(null);\n\n  return (\n    <View style={styles.container}>\n      <Button title=\"Open Sheet\" onPress={() => sheetRef.current?.open()} />\n      <BottomSheet ref={sheetRef}>\n        <Text style={styles.text}>\n          The 😎smart, 📦tiny, and 🎗flexible bottom sheet your app craves\n        </Text>\n      </BottomSheet>\n    </View>\n  );\n}\n\nconst styles = StyleSheet.create({\n  text: {\n    textAlign: 'center',\n    fontSize: 20,\n    fontWeight: '700',\n    padding: 20,\n  },\n  container: {\n    flex: 1,\n    alignItems: 'center',\n    justifyContent: 'center',\n  },\n});\n"
  },
  {
    "path": "example/tsconfig.json",
    "content": "{\n  \"extends\": \"../tsconfig\",\n  \"compilerOptions\": {\n    // Avoid expo-cli auto-generating a tsconfig\n  }\n}\n"
  },
  {
    "path": "example/webpack.config.js",
    "content": "const path = require('path');\nconst createExpoWebpackConfigAsync = require('@expo/webpack-config');\nconst { resolver } = require('./metro.config');\n\nconst root = path.resolve(__dirname, '..');\nconst node_modules = path.join(__dirname, 'node_modules');\n\nmodule.exports = async function (env, argv) {\n  const config = await createExpoWebpackConfigAsync(env, argv);\n\n  config.module.rules.push({\n    test: /\\.(js|jsx|ts|tsx)$/,\n    include: path.resolve(root, 'src'),\n    use: 'babel-loader',\n  });\n\n  // We need to make sure that only one version is loaded for peerDependencies\n  // So we alias them to the versions in example's node_modules\n  Object.assign(config.resolve.alias, {\n    ...resolver.extraNodeModules,\n    'react-native-web': path.join(node_modules, 'react-native-web'),\n  });\n\n  return config;\n};\n"
  },
  {
    "path": "lefthook.yml",
    "content": "pre-commit:\n  parallel: true\n  commands:\n    lint:\n      files: git diff --name-only @{push}\n      glob: \"*.{js,ts,jsx,tsx}\"\n      run: npx eslint {files}\n    types:\n      files: git diff --name-only @{push}\n      glob: \"*.{js,ts, jsx, tsx}\"\n      run: npx tsc --noEmit\ncommit-msg:\n  parallel: true\n  commands:\n    commitlint:\n      run: npx commitlint --edit\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"@devvie/bottom-sheet\",\n  \"version\": \"0.4.3\",\n  \"description\": \"The 😎smart , 📦tiny , and 🎗flexible bottom sheet your app craves 🚀\",\n  \"main\": \"lib/commonjs/index\",\n  \"module\": \"lib/module/index\",\n  \"types\": \"lib/typescript/src/index.d.ts\",\n  \"react-native\": \"src/index\",\n  \"source\": \"src/index\",\n  \"files\": [\n    \"src\",\n    \"lib\",\n    \"scripts\"\n  ],\n  \"scripts\": {\n    \"test\": \"jest\",\n    \"typecheck\": \"tsc --noEmit\",\n    \"lint\": \"eslint \\\"**/*.{js,ts,tsx}\\\"\",\n    \"del-build-dir\": \"node scripts/delete-lib-dir.js\",\n    \"build-dts\": \"tsc --project tsconfig.build.json\",\n    \"copy-dts:unix\": \"mkdir -p lib/typescript && rsync --prune-empty-dirs -av --include '*/' --include '*.d.ts' --exclude '*' src/ lib/typescript/\",\n    \"copy-dts:windows\": \"xcopy /S /Y \\\"src\\\\*.d.ts\\\" \\\"lib\\\\typescript\\\"\",\n    \"copy-dts\": \"if [ \\\"$OS\\\" = \\\"Windows_NT\\\" ]; then yarn copy-dts:windows; else yarn copy-dts:unix; fi\",\n    \"prepack\": \"yarn del-build-dir && yarn build-dts && yarn copy-dts && bob build\",\n    \"release\": \"yarn prepack && dotenv release-it --\",\n    \"example\": \"yarn --cwd example\",\n    \"bootstrap\": \"yarn example && yarn install\"\n  },\n  \"keywords\": [\n    \"react-native\",\n    \"ios\",\n    \"android\",\n    \"bottom-sheet\",\n    \"react-native-bottom-sheet\",\n    \"devvie-bottom-sheet\",\n    \"@devvie/bottom-sheet\",\n    \"tiny-bottom-sheet\",\n    \"flexible-bottom-sheet\",\n    \"modal-bottom-sheet\",\n    \"sheet\",\n    \"ui-sheet\"\n  ],\n  \"repository\": \"https://github.com/stanleyugwu/react-native-bottom-sheet\",\n  \"author\": \"Devvie <stanleyugwu2018@gmail.com> (https://github.com/stanleyugwu)\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/stanleyugwu/react-native-bottom-sheet/issues\"\n  },\n  \"homepage\": \"https://github.com/stanleyugwu/react-native-bottom-sheet#readme\",\n  \"publishConfig\": {\n    \"registry\": \"https://registry.npmjs.org/\",\n    \"access\": \"public\"\n  },\n  \"devDependencies\": {\n    \"@commitlint/config-conventional\": \"^17.0.2\",\n    \"@evilmartians/lefthook\": \"^1.2.2\",\n    \"@react-native/eslint-config\": \"^0.72.2\",\n    \"@release-it/conventional-changelog\": \"^5.0.0\",\n    \"@types/jest\": \"^28.1.2\",\n    \"@types/react\": \"~18.2.0\",\n    \"@types/react-native\": \"0.70.0\",\n    \"commitlint\": \"^17.0.2\",\n    \"del-cli\": \"^5.0.0\",\n    \"dotenv-cli\": \"^7.3.0\",\n    \"eslint\": \"^8.4.1\",\n    \"eslint-config-prettier\": \"^8.5.0\",\n    \"eslint-plugin-prettier\": \"^4.0.0\",\n    \"jest\": \"^28.1.1\",\n    \"pod-install\": \"^0.1.0\",\n    \"prettier\": \"^2.0.5\",\n    \"react\": \"18.2.0\",\n    \"react-native\": \"0.72.4\",\n    \"react-native-builder-bob\": \"^0.23.1\",\n    \"release-it\": \"^15.0.0\",\n    \"typescript\": \"^5.0.2\"\n  },\n  \"resolutions\": {\n    \"@types/react\": \"18.2.0\"\n  },\n  \"peerDependencies\": {\n    \"react\": \"*\",\n    \"react-native\": \"*\"\n  },\n  \"packageManager\": \"yarn@1.22.19\",\n  \"jest\": {\n    \"preset\": \"react-native\",\n    \"modulePathIgnorePatterns\": [\n      \"<rootDir>/example/node_modules\",\n      \"<rootDir>/lib/\"\n    ]\n  },\n  \"release-it\": {\n    \"git\": {\n      \"commitMessage\": \"🛠️🚀 build: release ${version}\",\n      \"tagName\": \"v${version}\",\n      \"requireBranch\": \"main\"\n    },\n    \"npm\": {\n      \"publish\": true\n    },\n    \"github\": {\n      \"release\": true,\n      \"releaseNotes\": \"git log --no-merges --pretty=format:\\\"* %s %h\\\" ${latestTag}...main\",\n      \"comments\": {\n        \"submit\": true,\n        \"issue\": \":rocket: _This issue has been resolved in v${version}. See [${releaseName}](${releaseUrl}) for release notes._\",\n        \"pr\": \":rocket: _This pull request is included in v${version}. See [${releaseName}](${releaseUrl}) for release notes._\"\n      }\n    },\n    \"plugins\": {\n      \"@release-it/conventional-changelog\": {\n        \"preset\": \"angular\"\n      }\n    }\n  },\n  \"eslintConfig\": {\n    \"root\": true,\n    \"extends\": [\n      \"@react-native\",\n      \"prettier\"\n    ],\n    \"rules\": {\n      \"prettier/prettier\": [\n        \"error\",\n        {\n          \"quoteProps\": \"consistent\",\n          \"singleQuote\": true,\n          \"tabWidth\": 2,\n          \"trailingComma\": \"es5\",\n          \"useTabs\": false,\n          \"endOfLine\": \"auto\"\n        }\n      ]\n    }\n  },\n  \"eslintIgnore\": [\n    \"node_modules/\",\n    \"lib/\"\n  ],\n  \"prettier\": {\n    \"quoteProps\": \"consistent\",\n    \"singleQuote\": true,\n    \"tabWidth\": 2,\n    \"trailingComma\": \"es5\",\n    \"useTabs\": false\n  },\n  \"react-native-builder-bob\": {\n    \"source\": \"src\",\n    \"output\": \"lib\",\n    \"targets\": [\n      \"commonjs\",\n      \"module\"\n    ]\n  }\n}\n"
  },
  {
    "path": "scripts/bootstrap.js",
    "content": "const os = require('os');\nconst path = require('path');\nconst child_process = require('child_process');\n\nconst root = path.resolve(__dirname, '..');\nconst args = process.argv.slice(2);\nconst options = {\n  cwd: process.cwd(),\n  env: process.env,\n  stdio: 'inherit',\n  encoding: 'utf-8',\n};\n\nif (os.type() === 'Windows_NT') {\n  options.shell = true;\n}\n\nlet result;\n\nif (process.cwd() !== root || args.length) {\n  // We're not in the root of the project, or additional arguments were passed\n  // In this case, forward the command to `yarn`\n  result = child_process.spawnSync('yarn', args, options);\n} else {\n  // If `yarn` is run without arguments, perform bootstrap\n  result = child_process.spawnSync('yarn', ['bootstrap'], options);\n}\n\nprocess.exitCode = result.status;\n"
  },
  {
    "path": "scripts/copy-dts.js",
    "content": "const fs = require('fs-extra');\nconst path = require('path');\n\nconst sourceDirectory = './src'; // Change this to your source directory\nconst destinationDirectory = './lib/typescript'; // Change this to your destination directory\n\n// Ensure the destination directory exists, create it if it doesn't\nfs.ensureDirSync(destinationDirectory);\n\n// Function to copy .d.ts files recursively\nfunction copyDeclarationFiles(src, dest) {\n  const files = fs.readdirSync(src);\n\n  files.forEach((file) => {\n    const sourceFilePath = path.join(src, file);\n    const destinationFilePath = path.join(dest, file);\n\n    if (fs.statSync(sourceFilePath).isDirectory()) {\n      // If it's a directory, copy its contents recursively\n      fs.ensureDirSync(destinationFilePath);\n      copyDeclarationFiles(sourceFilePath, destinationFilePath);\n    } else if (path.extname(file) === '.d.ts') {\n      // If it's a .d.ts file, copy it to the destination directory\n      fs.copyFileSync(sourceFilePath, destinationFilePath);\n    }\n  });\n}\n\n// Start copying recursively\ncopyDeclarationFiles(sourceDirectory, destinationDirectory);\n\nconsole.log('Declaration files copied recursively.');\n"
  },
  {
    "path": "scripts/delete-lib-dir.js",
    "content": "const fs = require('fs');\nconst os = require('os');\nconst { exec } = require('child_process');\n\n// Define the relative directory path\nconst directoryName = 'lib';\n\n// Function to check if a directory exists\nconst directoryExists = (directory) => {\n  try {\n    fs.accessSync(directory, fs.constants.F_OK);\n    return true;\n  } catch (err) {\n    return false;\n  }\n};\n\n// Detect the operating system\nconst platform = os.platform();\n\n// Conditional execution based on the detected OS\nif (directoryExists(directoryName)) {\n  if (platform === 'win32') {\n    // Windows\n    exec(`rmdir /s /q \"${directoryName}\"`, (error, stdout, stderr) => {\n      if (error) {\n        console.error(`Error: ${error.message}`);\n        return;\n      }\n    });\n  } else if (platform === 'linux' || platform === 'darwin') {\n    // Linux or macOS\n    exec(`rm -rf \"${directoryName}\"`, (error, stdout, stderr) => {\n      if (error) {\n        console.error(`Error: ${error.message}`);\n        return;\n      }\n    });\n  } else {\n    console.error('Unsupported operating system');\n  }\n}\n"
  },
  {
    "path": "src/__tests__/index.test.tsx",
    "content": "it.todo('write a test');\n"
  },
  {
    "path": "src/components/animatedTouchableBackdropMask/index.tsx",
    "content": "import React from 'react';\nimport { Animated, Pressable, StyleSheet } from 'react-native';\nimport { type AnimatedTouchableBackdropMaskProps } from './types.d';\n\n/**\n * Polymorphic and re-usable animated backdrop mask component\n */\nconst _AnimatedTouchableBackdropMask =\n  Animated.createAnimatedComponent(Pressable);\n\nconst AnimatedTouchableBackdropMask = ({\n  style,\n  isPressable,\n  pressHandler,\n  android_touchRippleColor,\n  ...otherProps\n}: AnimatedTouchableBackdropMaskProps) => {\n  return isPressable ? (\n    <_AnimatedTouchableBackdropMask\n      style={[style, styles.sharedBackdropStyle]}\n      android_ripple={\n        android_touchRippleColor\n          ? {\n              borderless: true,\n              color: android_touchRippleColor,\n              foreground: true,\n            }\n          : undefined\n      }\n      onPress={pressHandler}\n      {...otherProps}\n    />\n  ) : (\n    // @ts-expect-error\n    <Animated.View\n      style={[style, styles.sharedBackdropStyle]}\n      {...otherProps}\n    />\n  );\n};\n\nconst styles = StyleSheet.create({\n  sharedBackdropStyle: StyleSheet.absoluteFillObject,\n});\n\nexport default AnimatedTouchableBackdropMask;\n"
  },
  {
    "path": "src/components/animatedTouchableBackdropMask/types.d.ts",
    "content": "import {\n  Animated,\n  GestureResponderEvent,\n  OpaqueColorValue,\n  TouchableOpacityProps,\n  ViewProps,\n} from 'react-native';\n\nexport type RegularPropsFor<ComponentType extends 'Touch' | 'View'> =\n  Animated.AnimatedProps<\n    ComponentType extends 'Touch' ? TouchableOpacityProps : ViewProps\n  >;\n\nexport type PropsWithHandler = RegularPropsFor<'Touch'> & {\n  /**\n   * Determines whether backdrop mask should receive press event.\\\n   * Used internally for conditional rendering\n   */\n  isPressable: true;\n\n  /**\n   * Function to handle press event if `isPressable` is true\n   */\n  pressHandler: (evt: GestureResponderEvent) => void;\n};\n\nexport type PropsWithoutHandler = RegularPropsFor<'View'> & {\n  /**\n   * Determines whether backdrop mask should receive press event.\\\n   * Used internally for conditional rendering\n   */\n  isPressable?: false;\n\n  /**\n   * Function to handle press event if `isPressable` is true\n   */\n  pressHandler?: never;\n};\n\nexport type AnimatedTouchableBackdropMaskProps = (\n  | PropsWithHandler\n  | PropsWithoutHandler\n) & {\n  /**\n   * Ripple effect color of the backdrop when touched\n   */\n  android_touchRippleColor?: Animated.WithAnimatedValue<\n    string | OpaqueColorValue\n  >;\n};\n"
  },
  {
    "path": "src/components/backdrop/index.tsx",
    "content": "import React from 'react';\nimport { StyleSheet, View } from 'react-native';\nimport AnimatedTouchableBackdropMask from '../animatedTouchableBackdropMask';\nimport { CUSTOM_BACKDROP_POSITIONS } from '../../types.d';\nimport type { BackdropProps } from './types.d';\n\n/**\n * Abstracted, polymorphic backdrop that handles custom and default backdrop\n */\nconst Backdrop = ({\n  BackdropComponent,\n  backdropPosition = CUSTOM_BACKDROP_POSITIONS.BEHIND,\n  sheetOpen,\n  containerHeight,\n  contentContainerHeight,\n  _animatedHeight,\n  closeOnPress,\n  rippleColor,\n  pressHandler,\n  animatedBackdropOpacity,\n  backdropColor,\n}: BackdropProps) => {\n  const heightStyle = sheetOpen ? containerHeight - contentContainerHeight : 0;\n\n  return BackdropComponent ? (\n    <View\n      style={\n        backdropPosition === CUSTOM_BACKDROP_POSITIONS.BEHIND\n          ? StyleSheet.absoluteFillObject\n          : { height: heightStyle }\n      }\n    >\n      <BackdropComponent _animatedHeight={_animatedHeight} />\n    </View>\n  ) : closeOnPress ? (\n    <AnimatedTouchableBackdropMask\n      isPressable={true}\n      android_touchRippleColor={rippleColor}\n      pressHandler={pressHandler}\n      style={{\n        opacity: animatedBackdropOpacity,\n        backgroundColor: backdropColor,\n      }}\n      touchSoundDisabled\n      key={'TouchableBackdropMask'}\n    />\n  ) : (\n    <AnimatedTouchableBackdropMask\n      isPressable={false}\n      style={{\n        opacity: animatedBackdropOpacity,\n        backgroundColor: backdropColor,\n      }}\n      key={'TouchableBackdropMask'}\n    />\n  );\n};\nexport default Backdrop;\n"
  },
  {
    "path": "src/components/backdrop/types.d.ts",
    "content": "import React from 'react';\nimport {\n  Animated,\n  GestureResponderEvent,\n  OpaqueColorValue,\n} from 'react-native';\nimport { CUSTOM_BACKDROP_POSITIONS } from '../bottomSheet/types.d';\n\nexport type Color =\n  | string\n  | Animated.Value\n  | Animated.AnimatedInterpolation<string | number>\n  | OpaqueColorValue\n  | undefined;\n\nexport type BackdropProps = {\n  BackdropComponent?: React.FunctionComponent<{\n    _animatedHeight: Animated.Value;\n  }>;\n  backdropPosition?:\n    | CUSTOM_BACKDROP_POSITIONS\n    | Lowercase<keyof typeof CUSTOM_BACKDROP_POSITIONS>;\n  sheetOpen: boolean;\n  containerHeight: number;\n  contentContainerHeight: number;\n  _animatedHeight: Animated.Value;\n  closeOnPress: boolean;\n  rippleColor: Color;\n  pressHandler: (evt: GestureResponderEvent) => void;\n  animatedBackdropOpacity: Animated.Value;\n  backdropColor: Color;\n};\n"
  },
  {
    "path": "src/components/bottomSheet/index.tsx",
    "content": "import React, {\n  forwardRef,\n  useCallback,\n  useImperativeHandle,\n  useMemo,\n  useRef,\n  useState,\n  useLayoutEffect,\n  useEffect,\n} from 'react';\nimport {\n  Animated,\n  View,\n  PanResponder,\n  StyleSheet,\n  type LayoutChangeEvent,\n  useWindowDimensions,\n  Keyboard,\n  Platform,\n} from 'react-native';\nimport {\n  DEFAULT_ANIMATION,\n  DEFAULT_BACKDROP_MASK_COLOR,\n  DEFAULT_CLOSE_ANIMATION_DURATION,\n  DEFAULT_HEIGHT,\n  DEFAULT_OPEN_ANIMATION_DURATION,\n} from '../../constant';\nimport DefaultHandleBar from '../defaultHandleBar';\nimport Container from '../container';\nimport normalizeHeight from '../../utils/normalizeHeight';\nimport convertHeight from '../../utils/convertHeight';\nimport useHandleKeyboardEvents from '../../hooks/useHandleKeyboardEvents';\nimport useAnimatedValue from '../../hooks/useAnimatedValue';\nimport Backdrop from '../backdrop';\nimport {\n  type BottomSheetProps,\n  type ToValue,\n  ANIMATIONS,\n  type BottomSheetMethods,\n  CUSTOM_BACKDROP_POSITIONS,\n  type BOTTOMSHEET,\n} from './types.d';\nimport useHandleAndroidBackButtonClose from '../../hooks/useHandleAndroidBackButtonClose';\nimport separatePaddingStyles from '../../utils/separatePaddingStyles';\n\n/**\n * Main bottom sheet component\n */\nconst BottomSheet = forwardRef<BottomSheetMethods, BottomSheetProps>(\n  (\n    {\n      backdropMaskColor = DEFAULT_BACKDROP_MASK_COLOR,\n      children: Children,\n      animationType = DEFAULT_ANIMATION,\n      closeOnBackdropPress = true,\n      height = DEFAULT_HEIGHT,\n      hideDragHandle = false,\n      android_backdropMaskRippleColor,\n      dragHandleStyle,\n      disableBodyPanning = false,\n      disableDragHandlePanning = false,\n      customDragHandleComponent,\n      style: contentContainerStyle,\n      closeOnDragDown = true,\n      containerHeight: passedContainerHeight,\n      customBackdropComponent: CustomBackdropComponent,\n      customBackdropPosition = CUSTOM_BACKDROP_POSITIONS.BEHIND,\n      modal = true,\n      openDuration = DEFAULT_OPEN_ANIMATION_DURATION,\n      closeDuration = DEFAULT_CLOSE_ANIMATION_DURATION,\n      customEasingFunction,\n      android_closeOnBackPress = true,\n      onClose,\n      onOpen,\n      onAnimate,\n      disableKeyboardHandling = false,\n    },\n    ref\n  ) => {\n    /**\n     * ref instance callable methods\n     */\n    useImperativeHandle(ref, () => ({\n      open() {\n        openBottomSheet();\n      },\n      close() {\n        closeBottomSheet();\n      },\n    }));\n\n    /**\n     * If passed container height is a valid number we use that as final container height\n     * else, it may be a percentage value so then we need to change it to a number (so it can be animated).\n     * The change is handled with `onLayout` further down\n     */\n    const SCREEN_HEIGHT = useWindowDimensions().height; // actual container height is measured after layout\n    const [containerHeight, setContainerHeight] = useState(SCREEN_HEIGHT);\n    const [sheetOpen, setSheetOpen] = useState(false);\n\n    // animated properties\n    const _animatedContainerHeight = useAnimatedValue(0);\n    const _animatedBackdropMaskOpacity = useAnimatedValue(0);\n    const _animatedHeight = useAnimatedValue(0);\n\n    const contentWrapperRef = useRef<View>(null);\n\n    /** cached _nativeTag property of content container */\n    const cachedContentWrapperNativeTag = useRef<number | undefined>(undefined);\n\n    // here we separate all padding that may be applied via contentContainerStyle prop,\n    // these paddings will be applied to the `View` diretly wrapping `ChildNodes` in content container.\n    // All these is so that paddings applied to sheet doesn't affect the drag handle\n    // TODO: find better way to memoize `separatePaddingStyles` function return value to avoid\n    // redundant re-runs\n    const sepStyles = useMemo(\n      () => separatePaddingStyles(contentContainerStyle),\n      [contentContainerStyle]\n    );\n\n    // Animation utility\n    const Animators = useMemo(\n      () => ({\n        _slideEasingFn(value: number) {\n          return value === 1 ? 1 : 1 - Math.pow(2, -10 * value);\n        },\n        _springEasingFn(value: number) {\n          const c4 = (2 * Math.PI) / 2.5;\n          return value === 0\n            ? 0\n            : value === 1\n            ? 1\n            : Math.pow(2, -9 * value) * Math.sin((value * 4.5 - 0.75) * c4) + 1;\n        },\n        animateContainerHeight(toValue: ToValue, duration: number = 0) {\n          return Animated.timing(_animatedContainerHeight, {\n            toValue: toValue,\n            useNativeDriver: false,\n            duration: duration,\n          });\n        },\n        animateBackdropMaskOpacity(toValue: ToValue, duration: number) {\n          // we use passed open and close durations when animation type is fade\n          // but we use half of that for other animation types for good UX\n          const _duration =\n            animationType === ANIMATIONS.FADE ? duration : duration / 2.5;\n\n          return Animated.timing(_animatedBackdropMaskOpacity, {\n            toValue: toValue,\n            useNativeDriver: false,\n            duration: _duration,\n          });\n        },\n        animateHeight(toValue: ToValue, duration: number) {\n          return Animated.timing(_animatedHeight, {\n            toValue,\n            useNativeDriver: false,\n            duration: duration,\n            easing:\n              customEasingFunction && typeof customEasingFunction === 'function'\n                ? customEasingFunction\n                : animationType === ANIMATIONS.SLIDE\n                ? this._slideEasingFn\n                : this._springEasingFn,\n          });\n        },\n      }),\n      [\n        animationType,\n        customEasingFunction,\n        _animatedContainerHeight,\n        _animatedBackdropMaskOpacity,\n        _animatedHeight,\n      ]\n    );\n\n    const interpolatedOpacity = useMemo(\n      () =>\n        animationType === ANIMATIONS.FADE\n          ? _animatedBackdropMaskOpacity.interpolate({\n              inputRange: [0, 0.5, 1],\n              outputRange: [0, 0.3, 1],\n              extrapolate: 'clamp',\n            })\n          : contentContainerStyle?.opacity,\n      [animationType, contentContainerStyle, _animatedBackdropMaskOpacity]\n    );\n\n    /**\n     * `height` prop converted from percentage e.g `'50%'` to pixel unit e.g `320`,\n     * relative to `containerHeight` or `DEVICE_SCREEN_HEIGHT`.\n     * Also auto calculates and adjusts container wrapper height when `containerHeight`\n     * or `height` changes\n     */\n    const convertedHeight = useMemo(() => {\n      const newHeight = convertHeight(height, containerHeight, hideDragHandle);\n\n      // FIXME: we use interface-undefined but existing property `_value` here and it's risky\n      // @ts-expect-error\n      const curHeight = _animatedHeight._value;\n      if (sheetOpen && newHeight !== curHeight) {\n        if (animationType === ANIMATIONS.FADE)\n          _animatedHeight.setValue(newHeight);\n        else\n          Animators.animateHeight(\n            newHeight,\n            newHeight > curHeight ? openDuration : closeDuration\n          ).start();\n      }\n      return newHeight;\n    }, [\n      containerHeight,\n      height,\n      animationType,\n      sheetOpen,\n      Animators,\n      _animatedHeight,\n      closeDuration,\n      hideDragHandle,\n      openDuration,\n    ]);\n\n    /**\n     * If `disableKeyboardHandling` is false, handles keyboard pop up for both platforms,\n     * by auto adjusting sheet layout accordingly\n     */\n    const keyboardHandler = useHandleKeyboardEvents(\n      !disableKeyboardHandling,\n      convertedHeight,\n      sheetOpen,\n      Animators.animateHeight,\n      contentWrapperRef\n    );\n\n    /**\n     * Returns conditioned gesture handlers for content container and handle bar elements\n     */\n    const panHandlersFor = (view: 'handlebar' | 'contentwrapper') => {\n      if (view === 'handlebar' && disableDragHandlePanning) return null;\n      if (view === 'contentwrapper' && disableBodyPanning) return null;\n      return PanResponder.create({\n        onMoveShouldSetPanResponder: (evt) => {\n          /**\n           * `FiberNode._nativeTag` is stable across renders so we use it to determine\n           * whether content container or it's child should respond to touch move gesture.\n           *\n           * The logic is, when content container is laid out, we extract it's _nativeTag property and cache it\n           * So later when a move gesture event occurs within it, we compare the cached _nativeTag with the _nativeTag of\n           * the event target's _nativeTag, if they match, then content container should respond, else its children should.\n           * Also, when the target is the handle bar, we le it handle geture unless panning is disabled through props\n           */\n          return view === 'handlebar'\n            ? true\n            : cachedContentWrapperNativeTag.current ===\n                // @ts-expect-error\n                evt?.target?._nativeTag;\n        },\n        onPanResponderMove: (_, gestureState) => {\n          if (gestureState.dy > 0) {\n            // backdrop opacity relative to the height of the content sheet\n            // to makes the backdrop more transparent as you drag the content sheet down\n            const relativeOpacity = 1 - gestureState.dy / convertedHeight;\n            _animatedBackdropMaskOpacity.setValue(relativeOpacity);\n\n            if (animationType !== ANIMATIONS.FADE)\n              _animatedHeight.setValue(convertedHeight - gestureState.dy);\n          }\n        },\n        onPanResponderRelease(_, gestureState) {\n          if (gestureState.dy >= convertedHeight / 3 && closeOnDragDown) {\n            closeBottomSheet();\n          } else {\n            _animatedBackdropMaskOpacity.setValue(1);\n            if (animationType !== ANIMATIONS.FADE)\n              Animators.animateHeight(\n                convertedHeight,\n                openDuration / 2\n              ).start();\n          }\n        },\n      }).panHandlers;\n    };\n\n    /**\n     * Polymorphic content container handle bar component\n     */\n    /* eslint-disable react/no-unstable-nested-components, react-native/no-inline-styles */\n    const PolymorphicHandleBar: React.FunctionComponent<{}> = () => {\n      const CustomHandleBar = customDragHandleComponent;\n      return hideDragHandle ? null : CustomHandleBar &&\n        typeof CustomHandleBar === 'function' ? (\n        <View style={{ alignSelf: 'center' }} {...panHandlersFor('handlebar')}>\n          <CustomHandleBar _animatedHeight={_animatedHeight} />\n        </View>\n      ) : (\n        <DefaultHandleBar\n          style={dragHandleStyle}\n          {...panHandlersFor('handlebar')}\n        />\n      );\n    };\n    /* eslint-enable react/no-unstable-nested-components, react-native/no-inline-styles */\n\n    /**\n     * Extracts and caches the _nativeTag property of ContentWrapper\n     */\n    let extractNativeTag = useCallback(({ target }: LayoutChangeEvent) => {\n      const tag =\n        Platform.OS === 'web'\n          ? undefined\n          : // @ts-expect-error\n            target?._nativeTag;\n      if (!cachedContentWrapperNativeTag.current)\n        cachedContentWrapperNativeTag.current = tag;\n    }, []);\n\n    /**\n     * Expands the bottom sheet.\n     */\n    const openBottomSheet = () => {\n      // 1. open container\n      // 2. if using fade animation, set content container height convertedHeight manually, animate backdrop.\n      // else, animate backdrop and content container height in parallel\n      Animators.animateContainerHeight(\n        !modal ? convertedHeight : containerHeight\n      ).start();\n      if (animationType === ANIMATIONS.FADE) {\n        _animatedHeight.setValue(convertedHeight);\n        Animators.animateBackdropMaskOpacity(1, openDuration).start();\n      } else {\n        Animators.animateBackdropMaskOpacity(1, openDuration).start();\n        Animators.animateHeight(convertedHeight, openDuration).start();\n      }\n      setSheetOpen(true);\n\n      if (onOpen) {\n        onOpen();\n      }\n    };\n\n    const closeBottomSheet = () => {\n      // 1. fade backdrop\n      // 2. if using fade animation, close container, set content wrapper height to 0.\n      // else animate content container height & container height to 0, in sequence\n      Animators.animateBackdropMaskOpacity(0, closeDuration).start((anim) => {\n        if (anim.finished) {\n          if (animationType === ANIMATIONS.FADE) {\n            Animators.animateContainerHeight(0).start();\n            _animatedHeight.setValue(0);\n          } else {\n            Animators.animateHeight(0, closeDuration).start();\n            Animators.animateContainerHeight(0).start();\n          }\n        }\n      });\n      setSheetOpen(false);\n      keyboardHandler?.removeKeyboardListeners();\n      Keyboard.dismiss();\n\n      if (onClose) {\n        onClose();\n      }\n    };\n\n    const containerViewLayoutHandler = (event: LayoutChangeEvent) => {\n      const newHeight = event.nativeEvent.layout.height;\n      setContainerHeight(newHeight);\n      // incase `containerHeight` prop value changes when bottom sheet is expanded\n      // we need to manually update the container height\n      if (sheetOpen) _animatedContainerHeight.setValue(newHeight);\n    };\n\n    /**\n     * Implementation logic for `onAnimate` prop\n     */\n    useEffect(() => {\n      if (onAnimate && typeof onAnimate === 'function') {\n        const animate = (\n          state: Parameters<Animated.ValueListenerCallback>['0']\n        ) => onAnimate(state.value);\n        let listenerId: string;\n        if (animationType === 'fade')\n          listenerId = _animatedBackdropMaskOpacity.addListener(animate);\n        else listenerId = _animatedHeight.addListener(animate);\n\n        return () => {\n          if (animationType === 'fade')\n            _animatedBackdropMaskOpacity.removeListener(listenerId);\n          else _animatedHeight.removeListener(listenerId);\n        };\n      }\n\n      return;\n    }, [\n      onAnimate,\n      animationType,\n      _animatedBackdropMaskOpacity,\n      _animatedHeight,\n    ]);\n\n    /**\n     * Handles auto adjusting container view height and clamping\n     * and normalizing `containerHeight` prop upon change, if its a number.\n     * Also auto adjusts when orientation changes\n     */\n    useLayoutEffect(() => {\n      if (!modal) return; // no auto layout adjustment when backdrop is hidden\n      else {\n        if (typeof passedContainerHeight === 'number') {\n          setContainerHeight(normalizeHeight(passedContainerHeight));\n          if (sheetOpen)\n            _animatedContainerHeight.setValue(passedContainerHeight);\n        } else if (\n          typeof passedContainerHeight === 'undefined' &&\n          containerHeight !== SCREEN_HEIGHT\n        ) {\n          setContainerHeight(SCREEN_HEIGHT);\n          if (sheetOpen) _animatedContainerHeight.setValue(SCREEN_HEIGHT);\n        }\n      }\n    }, [\n      passedContainerHeight,\n      SCREEN_HEIGHT,\n      sheetOpen,\n      containerHeight,\n      modal,\n      _animatedContainerHeight,\n    ]);\n\n    /**\n     * Handles hardware back button press for android\n     */\n    useHandleAndroidBackButtonClose(\n      android_closeOnBackPress,\n      closeBottomSheet,\n      sheetOpen\n    );\n\n    // Children\n    const ChildNodes =\n      typeof Children === 'function' ? (\n        <Children _animatedHeight={_animatedHeight} />\n      ) : (\n        Children\n      );\n\n    return (\n      <>\n        {typeof passedContainerHeight === 'string' ? (\n          /**\n           * Below View handles converting `passedContainerHeight` from string to a number (to be animatable).\n           * It does this by taking the string height passed via `containerHeight` prop,\n           * and returning it's numeric equivalent after rendering, via its `onLayout` so we can\n           * use that as the final container height.\n           */\n          <View\n            onLayout={containerViewLayoutHandler}\n            style={{\n              height: passedContainerHeight,\n            }}\n          />\n        ) : null}\n\n        {/* Container */}\n        <Container style={{ height: _animatedContainerHeight }}>\n          {/* Backdrop */}\n          {modal ? (\n            <Backdrop\n              BackdropComponent={CustomBackdropComponent}\n              _animatedHeight={_animatedHeight}\n              animatedBackdropOpacity={_animatedBackdropMaskOpacity}\n              backdropColor={backdropMaskColor}\n              backdropPosition={customBackdropPosition}\n              closeOnPress={closeOnBackdropPress}\n              containerHeight={containerHeight}\n              contentContainerHeight={convertedHeight}\n              pressHandler={closeBottomSheet}\n              rippleColor={android_backdropMaskRippleColor}\n              sheetOpen={sheetOpen}\n            />\n          ) : null}\n          {/* content container */}\n          <Animated.View\n            ref={contentWrapperRef}\n            key={'BottomSheetContentContainer'}\n            onLayout={extractNativeTag}\n            /* Merge external and internal styles carefully and orderly */\n            style={[\n              !modal ? materialStyles.contentContainerShadow : false,\n              materialStyles.contentContainer,\n              // we apply styles other than padding here\n              sepStyles?.otherStyles,\n              {\n                height: _animatedHeight,\n                minHeight: _animatedHeight,\n                opacity: interpolatedOpacity,\n              },\n            ]}\n            {...panHandlersFor('contentwrapper')}\n          >\n            <PolymorphicHandleBar />\n\n            <View\n              // we apply padding styles here to not affect drag handle above\n              style={sepStyles?.paddingStyles}\n            >\n              {ChildNodes}\n            </View>\n          </Animated.View>\n        </Container>\n      </>\n    );\n  }\n) as BOTTOMSHEET;\n\nBottomSheet.displayName = 'BottomSheet';\nBottomSheet.ANIMATIONS = ANIMATIONS;\n\nconst materialStyles = StyleSheet.create({\n  contentContainer: {\n    backgroundColor: '#F7F2FA',\n    width: '100%',\n    overflow: 'hidden',\n    borderTopLeftRadius: 28,\n    borderTopRightRadius: 28,\n  },\n  contentContainerShadow:\n    Platform.OS === 'android'\n      ? {\n          elevation: 7,\n        }\n      : {\n          shadowColor: '#000',\n          shadowOffset: {\n            width: 0,\n            height: 3,\n          },\n          shadowOpacity: 0.29,\n          shadowRadius: 4.65,\n        },\n});\n\nexport default BottomSheet;\n"
  },
  {
    "path": "src/components/bottomSheet/types.d.ts",
    "content": "import { Animated, OpaqueColorValue, ViewProps, ViewStyle } from 'react-native';\nimport {\n  ANIMATIONS,\n  CUSTOM_BACKDROP_POSITIONS,\n  type BottomSheetMethods,\n} from '../../types.d';\nimport React from 'react';\n\nexport type SheetStyleProp = Omit<\n  ViewStyle,\n  'height' | 'minHeight' | 'maxHeight'\n>;\n\n// short hand for toValue key of Animator methods\ntype ToValue = Animated.TimingAnimationConfig['toValue'];\n\n// this is to accomodate static `ANIMATIONS` property of BottomSheet function below\ntype BOTTOMSHEET = React.ForwardRefExoticComponent<\n  BottomSheetProps & React.RefAttributes<BottomSheetMethods>\n> & { ANIMATIONS: typeof ANIMATIONS };\n\ntype AnimationType = ANIMATIONS | Lowercase<keyof typeof ANIMATIONS>;\n\ntype AnimationEasingFunction = (x: number) => number;\n\n/**\n * Props types for bottom sheet component\n */\ninterface BottomSheetProps {\n  /**\n   * Height of the bottom sheet when expanded. This value will be relative to `containerHeight`\n   * if it's supplied, or the screen's height otherwise.\n   * Value can be in pixel units (number) or percentage (string).\n   *\n   * `Default: '50%'`\n   *\n   * @type {number | string}\n   * @default '50%'\n   * @example\n   * height={300}\n   * // or\n   * height={'50%'}\n   *\n   */\n  height?: number | string;\n\n  /**\n   * Extra styles to apply to bottom sheet (the `View` that wraps its children).\n   *\n   * `Note:` style properties `height`, `maxHeight`, `minHeight` will be ignored.\n   * If you want to set sheet's height, pass the `height` prop instead.\n   * @type {SheetStyleProp}\n   */\n  style?: SheetStyleProp;\n\n  /**\n   * Height of the bottom sheet's overall container, this will be the height of\n   * the entire bottom sheet including the backdrop mask. height passed through the `height` prop\n   * will be relative to this.\n   *\n   * `Note:` By default this will be the height of the device's screen.\n   *\n   * `Default: DEVICE'S SCREEN HEIGHT`\n   * @type {number | string}\n   * @default {DEVICE SCREEN HEIGHT}\n   */\n  containerHeight?: ViewStyle['height'];\n\n  /**\n   * Animation to use when opening and closing the bottom sheet.\n   * Use exported `ANIMATIONS` enum to pass value to this prop\n   *\n   * `Default: 'slide'`\n   * @type {AnimationType | ANIMATIONS}\n   * @default \"slide\" | ANIMATIONS.SLIDE\n   * @example\n   * ```tsx\n   * import BottomSheet, {ANIMATIONS} from '@devvie/bottom-sheet';\n   * ...\n   * <BottomSheet animationType={ANIMATIONS.SLIDE}>\n   * ...\n   * </BottomSheet>\n   * ```\n   */\n  animationType?: AnimationType;\n\n  /**\n   * Color of the scrim or backdrop mask when `modal` is true.\n   *\n   * `Default: '#00000052'` (i.e black with 32% opacity)\n   * @type {string | OpaqueColorValue}\n   * @default '#00000052'\n   */\n  backdropMaskColor?: string | OpaqueColorValue;\n\n  /**\n   * Determines whether the bottom sheet will close when the scrim or backdrop mask is pressed.\n   *\n   * `Default: true`\n   * @type {boolean}\n   * @default true\n   */\n  closeOnBackdropPress?: boolean;\n\n  /**\n   * Determines whether bottom sheet will close when its dragged down\n   * below 1/3 (one quater) of its height.\n   *\n   * `Default:true`\n   * @type boolean\n   * @default true\n   */\n  closeOnDragDown?: boolean;\n\n  /**\n   * When true, hides the sheet's drag handle. The drag handle is visible by default.\n   *\n   * `Note:` When true, custom drag handle component will also be hidden.\n   *\n   * `Default: false`\n   *\n   * @type {boolean}\n   * @default false\n   */\n  hideDragHandle?: boolean;\n\n  /**\n       * Custom drag handle component to replace the default bottom sheet's drag handle.\n       *\n       * This component will be passed the animated `height` and `translateY` values of the bottom sheet,\n       * which can be used to interpolate or extended animations to its children.\n       *\n       * `Note:` Styles passed through `dragHandleStyle` prop won't be applied to it.\n       *\n       * @type {React.FC<{animatedHeight: Animated.Value, animatedYTranslation:Animated.Value}>}\n       * @example\n       * ```tsx\n       * // Below example will animate the custom drag handle's width as the \n       * // bottom sheet is being dragged/panned down\n       * <BottomSheet\n            customDragHandleComponent={(props) => (\n              <Animated.View\n              style={{\n                  height: 5,\n                  backgroundColor: 'orange',\n                  width: props._animatedYTranslation.interpolate({\n                    inputRange: [0, 25, 200],\n                    outputRange: [20, 50, 100],\n                  }),\n                }}\n              />\n            )}>\n            ...\n          </BottomSheet>\n       * ```\n       */\n  customDragHandleComponent?: React.FC<{\n    /**\n     * Animated height of the bottom sheet when expanding\n     * @type {Animated.Value}\n     */\n    _animatedHeight: Animated.Value;\n  }>;\n\n  /**\n   * Extra styles to apply to the drag handle.\n   *\n   * `Note:` These styles will be ignored when `customDragHandleComponent` is provided\n   * @type {ViewStyle}\n   */\n  dragHandleStyle?: ViewStyle;\n\n  /**\n   * When true, prevents the bottom sheet from being panned down by dragging its drag handle.\n   * This prop also applies to custom drag handle component provided via `customDragHandleComponent` prop.\n   *\n   * The bottom sheet is draggable by it's drag handle by default\n   *\n   * `Default:false`;\n   * @type {boolean}\n   * @default false\n   */\n  disableDragHandlePanning?: boolean;\n\n  /**\n   * When true, prevents the bottom sheet from being dragged/panned down on its body.\n   * The bottom sheet body is draggable by default.\n   *\n   * `Default:false`;\n   * @type boolean\n   * @default false\n   */\n  disableBodyPanning?: boolean;\n\n  /**\n   * When `closeOnBackdropPress` is `true`, color of the ripple effect that occurs when scrim or backdrop mask is pressed.\n   *\n   * `Note:` Only works for Android\n   *\n   * `Default: none` (i.e no ripple effect);\n   * @platform Android\n   * @type (string | OpaqueColorValue)\n   * @default undefined\n   */\n  android_backdropMaskRippleColor?: string | OpaqueColorValue;\n\n  /**\n   * Custom component for sheet's scrim or backdrop mask.\\\n   * `Note:` The component will be passed animated height value\n   *\n   * @type {React.Component}\n   * @default null\n   */\n  customBackdropComponent?: React.FunctionComponent<{\n    _animatedHeight: Animated.Value;\n  }>;\n\n  /**\n   * When `customBackdropComponent` is provided, determines its position within the sheet.\n   *\n   * **'top'** - positions the custom backdrop component directly above the sheet\\\n   * **'behind'** - positions the custom backdrop component behind the sheet.\n   * This is the default behaviour\n   *\n   * `Note:` This prop only applies to custom backdrop component\n   *\n   * `Default: 'behind'`\n   * @type {\"top\" | \"behind\" | CUSTOM_BACKDROP_POSITIONS}\n   * @default \"behind\" / CUSTOM_BACKDROP_POSITIONS.BEHIND\n   */\n  customBackdropPosition?:\n    | CUSTOM_BACKDROP_POSITIONS\n    | Lowercase<keyof typeof CUSTOM_BACKDROP_POSITIONS>;\n\n  /**\n   * Determines whether sheet is a modal.\n   *\n   * A modal sheet has a scrim or backdrop mask, while a standard (non-modal) sheet doesn't.\n   *\n   * `Note:` When false, this will also hide custom scrim/backdrop component supplied via `customBackdropComponent` prop.\n   *\n   * `Default: true`\n   * @type boolean\n   * @default true\n   */\n  modal?: boolean;\n\n  /**\n   * Contents of the bottom sheet. Can be element(s) or a component.\n   *\n   * If this is a component, it will be passed animated\n   * height of bottom sheet via `_animatedHeight` prop\n   *\n   * `Default: null`\n   * @type {ViewProps['children'] | React.FunctionComponent<{_animatedHeight: Animated.Value}>}\n   * @default null\n   */\n  children:\n    | ViewProps['children']\n    | React.FunctionComponent<{ _animatedHeight: Animated.Value }>;\n\n  /**\n   * Duration for sheet opening animation.\n   *\n   * `Default: 500`\n   * @type number\n   * @default 500\n   */\n  openDuration?: number;\n\n  /**\n   * Duration for sheet closing animation.\n   *\n   * `Default: 500`\n   * @type number\n   * @default 500\n   */\n  closeDuration?: number;\n\n  /**\n   * Custom easing function for driving sheet's animation.\\\n   * If provided, easing function for passed `animationType` will be replaced with this,\n   * rendering `animationType` prop obsolete\n   *\n   * `Default: ANIMATIONS.SLIDE`\n   * @type {(AnimationEasingFunction)}\n   * @default {ANIMATIONS.SLIDE}\n   */\n  customEasingFunction?: AnimationEasingFunction;\n\n  /**\n   * Determines whether sheet will close or not when device back button is pressed, on Android.\n   *\n   * __In a typical app screen (where back button takes user to the previous screen):__ \\\n   * _If `true` and sheet is open, the user has to press the back button twice to exit the screen.\n   * The first press closes the sheet and the second exits the screen.\\\n   * If `false` and sheet is open, the user has to close the sheet through another means like backdrop press\n   * and then press the back button after that to exit the screen_\n   *\n   * `Note:` Only applies for Android\n   *\n   * `Default: true`\n   * @platform Android\n   * @type {boolean}\n   * @default {true}\n   */\n  android_closeOnBackPress?: boolean;\n\n  /**\n   * Сallback function that is called when the bottom sheet starts to close\n   *\n   * @type {Function}\n   * @default undefined\n   * @example\n   * ```tsx\n   * <BottomSheet\n   *   onClose={() => {\n   *     console.log('Bottom Sheet closing.');\n   *   }}\n   * />\n   * ```\n   */\n  onClose?: Function;\n\n  /**\n   * Сallback function that is called when the bottom sheet starts to open\n   *\n   * @type {Function}\n   * @default undefined\n   * @example\n   * ```tsx\n   * <BottomSheet\n   *   onOpen={() => {\n   *     console.log('Bottom Sheet opening.');\n   *   }}\n   * />\n   * ```\n   */\n  onOpen?: Function;\n\n  /**\n   * Function called with an animated number indicating the\n   * progress of the opening/closing animation when sheet is being opened or closed.\n   *\n   * When `animationType` prop is __not__ set to `ANIMATION.FADE` or `'fade'`,\n   * the number passed to this function will go from _`0`_ to _`sheet's height`_ (when sheet's opening)\n   * or _`sheet's height`_ to  _`0`_ (when sheet's closing).\n   *\n   * When `animationType` prop is set to `ANIMATION.FADE` or `'fade'`,\n   * the number will go from `0.0` to `1.0` (when sheet's opening)\n   * or _`1.0`_ to  _`0.0`_ (when sheet's closing).\n   *\n   * `Default: undefined`\n   * @default {undefined}\n   * @type {(animatedHeightOrOpacity: number) => void}\n   * ### Example with 'slide' or 'spring' animation\n   * @example\n   * ```tsx\n   * <BottomSheet\n       ref={sheetRef}\n       height={300}\n       animationType=\"slide\"\n       onAnimate={(animatedValue) => {\n         console.log(animatedValue); // (0)...(150)...(300)\n       }}\n    >\n       ...\n    </BottomSheet>\n   * ```\n   * ### Example with 'fade' animation\n   * @example \n   * ```tsx\n   * <BottomSheet\n       ref={sheetRef}\n       height={300}\n       animationType=\"fade\"\n       onAnimate={(animatedValue) => {\n         console.log(animatedValue); // (0.0)...(0.5)...(1.0)\n       }}\n    >\n       ...\n    </BottomSheet>\n     ```\n   */\n  onAnimate?: (animatedHeightOrOpacity: number) => void;\n\n  /**\n   * By default, the sheet handles keyboard pop-out automatically and smartly,\n   * setting this to `true` disables that behavior, essentially leaving\n   * the system to handle the keyboard the native way.\n   *\n   * `Default: false`\n   * @type {boolean}\n   * @default {false}\n   */\n  disableKeyboardHandling?: boolean;\n}\n\nexport {\n  ANIMATIONS,\n  BottomSheetProps,\n  CUSTOM_BACKDROP_POSITIONS,\n  BottomSheetMethods,\n  BOTTOMSHEET,\n  AnimationEasingFunction,\n  ToValue,\n};\n"
  },
  {
    "path": "src/components/container/index.tsx",
    "content": "import React from 'react';\nimport { forwardRef } from 'react';\nimport { Animated, StyleSheet, View } from 'react-native';\n\n/**\n * This is the overall container view of the bottom sheet\n */\nconst Container = forwardRef<\n  View,\n  Animated.ComponentProps<typeof Animated.View>\n>(({ style, ...otherProps }, ref) => (\n  <Animated.View ref={ref} style={[styles.container, style]} {...otherProps} />\n));\n\nconst styles = StyleSheet.create({\n  container: {\n    position: 'absolute',\n    justifyContent: 'flex-end',\n    right: 0,\n    left: 0,\n    // required to snap container to bottom of screen, so animation will start from botom to up\n    bottom: 0,\n    backgroundColor: 'transparent',\n    opacity: 1,\n  },\n});\n\nexport default Container;\n"
  },
  {
    "path": "src/components/defaultHandleBar/index.tsx",
    "content": "import React from 'react';\nimport { StyleSheet, View } from 'react-native';\nimport type { DefaultHandleBarProps } from './types.d';\n\n/**\n * This is the default handle bar component used when no custom handle bar component is provided\n */\nconst DefaultHandleBar = ({ style, ...otherProps }: DefaultHandleBarProps) => (\n  <View style={materialStyles.dragHandleContainer} {...otherProps}>\n    <View style={[materialStyles.dragHandle, style]} />\n  </View>\n);\n\nconst materialStyles = StyleSheet.create({\n  dragHandleContainer: {\n    padding: 18,\n    width: 50,\n    alignSelf: 'center',\n  },\n  dragHandle: {\n    height: 4,\n    width: 32,\n    backgroundColor: '#49454F',\n    opacity: 0.4,\n    alignSelf: 'center',\n    borderRadius: 50,\n  },\n});\n\n// this will be used in `convertHeight` for clamping sheet height\nexport const DEFAULT_HANDLE_BAR_DEFAULT_HEIGHT = 25; // paddingTop (10) + paddingBottom (10) + height (5)\n\nexport default DefaultHandleBar;\n"
  },
  {
    "path": "src/components/defaultHandleBar/types.d.ts",
    "content": "import { ViewProps } from 'react-native';\n\nexport type DefaultHandleBarProps = ViewProps;\n"
  },
  {
    "path": "src/constant/index.ts",
    "content": "import { ANIMATIONS } from '../types.d';\n\nconst DEFAULT_BACKDROP_MASK_COLOR = '#00000052';\nconst DEFAULT_HEIGHT = '50%';\nconst DEFAULT_ANIMATION = ANIMATIONS.SLIDE;\nconst DEFAULT_OPEN_ANIMATION_DURATION = 500;\nconst DEFAULT_CLOSE_ANIMATION_DURATION = 500;\n\nexport {\n  DEFAULT_BACKDROP_MASK_COLOR,\n  DEFAULT_HEIGHT,\n  DEFAULT_ANIMATION,\n  DEFAULT_OPEN_ANIMATION_DURATION,\n  DEFAULT_CLOSE_ANIMATION_DURATION,\n};\n"
  },
  {
    "path": "src/hooks/useAnimatedValue.ts",
    "content": "import { useRef } from 'react';\nimport { Animated } from 'react-native';\n\n/**\n * Convenience hook for abstracting/storing Animated values.\n * Pass your initial number value, get an animated value back.\n * @param {number} initialValue Initial animated value\n */\nconst useAnimatedValue = (initialValue: number = 0): Animated.Value => {\n  const animatedValue = useRef(new Animated.Value(initialValue)).current;\n  return animatedValue;\n};\n\nexport default useAnimatedValue;\n"
  },
  {
    "path": "src/hooks/useHandleAndroidBackButtonClose/index.ts",
    "content": "import { useEffect, useRef } from 'react';\nimport { BackHandler, type NativeEventSubscription } from 'react-native';\nimport type { UseHandleAndroidBackButtonClose } from './types.d';\n\n/**\n * Handles closing sheet when back button is pressed on\n * android and sheet is opened\n *\n * @param {boolean} shouldClose Whether to close sheet when back button is pressed\n * @param {boolean} closeSheet Function to call to close the sheet\n * @param {boolean} sheetOpen Determines the visibility of the sheet\n */\nconst useHandleAndroidBackButtonClose: UseHandleAndroidBackButtonClose = (\n  shouldClose = true,\n  closeSheet,\n  sheetOpen = false\n) => {\n  const handler = useRef<NativeEventSubscription>();\n  useEffect(() => {\n    handler.current = BackHandler.addEventListener('hardwareBackPress', () => {\n      if (sheetOpen) {\n        if (shouldClose) {\n          closeSheet?.();\n        }\n        return true; // prevent back press event bubbling as long as sheet is open\n      } else return false; // when sheet is closed allow bubbling\n    });\n    return () => {\n      handler.current?.remove?.();\n    };\n  }, [shouldClose, closeSheet, sheetOpen]);\n};\n\nexport default useHandleAndroidBackButtonClose;\n"
  },
  {
    "path": "src/hooks/useHandleAndroidBackButtonClose/types.d.ts",
    "content": "export type HookReturn = void;\n\n/**\n * Function type\n */\nexport type UseHandleAndroidBackButtonClose = (\n  /** Whether to close sheet when back button is pressed*/\n  shouldClose: boolean,\n  /** Function to call to close the sheet */\n  closeSheet: () => void,\n  /** Determines the sheet's visibility state */\n  sheetOpen: boolean\n) => HookReturn;\n"
  },
  {
    "path": "src/hooks/useHandleKeyboardEvents/index.ts",
    "content": "import { useEffect, useRef } from 'react';\nimport {\n  type EmitterSubscription,\n  Keyboard,\n  View,\n  useWindowDimensions,\n} from 'react-native';\nimport type { HeightAnimationDriver, UseHandleKeyboardEvents } from './types.d';\n\n/**\n * Handles keyboard pop out by adjusting sheet's layout when a `TextInput` within\n * the sheet receives focus.\n *\n * @param {boolean} keyboardHandlingEnabled Determines whether this hook will go on to handle keyboard\n * @param {number} sheetHeight Initial sheet's height before keyboard pop out\n * @param {boolean} sheetOpen Indicates whether the sheet is open or closed\n * @param {HeightAnimationDriver} heightAnimationDriver Animator function to be called with new\n * sheet height when keyboard is out so it can adjust the sheet height with animation\n * @param {React.MutableRefObject<View>} contentWrapperRef Reference to the content wrapper view\n * @return {{removeKeyboardListeners:Function;} | null} An Object with an unsubscriber function or `null`\n *  when `keyboardHandlingEnabled` is false\n */\nconst useHandleKeyboardEvents: UseHandleKeyboardEvents = (\n  keyboardHandlingEnabled: boolean,\n  sheetHeight: number,\n  sheetOpen: boolean,\n  heightAnimationDriver: HeightAnimationDriver,\n  contentWrapperRef: React.RefObject<View>\n) => {\n  const SCREEN_HEIGHT = useWindowDimensions().height;\n  const keyboardHideSubscription = useRef<EmitterSubscription>();\n  const keyboardShowSubscription = useRef<EmitterSubscription>();\n\n  const unsubscribe = () => {\n    keyboardHideSubscription.current?.remove?.();\n    keyboardShowSubscription.current?.remove?.();\n  };\n\n  useEffect(() => {\n    if (keyboardHandlingEnabled) {\n      keyboardShowSubscription.current = Keyboard.addListener(\n        'keyboardDidShow',\n        ({ endCoordinates: { height: keyboardHeight } }) => {\n          if (sheetOpen) {\n            // Exaggeration of the actual height (24) of the autocorrect view\n            // that appears atop android keyboard\n            const keyboardAutoCorrectViewHeight = 50;\n            contentWrapperRef.current?.measure?.((...result) => {\n              const sheetYOffset = result[5]; // Y offset of the sheet after keyboard is out\n              const actualSheetHeight = SCREEN_HEIGHT - sheetYOffset; // new height of the sheet (after keyboard offset)\n              /**\n               * We determine soft input/keyboard mode based on the difference between the new sheet height and the\n               * initial height after keyboard is opened. If there's no much difference, it's adjustPan\n               * (keyboard overlays sheet), else it's adjustResize (sheet is pushed up)\n               */\n              const sheetIsOverlayed =\n                actualSheetHeight - sheetHeight < keyboardAutoCorrectViewHeight;\n              const remainingSpace = SCREEN_HEIGHT - keyboardHeight;\n\n              /**\n               * this is 50% of the remaining space (SCREEN_HEIGHT - keyboardHeight) that remains atop the keybaord\n               */\n              const fiftyPercent = 0.5 * remainingSpace;\n              const minSheetHeight = 50;\n              // allow very short sheet e.g 10 to increase to 50 and\n              // very long to clamp withing availablle space;\n              let newSheetHeight = Math.max(\n                minSheetHeight,\n                Math.min(sheetHeight, fiftyPercent)\n              );\n              if (sheetIsOverlayed) newSheetHeight += keyboardHeight;\n              heightAnimationDriver(newSheetHeight, 400).start();\n            });\n          }\n        }\n      );\n\n      keyboardHideSubscription.current = Keyboard.addListener(\n        'keyboardDidHide',\n        (_) => {\n          if (sheetOpen) heightAnimationDriver(sheetHeight, 200).start();\n        }\n      );\n      return unsubscribe;\n    }\n    return;\n  }, [\n    keyboardHandlingEnabled,\n    sheetHeight,\n    SCREEN_HEIGHT,\n    sheetOpen,\n    heightAnimationDriver,\n    contentWrapperRef,\n  ]);\n\n  return keyboardHandlingEnabled\n    ? {\n        removeKeyboardListeners: unsubscribe,\n      }\n    : null;\n};\n\nexport default useHandleKeyboardEvents;\n"
  },
  {
    "path": "src/hooks/useHandleKeyboardEvents/types.d.ts",
    "content": "import React from 'react';\nimport { Animated, View } from 'react-native';\n\nexport type HookReturn = {\n  /**\n   * Removes all keyboard listeners, typically when sheet is closed\n   */\n  removeKeyboardListeners: () => void;\n} | null;\n\n/**\n * Handles keyboard pop out\n */\nexport type UseHandleKeyboardEvents = (\n  /** Determines whether this hook will go on to handle keyboard */\n  keyboardHandlingEnabled: boolean,\n  /**\n   * initial height of the sheet\n   */\n  sheetHeight: number,\n  /** determines whether sheet is expanded */\n  sheetOpen: boolean,\n  /** function that can drive/animate sheet height */\n  SheetHeightAnimationDriver: HeightAnimationDriver,\n  /** ref to the content wrapper view for calculating sheet offset when keyboard is out */\n  contentWrapperRef: React.RefObject<View>\n) => HookReturn;\n\nexport type HeightAnimationDriver = (\n  height: number,\n  duration: number\n) => Animated.CompositeAnimation;\n"
  },
  {
    "path": "src/index.ts",
    "content": "import BottomSheet from './components/bottomSheet';\nexport { default as AnimatedTouchableBackdropMask } from './components/animatedTouchableBackdropMask';\nexport {\n  ANIMATIONS,\n  CUSTOM_BACKDROP_POSITIONS,\n  type BottomSheetMethods,\n} from './types.d';\nexport type { BottomSheetProps } from './components/bottomSheet/types.d';\nexport default BottomSheet;\n"
  },
  {
    "path": "src/types.d.ts",
    "content": "/**\n * Supported animation types\n */\nexport enum ANIMATIONS {\n  SLIDE = 'slide',\n  SPRING = 'spring',\n  FADE = 'fade',\n}\n\n/**\n * Alias for `ANIMATIONS` to allow literal animation type string as prop\n * @alias ANIMATIONS\n */\nexport type AnimationType = Lowercase<keyof typeof ANIMATIONS>;\n\n/**\n * Supported custom backdrop component position\n */\nexport enum CUSTOM_BACKDROP_POSITIONS {\n  TOP = 'top',\n  BEHIND = 'behind',\n}\n\n/**\n * Bottom sheet's ref instance methods\n */\nexport interface BottomSheetMethods {\n  /**\n   * Expands the bottom sheet to the `height` passed through props\n   */\n  open(): void;\n  /**\n   * Collapses the bottom sheet\n   */\n  close(): void;\n}\n"
  },
  {
    "path": "src/utils/convertHeight.ts",
    "content": "import { Dimensions } from 'react-native';\nimport { DEFAULT_HANDLE_BAR_DEFAULT_HEIGHT } from '../components/defaultHandleBar';\n\n/**\n * converts string `height` from percentage e.g `'50%'` to pixel unit e.g `320` relative to `containerHeight`,\n * or also clamps it to not exceed `containerHeight` if it's a number.\n *\n * _Note: When `height` > `containerHeight` and `containerHeight` === `SCREEN_HEIGHT`, and handle\n * bar is visible, we want to set `height` to `SCREEN_HEIGHT`\n * but deducting the height of handle bar so it's still visible._\n * @param {string | number} height height in number percentage string\n * @param {number} containerHeight height to convert and clamp relative to\n * @param {boolean} handleBarHidden Used to determine how height clamping is done\n * @returns {number} converted height\n */\nconst convertHeight = (\n  height: string | number,\n  containerHeight: number,\n  handleBarHidden: boolean\n): number => {\n  const SCREEN_HEIGHT = Dimensions.get('window').height;\n  let _height = height;\n  const errorMsg = 'Invalid `height` prop';\n  if (typeof height === 'number') {\n    // normalise height\n    if (height < 0) _height = 0;\n    if (height >= containerHeight) {\n      if (containerHeight === SCREEN_HEIGHT && !handleBarHidden) {\n        _height = containerHeight - DEFAULT_HANDLE_BAR_DEFAULT_HEIGHT;\n      } else _height = containerHeight;\n    }\n  } else if (typeof height === 'string') {\n    const lastPercentIdx = height.lastIndexOf('%');\n\n    // perform checks\n    if (!height.endsWith('%') || height.length <= 1 || height.length > 4)\n      throw errorMsg;\n    let parsedHeight = Math.abs(\n      parseInt(height.substring(0, lastPercentIdx), 10)\n    );\n    if (isNaN(parsedHeight)) throw errorMsg;\n\n    // normalise height\n    if (parsedHeight >= 100) {\n      parsedHeight = 100;\n    }\n\n    _height = Math.floor((parsedHeight / 100) * containerHeight);\n    if (containerHeight === SCREEN_HEIGHT && !handleBarHidden) {\n      _height -= DEFAULT_HANDLE_BAR_DEFAULT_HEIGHT;\n    }\n  } else throw errorMsg;\n\n  return _height as number;\n};\n\nexport default convertHeight;\n"
  },
  {
    "path": "src/utils/normalizeHeight.ts",
    "content": "import { Dimensions } from 'react-native';\n\n/**\n * Normalizes height to a number and clamps it\n * so it's not bigger that device screen height\n */\nconst normalizeHeight = (height?: number | string): number => {\n  const DEVICE_SCREEN_HEIGHT = Dimensions.get('window').height;\n  let clampedHeight = DEVICE_SCREEN_HEIGHT;\n  if (typeof height === 'number')\n    clampedHeight =\n      height < 0\n        ? 0\n        : height > DEVICE_SCREEN_HEIGHT\n        ? DEVICE_SCREEN_HEIGHT\n        : height;\n  return clampedHeight;\n};\n\nexport default normalizeHeight;\n"
  },
  {
    "path": "src/utils/separatePaddingStyles.ts",
    "content": "import { type ViewStyle } from 'react-native';\nimport type { SheetStyleProp } from '../components/bottomSheet/types';\n\ntype PadProps =\n  | 'padding'\n  | 'paddingBottom'\n  | 'paddingEnd'\n  | 'paddingHorizontal'\n  | 'paddingLeft'\n  | 'paddingRight'\n  | 'paddingStart'\n  | 'paddingTop'\n  | 'paddingVertical';\ntype Styles = {\n  paddingStyles: Pick<ViewStyle, PadProps>;\n  otherStyles: Omit<ViewStyle, PadProps>;\n};\n\n/**\n * Extracts and separates `padding` styles from\n * other styles from the given `style`\n */\nconst separatePaddingStyles = (\n  style: SheetStyleProp | undefined\n): Styles | undefined => {\n  if (!style) return;\n  const styleKeys = Object.keys(style || {});\n  if (!styleKeys.length) return;\n\n  const styles: Styles = {\n    paddingStyles: {},\n    otherStyles: {},\n  };\n\n  for (const key of styleKeys) {\n    // @ts-ignore\n    styles[key.startsWith('padding') ? 'paddingStyles' : 'otherStyles'][key] =\n      // @ts-ignore\n      style[key];\n  }\n\n  return styles;\n};\n\nexport default separatePaddingStyles;\n"
  },
  {
    "path": "tsconfig.build.json",
    "content": "{\n  \"exclude\": [\"example\", \"src/__tests__\"],\n  \"compilerOptions\": {\n    \"skipLibCheck\": true,\n    \"jsx\": \"react\",\n    \"declaration\": true,\n    \"declarationMap\": true,\n    \"esModuleInterop\": true,\n    \"outDir\": \"./lib/typescript\",\n    \"pretty\": true,\n    \"emitDeclarationOnly\": true\n  }\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"rootDir\": \".\",\n    \"paths\": {\n      \"@devvie/bottom-sheet\": [\"./src/index\"]\n    },\n    \"allowUnreachableCode\": false,\n    \"allowUnusedLabels\": false,\n    \"esModuleInterop\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"jsx\": \"react\",\n    \"lib\": [\"esnext\"],\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"noFallthroughCasesInSwitch\": true,\n    \"noImplicitReturns\": true,\n    \"noImplicitUseStrict\": false,\n    \"noStrictGenericChecks\": false,\n    \"noUncheckedIndexedAccess\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"resolveJsonModule\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"target\": \"esnext\",\n    \"verbatimModuleSyntax\": true\n  }\n}\n"
  }
]