├── .circleci
└── config.yml
├── .github
├── FUNDING.yml
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── dist
├── core
│ └── index.js
├── cupertino-pane.esm.js
├── cupertino-pane.esm.min.js
├── cupertino-pane.esm.min.js.map
├── cupertino-pane.js
├── cupertino-pane.js.map
├── cupertino-pane.min.js
├── cupertino-pane.min.js.map
├── modules
│ ├── backdrop.js
│ ├── fit-height.js
│ ├── follower.js
│ ├── horizontal.js
│ ├── index.js
│ ├── inverse.js
│ ├── modal.js
│ └── z-stack.js
└── types
│ ├── breakpoints.d.ts
│ ├── cupertino-pane.d.ts
│ ├── device.d.ts
│ ├── events-emitter.d.ts
│ ├── events
│ ├── events.d.ts
│ ├── index.d.ts
│ └── keyboard.d.ts
│ ├── index.d.ts
│ ├── models.d.ts
│ ├── modules
│ ├── backdrop.d.ts
│ ├── fit-height.d.ts
│ ├── follower.d.ts
│ ├── horizontal.d.ts
│ ├── index.d.ts
│ ├── inverse.d.ts
│ ├── modal.d.ts
│ └── z-stack.d.ts
│ ├── public-api.d.ts
│ ├── settings.d.ts
│ ├── support.d.ts
│ └── transitions.d.ts
├── docs
├── images
│ ├── bulletin.gif
│ ├── custom-transitions.gif
│ ├── notifications.gif
│ ├── overflow.gif
│ ├── picture-in-picture.gif
│ └── z-stack.gif
└── logo
│ ├── logo-1-mini.jpg
│ ├── logo-1.jpg
│ ├── logo-2-mini.png
│ ├── logo-2.png
│ ├── logo-3.png
│ ├── logo-4.jpg
│ ├── logo-5.jpg
│ └── logo-6.jpg
├── gulp
├── banner.js
├── build-bundle.js
├── build-core.js
├── build-modules.js
└── gulpfile.js
├── gulpfile.js
├── package.json
├── playground
├── auto-height.html
├── base.html
├── calc-fit-height.html
├── follower.html
├── img
│ ├── app-clips.jpg
│ ├── cup-1.png
│ ├── cup-2.png
│ ├── nike-1.jpg
│ ├── nike-2.jpg
│ ├── nike-3.jpg
│ ├── nike-app.png
│ └── starbucks.png
├── index.html
├── modal
│ ├── basic.html
│ ├── custom-transition.html
│ ├── dismiss-on-intense.html
│ ├── flying.html
│ └── zoom-transition.html
├── overflow.html
├── top-to-bottom.html
├── with-swiper.html
└── z-stack.html
├── src
├── breakpoints.ts
├── cupertino-pane.ts
├── device.ts
├── events-emitter.ts
├── events
│ ├── events.ts
│ ├── index.ts
│ └── keyboard.ts
├── index.ts
├── models.ts
├── modules
│ ├── backdrop.ts
│ ├── fit-height.ts
│ ├── follower.ts
│ ├── horizontal.ts
│ ├── index.ts
│ ├── inverse.ts
│ ├── modal.ts
│ └── z-stack.ts
├── public-api.ts
├── settings.ts
├── support.ts
└── transitions.ts
├── tsconfig.json
└── tslint.json
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | defaults: &defaults
4 | working_directory: ~/cupertino-pane
5 | docker:
6 | - image: circleci/node:16
7 |
8 | jobs:
9 | test:
10 | <<: *defaults
11 | steps:
12 | - checkout
13 |
14 | - run:
15 | name: update-npm
16 | # command: 'sudo npm install -g npm@latest && sudo npm install -g gulp'
17 | command: 'sudo npm install -g gulp'
18 |
19 | - restore_cache:
20 | keys:
21 | - v1-dependencies-{{ checksum "package.json" }}
22 | - v1-dependencies- # fallback to using the latest cache if no exact match is found
23 |
24 | - run: npm install
25 |
26 | - save_cache:
27 | paths:
28 | - node_modules
29 | key: v1-dependencies-{{ checksum "package.json" }}
30 |
31 | - run: # run build
32 | name: Make build
33 | command: npm run build
34 |
35 | - persist_to_workspace:
36 | root: ~/cupertino-pane
37 | paths: .
38 |
39 | deploy:
40 | <<: *defaults
41 | steps:
42 | - attach_workspace:
43 | at: ~/cupertino-pane
44 | - run:
45 | name: Authenticate with registry
46 | command: echo "//registry.npmjs.org/:_authToken=$npm_TOKEN" > ~/cupertino-pane/.npmrc
47 | - run:
48 | name: Publish package
49 | command: npm publish
50 |
51 | workflows:
52 | version: 2
53 | test-deploy:
54 | jobs:
55 | - test:
56 | filters:
57 | tags:
58 | only: /^v.*/
59 | - deploy:
60 | requires:
61 | - test
62 | filters:
63 | tags:
64 | only: /^v.*/
65 | branches:
66 | ignore: /.*/
67 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 | open_collective: cupertino-pane
3 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 🐜 Bug report
3 | about: If something is not working as excepted
4 | title: '[BUG] '
5 | ---
6 |
7 | **Describe the bug**
8 | A clear and concise description of what the bug is.
9 |
10 | **To Reproduce**
11 | Steps to reproduce the behavior:
12 | 1. Go to '...'
13 | 2. Click on '....'
14 | 3. Scroll down to '....'
15 | 4. See error
16 |
17 | **Expected behavior**
18 | A clear and concise description of what you expected to happen.
19 |
20 | **Screenshots**
21 | If applicable, add screenshots to help explain your problem.
22 |
23 | **Desktop (please complete the following information):**
24 | - OS: [e.g. iOS]
25 | - Browser [e.g. chrome, safari]
26 | - Version [e.g. 22]
27 |
28 | **Smartphone (please complete the following information):**
29 | - Device: [e.g. iPhone6]
30 | - OS: [e.g. iOS8.1]
31 | - Browser [e.g. stock browser, safari]
32 | - Version [e.g. 22]
33 |
34 | **Additional context**
35 | Add any other context about the problem here.
36 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 🚀 Feature request
3 | about: If you have a feature request
4 | title: '[FEAT] '
5 | ---
6 |
7 | **What are you trying to add to Cupertino-Pane?**
8 |
9 | *Describe your feature request **detailed***
10 |
11 | **Is there an alternative at the latest version?**
12 |
13 | [ ] Yes (descripe the alternative)
14 | [ ] No
15 |
16 | **Is this related to an issue?**
17 |
18 | [ ] Yes (Give a link to the issue)
19 | [ ] No
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Specifies intentionally untracked files to ignore when using Git
2 | # http://git-scm.com/docs/gitignore
3 |
4 | *~
5 | *.sw[mnpcod]
6 | .tmp
7 | *.tmp
8 | *.tmp.*
9 | *.sublime-project
10 | *.sublime-workspace
11 | .DS_Store
12 | Thumbs.db
13 | UserInterfaceState.xcuserstate
14 | $RECYCLE.BIN/
15 | .rpt2_cache/
16 |
17 | *.log
18 | log.txt
19 | npm-debug.log*
20 | package-lock.json
21 |
22 | /.idea
23 | /.ionic
24 | /.sass-cache
25 | /.sourcemaps
26 | /.versions
27 | /.vscode
28 | /coverage
29 | /node_modules
30 | /platforms
31 | /plugins
32 | /www
33 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at romwtb@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to contribute
2 |
3 | There are a few guidelines that we need contributors to follow so that we have a
4 | chance of keeping on top of things.
5 |
6 | ### 1. Where do I go from here?
7 | If you've noticed a bug or have a question, [make an issue](https://github.com/roman-rr/cupertino-pane/issues/new),
8 | we'll try to answer it as fast as possible.
9 | ### 2. Fork & Create a branch
10 | If this is something you think you can fix, then
11 | [fork Cupertino Pane](https://help.github.com/articles/fork-a-repo)
12 | and create a branch.
13 | ```sh
14 | # Create new branch
15 | git checkout -b my_issue
16 |
17 | # Then we install the dependencies
18 | npm install
19 | ```
20 | ### 3. Test with Playground
21 | ```sh
22 | npm run serve
23 | ```
24 | ### 4. Changes & Build
25 | ```sh
26 | # Make bundles
27 | npm run build
28 | ```
29 | This will output the files into the dist directory.
30 | ### 5. Push changes
31 | Push your changes to a topic branch in your fork of the repository.
32 | Submit a pull request to the repository.
33 | It can take several days before we can review the code you've submitted.
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Roman Antonov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Get Started |
8 | Demonstration |
9 | Documentation
10 |
11 |
12 | # Cupertino Panes
13 |
14 | [](https://circleci.com/gh/tech-systems/panes)
15 | 
16 | 
17 | 
18 | [](https://www.typescriptlang.org/)
19 | [](https://github.com/airbnb/javascript)
20 | [](https://opencollective.com/cupertino-pane)
21 |
22 |
23 |
25 |
26 | ### Cupertino Panes is multi-functional modals, cards & panes with touch technologies.
27 |
28 | #### One instance – Thousands solutions.
29 |
30 | * **Small.** 12kb (minified and gzipped bundle with all modules). No dependencies.
31 | * **Reusable.** Create your configuration once. Use everywhere.
32 | * **Modularized.** Add extra features to your panes and create own modules.
33 | * **Accelerated.** Hardware accelerated transitions and amazing native behavior.
34 | * **Progressive.** Useful for mobile/web/hybrid applications.
35 |
36 | > Right like in Apple Maps, Apple Stocks, Apple Music and other modern apps.
37 |
38 | ⭐ We appreciate your star, it helps!
39 |
40 | ## Financial Contributors
41 |
42 | Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/cupertino-pane/contribute)]
43 |
44 | #### Individuals
45 |
46 |
47 |
48 | #### Organizations
49 |
50 | Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/cupertino-pane/contribute)]
51 |
52 |
53 |
54 | ## Demonstration
55 | - [Modal](https://panejs.com/demo/modal.html)
56 | - [Base](https://panejs.com/demo/base.html)
57 | - [Overflow Top](https://panejs.com/demo/overflow-top.html)
58 | - [Auto Height](https://panejs.com/demo/auto-height.html)
59 | - [Top-to-Bottom](https://panejs.com/demo/top-to-bottom.html)
60 | - [Synthwave](https://panejs.com/demo/synthwave.html)
61 | - [Picture-in-Picture](https://panejs.com/demo/picture-in-picture.html)
62 | - [Rich notifications](https://panejs.com/demo/rich-notifications.html)
63 | - [Z-stack full](https://panejs.com/demo/z-stack-full.html)
64 | - [Z-Stack simple](https://panejs.com/demo/z-stack-simple.html)
65 | - [3D Push](https://panejs.com/demo/3d-push.html)
66 | - [Backdrop drag-opacity](https://panejs.com/demo/backdrop-drag-opacity.html)
67 | - [Overflow Top-Middle](https://panejs.com/demo/overflow-top-middle.html)
68 | - [Draggable Over](https://panejs.com/demo/draggable-over.html)
69 | - [Prevent Dismiss](https://panejs.com/demo/prevent-dismiss.html)
70 | - [Follower](https://panejs.com/demo/follower.html)
71 | - [Apple Clips](https://panejs.com/demo/apple-clips.html)
72 | - [Starbucks](https://panejs.com/demo/starbucks.html)
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | ## Docs & Guidelines
84 | Documentation moved [here](https://panejs.com)
85 |
86 | ## Future Goals
87 | Project under regularly maintanance and bug fixes.
88 | All **new features** and **new investigations** moved to open collective [Goals](https://opencollective.com/cupertino-pane/conversations/all-goals-and-featured-packages-o60ddaqg)
89 |
90 | ## Contributors
91 | We are welcome contributions of all kinds from anyone.
92 | Please review the [contributing](https://github.com/tech-systems/panes/blob/master/CONTRIBUTING.md) guideline.
93 |
94 | Commit Message Format [angular commit format](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-commit-message-format)
95 |
96 | ## License
97 | Licensed under the MIT License. [View license](/LICENSE).
98 |
--------------------------------------------------------------------------------
/dist/cupertino-pane.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"cupertino-pane.js.map","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
--------------------------------------------------------------------------------
/dist/cupertino-pane.min.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"cupertino-pane.min.js","names":[],"sources":[],"mappings":"","ignoreList":[]}
--------------------------------------------------------------------------------
/dist/modules/backdrop.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Cupertino Pane 1.4.22
3 | * Cupertino Panes is multi-functional modals, cards & panes with touch technologies.
4 | * https://panejs.com
5 | *
6 | * Copyright 2019-2024 Roman Antonov (roman-rr)
7 | *
8 | * Released under the MIT License
9 | *
10 | * Released on: October 7, 2024
11 | */
12 |
13 | var CupertinoTransition;"function"==typeof SuppressedError&&SuppressedError,function(t){t.Present="present",t.Destroy="destroy",t.Move="move",t.Breakpoint="breakpoint",t.Hide="hide",t.TouchEnd="end"}(CupertinoTransition||(CupertinoTransition={}));class Support{static get touch(){return window.Modernizr&&!0===window.Modernizr.touch||!!(window.navigator.maxTouchPoints>0||"ontouchstart"in window||window.DocumentTouch&&document instanceof window.DocumentTouch)}static get observer(){return"MutationObserver"in window||"WebkitMutationObserver"in window}static get backdropFilter(){return CSS.supports("backdrop-filter","blur(0px)")||CSS.supports("-webkit-backdrop-filter","blur(0px)")}static get passiveListener(){let t=!1;try{const e=Object.defineProperty({},"passive",{get(){t=!0}});window.addEventListener("testPassiveListener",null,e)}catch(t){}return t}static get gestures(){return"ongesturestart"in window}}class BackdropModule{constructor(t){this.instance=t,this.touchMoveBackdropCb=t=>this.touchMoveBackdrop(t),this.settings=this.instance.settings,this.events=this.instance.events,this.settings.backdrop&&(this.instance.backdrop=t=>this.backdrop(t),this.instance.on("rendered",(()=>{this.instance.addStyle(`\n .cupertino-pane-wrapper .backdrop {\n overflow: hidden;\n position: fixed;\n width: 100%;\n bottom: 0;\n right: 0;\n left: 0;\n top: 0;\n display: none;\n z-index: 10;\n ${Support.backdropFilter&&this.settings.backdropBlur?"\n backdrop-filter: saturate(180%) blur(10px);\n -webkit-backdrop-filter: saturate(180%) blur(10px);\n ":""}\n }\n `),this.settings.backdrop&&this.renderBackdrop()})),this.instance.on("beforePresentTransition",(t=>{t.animate||(this.backdropEl.style.display="block")})),this.instance.on("onTransitionStart",(t=>{this.settings.backdrop&&(this.instance.isHidden()||t.type===CupertinoTransition.Hide||t.type===CupertinoTransition.Destroy||t.type===CupertinoTransition.Present)&&(this.backdropEl.style.backgroundColor="rgba(0,0,0,.0)",this.backdropEl.style.transition=`all ${this.settings.animationDuration}ms ${this.settings.animationType} 0s`,t.type!==CupertinoTransition.Hide&&t.type!==CupertinoTransition.Destroy&&(this.backdropEl.style.display="block",setTimeout((()=>{this.backdropEl.style.backgroundColor=`rgba(0,0,0, ${this.settings.backdropOpacity})`}),50)))})),this.instance.on("onTransitionEnd",(t=>{this.backdropEl&&(t.type!==CupertinoTransition.Destroy&&t.type!==CupertinoTransition.Hide||(this.backdropEl.style.transition="initial",this.backdropEl.style.display="none"))})),Support.touch&&(this.instance.on("onDidPresent",(()=>{var t;null===(t=this.backdropEl)||void 0===t||t.addEventListener(this.events.touchEvents.move,this.touchMoveBackdropCb,!!Support.passiveListener&&{passive:!1,capture:!1})})),this.instance.on("onDidDismiss",(t=>{var e;null===(e=this.backdropEl)||void 0===e||e.removeEventListener(this.events.touchEvents.move,this.touchMoveBackdropCb)}))))}backdrop(t={show:!0}){var e,i;if(!this.instance.isPanePresented())return console.warn("Cupertino Pane: Present pane before call backdrop()"),null;this.isBackdropPresented()||(this.renderBackdrop(),Support.touch&&(null===(e=this.backdropEl)||void 0===e||e.removeEventListener(this.events.touchEvents.move,this.touchMoveBackdropCb),null===(i=this.backdropEl)||void 0===i||i.addEventListener(this.events.touchEvents.move,this.touchMoveBackdropCb,!!Support.passiveListener&&{passive:!1,capture:!1})));const n=()=>{this.backdropEl.style.transition="initial",this.backdropEl.style.display="none",this.backdropEl.removeEventListener("transitionend",n)};if(this.backdropEl.style.transition=`all ${this.settings.animationDuration}ms ${this.settings.animationType} 0s`,this.backdropEl.style.backgroundColor="rgba(0,0,0,.0)",t.show)this.backdropEl.style.display="block",setTimeout((()=>{this.backdropEl.style.backgroundColor=`rgba(0,0,0, ${this.settings.backdropOpacity})`}),50);else{if("none"===this.backdropEl.style.display)return;this.backdropEl.addEventListener("transitionend",n)}}renderBackdrop(){this.backdropEl=document.createElement("div"),this.backdropEl.classList.add("backdrop"),this.backdropEl.style.transition=`all ${this.settings.animationDuration}ms ${this.settings.animationType} 0s`,this.backdropEl.style.backgroundColor=`rgba(0,0,0, ${this.settings.backdropOpacity})`,this.instance.wrapperEl.appendChild(this.backdropEl),this.backdropEl.addEventListener("click",(t=>this.instance.emit("onBackdropTap",t)))}isBackdropPresented(){return!!document.querySelector(".cupertino-pane-wrapper .backdrop")}touchMoveBackdrop(t){this.settings.touchMoveStopPropagation&&t.stopPropagation()}}export{BackdropModule};
--------------------------------------------------------------------------------
/dist/modules/fit-height.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Cupertino Pane 1.4.22
3 | * Cupertino Panes is multi-functional modals, cards & panes with touch technologies.
4 | * https://panejs.com
5 | *
6 | * Copyright 2019-2024 Roman Antonov (roman-rr)
7 | *
8 | * Released under the MIT License
9 | *
10 | * Released on: October 7, 2024
11 | */
12 |
13 | function __awaiter(t,e,i,s){return new(i||(i=Promise))((function(n,h){function a(t){try{o(s.next(t))}catch(t){h(t)}}function r(t){try{o(s.throw(t))}catch(t){h(t)}}function o(t){var e;t.done?n(t.value):(e=t.value,e instanceof i?e:new i((function(t){t(e)}))).then(a,r)}o((s=s.apply(t,e||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;class FitHeightModule{constructor(t){this.instance=t,this.calcHeightInProcess=!1,this.breakpoints=this.instance.breakpoints,this.settings=this.instance.settings,this.settings.fitHeight&&(this.instance.calcFitHeight=t=>__awaiter(this,void 0,void 0,(function*(){return this.calcFitHeight(t)})),this.instance.setOverflowHeight=()=>this.setOverflowHeight(),this.instance.on("DOMElementsReady",(()=>{this.instance.wrapperEl.classList.add("fit-height")})),this.instance.on("onDidPresent",(()=>{this.instance.paneEl.style.height="unset"})),this.instance.on("onTransitionEnd",(()=>{this.instance.paneEl.style.height="unset"})),this.instance.on("onWillPresent",(()=>{this.breakpoints.beforeBuildBreakpoints=()=>this.beforeBuildBreakpoints()})),this.instance.on("beforeBreakHeightApplied",(t=>{var e;this.settings.fitScreenHeight&&((null===(e=this.settings.breaks[t.break])||void 0===e?void 0:e.height)>this.instance.screen_height&&(this.settings.breaks[t.break].height=this.instance.screen_height-this.settings.bottomOffset),this.settings.breaks.top&&this.settings.breaks.middle&&this.settings.breaks.top.height-50<=this.settings.breaks.middle.height&&(this.settings.breaks.middle.enabled=!1,this.settings.initialBreak="top")),"top"===t.break&&(this.settings.breaks.top.height>this.instance.screen_height?(this.settings.breaks.top.height=this.instance.screen_height-2*this.settings.bottomOffset,this.settings.topperOverflow=!0,this.settings.upperThanTop=!1):this.instance.overflowEl&&!this.settings.maxFitHeight&&(this.settings.topperOverflow=!1,this.instance.overflowEl.style.overflowY="hidden"))}),!0))}beforeBuildBreakpoints(){var t,e,i;return __awaiter(this,void 0,void 0,(function*(){this.settings.fitScreenHeight=!1,this.settings.initialBreak="top",this.settings.topperOverflow=!1;let s=yield this.getPaneFitHeight();this.settings.maxFitHeight&&s>this.settings.maxFitHeight&&(s=this.settings.maxFitHeight,this.settings.topperOverflow=!0),this.breakpoints.conf={top:{enabled:!0,height:s},middle:{enabled:!1}},this.breakpoints.conf.top.bounce=null===(e=null===(t=this.settings.breaks)||void 0===t?void 0:t.top)||void 0===e?void 0:e.bounce,this.breakpoints.conf.bottom=(null===(i=this.settings.breaks)||void 0===i?void 0:i.bottom)||{enabled:!0,height:0}}))}calcFitHeight(t=!0){return __awaiter(this,void 0,void 0,(function*(){return this.instance.wrapperEl&&this.instance.el?this.calcHeightInProcess?(console.warn("Cupertino Pane: calcFitHeight() already in process"),null):void(yield this.breakpoints.buildBreakpoints(this.breakpoints.lockedBreakpoints,null,t)):null}))}setOverflowHeight(t=0){this.paneElHeight>this.instance.screen_height&&(this.instance.paneEl.style.height=`${this.instance.getPaneHeight()}px`,this.instance.overflowEl.style.height=this.instance.getPaneHeight()-this.settings.topperOverflowOffset-this.instance.overflowEl.offsetTop-t+"px")}getPaneFitHeight(){return __awaiter(this,void 0,void 0,(function*(){this.calcHeightInProcess=!0;let t=this.instance.el.querySelectorAll("img");this.instance.el.style.height="unset",this.instance.rendered||(this.instance.el.style.visibility="hidden",this.instance.el.style.pointerEvents="none",this.instance.el.style.display="block",this.instance.wrapperEl.style.visibility="hidden",this.instance.wrapperEl.style.pointerEvents="none",this.instance.wrapperEl.style.display="block");let e=[];t.length&&(e=Array.from(t).map((t=>new Promise((e=>{if(t.height||t.complete&&t.naturalHeight)return e(!0);t.onload=()=>e(!0),t.onerror=()=>e(!0)}))))),yield Promise.all(e),yield new Promise((t=>requestAnimationFrame(t)));let i=Math.floor(this.instance.paneEl.getBoundingClientRect().height);return this.paneElHeight!==i&&(this.instance.paneEl.style.height=`${i<=this.paneElHeight?this.paneElHeight:i}px`),this.instance.rendered||(this.instance.el.style.visibility="unset",this.instance.el.style.pointerEvents="unset",this.instance.el.style.display="none",this.instance.wrapperEl.style.visibility="unset",this.instance.wrapperEl.style.pointerEvents="unset",this.instance.wrapperEl.style.display="none"),this.calcHeightInProcess=!1,this.paneElHeight=i,this.paneElHeight}))}}export{FitHeightModule};
--------------------------------------------------------------------------------
/dist/modules/follower.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Cupertino Pane 1.4.22
3 | * Cupertino Panes is multi-functional modals, cards & panes with touch technologies.
4 | * https://panejs.com
5 | *
6 | * Copyright 2019-2024 Roman Antonov (roman-rr)
7 | *
8 | * Released under the MIT License
9 | *
10 | * Released on: October 7, 2024
11 | */
12 |
13 | class FollowerModule{constructor(t){this.instance=t,this.breakpoints=this.instance.breakpoints,this.transitions=this.instance.transitions,this.settings=this.instance.settings,this.settings.followerElement&&(this.instance.on("rendered",(()=>{var t;document.querySelector(this.settings.followerElement)?(this.followerEl=document.querySelector(this.settings.followerElement),this.followerEl.style.willChange="transform, border-radius",this.followerEl.style.transform="translateY(0px) translateZ(0px)",this.followerEl.style.transition=this.transitions.buildTransitionValue(null===(t=this.settings.breaks[this.instance.currentBreak()])||void 0===t?void 0:t.bounce)):console.warn("Cupertino Pane: wrong follower element selector specified",this.settings.followerElement)})),this.instance.on("onMoveTransitionStart",(t=>{this.followerEl.style.transition="all 0ms linear 0ms",this.followerEl.style.transform=`translateY(${t.translateY-this.breakpoints.breaks[this.settings.initialBreak]}px) translateZ(0px)`})),this.instance.on("onMoveTransitionStart",(t=>{this.followerEl.style.transition="initial"})),this.instance.on("onTransitionStart",(t=>{this.followerEl.style.transition=t.transition,this.followerEl.style.transform=`translateY(${t.translateY.new-this.breakpoints.breaks[this.settings.initialBreak]}px) translateZ(0px)`})))}}export{FollowerModule};
--------------------------------------------------------------------------------
/dist/modules/horizontal.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Cupertino Pane 1.4.22
3 | * Cupertino Panes is multi-functional modals, cards & panes with touch technologies.
4 | * https://panejs.com
5 | *
6 | * Copyright 2019-2024 Roman Antonov (roman-rr)
7 | *
8 | * Released under the MIT License
9 | *
10 | * Released on: October 7, 2024
11 | */
12 |
13 | class HorizontalModule{static CollectSettings(t){return t.horizontal?Object.assign(Object.assign({},t),HorizontalModule.forceSettings):t}constructor(t){if(this.instance=t,this.settings=this.instance.settings,this.transitions=this.instance.transitions,this.events=this.instance.events,!this.settings.horizontal)return null;this.transitions.setPaneElTransform=t=>this.setPaneElTransform(t),this.instance.on("onTransitionEnd",(t=>{"breakpoint"!==t.type&&"present"!==t.type||this.instance.getPanelTransformX()||this.calcHorizontalBreaks()})),this.instance.on("onDidPresent",(t=>{t.animate||this.calcHorizontalBreaks()})),this.instance.on("onDragEnd",(t=>{this.fastSwipeNext=this.events.fastSwipeNext("X")}))}calcHorizontalBreaks(){this.defaultRect={width:this.instance.paneEl.getBoundingClientRect().width,left:this.instance.paneEl.getBoundingClientRect().left,right:this.instance.paneEl.getBoundingClientRect().right},this.horizontalBreaks=[-this.defaultRect.left+this.settings.horizontalOffset,window.innerWidth-this.defaultRect.left-this.defaultRect.width-this.settings.horizontalOffset]}setPaneElTransform(t){let e=t.translateX;"end"===t.type&&(e=this.getClosestBreakX(),this.fastSwipeNext&&("left"===this.currentBreakpoint&&this.instance.getPanelTransformX()>this.horizontalBreaks[0]&&(e=this.horizontalBreaks[1]),"right"===this.currentBreakpoint&&this.instance.getPanelTransformX()Math.abs(e-this.instance.getPanelTransformX())this.getPaneHeight(),this.instance.updateScreenHeights=()=>this.updateScreenHeights(),this.instance.setOverflowHeight=()=>this.settings.fitHeight?{}:this.setOverflowHeight(),this.instance.checkOpacityAttr=()=>{},this.instance.checkOverflowAttr=t=>this.checkOverflowAttr(t),this.instance.prepareBreaksSwipeNextPoint=()=>this.prepareBreaksSwipeNextPoint(),this.events.handleSuperposition=t=>this.handleSuperposition(t),this.events.scrollPreventDrag=t=>this.scrollPreventDrag(t),this.events.onScroll=()=>this.onScroll(),this.instance.on("DOMElementsReady",(()=>{this.instance.wrapperEl.classList.add("inverse")})),this.instance.on("rendered",(()=>{this.instance.addStyle("\n .cupertino-pane-wrapper.inverse .pane {\n border-radius: 0 0 20px 20px;\n border-radius: 0 0\n var(--cupertino-pane-border-radius, 20px) \n var(--cupertino-pane-border-radius, 20px);\n }\n .cupertino-pane-wrapper.inverse:not(.fit-height) .pane {\n padding-bottom: 15px; \n }\n .cupertino-pane-wrapper.inverse .draggable {\n bottom: 0;\n top: initial;\n }\n .cupertino-pane-wrapper.inverse .draggable.over {\n bottom: -30px;\n top: initial;\n }\n .cupertino-pane-wrapper.inverse .move {\n margin-top: 15px;\n }\n .cupertino-pane-wrapper.inverse .draggable.over .move {\n margin-top: -5px;\n }\n ")})),this.instance.on("beforeBreakHeightApplied",(t=>{var e;(null===(e=this.settings.breaks[t.break])||void 0===e?void 0:e.enabled)&&(this.breakpoints.breaks[t.break]=2*(this.settings.breaks[t.break].height+this.settings.bottomOffset))}),!1),this.instance.on("buildBreakpointsCompleted",(()=>{this.breakpoints.topper=this.breakpoints.bottomer,this.instance.paneEl.style.top=`-${this.breakpoints.bottomer-this.settings.bottomOffset}px`})))}getPaneHeight(){return this.breakpoints.bottomer-this.settings.bottomOffset}updateScreenHeights(){this.instance.screen_height=window.innerHeight,this.instance.screenHeightOffset=0}setOverflowHeight(){this.instance.overflowEl.style.height=this.getPaneHeight()-30-this.settings.topperOverflowOffset-this.instance.overflowEl.offsetTop+"px"}checkOverflowAttr(t){this.settings.topperOverflow&&this.instance.overflowEl&&(this.instance.overflowEl.style.overflowY=t>=this.breakpoints.bottomer?"auto":"hidden")}prepareBreaksSwipeNextPoint(){let t={},e={};return t.top=this.breakpoints.breaks.bottom,t.middle=this.breakpoints.breaks.middle,t.bottom=this.breakpoints.breaks.top,e.top=Object.assign({},this.settings.breaks.bottom),e.middle=Object.assign({},this.settings.breaks.middle),e.bottom=Object.assign({},this.settings.breaks.top),{brs:t,settingsBreaks:e}}handleSuperposition(t){if(this.settings.upperThanTop&&(t.newVal>=this.breakpoints.topper||this.events.startPointOverTop)){this.events.startPointOverTop||(this.events.startPointOverTop=t.clientY),this.events.startPointOverTop>t.clientY&&delete this.events.startPointOverTop;const e=this.instance.screen_height-this.instance.screenHeightOffset,n=(e-this.instance.getPanelTransformY())/(e-this.breakpoints.topper)/8;return{y:this.instance.getPanelTransformY()+t.diffY*n}}if(!this.settings.upperThanTop&&t.newVal>=this.breakpoints.topper)return{y:this.breakpoints.topper}}scrollPreventDrag(t){let e=!1;return this.events.willScrolled()&&this.isOverflowEl(t.target)&&(e=!0),e}isOverflowEl(t){if(!t)return!1;let e=t.parentNode;for(;null!=e;){if(e==this.instance.overflowEl)return!0;e=e.parentNode}return!1}onScroll(){return __awaiter(this,void 0,void 0,(function*(){this.events.isScrolling=!0}))}}export{InverseModule};
--------------------------------------------------------------------------------
/dist/modules/modal.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Cupertino Pane 1.4.22
3 | * Cupertino Panes is multi-functional modals, cards & panes with touch technologies.
4 | * https://panejs.com
5 | *
6 | * Copyright 2019-2024 Roman Antonov (roman-rr)
7 | *
8 | * Released under the MIT License
9 | *
10 | * Released on: October 7, 2024
11 | */
12 |
13 | class ModalModule{static CollectSettings(t){return t.modal?Object.assign(Object.assign({},t),ModalModule.ForceSettings):t}constructor(t){this.instance=t,this.modalDefaults={transition:"fade",flying:!1,dismissOnIntense:!1},this.settings=this.instance.settings,this.events=this.instance.events,this.breakpoints=this.instance.breakpoints,this.transitions=this.instance.transitions,this.settings.modal&&(this.settings.modal="object"==typeof this.settings.modal?Object.assign(Object.assign({},this.modalDefaults),this.settings.modal):this.modalDefaults,this.instance.customPresent=this.instance.present,this.instance.present=t=>this.present(t),this.instance.customDestroy=this.instance.destroy,this.instance.destroy=t=>this.destroy(t),this.events.handleSuperposition=t=>this.handleSuperposition(t),this.transitions.setPaneElTransform=t=>this.setPaneElTransform(t),this.instance.on("beforeBreakHeightApplied",(t=>{"top"===t.break&&(this.settings.breaks.top.height-=2*this.settings.bottomOffset,this.settings.breaks.top.height+=(this.instance.screen_height-this.settings.breaks.top.height)/2),"bottom"===t.break&&(this.settings.breaks.bottom={enabled:!1}),this.instance.addStyle(`\n .cupertino-pane-wrapper .pane {\n transform-origin: center ${this.breakpoints.breaks[this.settings.initialBreak]}px\n }\n `)})),this.instance.on("rendered",(()=>{this.instance.addStyle("\n .cupertino-pane-wrapper .pane {\n border-radius: var(--cupertino-pane-border-radius, 20px) \n var(--cupertino-pane-border-radius, 20px)\n var(--cupertino-pane-border-radius, 20px)\n var(--cupertino-pane-border-radius, 20px);\n width: calc(100% - 16px) !important;\n margin: auto;\n }\n .cupertino-pane-wrapper .pane.modal-flying {\n animation: modalFlyingX 2000ms ease-in-out infinite alternate,\n modalFlyingY 3000ms ease-in-out infinite alternate;\n }\n @keyframes modalFlyingX {\n 0% { left: -10px; }\n 100% { left: 10px; }\n }\n @keyframes modalFlyingY {\n 0% { top: -10px; }\n 100% { top: 0px; }\n }\n "),this.settings.modal.flying&&this.instance.paneEl.classList.add("modal-flying"),this.settings.modal.dismissOnIntense&&this.instance.enableDrag()})))}setPaneElTransform(t){let n="end"===t.type?0:t.translateX;this.instance.paneEl.style.transform=`translateX(${n||0}px) translateY(${t.translateY}px) translateZ(0px)`}present(t){let{transition:n}=t;return n||(n=ModalModule.BuildInTransition[this.settings.modal.transition]),this.instance.customPresent(Object.assign(Object.assign({},t),{transition:n}))}destroy(t){let{transition:n}=t;if(n||(n=JSON.parse(JSON.stringify({duration:ModalModule.BuildInTransition[this.settings.modal.transition].duration,from:ModalModule.BuildInTransition[this.settings.modal.transition].to,to:ModalModule.BuildInTransition[this.settings.modal.transition].from}))),t.fromCurrentPosition){let t=new WebKitCSSMatrix(window.getComputedStyle(this.instance.paneEl).transform);n.to.transform=`translateY(${t.m42}px) translateX(${t.m41}px) translateZ(0px)`}return this.instance.customDestroy(Object.assign(Object.assign({},t),{transition:n}))}handleSuperposition(t){let n=Math.abs(this.instance.getPanelTransformY()-this.breakpoints.topper),s=Math.abs(this.instance.getPanelTransformX());if(this.settings.modal.dismissOnIntense&&(n>40||s>30))return this.instance.disableDrag(),this.destroy({animate:!0,fromCurrentPosition:!0}),!1;const e=this.instance.getPanelTransformY()/this.breakpoints.topper/8,i=this.instance.getPanelTransformX()/this.breakpoints.topper/8;return{y:this.instance.getPanelTransformY()+t.diffY*(e+i),x:this.instance.getPanelTransformX()+t.diffX*(e+i)}}}ModalModule.BuildInTransition={fade:{duration:300,from:{opacity:0},to:{opacity:1}},zoom:{duration:300,from:{opacity:0,scale:.5},to:{opacity:1,scale:1}}},ModalModule.ForceSettings={fitHeight:!0,touchAngle:null,showDraggable:!1};export{ModalModule};
--------------------------------------------------------------------------------
/dist/modules/z-stack.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Cupertino Pane 1.4.22
3 | * Cupertino Panes is multi-functional modals, cards & panes with touch technologies.
4 | * https://panejs.com
5 | *
6 | * Copyright 2019-2024 Roman Antonov (roman-rr)
7 | *
8 | * Released under the MIT License
9 | *
10 | * Released on: October 7, 2024
11 | */
12 |
13 | function __awaiter(t,s,e,i){return new(e||(e=Promise))((function(n,a){function r(t){try{o(i.next(t))}catch(t){a(t)}}function c(t){try{o(i.throw(t))}catch(t){a(t)}}function o(t){var s;t.done?n(t.value):(s=t.value,s instanceof e?s:new e((function(t){t(s)}))).then(r,c)}o((i=i.apply(t,s||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;class ZStackModule{constructor(t){this.instance=t,this.zStackDefaults={pushElements:null,minPushHeight:null,cardBorderRadius:null,cardYOffset:0,cardZScale:.93,cardContrast:.85,stackZAngle:160},this.breakpoints=this.instance.breakpoints,this.settings=this.instance.settings,this.settings.zStack&&(this.instance.setZstackConfig=t=>__awaiter(this,void 0,void 0,(function*(){return this.setZstackConfig(t)})),this.instance.on("rendered",(()=>{this.setZstackConfig(this.settings.zStack),this.setPushMultiplicators()})),this.instance.on("beforePresentTransition",(t=>{t.animate||this.settings.zStack.pushElements.forEach((t=>this.pushTransition(document.querySelector(t),this.breakpoints.breaks[this.settings.initialBreak],"unset")))})),this.instance.on("onMoveTransitionStart",(()=>{this.settings.zStack.pushElements.forEach((t=>this.pushTransition(document.querySelector(t),this.instance.getPanelTransformY(),"all 0ms linear 0ms")))})),this.instance.on("onTransitionStart",(t=>{this.settings.zStack.pushElements.forEach((s=>this.pushTransition(document.querySelector(s),t.translateY.new,`all ${this.settings.animationDuration}ms ${this.settings.animationType} 0s`)))})))}setZstackConfig(t){this.settings.zStack=t?Object.assign(Object.assign({},this.zStackDefaults),t):null}pushTransition(t,s,e){let i=this.settings.zStack.pushElements;t.style.transition=e,t.style.overflow=this.settings.zStack.cardBorderRadius&&"hidden",s=this.instance.screenHeightOffset-s;const n=this.settings.zStack.minPushHeight?this.settings.zStack.minPushHeight:this.instance.screenHeightOffset-this.breakpoints.bottomer,a=this.instance.screenHeightOffset-this.breakpoints.topper;let r=this.getPushMulitplicator(t),c=Math.pow(this.settings.zStack.cardZScale,r),o=Math.pow(this.settings.zStack.cardZScale,r-1),h=6+this.settings.zStack.cardYOffset,l=h*r*-1,u=l+h,p=Math.pow(this.settings.zStack.cardContrast,r),g=Math.pow(this.settings.zStack.cardContrast,r-1);const d=(e,n,a,r)=>{let c=Math.pow(e,this.settings.zStack.stackZAngle/100);t.style.transform=`translateY(${n*(c/e)}px) scale(${e})`,t.style.borderRadius=`${r}px`,t.style.filter=`contrast(${a})`;let o=document.querySelector(i[i.length-1]);s||t.className!==o.className||this.clearPushMultiplicators()};if(s<=n)return void d(o,u,g,0);const f=(t,e)=>{let i=-1*(a*e-n*t);return i-=(t-e)*s,i/=n-a,i>e&&(i=e),i{let s=document.querySelector(t),e=this.getPushMulitplicator(s);e=e?e+1:1,s.style.setProperty("--push-multiplicator",`${e}`)}))}getPushMulitplicator(t){let s=t.style.getPropertyValue("--push-multiplicator");return parseInt(s)}clearPushMultiplicators(){for(let t=0;t any;
16 | conf: PaneBreaks;
17 | private defaultBreaksConf;
18 | private settings;
19 | constructor(instance: CupertinoPane);
20 | /**
21 | * Function builder for breakpoints and heights
22 | * @param conf breakpoints
23 | */
24 | buildBreakpoints(conf?: PaneBreaks, bottomOffset?: number, animated?: boolean): Promise;
25 | getCurrentBreakName(): (string | null);
26 | getClosestBreakY(): number;
27 | }
28 |
--------------------------------------------------------------------------------
/dist/types/cupertino-pane.d.ts:
--------------------------------------------------------------------------------
1 | import { Device } from './device';
2 | import { Events, KeyboardEvents } from './events';
3 | import { CupertinoSettings, PaneBreaks } from './models';
4 | import { Breakpoints } from './breakpoints';
5 | import { Transitions } from './transitions';
6 | export declare class CupertinoPane {
7 | private selector;
8 | disableDragEvents: boolean;
9 | screen_height: number;
10 | screenHeightOffset: number;
11 | preventDismissEvent: boolean;
12 | preventedDismiss: boolean;
13 | rendered: boolean;
14 | wrapperEl: HTMLDivElement;
15 | paneEl: HTMLDivElement;
16 | overflowEl: HTMLElement;
17 | el: HTMLElement;
18 | contentEl: HTMLElement;
19 | parentEl: HTMLElement;
20 | ionContent: HTMLElement;
21 | ionApp: HTMLElement;
22 | draggableEl: HTMLDivElement;
23 | moveEl: HTMLDivElement;
24 | private styleEl;
25 | private destroyButtonEl;
26 | settings: CupertinoSettings;
27 | device: Device;
28 | keyboardEvents: KeyboardEvents;
29 | events: Events;
30 | breakpoints: Breakpoints;
31 | transitions: Transitions;
32 | modules: {};
33 | eventsListeners: {};
34 | on: Function;
35 | emit: Function;
36 | calcFitHeight: (animated?: any) => Promise;
37 | backdrop: (conf: {
38 | show: true;
39 | }) => void;
40 | setZstackConfig: (zStack: any) => void;
41 | constructor(selector: (string | HTMLElement), conf?: CupertinoSettings);
42 | private drawBaseElements;
43 | present(conf?: {
44 | animate: boolean;
45 | transition?: {
46 | duration?: number;
47 | from?: {};
48 | to?: {};
49 | };
50 | }): Promise;
51 | getPaneHeight(): number;
52 | updateScreenHeights(): void;
53 | scrollElementInit(): void;
54 | setOverflowHeight(offset?: number): void;
55 | checkOpacityAttr(val: any): void;
56 | checkOverflowAttr(val: any): void;
57 | isPanePresented(): boolean;
58 | private prepareBreaksSwipeNextPoint;
59 | swipeNextPoint: (diff: any, maxDiff: any, closest: any) => any;
60 | /**
61 | * Utility function to add minified internal CSS to head.
62 | * @param {string} styleString
63 | */
64 | addStyle(styleString: any): void;
65 | private getModuleRef;
66 | /************************************
67 | * Public user methods
68 | */
69 | getPanelTransformY(): number;
70 | getPanelTransformX(): number;
71 | /**
72 | * Prevent dismiss event
73 | */
74 | preventDismiss(val?: boolean): void;
75 | /**
76 | * GrabCursor for desktop
77 | */
78 | setGrabCursor(enable: boolean, moving?: boolean): void;
79 | /**
80 | * Disable pane drag events
81 | */
82 | disableDrag(): void;
83 | /**
84 | * Enable pane drag events
85 | */
86 | enableDrag(): void;
87 | /**
88 | * Public user method to reset breakpoints
89 | * @param conf
90 | */
91 | setBreakpoints(conf?: PaneBreaks, bottomOffset?: number): Promise;
92 | moveToBreak(val: string, type?: string): Promise;
93 | moveToHeight(val: number): Promise;
94 | hide(): Promise;
95 | isHidden(): (boolean | null);
96 | currentBreak(): (string | null);
97 | destroy(conf?: {
98 | animate: boolean;
99 | destroyButton?: boolean;
100 | transition?: {
101 | duration?: number;
102 | from?: {};
103 | to?: {};
104 | };
105 | }): Promise;
106 | destroyResets(): void;
107 | }
108 |
--------------------------------------------------------------------------------
/dist/types/device.d.ts:
--------------------------------------------------------------------------------
1 | export declare class Device {
2 | ios: boolean;
3 | android: boolean;
4 | androidChrome: boolean;
5 | desktop: boolean;
6 | iphone: boolean;
7 | ipod: boolean;
8 | ipad: boolean;
9 | edge: boolean;
10 | ie: boolean;
11 | firefox: boolean;
12 | macos: boolean;
13 | windows: boolean;
14 | cordova: boolean;
15 | phonegap: boolean;
16 | electron: boolean;
17 | os: string;
18 | osVersion: string;
19 | webView: any;
20 | webview: any;
21 | standalone: any;
22 | pixelRatio: any;
23 | ionic: boolean;
24 | constructor();
25 | }
26 |
--------------------------------------------------------------------------------
/dist/types/events-emitter.d.ts:
--------------------------------------------------------------------------------
1 | export declare function on(events: any, handler: any, priority: any): void;
2 | export declare function emit(...args: any[]): void;
3 |
--------------------------------------------------------------------------------
/dist/types/events/events.d.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from '../cupertino-pane';
2 | /**
3 | * Touch start, Touch move, Touch end
4 | * Click, Scroll
5 | */
6 | export declare class Events {
7 | private instance;
8 | touchEvents: {
9 | start: string;
10 | move: string;
11 | end: string;
12 | cancel: string;
13 | };
14 | private allowClick;
15 | private disableDragAngle;
16 | private mouseDown;
17 | contentScrollTop: number;
18 | private startY;
19 | private startX;
20 | private steps;
21 | isScrolling: boolean;
22 | startPointOverTop: number;
23 | swipeNextSensivity: number;
24 | private settings;
25 | private device;
26 | private breakpoints;
27 | private transitions;
28 | private keyboardEvents;
29 | constructor(instance: CupertinoPane);
30 | private getTouchEvents;
31 | attachAllEvents(): void;
32 | detachAllEvents(): void;
33 | resetEvents(): void;
34 | /**
35 | * Core DOM elements event listeners
36 | * @param type
37 | * @param el
38 | */
39 | private eventListeners;
40 | /**
41 | * Touch Start Event
42 | * @param t
43 | */
44 | touchStartCb: (t: any) => void;
45 | private touchStart;
46 | /**
47 | * Touch Move Event
48 | * @param t
49 | */
50 | touchMoveCb: (t: any) => void;
51 | private touchMove;
52 | /**
53 | * Touch End Event
54 | * @param t
55 | */
56 | touchEndCb: (t: any) => void;
57 | private touchEnd;
58 | /**
59 | * Click Event
60 | * @param t
61 | */
62 | onScrollCb: (t: any) => Promise;
63 | private onScroll;
64 | /**
65 | * Click Event
66 | * @param t
67 | */
68 | onClickCb: (t: any) => void;
69 | private onClick;
70 | fastSwipeNext(axis: 'Y' | 'X'): boolean;
71 | /**
72 | * Private class methods
73 | */
74 | /**
75 | * Superposition handler.
76 | * Superposition is the ability of a quantum system to be in multiple states at the same time until it is measured.
77 | * Topper Than Top
78 | * Lower Than Bottom
79 | * Lefter Than Left
80 | * Righter Than Right
81 | */
82 | private handleSuperposition;
83 | private getEventClientYX;
84 | scrollPreventDrag(t: any): boolean;
85 | willScrolled(): boolean;
86 | private isDraggableElement;
87 | private isFormElement;
88 | isElementScrollable(el: any): boolean;
89 | }
90 |
--------------------------------------------------------------------------------
/dist/types/events/index.d.ts:
--------------------------------------------------------------------------------
1 | import { Events } from "./events";
2 | import { KeyboardEvents } from "./keyboard";
3 | export { Events, KeyboardEvents };
4 |
--------------------------------------------------------------------------------
/dist/types/events/keyboard.d.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from '../cupertino-pane';
2 | /**
3 | * Resize, Keyboard show, Keyboard hide
4 | */
5 | export declare class KeyboardEvents {
6 | private instance;
7 | inputBluredbyMove: boolean;
8 | private keyboardVisibleResize;
9 | private inputBottomOffset;
10 | private previousInputBottomOffset;
11 | private prevNewHeight;
12 | private prevFocusedElement;
13 | private device;
14 | private breakpoints;
15 | constructor(instance: CupertinoPane);
16 | /**
17 | * Open Cordova Keyboard event
18 | * @param e
19 | */
20 | onKeyboardShowCb: (e: any) => Promise;
21 | private onKeyboardShow;
22 | /**
23 | * Close Cordova Keyboard event
24 | * @param e
25 | */
26 | onKeyboardWillHideCb: (e: any) => void;
27 | private onKeyboardWillHide;
28 | /**
29 | * Window resize event
30 | * We handle here keyboard event as well
31 | * @param e
32 | */
33 | onWindowResizeCb: (e: any) => Promise;
34 | private onWindowResize;
35 | /**
36 | * Private class methods
37 | */
38 | private isPaneDescendant;
39 | private isFormElement;
40 | private isOnViewport;
41 | /**
42 | * Deal with Ionic Framework.
43 | * ion-input, ion-textarea changes in Client rects after window resize.
44 | * get rects by parent, not shadowDom el
45 | */
46 | private getActiveInputClientBottomRect;
47 | /**
48 | * Using only to fix follower elemennts jumps out by OSK
49 | * Fix OSK
50 | * https://developer.chrome.com/blog/viewport-resize-behavior/
51 | * Chrome 108+ will adjust with overlays-content
52 | * When everyones updates, can be replaced with adding content-overlays to meta
53 | */
54 | fixBodyKeyboardResize(showKeyboard: any): void;
55 | }
56 |
--------------------------------------------------------------------------------
/dist/types/models.d.ts:
--------------------------------------------------------------------------------
1 | export interface PaneBreak {
2 | enabled: boolean;
3 | height?: number;
4 | bounce?: boolean;
5 | }
6 | export interface PaneBreaks {
7 | top?: PaneBreak;
8 | middle?: PaneBreak;
9 | bottom?: PaneBreak;
10 | }
11 | export interface ZStackSettings {
12 | pushElements: string[];
13 | minPushHeight?: number;
14 | cardBorderRadius: number;
15 | cardYOffset?: number;
16 | cardZScale?: number;
17 | cardContrast?: number;
18 | stackZAngle?: number;
19 | }
20 | export interface ModalSettings {
21 | transition?: 'fade' | 'zoom';
22 | flying?: boolean;
23 | dismissOnIntense?: boolean;
24 | }
25 | export interface TransitionStartEvent {
26 | translateY: {
27 | new: number;
28 | };
29 | }
30 | export interface CupertinoEvents {
31 | onDidDismiss?: (event?: CustomEvent) => void;
32 | onWillDismiss?: (event?: CustomEvent) => void;
33 | onDidPresent?: (event?: CustomEvent) => void;
34 | onWillPresent?: (event?: CustomEvent) => void;
35 | onDragStart?: (event?: CustomEvent) => void;
36 | onDrag?: (event?: any) => void;
37 | onDragEnd?: (event?: CustomEvent) => void;
38 | onBackdropTap?: (event?: CustomEvent) => void;
39 | onTransitionStart?: (event?: TransitionStartEvent) => void;
40 | onTransitionEnd?: (event?: any) => void;
41 | }
42 | export interface PaneSettings {
43 | initialBreak: ('top' | 'middle' | 'bottom');
44 | horizontal: boolean;
45 | horizontalOffset: number;
46 | inverse: boolean;
47 | parentElement: string | HTMLElement;
48 | followerElement: string;
49 | cssClass: string;
50 | fitHeight: boolean;
51 | maxFitHeight: number;
52 | fitScreenHeight: boolean;
53 | ionContentScroll: boolean;
54 | backdrop: boolean;
55 | backdropBlur: boolean;
56 | backdropOpacity: number;
57 | animationType: string;
58 | animationDuration: number;
59 | bottomOffset: number;
60 | bottomClose: boolean;
61 | fastSwipeClose: boolean;
62 | fastSwipeSensivity: number;
63 | freeMode: boolean;
64 | buttonDestroy: boolean;
65 | topperOverflow: boolean;
66 | topperOverflowOffset: number;
67 | lowerThanBottom: boolean;
68 | upperThanTop: boolean;
69 | showDraggable: boolean;
70 | draggableOver: boolean;
71 | clickBottomOpen: boolean;
72 | dragBy: string[];
73 | preventClicks: boolean;
74 | handleKeyboard: boolean;
75 | simulateTouch: boolean;
76 | passiveListeners: boolean;
77 | touchMoveStopPropagation: boolean;
78 | touchAngle: number;
79 | breaks: PaneBreaks;
80 | modal: ModalSettings | boolean;
81 | zStack: ZStackSettings;
82 | events: CupertinoEvents;
83 | modules: any[];
84 | }
85 | export type CupertinoSettings = Partial;
86 |
--------------------------------------------------------------------------------
/dist/types/modules/backdrop.d.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from '../cupertino-pane';
2 | /**
3 | * Backdrop module
4 | */
5 | export declare class BackdropModule {
6 | private instance;
7 | backdropEl: HTMLDivElement;
8 | private settings;
9 | private events;
10 | constructor(instance: CupertinoPane);
11 | /**
12 | * Show/Hide backdrop primary function
13 | */
14 | private backdrop;
15 | /**
16 | * Private class methods
17 | */
18 | private renderBackdrop;
19 | private isBackdropPresented;
20 | /**
21 | * Touch Move Event
22 | * @param t
23 | */
24 | touchMoveBackdropCb: (t: any) => void;
25 | private touchMoveBackdrop;
26 | }
27 |
--------------------------------------------------------------------------------
/dist/types/modules/fit-height.d.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from '../cupertino-pane';
2 | /**
3 | * FitHeight module
4 | * fitHeight / fitScreenHeight / maxFitHeight
5 | */
6 | export declare class FitHeightModule {
7 | private instance;
8 | calcHeightInProcess: boolean;
9 | private breakpoints;
10 | private settings;
11 | private paneElHeight;
12 | constructor(instance: CupertinoPane);
13 | private beforeBuildBreakpoints;
14 | calcFitHeight(animated?: boolean): Promise;
15 | setOverflowHeight(offset?: number): void;
16 | private getPaneFitHeight;
17 | }
18 |
--------------------------------------------------------------------------------
/dist/types/modules/follower.d.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from '../cupertino-pane';
2 | /**
3 | * Follower Element module
4 | */
5 | export declare class FollowerModule {
6 | private instance;
7 | private followerEl;
8 | private settings;
9 | private breakpoints;
10 | private transitions;
11 | constructor(instance: CupertinoPane);
12 | }
13 |
--------------------------------------------------------------------------------
/dist/types/modules/horizontal.d.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from '../cupertino-pane';
2 | /**
3 | * Horizontal module
4 | */
5 | export declare class HorizontalModule {
6 | private instance;
7 | private static readonly forceSettings;
8 | static CollectSettings(settings: any): any;
9 | private defaultRect;
10 | private horizontalBreaks;
11 | private currentBreakpoint;
12 | private fastSwipeNext;
13 | private settings;
14 | private transitions;
15 | private events;
16 | constructor(instance: CupertinoPane);
17 | private calcHorizontalBreaks;
18 | setPaneElTransform(params: any): void;
19 | private getClosestBreakX;
20 | }
21 |
--------------------------------------------------------------------------------
/dist/types/modules/index.d.ts:
--------------------------------------------------------------------------------
1 | import { ZStackModule } from "./z-stack";
2 | import { FollowerModule } from "./follower";
3 | import { BackdropModule } from "./backdrop";
4 | import { FitHeightModule } from "./fit-height";
5 | import { InverseModule } from './inverse';
6 | import { HorizontalModule } from './horizontal';
7 | import { ModalModule } from "./modal";
8 | export { ZStackModule, FollowerModule, BackdropModule, FitHeightModule, InverseModule, HorizontalModule, ModalModule };
9 |
--------------------------------------------------------------------------------
/dist/types/modules/inverse.d.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from '../cupertino-pane';
2 | /**
3 | * Inverse module
4 | */
5 | export declare class InverseModule {
6 | private instance;
7 | private breakpoints;
8 | private settings;
9 | private events;
10 | constructor(instance: CupertinoPane);
11 | getPaneHeight(): number;
12 | updateScreenHeights(): void;
13 | setOverflowHeight(): void;
14 | checkOverflowAttr(val: any): void;
15 | private prepareBreaksSwipeNextPoint;
16 | /**
17 | * Topper Than Top
18 | * Lower Than Bottom
19 | * Otherwise don't changes
20 | */
21 | private handleSuperposition;
22 | scrollPreventDrag(t: any): boolean;
23 | private isOverflowEl;
24 | private onScroll;
25 | }
26 |
--------------------------------------------------------------------------------
/dist/types/modules/modal.d.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from '../cupertino-pane';
2 | import { ModalSettings } from '../models';
3 | /**
4 | * Modal module
5 | */
6 | export declare class ModalModule {
7 | private instance;
8 | private static readonly BuildInTransition;
9 | private static readonly ForceSettings;
10 | static CollectSettings(settings: any): any;
11 | modalDefaults: ModalSettings;
12 | private settings;
13 | private events;
14 | private breakpoints;
15 | private transitions;
16 | constructor(instance: CupertinoPane);
17 | setPaneElTransform(params: any): void;
18 | /**
19 | * Private class methods
20 | */
21 | private present;
22 | private destroy;
23 | private handleSuperposition;
24 | }
25 |
--------------------------------------------------------------------------------
/dist/types/modules/z-stack.d.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from '../cupertino-pane';
2 | import { ZStackSettings } from '../models';
3 | /**
4 | * Z-Stack Module
5 | */
6 | export declare class ZStackModule {
7 | private instance;
8 | zStackDefaults: ZStackSettings;
9 | private settings;
10 | private breakpoints;
11 | constructor(instance: CupertinoPane);
12 | /**
13 | * Change z-stack configuration on the way
14 | */
15 | setZstackConfig(zStack: ZStackSettings): void;
16 | /**
17 | * Z-Stack push transitions
18 | * @param pushElement - element be pushed
19 | * @param newPaneY - translateY of new pane
20 | * @param transition - transition style
21 | * @returns
22 | */
23 | pushTransition(pushElement: HTMLElement, newPaneY: number, transition: string): void;
24 | setPushMultiplicators(): void;
25 | /**
26 | * Private class methods
27 | */
28 | private getPushMulitplicator;
29 | private clearPushMultiplicators;
30 | }
31 |
--------------------------------------------------------------------------------
/dist/types/public-api.d.ts:
--------------------------------------------------------------------------------
1 | export { CupertinoPane } from "./cupertino-pane";
2 | export { CupertinoSettings } from "./models";
3 |
--------------------------------------------------------------------------------
/dist/types/settings.d.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoSettings } from './models';
2 | export declare class Settings {
3 | instance: CupertinoSettings;
4 | constructor();
5 | }
6 |
--------------------------------------------------------------------------------
/dist/types/support.d.ts:
--------------------------------------------------------------------------------
1 | export declare class Support {
2 | static get touch(): boolean;
3 | static get observer(): boolean;
4 | static get backdropFilter(): boolean;
5 | static get passiveListener(): boolean;
6 | static get gestures(): boolean;
7 | }
8 |
--------------------------------------------------------------------------------
/dist/types/transitions.d.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from './cupertino-pane';
2 | /**
3 | * Transitions class
4 | * Z-Push transitions class
5 | */
6 | export declare enum CupertinoTransition {
7 | Present = "present",
8 | Destroy = "destroy",
9 | Move = "move",
10 | Breakpoint = "breakpoint",
11 | Hide = "hide",
12 | TouchEnd = "end"
13 | }
14 | export declare class Transitions {
15 | private instance;
16 | isPaneHidden: boolean;
17 | private settings;
18 | private breakpoints;
19 | constructor(instance: CupertinoPane);
20 | /***********************************
21 | * Transitions handler
22 | */
23 | doTransition(params?: any): Promise;
24 | private setPaneElTransform;
25 | buildTransitionValue(bounce: boolean, duration?: number): string;
26 | /**
27 | * Private class methods
28 | */
29 | private doesPanesExists;
30 | }
31 |
--------------------------------------------------------------------------------
/docs/images/bulletin.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/docs/images/bulletin.gif
--------------------------------------------------------------------------------
/docs/images/custom-transitions.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/docs/images/custom-transitions.gif
--------------------------------------------------------------------------------
/docs/images/notifications.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/docs/images/notifications.gif
--------------------------------------------------------------------------------
/docs/images/overflow.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/docs/images/overflow.gif
--------------------------------------------------------------------------------
/docs/images/picture-in-picture.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/docs/images/picture-in-picture.gif
--------------------------------------------------------------------------------
/docs/images/z-stack.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/docs/images/z-stack.gif
--------------------------------------------------------------------------------
/docs/logo/logo-1-mini.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/docs/logo/logo-1-mini.jpg
--------------------------------------------------------------------------------
/docs/logo/logo-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/docs/logo/logo-1.jpg
--------------------------------------------------------------------------------
/docs/logo/logo-2-mini.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/docs/logo/logo-2-mini.png
--------------------------------------------------------------------------------
/docs/logo/logo-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/docs/logo/logo-2.png
--------------------------------------------------------------------------------
/docs/logo/logo-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/docs/logo/logo-3.png
--------------------------------------------------------------------------------
/docs/logo/logo-4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/docs/logo/logo-4.jpg
--------------------------------------------------------------------------------
/docs/logo/logo-5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/docs/logo/logo-5.jpg
--------------------------------------------------------------------------------
/docs/logo/logo-6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/docs/logo/logo-6.jpg
--------------------------------------------------------------------------------
/gulp/banner.js:
--------------------------------------------------------------------------------
1 | const pkg = require('../package.json');
2 |
3 | const date = {
4 | day: new Date().getDate(),
5 | month: ('January February March April May June July August September October November December').split(' ')[new Date().getMonth()],
6 | year: new Date().getFullYear(),
7 | };
8 |
9 | module.exports = `${`
10 | /**
11 | * Cupertino Pane ${pkg.version}
12 | * ${pkg.description}
13 | * ${pkg.homepage}
14 | *
15 | * Copyright 2019-${date.year} ${pkg.author}
16 | *
17 | * Released under the ${pkg.license} License
18 | *
19 | * Released on: ${date.month} ${date.day}, ${date.year}
20 | */
21 | `.trim()}\n`;
--------------------------------------------------------------------------------
/gulp/build-bundle.js:
--------------------------------------------------------------------------------
1 | /* eslint import/no-extraneous-dependencies: ["error", {"devDependencies": true}] */
2 | /* eslint no-console: "off" */
3 | const fs = require('fs-extra');
4 | const rollup = require('rollup');
5 | const Terser = require('terser');
6 | const typescript = require('rollup-plugin-typescript2');
7 | const replace = require('@rollup/plugin-replace');
8 | const elapsed = require('elapsed-time-logger');
9 | const chalk = require('chalk');
10 |
11 | const banner = require('./banner.js');
12 | const env = process.env.NODE_ENV;
13 |
14 | async function buildEntry(format, includeModules) {
15 | const bundleName = 'CupertinoPane';
16 | const isUMD = format === 'umd';
17 | const isESM = format === 'esm';
18 | const isProd = env === 'production';
19 | const isDev = env === 'development';
20 | const sourcemap = isProd && isUMD;
21 | let filename = 'cupertino-pane';
22 | const outputDir = 'dist';
23 | if (isESM) filename += `.esm`;
24 |
25 | // Bundle
26 | new Promise(async(resolve, reject) => {
27 | let bundle = await rollup.rollup({
28 | input: './src/index.ts',
29 | plugins: [
30 | replace({
31 | delimiters: ['', ''],
32 | preventAssignment: true,
33 | '//EXPORT': isUMD ? `export default ${bundleName};` : `export { ${bundleName} }`,
34 | // Include modules
35 | 'import * as Modules from \'./modules\';': `${includeModules.map(
36 | module => `import {${module}} from './modules';`
37 | ).join('\n')}\nconst Modules = {${includeModules.map(item => `${item}:${item}`).join(', ')}};`
38 | }),
39 | typescript({
40 | useTsconfigDeclarationDir: true,
41 | cacheRoot: process.cwd() + `/.rpt2_cache/${filename}`,
42 | })
43 | ],
44 | });
45 |
46 | bundle = await bundle.write({
47 | name: bundleName,
48 | format, banner, sourcemap,
49 | strict: true,
50 | exports: isUMD ? 'default' : 'named',
51 | sourcemapFile: `./${outputDir}/${filename}.js.map`,
52 | file: `./${outputDir}/${filename}.js`,
53 | });
54 |
55 | // Build types only once
56 | if (isESM) {
57 | let typings = await rollup.rollup({
58 | input: './src/public-api.ts',
59 | plugins: [
60 | require("rollup-plugin-dts").default()
61 | ]
62 | });
63 |
64 | typings = await typings.write({
65 | file: `${outputDir}/types/index.d.ts`,
66 | format: "es"
67 | });
68 | }
69 |
70 | if (isDev) {
71 | return resolve();
72 | };
73 |
74 | const result = bundle.output[0];
75 | const { code, map } = await Terser.minify(result.code, {
76 | sourceMap: {
77 | content: sourcemap ? result.map : undefined,
78 | filename: sourcemap ? `${filename}.min.js` : undefined,
79 | url: `${filename}.min.js.map`,
80 | },
81 | output: {
82 | preamble: banner,
83 | },
84 | }).catch((err) => {
85 | console.error(`Terser failed on file ${filename}: ${err.toString()}`);
86 | return reject();
87 | });
88 |
89 | await fs.writeFile(`./${outputDir}/${filename}.min.js`, code);
90 | await fs.writeFile(`./${outputDir}/${filename}.min.js.map`, map);
91 |
92 | return resolve();
93 | });
94 | }
95 |
96 | async function build() {
97 | elapsed.start('bundles');
98 |
99 | // Get all modules
100 | let indexTs = await fs.readFileSync('./src/modules/index.ts', {encoding: 'utf-8'});
101 | let allModules = indexTs.replaceAll(/\r?\n|\r/g, '').replaceAll(/\s/g,'');
102 | allModules = allModules.substring(
103 | allModules.indexOf("export{") + 7,
104 | allModules.lastIndexOf("};")
105 | ).split(',')
106 |
107 | // Prepare modules to exclude
108 | let includeModules = process.argv.find(item => item.includes('--modules='));
109 | if (includeModules) {
110 | // Includes Modules
111 | includeModules = includeModules.replace('--modules=', '').replaceAll(/\s/g,'').split(',');
112 | includeModules = includeModules.filter(item => allModules.includes(item));
113 | } else {
114 | includeModules = allModules;
115 | }
116 |
117 | console.log(chalk.yellow(`Modules includes to bundle: ${includeModules.join(', ')}`));
118 |
119 | return Promise.all(
120 | [
121 | buildEntry('umd', includeModules),
122 | buildEntry('esm', includeModules)
123 | ]
124 | ).then(() => {
125 | elapsed.end('bundles', chalk.green('Rollup bundles build completed!'));
126 | });
127 | }
128 |
129 | module.exports = build;
--------------------------------------------------------------------------------
/gulp/build-core.js:
--------------------------------------------------------------------------------
1 | /* eslint import/no-extraneous-dependencies: ["error", {"devDependencies": true}] */
2 | /* eslint no-console: "off" */
3 |
4 | const fs = require('fs-extra');
5 | const rollup = require('rollup');
6 | const Terser = require('terser');
7 | const typescript = require('rollup-plugin-typescript2');
8 | const replace = require('@rollup/plugin-replace');
9 | const elapsed = require('elapsed-time-logger');
10 | const chalk = require('chalk');
11 |
12 | const banner = require('./banner.js');
13 | const env = process.env.NODE_ENV;
14 |
15 | async function buildEntry(format) {
16 | const bundleName = 'CupertinoPane';
17 | const filename = 'cupertino-pane.esm';
18 | const outputDir = 'dist/core';
19 |
20 | // Bundle
21 | new Promise(async(resolve, reject) => {
22 | let bundle = await rollup.rollup({
23 | input: './src/index.ts',
24 | plugins: [
25 | replace({
26 | delimiters: ['', ''],
27 | preventAssignment: true,
28 | '//EXPORT': `export { ${bundleName} }`,
29 | // Remove modules from package, keep only core
30 | 'import * as Modules from \'./modules\'': 'const Modules = {};'
31 | }),
32 | typescript({
33 | useTsconfigDeclarationDir: true,
34 | cacheRoot: process.cwd() + `/.rpt2_cache/core.${filename}`,
35 | })
36 | ],
37 | });
38 |
39 | bundle = await bundle.write({
40 | name: bundleName,
41 | format: 'esm',
42 | banner,
43 | strict: true,
44 | exports: 'named',
45 | file: `./${outputDir}/index.js`,
46 | });
47 |
48 | const result = bundle.output[0];
49 | const { code } = await Terser.minify(result.code, {
50 | output: { preamble: banner },
51 | }).catch((err) => {
52 | console.error(`Terser failed on file ${filename}: ${err.toString()}`);
53 | return reject();
54 | });
55 |
56 | await fs.writeFile(`./${outputDir}/index.js`, code);
57 | return resolve();
58 | });
59 | }
60 |
61 | async function build() {
62 | elapsed.start('build-core');
63 | return Promise.all(
64 | [
65 | buildEntry('esm')
66 | ]
67 | ).then(() => {
68 | elapsed.end('build-core', chalk.green('Rollup core build completed!'));
69 | });
70 | }
71 |
72 | module.exports = build;
--------------------------------------------------------------------------------
/gulp/build-modules.js:
--------------------------------------------------------------------------------
1 | /* eslint import/no-extraneous-dependencies: ["error", {"devDependencies": true}] */
2 | /* eslint no-console: "off" */
3 |
4 | const fs = require('fs-extra');
5 | const rollup = require('rollup');
6 | const Terser = require('terser');
7 | const typescript = require('rollup-plugin-typescript2');
8 | const replace = require('@rollup/plugin-replace');
9 | const elapsed = require('elapsed-time-logger');
10 | const chalk = require('chalk');
11 |
12 | const banner = require('./banner.js');
13 | const env = process.env.NODE_ENV;
14 | const outputDir = 'dist/modules';
15 |
16 | async function buildEntry(file) {
17 | const filename = file.replace('.ts', '');
18 |
19 | // Bundle
20 | new Promise(async(resolve, reject) => {
21 | let bundle = await rollup.rollup({
22 | input: `./src/modules/${file}`,
23 | plugins: [
24 | replace({
25 | delimiters: ['', ''],
26 | preventAssignment: true
27 | }),
28 | typescript({
29 | useTsconfigDeclarationDir: true,
30 | cacheRoot: process.cwd() + `/.rpt2_cache/modules.${filename}`,
31 | })
32 | ],
33 | });
34 |
35 | bundle = await bundle.write({
36 | name: `${filename}Module`,
37 | format: 'esm',
38 | banner,
39 | strict: true,
40 | exports: 'named',
41 | file: `./${outputDir}/${filename}.js`,
42 | });
43 |
44 | const result = bundle.output[0];
45 | const { code } = await Terser.minify(result.code, {
46 | output: { preamble: banner },
47 | }).catch((err) => {
48 | console.error(`Terser failed on file ${filename}: ${err.toString()}`);
49 | return reject();
50 | });
51 |
52 | await fs.writeFile(`./${outputDir}/${filename}.js`, code);
53 | return resolve();
54 | });
55 | }
56 |
57 | async function build() {
58 | elapsed.start('build-modules');
59 | // Get module files
60 | let moduleFiles = await fs.readdirSync('./src/modules/', (err, files) => files);
61 | moduleFiles = moduleFiles.filter((file) => file.endsWith(".ts") && file !== 'index.ts');
62 |
63 | // Copy index.ts
64 | if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
65 | fs.copyFileSync('./src/modules/index.ts', `${outputDir}/index.js`);
66 |
67 | return Promise.all(
68 | moduleFiles.map(file => buildEntry(file)
69 | )).then(() => {
70 | elapsed.end('build-modules', chalk.green('Rollup modules build completed!'));
71 | });
72 | }
73 |
74 | module.exports = build;
--------------------------------------------------------------------------------
/gulp/gulpfile.js:
--------------------------------------------------------------------------------
1 | /* eslint no-console: ["error", { allow: ["log"] }] */
2 | const gulp = require('gulp');
3 | const connect = require('gulp-connect');
4 | const gopen = require('gulp-open');
5 | const fs = require('fs');
6 | const path = require('path');
7 | const env = process.env.NODE_ENV;
8 |
9 | const buildBundle = require('./build-bundle.js');
10 | const buildCore = require('./build-core.js');
11 | const buildModules = require('./build-modules.js');
12 |
13 | // js bundle
14 | gulp.task('js', (cb) => {
15 | buildBundle(cb);
16 |
17 | if (env === 'production') {
18 | buildCore(cb);
19 | buildModules(cb);
20 | }
21 |
22 | return gulp
23 | .src('./src/**/*.ts')
24 | .pipe(connect.reload());
25 | });
26 |
27 | // in prod builds, adjust sourcemap paths to actual src location
28 | gulp.task('prod-source-sourcemap-fix-paths', (cb) => {
29 | const env = process.env.NODE_ENV || 'development';
30 | if (env === 'production') {
31 | const jsDir = path.resolve(__dirname, '../dist/');
32 | const mapFiles = fs
33 | .readdirSync(jsDir)
34 | .filter((file) => file.toLowerCase().endsWith('.map'));
35 | mapFiles.forEach((mapFile) => {
36 | const mapFilePath = path.resolve(jsDir, mapFile);
37 | let content = fs.readFileSync(mapFilePath, 'utf8');
38 | content = content
39 | .replace(/"\.\.\/\.\.\//g, '"../')
40 | .replace(/"\.\.\/node_modules\//g, '"~/');
41 | fs.writeFileSync(mapFilePath, content);
42 | });
43 | }
44 | if (cb) cb();
45 | });
46 |
47 | gulp.task('build', gulp.series(['js', 'prod-source-sourcemap-fix-paths']));
48 |
49 | gulp.task('watch', () => {
50 | gulp.watch('./src/**/**/*.ts', gulp.series('js'));
51 | });
52 |
53 | gulp.task('connect', () => {
54 | connect.server({
55 | root: ['./'],
56 | livereload: true,
57 | host: '0.0.0.0',
58 | port: '3000',
59 | });
60 | });
61 |
62 | gulp.task('open', () => {
63 | gulp.src('./playground/index.html').pipe(gopen({ uri: 'http://localhost:3000/playground/' }));
64 | });
65 |
66 | gulp.task('server', gulp.parallel(['watch', 'connect', 'open']));
67 |
68 | gulp.task('default', gulp.series('server'));
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | require('./gulp/gulpfile');
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cupertino-pane",
3 | "description": "Cupertino Panes is multi-functional modals, cards & panes with touch technologies.",
4 | "version": "1.4.22",
5 | "author": "Roman Antonov (roman-rr)",
6 | "homepage": "https://panejs.com",
7 | "repository": "tech-systems/panes",
8 | "license": "MIT",
9 | "main": "dist/cupertino-pane.min.js",
10 | "module": "dist/cupertino-pane.esm.min.js",
11 | "types": "dist/types/index.d.ts",
12 | "scripts": {
13 | "build": "rimraf dist && NODE_ENV=production gulp build",
14 | "serve": "rimraf dist && NODE_ENV=development gulp build && gulp server",
15 | "tslint": "tslint src/**/*.ts",
16 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
17 | },
18 | "devDependencies": {
19 | "@rollup/plugin-replace": "^5.0.2",
20 | "chalk": "^4.1.2",
21 | "elapsed-time-logger": "^1.1.7",
22 | "fs-extra": "^10.0.0",
23 | "glob": "^7.1.6",
24 | "gulp": "4.0.2",
25 | "gulp-connect": "5.7.0",
26 | "gulp-open": "3.0.1",
27 | "rimraf": "^3.0.2",
28 | "rollup": "3.29.5",
29 | "rollup-plugin-dts": "5.3.0",
30 | "rollup-plugin-typescript2": "^0.31.2",
31 | "terser": "5.14.2",
32 | "tslib": "^1.10.0",
33 | "tslint": "^5.20.1",
34 | "typescript": "^4.4.4"
35 | },
36 | "bugs": {
37 | "url": "https://github.com/tech-systems/panes/issues"
38 | },
39 | "browserslist": [
40 | "Android >= 7",
41 | "IOS >= 11",
42 | "Safari >= 11",
43 | "Chrome >= 49",
44 | "Firefox >= 31",
45 | "Samsung >= 5"
46 | ],
47 | "keywords": [
48 | "app",
49 | "modal",
50 | "cupertino",
51 | "pane",
52 | "web",
53 | "ios",
54 | "maps",
55 | "javascript",
56 | "touch",
57 | "slide",
58 | "mobile",
59 | "cordova",
60 | "plugin",
61 | "web3",
62 | "progressive"
63 | ]
64 | }
65 |
--------------------------------------------------------------------------------
/playground/auto-height.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Panes - Auto Height
7 |
8 |
9 |
10 |
11 |
12 |
13 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | Cats
76 |
77 |
78 | Dogs
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | Welcome to pet board!
99 | 😻
100 |
101 | Explore and choose a pet that you wanna play with.
102 |
103 |
104 | Set bottom offset
105 |
106 |
107 |
108 |
109 |
110 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/playground/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Cupertino Pane - Base
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | Header
20 | Content
21 |
22 |
23 |
24 | Extra header
25 | Extra content
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | Favorite
39 | Share
40 |
41 |
42 | Item Options
43 |
44 |
45 | Unread
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | Slide 1
54 |
55 |
56 | Slide 2
57 |
58 |
59 | Slide 3
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | Present Drawer
71 | Destroy Drawer
72 |
73 | Hide Drawer
74 | Drawer is hidden
75 |
76 | Set Top
77 | Set Middle
78 | Set Bottom
79 |
80 | Show Backdrop
81 | Extra Pane
82 |
83 |
84 |
85 |
176 |
177 |
178 |
--------------------------------------------------------------------------------
/playground/calc-fit-height.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Cupertino Pane - Calc Fit Height
7 |
8 |
9 |
53 |
54 |
55 |
56 |
57 |
Content B (200px)
58 |
Content C (175px)
59 |
Content D (175px)
60 |
Content E (300px)
61 |
62 |
63 | Content A (100px)
64 | Test 1
65 |
66 |
67 |
68 |
69 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/playground/follower.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Cupertino Pane - Follower
7 |
8 |
9 |
10 |
11 |
12 |
13 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | Header
32 | Content
33 |
34 |
35 | Present Drawer
36 | Destroy Drawer
37 |
38 | Hide Drawer
39 | Drawer is hidden
40 |
41 | Set Middle
42 | Set Bottom
43 |
44 | Set Breakpoints
45 |
46 |
47 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/playground/img/app-clips.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/playground/img/app-clips.jpg
--------------------------------------------------------------------------------
/playground/img/cup-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/playground/img/cup-1.png
--------------------------------------------------------------------------------
/playground/img/cup-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/playground/img/cup-2.png
--------------------------------------------------------------------------------
/playground/img/nike-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/playground/img/nike-1.jpg
--------------------------------------------------------------------------------
/playground/img/nike-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/playground/img/nike-2.jpg
--------------------------------------------------------------------------------
/playground/img/nike-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/playground/img/nike-3.jpg
--------------------------------------------------------------------------------
/playground/img/nike-app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/playground/img/nike-app.png
--------------------------------------------------------------------------------
/playground/img/starbucks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tech-systems/panes/1f3cf9800534311a1a35d19c1fbb72b9064f8f77/playground/img/starbucks.png
--------------------------------------------------------------------------------
/playground/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Cupertino Pane - Playground
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Main
17 |
18 |
19 |
20 |
21 | Base
22 |
23 |
24 |
25 |
26 | Z-Stack
27 |
28 |
29 |
30 |
31 | Overflow
32 |
33 |
34 |
35 |
36 | Follower
37 |
38 |
39 |
40 |
41 | Auto Height (fitHeight)
42 |
43 |
44 |
45 |
46 | Top to bottom
47 |
48 |
49 |
50 |
51 | Calc fitHeight
52 |
53 |
54 |
55 |
56 |
57 |
58 | Modal
59 |
60 |
61 |
62 |
63 | Basic Modal
64 |
65 |
66 |
67 |
68 |
69 | Zoom Transition
70 |
71 |
72 |
73 |
74 |
75 | Custom Transition
76 |
77 |
78 |
79 |
80 |
81 | Flying Mode
82 |
83 |
84 |
85 |
86 |
87 | Dismiss-on-Intense
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/playground/modal/basic.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Panes - Modal - Basic
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
Default modal example
28 |
With basic default fade built-in transition, and automatically calculated modal height.
29 |
Try to move this modal
30 |
31 | Dismiss Modal
32 |
33 |
34 |
35 |
36 | Present Modal
37 |
38 |
39 |
40 |
41 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/playground/modal/custom-transition.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Panes - Modal - Custom Transition
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
Custom Transition modal
28 |
Modal with custom configured transition for present and destroy functions.
29 |
Try to move this modal
30 |
31 | Dismiss Modal
32 |
33 |
34 |
35 |
36 | Present Modal
37 |
38 |
39 |
40 |
41 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/playground/modal/dismiss-on-intense.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Panes - Modal - Dismiss-on-Intense
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 |
23 |
24 |
25 |
26 |
Dismiss-on-Intense modal
27 |
Modal will be nicely dismissed on high-intense move events.
28 |
Try to move pane out of the screen
29 |
30 | Dismiss Modal
31 |
32 |
33 |
34 |
35 | Present Modal
36 |
37 |
38 |
39 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/playground/modal/flying.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Panes - Modal - Flying
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
Flying modal example
28 |
Modal with dynamic flying effect animation.
29 |
Try to move this modal
30 |
31 | Dismiss Modal
32 |
33 |
34 |
35 |
36 | Present Modal
37 |
38 |
39 |
40 |
41 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/playground/modal/zoom-transition.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Panes - Modal - Zoom Transition
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
Zoom Transition modal
28 |
Modal with default zoom built-in transition, and automatically calculated modal height.
29 |
Try to move this modal
30 |
31 | Dismiss Modal
32 |
33 |
34 |
35 |
36 | Present Modal
37 |
38 |
39 |
40 |
41 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/playground/overflow.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Cupertino Pane - Overflow
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | Header
33 |
34 |
35 |
36 | Recent Conversations
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | Finn
45 | I'm a big deal
46 | Listen, I've had a pretty messed up day...
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | Han
56 | Look, kid...
57 | I've got enough on my plate as it is, and I...
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Rey
67 | I can handle myself
68 | You will remove these restraints and leave...
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | Luke
78 | Your thoughts betray you
79 | I feel the good in you, the conflict...
80 |
81 |
82 |
83 |
84 |
85 |
86 | Online
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | Poe
95 | New Ride
96 | I just upgraded my X-Wing. Next time...
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | Ben
106 | Move Along
107 | These aren't the droids you're looking for...
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | Leia
117 | You're My Only Hope
118 | I've placed information vital to the survival...
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 | Yoda
128 | Size matters not
129 | Do or do not. There is no try...
130 |
131 |
132 |
133 |
134 |
135 |
136 | Present Drawer
137 | Destroy Drawer
138 |
139 | Hide Drawer
140 | Drawer is hidden
141 | Toggle dark mode
142 | Move to height = 575
143 |
144 | Set Top
145 | Set Middle
146 | Set Bottom
147 |
148 | Set Breakpoints
149 |
150 |
151 |
219 |
220 |
221 |
--------------------------------------------------------------------------------
/playground/top-to-bottom.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Cupertino Pane - Top to bottom
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
header here
22 |
23 |
Header
24 |
This is good example of Cupertino Pane with inversed direction.
25 |
Events switched into top-to-bottom
26 |
27 |
28 |
29 |
30 |
31 | Present Drawer
32 | Destroy Drawer
33 |
34 | Hide Drawer
35 | Drawer is hidden
36 |
37 | Set breakpoints
38 |
39 |
40 |
41 |
64 |
123 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/playground/with-swiper.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Cupertino Pane - Using with swiperjs
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | Header
28 | Content
29 |
30 |
31 | Slide 1
32 | Slide 2
33 | Slide 3
34 |
35 |
36 |
37 |
38 | Present Drawer
39 | Destroy Drawer
40 |
41 |
42 |
43 |
44 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/playground/z-stack.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Cupertino Pane - Z-stack
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | #1
52 |
53 |
54 | #2
55 |
56 |
57 | #3
58 |
59 |
60 | #4
61 |
62 |
63 | #5
64 |
65 |
66 |
67 |
68 |
69 | Card #1
70 | Content
71 |
72 |
73 | Card #2
74 | Content
75 |
76 |
77 | Card #3
78 | Content
79 |
80 |
81 | Card #4
82 | Content
83 |
84 |
85 | Card #5
86 | Content
87 |
88 |
89 |
90 |
91 |
195 |
196 |
197 |
--------------------------------------------------------------------------------
/src/breakpoints.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from './cupertino-pane';
2 | import { PaneBreaks, CupertinoSettings } from './models';
3 |
4 | /**
5 | * Breakpoints builder
6 | */
7 |
8 | export class Breakpoints {
9 |
10 | public topper: number;
11 | public bottomer: number;
12 | public breaks: {} = {}
13 | public lockedBreakpoints: any;
14 | public currentBreakpoint: number;
15 | public prevBreakpoint: string;
16 | public brs: number[] = [];
17 | public beforeBuildBreakpoints:() => any = () => {};
18 | public conf: PaneBreaks;
19 | private defaultBreaksConf: PaneBreaks = {
20 | top: { enabled: true, height: window.innerHeight - (135 * 0.35) },
21 | middle: { enabled: true, height: 300 },
22 | bottom: { enabled: true, height: 100 },
23 | };
24 |
25 | private settings: CupertinoSettings;
26 | constructor(private instance: CupertinoPane) {
27 | this.settings = this.instance.settings;
28 | }
29 |
30 | /**
31 | * Function builder for breakpoints and heights
32 | * @param conf breakpoints
33 | */
34 | public async buildBreakpoints(conf?: PaneBreaks, bottomOffset: number = 0, animated: boolean = true) {
35 | this.breaks = {};
36 | this.conf = conf;
37 | this.settings.bottomOffset = bottomOffset || this.settings.bottomOffset;
38 |
39 | // Async hook for modules injections
40 | await this.beforeBuildBreakpoints();
41 |
42 | ['top', 'middle', 'bottom'].forEach((val) => {
43 | // Set default if no exist
44 | if (!this.settings.breaks[val]) {
45 | this.settings.breaks[val] = this.defaultBreaksConf[val];
46 | }
47 |
48 | // Override from user conf on updating
49 | if (this.conf && this.conf[val]) {
50 | this.settings.breaks[val] = this.conf[val];
51 | }
52 |
53 | // System event
54 | this.instance.emit('beforeBreakHeightApplied', {break: val});
55 |
56 | // Apply initial breaks
57 | if (this.settings.breaks[val]?.enabled) {
58 | this.breaks[val] = this.breaks[val] || this.instance.screenHeightOffset;
59 | this.breaks[val] -= this.settings.bottomOffset;
60 | this.breaks[val] -= this.settings.breaks[val].height;
61 | }
62 | });
63 |
64 | // initial lock on present
65 | if (!this.lockedBreakpoints) {
66 | this.lockedBreakpoints = JSON.stringify(this.settings.breaks);
67 | }
68 |
69 | // Warnings
70 | if (!this.instance.isPanePresented()) {
71 | if (!this.settings.breaks[this.settings.initialBreak].enabled) {
72 | console.warn('Cupertino Pane: Please set initialBreak for enabled breakpoint');
73 | }
74 | }
75 | if (this.settings.breaks['middle'].height >= this.settings.breaks['top'].height) {
76 | console.warn('Cupertino Pane: Please set middle height lower than top height');
77 | }
78 | if (this.settings.breaks['middle'].height <= this.settings.breaks['bottom'].height) {
79 | console.warn('Cupertino Pane: Please set bottom height lower than middle height');
80 | }
81 |
82 | // Prepare breakpoint numbers array
83 | // TODO: this.brs to this.breaks.map()
84 | this.brs = [];
85 | ['top', 'middle', 'bottom'].forEach((val) => {
86 | if (this.settings.breaks[val].enabled) {
87 | this.brs.push(this.breaks[val]);
88 | }
89 | });
90 |
91 | // Determinate topper point
92 | this.topper = this.brs.reduce((prev, curr) => {
93 | return (curr < prev ? curr : prev);
94 | });
95 | // Determinate bottomer point
96 | this.bottomer = this.brs.reduce((prev, curr) => {
97 | return (Math.abs(curr) > Math.abs(prev) ? curr : prev);
98 | });
99 |
100 | if (!this.instance.isPanePresented()) {
101 | this.currentBreakpoint = this.breaks[this.settings.initialBreak];
102 | }
103 |
104 | if (this.instance.isPanePresented()) {
105 | // Move to current if updated
106 | if (this.settings.breaks[this.prevBreakpoint]?.enabled) {
107 | if (!this.instance.isHidden()) {
108 | // Move to any if removed
109 | this.instance.moveToBreak(
110 | this.prevBreakpoint,
111 | animated ? 'breakpoint' : 'move'
112 | );
113 | }
114 | }
115 |
116 | if (!this.settings.breaks[this.prevBreakpoint]?.enabled) {
117 | if (!this.instance.isHidden()) {
118 | let nextY = this.instance.swipeNextPoint(1, 1, this.getClosestBreakY());
119 | const nextBreak = Object.entries(this.breaks).find(val => val[1] === nextY);
120 | this.instance.moveToBreak(nextBreak[0]);
121 | }
122 | }
123 | }
124 |
125 | // Re-calc heights and scrolls
126 | this.instance.scrollElementInit();
127 |
128 | // Checks
129 | this.instance.checkOpacityAttr(this.currentBreakpoint);
130 | this.instance.checkOverflowAttr(this.currentBreakpoint);
131 |
132 | // System event
133 | this.instance.emit('buildBreakpointsCompleted');
134 | }
135 |
136 | // TODO: Replace currentBreakpoint with prevBreakpoint if possible
137 | public getCurrentBreakName(): (string|null) {
138 | if (this.breaks['top'] === this.currentBreakpoint) return 'top';
139 | if (this.breaks['middle'] === this.currentBreakpoint) return 'middle';
140 | if (this.breaks['bottom'] === this.currentBreakpoint) return 'bottom';
141 | return null;
142 | }
143 |
144 | public getClosestBreakY(): number {
145 | return this.brs.reduce((prev, curr) => {
146 | return (Math.abs(curr - this.instance.getPanelTransformY()) < Math.abs(prev - this.instance.getPanelTransformY()) ? curr : prev);
147 | });
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/src/device.ts:
--------------------------------------------------------------------------------
1 | import { Support } from './support';
2 |
3 | export class Device {
4 | public ios: boolean = false;
5 | public android: boolean = false;
6 | public androidChrome: boolean = false;
7 | public desktop:boolean = false;
8 | public iphone: boolean = false;
9 | public ipod: boolean = false;
10 | public ipad: boolean = false;
11 | public edge: boolean = false;
12 | public ie: boolean = false;
13 | public firefox: boolean = false;
14 | public macos: boolean = false;
15 | public windows: boolean = false;
16 | public cordova: boolean = !!(window['cordova'] || window['phonegap']);
17 | public phonegap: boolean = !!(window['cordova'] || window['phonegap']);
18 | public electron: boolean = false;
19 | public os: string;
20 | public osVersion: string;
21 | public webView: any;
22 | public webview: any;
23 | public standalone: any;
24 | public pixelRatio: any;
25 | public ionic: boolean = !!document.querySelector('ion-app');
26 |
27 | constructor() {
28 | const platform = window.navigator.platform;
29 | const ua = window.navigator.userAgent;
30 | const screenWidth = window.screen.width;
31 | const screenHeight = window.screen.height;
32 |
33 | let android = ua.match(/(Android);?[\s\/]+([\d.]+)?/); // eslint-disable-line
34 | let ipad = ua.match(/(iPad).*OS\s([\d_]+)/);
35 | let ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/);
36 | let iphone = !this.ipad && ua.match(/(iPhone\sOS|iOS)\s([\d_]+)/);
37 | let ie = ua.indexOf('MSIE ') >= 0 || ua.indexOf('Trident/') >= 0;
38 | let edge = ua.indexOf('Edge/') >= 0;
39 | let firefox = ua.indexOf('Gecko/') >= 0 && ua.indexOf('Firefox/') >= 0;
40 | let windows = platform === 'Win32';
41 | let electron = ua.toLowerCase().indexOf('electron') >= 0;
42 | let macos = platform === 'MacIntel';
43 |
44 | // iPadOs 13 fix
45 | if (!ipad
46 | && macos
47 | && Support.touch
48 | && (
49 | (screenWidth === 1024 && screenHeight === 1366) // Pro 12.9
50 | || (screenWidth === 834 && screenHeight === 1194) // Pro 11
51 | || (screenWidth === 834 && screenHeight === 1112) // Pro 10.5
52 | || (screenWidth === 768 && screenHeight === 1024) // other
53 | )
54 | ) {
55 | ipad = ua.match(/(Version)\/([\d.]+)/);
56 | macos = false;
57 | }
58 | this.ie = ie;
59 | this.edge = edge;
60 | this.firefox = firefox;
61 |
62 | // Android
63 | if (android && !windows) {
64 | this.os = 'android';
65 | this.osVersion = android[2];
66 | this.android = true;
67 | this.androidChrome = ua.toLowerCase().indexOf('chrome') >= 0;
68 | }
69 | if (ipad || iphone || ipod) {
70 | this.os = 'ios';
71 | this.ios = true;
72 | }
73 | // iOS
74 | if (iphone && !ipod) {
75 | this.osVersion = iphone[2].replace(/_/g, '.');
76 | this.iphone = true;
77 | }
78 | if (ipad) {
79 | this.osVersion = ipad[2].replace(/_/g, '.');
80 | this.ipad = true;
81 | }
82 | if (ipod) {
83 | this.osVersion = ipod[3] ? ipod[3].replace(/_/g, '.') : null;
84 | this.ipod = true;
85 | }
86 | // iOS 8+ changed UA
87 | if (this.ios && this.osVersion && ua.indexOf('Version/') >= 0) {
88 | if (this.osVersion.split('.')[0] === '10') {
89 | this.osVersion = ua.toLowerCase().split('version/')[1].split(' ')[0];
90 | }
91 | }
92 |
93 | // Webview
94 | this.webView = !!((iphone || ipad || ipod) && (ua.match(/.*AppleWebKit(?!.*Safari)/i) || window.navigator['standalone']))
95 | || (window.matchMedia && window.matchMedia('(display-mode: standalone)').matches);
96 | this.webview = this.webView;
97 | this.standalone = this.webView;
98 |
99 | // Desktop
100 | this.desktop = !(this.ios || this.android) || electron;
101 | if (this.desktop) {
102 | this.electron = electron;
103 | this.macos = macos;
104 | this.windows = windows;
105 | if (this.macos) {
106 | this.os = 'macos';
107 | }
108 | if (this.windows) {
109 | this.os = 'windows';
110 | }
111 | }
112 |
113 | // Pixel Ratio
114 | this.pixelRatio = window.devicePixelRatio || 1;
115 | }
116 | }
--------------------------------------------------------------------------------
/src/events-emitter.ts:
--------------------------------------------------------------------------------
1 | // Add listeners
2 | export function on(events, handler, priority): void {
3 | if (!this.eventsListeners) {
4 | return;
5 | }
6 | if (typeof handler !== 'function') {
7 | return;
8 | }
9 | const method = priority ? 'unshift' : 'push';
10 | events.split(' ').forEach((event) => {
11 | if (!this.eventsListeners[event]) {
12 | this.eventsListeners[event] = [];
13 | }
14 | this.eventsListeners[event][method](handler);
15 | });
16 | }
17 |
18 | // Emit events
19 | export function emit(...args): void {
20 | if (!this.eventsListeners) {
21 | return;
22 | }
23 | let events = args[0];
24 | let data = args.slice(1, args.length);
25 | const eventsArray = Array.isArray(events) ? events : events.split(' ');
26 | eventsArray.forEach((event) => {
27 | if (this.eventsListeners?.[event]) {
28 | this.eventsListeners[event].forEach(
29 | (eventHandler) => eventHandler.apply(this, data)
30 | );
31 | }
32 | });
33 | }
--------------------------------------------------------------------------------
/src/events/index.ts:
--------------------------------------------------------------------------------
1 | import { Events } from "./events";
2 | import { KeyboardEvents } from "./keyboard";
3 | export {
4 | Events,
5 | KeyboardEvents
6 | };
--------------------------------------------------------------------------------
/src/events/keyboard.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from '../cupertino-pane';
2 | import { Device } from '../device';
3 | import { Breakpoints } from '../breakpoints';
4 |
5 | /**
6 | * Resize, Keyboard show, Keyboard hide
7 | */
8 |
9 | export class KeyboardEvents {
10 |
11 | // Keyboard help vars
12 | public inputBluredbyMove: boolean = false;
13 |
14 | private keyboardVisibleResize: boolean = false;
15 | private inputBottomOffset: number = 0;
16 | private previousInputBottomOffset: number = 0;
17 | private prevNewHeight: number = 0;
18 | private prevFocusedElement: Element;
19 | private device: Device;
20 | private breakpoints: Breakpoints;
21 | constructor(private instance: CupertinoPane) {
22 | this.device = this.instance.device;
23 | this.breakpoints = this.instance.breakpoints;
24 | }
25 |
26 | /**
27 | * Open Cordova Keyboard event
28 | * @param e
29 | */
30 | public onKeyboardShowCb = (e) => this.onKeyboardShow(e);
31 | private async onKeyboardShow(e) {
32 | // focus element not inside pane
33 | if (!this.isPaneDescendant(document.activeElement)) {
34 | return;
35 | }
36 |
37 | // pane not visible on viewport
38 | if (!this.isOnViewport()) {
39 | return;
40 | }
41 |
42 | this.keyboardVisibleResize = true;
43 | this.fixBodyKeyboardResize(true);
44 |
45 | // calculate distances based on transformY
46 | let currentHeight = (this.instance.getPanelTransformY() - this.instance.screen_height) * -1;
47 | const inputEl = document.activeElement;
48 | const inputElBottomBound: number = this.getActiveInputClientBottomRect();
49 | const inputSpaceBelow = this.instance.screen_height - inputElBottomBound - this.inputBottomOffset;
50 |
51 | let offset = this.device.cordova && this.device.android ? 130 : 100;
52 | let spaceBelowOffset = 0;
53 | let newHeight = currentHeight + (e.keyboardHeight - inputSpaceBelow);
54 |
55 | // Multiple event fired with opened keyboard
56 | if (this.prevNewHeight) {
57 | spaceBelowOffset = this.previousInputBottomOffset - inputElBottomBound;
58 | newHeight = this.prevNewHeight;
59 | }
60 |
61 | // Re-focus input dublicate events
62 | if (inputEl.isEqualNode(this.prevFocusedElement)) {
63 | return;
64 | }
65 |
66 | // Keyboard will overlaps input
67 | if (e.keyboardHeight > inputSpaceBelow) {
68 |
69 | this.prevNewHeight = newHeight - spaceBelowOffset;
70 | this.prevFocusedElement = document.activeElement;
71 |
72 | let nextHeight = newHeight - spaceBelowOffset + offset;
73 |
74 | // Not push more than pane height
75 | if (nextHeight > this.instance.getPaneHeight() + e.keyboardHeight) {
76 | nextHeight = this.instance.getPaneHeight() + e.keyboardHeight;
77 | }
78 |
79 | /**
80 | * TODO: textarea issues
81 | */
82 | await this.instance.moveToHeight(nextHeight);
83 |
84 | // Determinate device offset for presented keyboard
85 | const newInputBottomOffset: number = this.getActiveInputClientBottomRect();
86 | this.previousInputBottomOffset = newInputBottomOffset;
87 | if (!this.inputBottomOffset) {
88 | this.inputBottomOffset = inputElBottomBound - newInputBottomOffset;
89 | }
90 | }
91 | }
92 |
93 | /**
94 | * Close Cordova Keyboard event
95 | * @param e
96 | */
97 | public onKeyboardWillHideCb = (e) => this.onKeyboardWillHide(e);
98 | private onKeyboardWillHide(e) {
99 | // pane not visible on viewport
100 | if (!this.isOnViewport()) {
101 | return;
102 | }
103 |
104 | this.fixBodyKeyboardResize(false);
105 |
106 | // Clear
107 | this.inputBottomOffset = 0;
108 | this.previousInputBottomOffset = 0;
109 | this.prevNewHeight = 0;
110 | delete this.prevFocusedElement;
111 |
112 | if (this.inputBluredbyMove) {
113 | this.inputBluredbyMove = false;
114 | return;
115 | }
116 |
117 | if (this.instance.isHidden()) {
118 | return;
119 | }
120 |
121 | // Position doesn't changed
122 | if (this.instance.getPanelTransformY() === this.breakpoints.breaks[this.breakpoints.prevBreakpoint]) {
123 | return;
124 | }
125 |
126 | this.instance.moveToBreak(this.breakpoints.prevBreakpoint);
127 | }
128 |
129 | /**
130 | * Window resize event
131 | * We handle here keyboard event as well
132 | * @param e
133 | */
134 | public onWindowResizeCb = (e) => this.onWindowResize(e);
135 | private async onWindowResize(e) {
136 |
137 | /**
138 | * Keyboard event detection
139 | * We should separate keyboard and resize events
140 | */
141 | if (this.isFormElement(document.activeElement)) {
142 | // Only for non-cordova
143 | if (!this.device.cordova) {
144 | this.onKeyboardShow({keyboardHeight: this.instance.screen_height - window.innerHeight});
145 | }
146 | return;
147 | }
148 |
149 | if (this.keyboardVisibleResize) {
150 | this.keyboardVisibleResize = false;
151 |
152 | // Only for non-cordova
153 | if (!this.device.cordova) {
154 | this.onKeyboardWillHide({});
155 | }
156 | return;
157 | }
158 |
159 | await new Promise((resolve) => setTimeout(() => resolve(true), 150));
160 | this.instance.updateScreenHeights();
161 | this.breakpoints.buildBreakpoints(JSON.parse(this.breakpoints.lockedBreakpoints));
162 | }
163 |
164 |
165 | /**
166 | * Private class methods
167 | */
168 |
169 |
170 |
171 | // TODO: switch to contains
172 | private isPaneDescendant(el): boolean {
173 | if (!el) {
174 | return false;
175 | }
176 | let node = el.parentNode;
177 | while (node != null) {
178 | if (node == this.instance.paneEl) {
179 | return true;
180 | }
181 | node = node.parentNode;
182 | }
183 | return false;
184 | }
185 |
186 | private isFormElement(el):boolean {
187 | const formElements: string[] = [
188 | 'input', 'select', 'option',
189 | 'textarea', 'button', 'label'
190 | ];
191 |
192 | if (el && el.tagName
193 | && formElements.includes(el.tagName.toLowerCase())) {
194 | return true;
195 | }
196 | return false;
197 | }
198 |
199 | private isOnViewport(): boolean {
200 | if (this.instance.paneEl
201 | && this.instance.paneEl.offsetWidth === 0
202 | && this.instance.paneEl.offsetHeight === 0 ) {
203 | return false;
204 | }
205 |
206 | return true;
207 | }
208 |
209 | /**
210 | * Deal with Ionic Framework.
211 | * ion-input, ion-textarea changes in Client rects after window resize.
212 | * get rects by parent, not shadowDom el
213 | */
214 | private getActiveInputClientBottomRect(): number {
215 | if (document.activeElement.classList.contains('native-textarea')
216 | || document.activeElement.classList.contains('native-input')) {
217 | // Go top until ionic element
218 | let ionElement = document.activeElement.parentElement?.parentElement?.parentElement;
219 | return ionElement.getBoundingClientRect().bottom;
220 | }
221 |
222 | return document.activeElement.getBoundingClientRect().bottom;
223 | }
224 |
225 |
226 | /**
227 | * Using only to fix follower elemennts jumps out by OSK
228 | * Fix OSK
229 | * https://developer.chrome.com/blog/viewport-resize-behavior/
230 | * Chrome 108+ will adjust with overlays-content
231 | * When everyones updates, can be replaced with adding content-overlays to meta
232 | */
233 | public fixBodyKeyboardResize(showKeyboard) {
234 | if (!this.instance.paneEl) return;
235 | const metaViewport = document.querySelector('meta[name=viewport]');
236 |
237 | window.requestAnimationFrame(() => {
238 | if (showKeyboard) {
239 | document.documentElement.style.setProperty('overflow', 'hidden');
240 | document.body.style.setProperty('min-height', `${this.instance.screen_height}px`);
241 | metaViewport.setAttribute('content', 'height=' + this.instance.screen_height + ', width=device-width, initial-scale=1.0')
242 | } else {
243 | document.documentElement.style.removeProperty('overflow');
244 | document.body.style.removeProperty('min-height');
245 | metaViewport.setAttribute('content', 'viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no');
246 | }
247 | });
248 | }
249 |
250 |
251 | }
252 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from "./cupertino-pane";
2 | //umd-ems export will replaced on rollup build
3 | //EXPORT
--------------------------------------------------------------------------------
/src/models.ts:
--------------------------------------------------------------------------------
1 | export interface PaneBreak {
2 | enabled: boolean,
3 | height?: number,
4 | bounce?: boolean
5 | }
6 |
7 | export interface PaneBreaks {
8 | top?: PaneBreak;
9 | middle?: PaneBreak;
10 | bottom?: PaneBreak;
11 | }
12 |
13 | export interface ZStackSettings {
14 | pushElements: string[];
15 | minPushHeight?: number;
16 | cardBorderRadius: number,
17 | cardYOffset?: number;
18 | cardZScale?: number;
19 | cardContrast?: number;
20 | stackZAngle?: number;
21 | }
22 |
23 | export interface ModalSettings {
24 | transition?: 'fade' | 'zoom';
25 | flying?: boolean;
26 | dismissOnIntense?: boolean;
27 | }
28 |
29 | export interface TransitionStartEvent {
30 | translateY: { new: number};
31 | }
32 |
33 | export interface CupertinoEvents {
34 | onDidDismiss?: (event?: CustomEvent) => void;
35 | onWillDismiss?: (event?: CustomEvent) => void;
36 | onDidPresent?: (event?: CustomEvent) => void;
37 | onWillPresent?: (event?: CustomEvent) => void;
38 | onDragStart?: (event?: CustomEvent) => void;
39 | onDrag?: (event?: any) => void;
40 | onDragEnd?: (event?: CustomEvent) => void;
41 | onBackdropTap?: (event?: CustomEvent) => void;
42 | onTransitionStart?: (event?: TransitionStartEvent) => void;
43 | onTransitionEnd?: (event?: any) => void;
44 | }
45 |
46 | export interface PaneSettings {
47 | initialBreak: ('top' | 'middle' | 'bottom');
48 | horizontal: boolean;
49 | horizontalOffset: number;
50 | inverse: boolean;
51 | parentElement: string | HTMLElement;
52 | followerElement: string;
53 | cssClass: string;
54 | fitHeight: boolean;
55 | maxFitHeight: number;
56 | fitScreenHeight: boolean;
57 | ionContentScroll: boolean;
58 | backdrop: boolean;
59 | backdropBlur: boolean;
60 | backdropOpacity: number;
61 | animationType: string;
62 | animationDuration: number;
63 | bottomOffset: number,
64 | bottomClose: boolean;
65 | fastSwipeClose: boolean;
66 | fastSwipeSensivity: number;
67 | freeMode: boolean;
68 | buttonDestroy: boolean;
69 | topperOverflow: boolean;
70 | topperOverflowOffset: number;
71 | lowerThanBottom: boolean;
72 | upperThanTop: boolean;
73 | showDraggable: boolean;
74 | draggableOver: boolean;
75 | clickBottomOpen: boolean;
76 | dragBy: string[];
77 | preventClicks: boolean;
78 | handleKeyboard: boolean;
79 | simulateTouch: boolean;
80 | passiveListeners: boolean;
81 | touchMoveStopPropagation: boolean;
82 | touchAngle: number;
83 | breaks: PaneBreaks;
84 | modal: ModalSettings | boolean;
85 | zStack: ZStackSettings;
86 | events: CupertinoEvents;
87 | modules: any[];
88 | }
89 |
90 | export type CupertinoSettings = Partial;
--------------------------------------------------------------------------------
/src/modules/backdrop.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from '../cupertino-pane';
2 | import { CupertinoSettings } from '../models';
3 | import { Events } from '../events/events';
4 | import { CupertinoTransition } from '../transitions';
5 | import { Support } from '../support';
6 |
7 | /**
8 | * Backdrop module
9 | */
10 |
11 | export class BackdropModule {
12 |
13 | public backdropEl: HTMLDivElement;
14 |
15 | private settings: CupertinoSettings;
16 | private events: Events;
17 | constructor(private instance: CupertinoPane) {
18 | this.settings = this.instance.settings;
19 | this.events = this.instance.events;
20 |
21 | if (!this.settings.backdrop) {
22 | return;
23 | }
24 |
25 | // bind to primary instance
26 | this.instance['backdrop'] = (conf) => this.backdrop(conf);
27 |
28 | this.instance.on('rendered', () => {
29 | this.instance.addStyle( `
30 | .cupertino-pane-wrapper .backdrop {
31 | overflow: hidden;
32 | position: fixed;
33 | width: 100%;
34 | bottom: 0;
35 | right: 0;
36 | left: 0;
37 | top: 0;
38 | display: none;
39 | z-index: 10;
40 | ${Support.backdropFilter && this.settings.backdropBlur ? `
41 | backdrop-filter: saturate(180%) blur(10px);
42 | -webkit-backdrop-filter: saturate(180%) blur(10px);
43 | ` : ``}
44 | }
45 | `);
46 |
47 | if (this.settings.backdrop) {
48 | this.renderBackdrop();
49 | }
50 | });
51 |
52 | this.instance.on('beforePresentTransition', (ev) => {
53 | if (!ev.animate) {
54 | this.backdropEl.style.display = `block`;
55 | }
56 | });
57 |
58 | this.instance.on('onTransitionStart', (ev) => {
59 | if (!this.settings.backdrop) {
60 | return;
61 | }
62 |
63 | if (this.instance.isHidden()
64 | || ev.type === CupertinoTransition.Hide
65 | || ev.type === CupertinoTransition.Destroy
66 | || ev.type === CupertinoTransition.Present) {
67 | this.backdropEl.style.backgroundColor = 'rgba(0,0,0,.0)';
68 | this.backdropEl.style.transition = `all ${this.settings.animationDuration}ms ${this.settings.animationType} 0s`;
69 |
70 | if (ev.type !== CupertinoTransition.Hide
71 | && ev.type !== CupertinoTransition.Destroy) {
72 | this.backdropEl.style.display = 'block';
73 | setTimeout(() => {
74 | this.backdropEl.style.backgroundColor = `rgba(0,0,0, ${this.settings.backdropOpacity})`;
75 | }, 50);
76 | }
77 | }
78 | });
79 |
80 | this.instance.on('onTransitionEnd', (ev) => {
81 | if (!this.backdropEl) {
82 | return;
83 | }
84 |
85 | if (ev.type === CupertinoTransition.Destroy
86 | || ev.type === CupertinoTransition.Hide) {
87 | this.backdropEl.style.transition = `initial`;
88 | this.backdropEl.style.display = `none`;
89 | }
90 | });
91 |
92 | // Stop propagation for touchmove events
93 | if (Support.touch) {
94 | // Attach events
95 | this.instance.on('onDidPresent', () => {
96 | this.backdropEl?.addEventListener(
97 | this.events.touchEvents.move,
98 | this.touchMoveBackdropCb,
99 | Support.passiveListener ? { passive: false, capture: false } : false);
100 | });
101 |
102 | // Detach events
103 | this.instance.on('onDidDismiss', (ev) => {
104 | this.backdropEl?.removeEventListener(this.events.touchEvents.move, this.touchMoveBackdropCb);
105 | });
106 | }
107 | }
108 |
109 | /**
110 | * Show/Hide backdrop primary function
111 | */
112 | private backdrop(conf = { show: true }): void {
113 | if (!this.instance.isPanePresented()) {
114 | console.warn(`Cupertino Pane: Present pane before call backdrop()`);
115 | return null;
116 | }
117 |
118 | if (!this.isBackdropPresented()) {
119 | this.renderBackdrop();
120 |
121 | // Reset events to attach backdrop stop propagation
122 | if (Support.touch) {
123 | this.backdropEl?.removeEventListener(this.events.touchEvents.move, this.touchMoveBackdropCb);
124 | this.backdropEl?.addEventListener(
125 | this.events.touchEvents.move,
126 | this.touchMoveBackdropCb,
127 | Support.passiveListener ? { passive: false, capture: false } : false);
128 | }
129 | }
130 |
131 | const transitionEnd = () => {
132 | this.backdropEl.style.transition = `initial`;
133 | this.backdropEl.style.display = `none`;
134 | this.backdropEl.removeEventListener('transitionend', transitionEnd);
135 | }
136 |
137 | this.backdropEl.style.transition = `all ${this.settings.animationDuration}ms ${this.settings.animationType} 0s`;
138 | this.backdropEl.style.backgroundColor = 'rgba(0,0,0,.0)';
139 |
140 | if (!conf.show) {
141 | // Destroy
142 | if (this.backdropEl.style.display === 'none') return;
143 | this.backdropEl.addEventListener('transitionend', transitionEnd);
144 | } else {
145 | // Present
146 | this.backdropEl.style.display = 'block';
147 | setTimeout(() => {
148 | this.backdropEl.style.backgroundColor = `rgba(0,0,0, ${this.settings.backdropOpacity})`;
149 | }, 50);
150 | }
151 | }
152 |
153 | /**
154 | * Private class methods
155 | */
156 | private renderBackdrop() {
157 | this.backdropEl = document.createElement('div');
158 | this.backdropEl.classList.add('backdrop');
159 | this.backdropEl.style.transition = `all ${this.settings.animationDuration}ms ${this.settings.animationType} 0s`;
160 | this.backdropEl.style.backgroundColor = `rgba(0,0,0, ${this.settings.backdropOpacity})`;
161 | this.instance.wrapperEl.appendChild(this.backdropEl);
162 | this.backdropEl.addEventListener('click', (event) => this.instance.emit('onBackdropTap', event));
163 | }
164 |
165 | private isBackdropPresented() {
166 | return document.querySelector(`.cupertino-pane-wrapper .backdrop`)
167 | ? true : false;
168 | }
169 |
170 | /**
171 | * Touch Move Event
172 | * @param t
173 | */
174 | public touchMoveBackdropCb = (t) => this.touchMoveBackdrop(t);
175 | private touchMoveBackdrop(t) {
176 | if (this.settings.touchMoveStopPropagation) {
177 | t.stopPropagation();
178 | }
179 | }
180 |
181 | }
182 |
--------------------------------------------------------------------------------
/src/modules/fit-height.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from '../cupertino-pane';
2 | import { Breakpoints } from '../breakpoints';
3 | import { CupertinoSettings } from '../models';
4 |
5 | /**
6 | * FitHeight module
7 | * fitHeight / fitScreenHeight / maxFitHeight
8 | */
9 |
10 | export class FitHeightModule {
11 |
12 | public calcHeightInProcess: boolean = false;
13 | private breakpoints: Breakpoints;
14 | private settings: CupertinoSettings;
15 | private paneElHeight: number;
16 |
17 | constructor(private instance: CupertinoPane) {
18 | this.breakpoints = this.instance.breakpoints;
19 | this.settings = this.instance.settings;
20 |
21 | if (!this.settings.fitHeight) {
22 | return;
23 | }
24 |
25 | // bind to primary instance
26 | // TODO: change binding strategy according to TypeScript
27 | // E.G. Using public module methods from modules
28 | this.instance['calcFitHeight'] = async(animated) => this.calcFitHeight(animated);
29 | this.instance['setOverflowHeight'] = () => this.setOverflowHeight();
30 |
31 | // Class to wrapper
32 | this.instance.on('DOMElementsReady', () => {
33 | this.instance.wrapperEl.classList.add('fit-height');
34 | });
35 |
36 | this.instance.on('onDidPresent', () => {
37 | this.instance.paneEl.style.height = `unset`;
38 | });
39 |
40 | this.instance.on('onTransitionEnd', () => {
41 | this.instance.paneEl.style.height = `unset`;
42 | });
43 |
44 | // Pass our code into function buildBreakpoints()
45 | this.instance.on('onWillPresent', () => {
46 | this.breakpoints.beforeBuildBreakpoints = () => this.beforeBuildBreakpoints();
47 | });
48 |
49 | // buildBreakpoints() function hook
50 | this.instance.on('beforeBreakHeightApplied', (ev) => {
51 | // fitScreenHeight (breaks styles fit screen)
52 | if (this.settings.fitScreenHeight) {
53 | if (this.settings.breaks[ev.break]?.height > this.instance.screen_height) {
54 | this.settings.breaks[ev.break].height = this.instance.screen_height - this.settings.bottomOffset;
55 | }
56 |
57 | // Merge breakpoints if not much difference
58 | if (this.settings.breaks['top'] && this.settings.breaks['middle']) {
59 | if (this.settings.breaks['top'].height - 50 <= this.settings.breaks['middle'].height) {
60 | this.settings.breaks['middle'].enabled = false;
61 | this.settings.initialBreak = 'top';
62 | }
63 | }
64 | }
65 |
66 | // fitHeight (bullet-in styles for screen)
67 | if (ev.break === 'top') {
68 | if (this.settings.breaks['top'].height > this.instance.screen_height) {
69 | this.settings.breaks['top'].height = this.instance.screen_height - (this.settings.bottomOffset * 2);
70 | this.settings.topperOverflow = true;
71 | this.settings.upperThanTop = false;
72 | } else {
73 | if (this.instance.overflowEl && !this.settings.maxFitHeight) {
74 | this.settings.topperOverflow = false;
75 | this.instance.overflowEl.style.overflowY = 'hidden';
76 | }
77 | }
78 | }
79 | }, true);
80 | }
81 |
82 | private async beforeBuildBreakpoints(): Promise {
83 | this.settings.fitScreenHeight = false;
84 | this.settings.initialBreak = 'top';
85 | this.settings.topperOverflow = false;
86 | let height = await this.getPaneFitHeight();
87 |
88 | // maxFitHeight
89 | if (this.settings.maxFitHeight
90 | && height > this.settings.maxFitHeight) {
91 | height = this.settings.maxFitHeight;
92 | this.settings.topperOverflow = true;
93 | }
94 |
95 | this.breakpoints.conf = {
96 | top: { enabled: true, height },
97 | middle: { enabled: false }
98 | };
99 | this.breakpoints.conf.top.bounce = this.settings.breaks?.top?.bounce;
100 | this.breakpoints.conf.bottom = this.settings.breaks?.bottom || { enabled: true, height: 0 };
101 | }
102 |
103 | public async calcFitHeight(animated: boolean = true) {
104 | // Allow user to call method asap, dont check with this.isPanePresented()
105 | if (!this.instance.wrapperEl || !this.instance.el) {
106 | return null;
107 | }
108 |
109 | if (this.calcHeightInProcess) {
110 | console.warn(`Cupertino Pane: calcFitHeight() already in process`);
111 | return null;
112 | }
113 |
114 | await this.breakpoints.buildBreakpoints(this.breakpoints.lockedBreakpoints, null, animated);
115 | }
116 |
117 | public setOverflowHeight(offset = 0) {
118 | if (this.paneElHeight > this.instance.screen_height) {
119 | this.instance.paneEl.style.height = `${this.instance.getPaneHeight()}px`;
120 | this.instance.overflowEl.style.height = `${this.instance.getPaneHeight()
121 | - this.settings.topperOverflowOffset
122 | - this.instance.overflowEl.offsetTop
123 | - offset}px`;
124 | }
125 | }
126 |
127 | private async getPaneFitHeight(): Promise {
128 | this.calcHeightInProcess = true;
129 | let images: NodeListOf = this.instance.el.querySelectorAll('img');
130 |
131 | // Make element visible to calculate height
132 | this.instance.el.style.height = 'unset';
133 |
134 | if (!this.instance.rendered) {
135 | this.instance.el.style.visibility = 'hidden';
136 | this.instance.el.style.pointerEvents = 'none';
137 | this.instance.el.style.display = 'block';
138 | this.instance.wrapperEl.style.visibility = 'hidden';
139 | this.instance.wrapperEl.style.pointerEvents = 'none';
140 | this.instance.wrapperEl.style.display = 'block';
141 | }
142 |
143 | // Bulletins with image height we get after images render
144 | let promises = [];
145 | if (images.length) {
146 | promises = Array.from(images).map(
147 | (image) => new Promise((resolve) => {
148 | // Already rendered or passed height attr
149 | if (image.height
150 | || (image.complete && image.naturalHeight)) {
151 | return resolve(true);
152 | }
153 |
154 | image.onload = () => resolve(true);
155 | image.onerror = () => resolve(true);
156 | })
157 | );
158 | }
159 | await Promise.all(promises);
160 | await new Promise(resolve => requestAnimationFrame(resolve));
161 |
162 | let newPaneElHeight = Math.floor(this.instance.paneEl.getBoundingClientRect().height);
163 |
164 | /**
165 | * To prevent raggy transition on pane icrease/decrease,
166 | * we set height before animation transition,
167 | * and afrer transition we release height to be 'unset'
168 | * for proper calculations in further.
169 | *
170 | * Only for changes in pane height,
171 | * to release `height` on 'onTransitionEnd'.
172 | */
173 |
174 | if (this.paneElHeight !== newPaneElHeight) {
175 | this.instance.paneEl.style.height = `${(newPaneElHeight <= this.paneElHeight) ? this.paneElHeight : newPaneElHeight}px`;
176 | }
177 |
178 | // Hide elements back
179 | if (!this.instance.rendered) {
180 | this.instance.el.style.visibility = 'unset';
181 | this.instance.el.style.pointerEvents = 'unset';
182 | this.instance.el.style.display = 'none';
183 | this.instance.wrapperEl.style.visibility = 'unset';
184 | this.instance.wrapperEl.style.pointerEvents = 'unset';
185 | this.instance.wrapperEl.style.display = 'none';
186 | }
187 |
188 | this.calcHeightInProcess = false;
189 |
190 | this.paneElHeight = newPaneElHeight;
191 | return this.paneElHeight;
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/src/modules/follower.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from '../cupertino-pane';
2 | import { CupertinoSettings } from '../models';
3 | import { Breakpoints } from '../breakpoints';
4 | import { Transitions } from '../transitions';
5 |
6 | /**
7 | * Follower Element module
8 | */
9 |
10 | export class FollowerModule {
11 |
12 | private followerEl: HTMLElement;
13 | private settings: CupertinoSettings;
14 | private breakpoints: Breakpoints;
15 | private transitions: Transitions;
16 |
17 | constructor(private instance: CupertinoPane) {
18 | this.breakpoints = this.instance.breakpoints;
19 | this.transitions = this.instance.transitions;
20 | this.settings = this.instance.settings;
21 |
22 | if (!this.settings.followerElement) {
23 | return;
24 | }
25 |
26 | // Set follower initial transitions
27 | this.instance.on('rendered', () => {
28 | if (!document.querySelector(this.settings.followerElement)) {
29 | console.warn('Cupertino Pane: wrong follower element selector specified', this.settings.followerElement);
30 | return;
31 | }
32 |
33 | this.followerEl = document.querySelector(
34 | this.settings.followerElement
35 | );
36 | this.followerEl.style.willChange = 'transform, border-radius';
37 | this.followerEl.style.transform = `translateY(0px) translateZ(0px)`;
38 | this.followerEl.style.transition = this.transitions.buildTransitionValue(this.settings.breaks[this.instance.currentBreak()]?.bounce);
39 | });
40 |
41 | // Move transition same for follower element (minus pane height)
42 | this.instance.on('onMoveTransitionStart', (ev) => {
43 | this.followerEl.style.transition = 'all 0ms linear 0ms';
44 | this.followerEl.style.transform = `translateY(${ev.translateY - this.breakpoints.breaks[this.settings.initialBreak]}px) translateZ(0px)`;
45 | });
46 |
47 | // Reset transition same as for pane element
48 | this.instance.on('onMoveTransitionStart', (ev) => {
49 | this.followerEl.style.transition = `initial`;
50 | });
51 |
52 | this.instance.on('onTransitionStart', (ev) => {
53 | this.followerEl.style.transition = ev.transition;
54 | this.followerEl.style.transform = `translateY(${ev.translateY.new - this.breakpoints.breaks[this.settings.initialBreak]}px) translateZ(0px)`;
55 | });
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/modules/horizontal.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from '../cupertino-pane';
2 | import { Breakpoints } from '../breakpoints';
3 | import { CupertinoSettings } from '../models';
4 | import { Events } from '../events/events';
5 | import { Transitions } from '../transitions';
6 |
7 | /**
8 | * Horizontal module
9 | */
10 |
11 | export class HorizontalModule {
12 |
13 | private static readonly forceSettings = {
14 | touchAngle: null
15 | }
16 |
17 | // Force to use settings by module
18 | public static CollectSettings(settings) {
19 | return settings.horizontal
20 | ? { ...settings, ...HorizontalModule.forceSettings}
21 | : settings;
22 | }
23 |
24 |
25 | private defaultRect;
26 | private horizontalBreaks;
27 | private currentBreakpoint: string;
28 | private fastSwipeNext: boolean;
29 |
30 | private settings: CupertinoSettings;
31 | private transitions: Transitions;
32 | private events: Events;
33 | constructor(private instance: CupertinoPane) {
34 | this.settings = this.instance.settings;
35 | this.transitions = this.instance.transitions;
36 | this.events = this.instance.events;
37 |
38 | if (!this.settings.horizontal) {
39 | return null;
40 | }
41 |
42 | // re-bind functions
43 | this.transitions['setPaneElTransform'] = (params) => this.setPaneElTransform(params);
44 |
45 | // Calculate horizontal breakpoints on left-right edges
46 | // On present or window resized when transformX = 0
47 | this.instance.on('onTransitionEnd', (ev) => {
48 | if ((ev.type === 'breakpoint' || ev.type === 'present')
49 | && !this.instance.getPanelTransformX()) {
50 | this.calcHorizontalBreaks();
51 | }
52 | });
53 |
54 | // In case of present({animate: false})
55 | this.instance.on('onDidPresent', (ev) => {
56 | if (!ev.animate) this.calcHorizontalBreaks();
57 | });
58 |
59 | this.instance.on('onDragEnd', (ev) => {
60 | this.fastSwipeNext = this.events.fastSwipeNext('X');
61 | });
62 | }
63 |
64 | private calcHorizontalBreaks() {
65 | this.defaultRect = {
66 | width: this.instance.paneEl.getBoundingClientRect().width,
67 | left: this.instance.paneEl.getBoundingClientRect().left,
68 | right: this.instance.paneEl.getBoundingClientRect().right
69 | };
70 | this.horizontalBreaks = [
71 | -this.defaultRect.left + this.settings.horizontalOffset,
72 | window.innerWidth - this.defaultRect.left - this.defaultRect.width - this.settings.horizontalOffset
73 | ];
74 | }
75 |
76 | public setPaneElTransform(params) {
77 | let closest = params.translateX;
78 | if (params.type === 'end') {
79 | closest = this.getClosestBreakX();
80 |
81 | if (this.fastSwipeNext) {
82 | if (this.currentBreakpoint === 'left'
83 | && this.instance.getPanelTransformX() > this.horizontalBreaks[0]) {
84 | closest = this.horizontalBreaks[1];
85 | }
86 | if (this.currentBreakpoint === 'right'
87 | && this.instance.getPanelTransformX() < this.horizontalBreaks[1]) {
88 | closest = this.horizontalBreaks[0];
89 | }
90 | }
91 |
92 | this.currentBreakpoint = closest === this.horizontalBreaks[0] ? 'left' : 'right';
93 | }
94 |
95 | this.instance.paneEl.style.transform = `translateX(${closest || 0}px) translateY(${params.translateY}px) translateZ(0px)`;
96 | }
97 |
98 | private getClosestBreakX(): number {
99 | return this.horizontalBreaks.reduce((prev, curr) => {
100 | return (Math.abs(curr - this.instance.getPanelTransformX()) < Math.abs(prev - this.instance.getPanelTransformX()) ? curr : prev);
101 | });
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/modules/index.ts:
--------------------------------------------------------------------------------
1 | import { ZStackModule } from "./z-stack";
2 | import { FollowerModule } from "./follower";
3 | import { BackdropModule } from "./backdrop";
4 | import { FitHeightModule } from "./fit-height";
5 | import { InverseModule } from './inverse';
6 | import { HorizontalModule } from './horizontal';
7 | import { ModalModule } from "./modal";
8 | export {
9 | ZStackModule,
10 | FollowerModule,
11 | BackdropModule,
12 | FitHeightModule,
13 | InverseModule,
14 | HorizontalModule,
15 | ModalModule
16 | };
--------------------------------------------------------------------------------
/src/modules/inverse.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from '../cupertino-pane';
2 | import { Breakpoints } from '../breakpoints';
3 | import { CupertinoSettings } from '../models';
4 | import { Events } from '../events/events';
5 |
6 | /**
7 | * Inverse module
8 | */
9 |
10 | export class InverseModule {
11 |
12 |
13 | private breakpoints: Breakpoints;
14 | private settings: CupertinoSettings;
15 | private events: Events;
16 | constructor(private instance: CupertinoPane) {
17 | this.breakpoints = this.instance.breakpoints;
18 | this.settings = this.instance.settings;
19 | this.events = this.instance.events;
20 |
21 | if (!this.settings.inverse) {
22 | return;
23 | }
24 |
25 | // Forcely remove
26 | this.settings.buttonDestroy = false;
27 |
28 | // re-bind functions
29 | this.instance['getPaneHeight'] = () => this.getPaneHeight();
30 | this.instance['updateScreenHeights'] = () => this.updateScreenHeights();
31 | this.instance['setOverflowHeight'] = () => this.settings.fitHeight ? {} : this.setOverflowHeight();
32 | this.instance['checkOpacityAttr'] = () => {};
33 | this.instance['checkOverflowAttr'] = (val) => this.checkOverflowAttr(val);
34 | this.instance['prepareBreaksSwipeNextPoint'] = () => this.prepareBreaksSwipeNextPoint();
35 | // re-bind events functions
36 | this.events['handleSuperposition'] = (coords) => this.handleSuperposition(coords);
37 | this.events['scrollPreventDrag'] = (t) => this.scrollPreventDrag(t);
38 | this.events['onScroll'] = () => this.onScroll();
39 |
40 | // Class to wrapper
41 | this.instance.on('DOMElementsReady', () => {
42 | this.instance.wrapperEl.classList.add('inverse');
43 | });
44 |
45 | // Styles to elements
46 | this.instance.on('rendered', () => {
47 | this.instance.addStyle(`
48 | .cupertino-pane-wrapper.inverse .pane {
49 | border-radius: 0 0 20px 20px;
50 | border-radius: 0 0
51 | var(--cupertino-pane-border-radius, 20px)
52 | var(--cupertino-pane-border-radius, 20px);
53 | }
54 | .cupertino-pane-wrapper.inverse:not(.fit-height) .pane {
55 | padding-bottom: 15px;
56 | }
57 | .cupertino-pane-wrapper.inverse .draggable {
58 | bottom: 0;
59 | top: initial;
60 | }
61 | .cupertino-pane-wrapper.inverse .draggable.over {
62 | bottom: -30px;
63 | top: initial;
64 | }
65 | .cupertino-pane-wrapper.inverse .move {
66 | margin-top: 15px;
67 | }
68 | .cupertino-pane-wrapper.inverse .draggable.over .move {
69 | margin-top: -5px;
70 | }
71 | `);
72 | });
73 |
74 | this.instance.on('beforeBreakHeightApplied', (ev) => {
75 | if (this.settings.breaks[ev.break]?.enabled) {
76 | this.breakpoints.breaks[ev.break] = 2 * (this.settings.breaks[ev.break].height + this.settings.bottomOffset);
77 | }
78 | }, false);
79 |
80 | this.instance.on('buildBreakpointsCompleted', () => {
81 | this.breakpoints.topper = this.breakpoints.bottomer;
82 |
83 | // Re-calc top after setBreakpoints();
84 | this.instance.paneEl.style.top = `-${this.breakpoints.bottomer - this.settings.bottomOffset}px`;
85 | });
86 | }
87 |
88 | public getPaneHeight(): number {
89 | return this.breakpoints.bottomer - this.settings.bottomOffset;
90 | }
91 |
92 | public updateScreenHeights():void {
93 | this.instance.screen_height = window.innerHeight;
94 | this.instance.screenHeightOffset = 0;
95 | }
96 |
97 | public setOverflowHeight() {
98 | this.instance.overflowEl.style.height = `${this.getPaneHeight()
99 | - 30
100 | - this.settings.topperOverflowOffset
101 | - this.instance.overflowEl.offsetTop}px`;
102 | }
103 |
104 | public checkOverflowAttr(val) {
105 | if (!this.settings.topperOverflow
106 | || !this.instance.overflowEl) {
107 | return;
108 | }
109 |
110 | this.instance.overflowEl.style.overflowY = (val >= this.breakpoints.bottomer) ? 'auto' : 'hidden';
111 | }
112 |
113 | private prepareBreaksSwipeNextPoint(): {brs: {}, settingsBreaks: {}} {
114 | let brs = {};
115 | let settingsBreaks = {};
116 |
117 | brs['top'] = this.breakpoints.breaks['bottom'];
118 | brs['middle'] = this.breakpoints.breaks['middle'];
119 | brs['bottom'] = this.breakpoints.breaks['top'];
120 | settingsBreaks['top'] = {...this.settings.breaks['bottom']};
121 | settingsBreaks['middle'] = {...this.settings.breaks['middle']};
122 | settingsBreaks['bottom'] = {...this.settings.breaks['top']};
123 |
124 | return { brs, settingsBreaks };
125 | }
126 |
127 | /**
128 | * Topper Than Top
129 | * Lower Than Bottom
130 | * Otherwise don't changes
131 | */
132 | private handleSuperposition(coords: {
133 | clientX: number, clientY: number, newVal: number,
134 | newValX: number, diffY: number, diffX: number
135 | }): {x?: number, y?: number} {
136 | // Inverse gestures
137 | // Allow drag topper than top point
138 | if (this.settings.upperThanTop
139 | && ((coords.newVal >= this.breakpoints.topper)
140 | || this.events.startPointOverTop)) {
141 | // check that finger reach same position before enable normal swipe mode
142 | if (!this.events.startPointOverTop) {
143 | this.events.startPointOverTop = coords.clientY;
144 | }
145 | if (this.events.startPointOverTop > coords.clientY) {
146 | delete this.events.startPointOverTop;
147 | }
148 | const screenDelta = this.instance.screen_height - this.instance.screenHeightOffset;
149 | const differKoef = (screenDelta - this.instance.getPanelTransformY()) / (screenDelta - this.breakpoints.topper) / 8;
150 | return { y: this.instance.getPanelTransformY() + (coords.diffY * differKoef) };
151 | }
152 |
153 | // Disallow drag topper than top point
154 | if (!this.settings.upperThanTop
155 | && (coords.newVal >= this.breakpoints.topper)) {
156 | return { y: this.breakpoints.topper };
157 | }
158 | }
159 |
160 | public scrollPreventDrag(t): boolean {
161 | let prevention: boolean = false;
162 | if (this.events.willScrolled()
163 | && this.isOverflowEl(t.target)) {
164 | prevention = true;
165 | }
166 | return prevention;
167 | }
168 |
169 | private isOverflowEl(el): boolean {
170 | if (!el) {
171 | return false;
172 | }
173 | let node = el.parentNode;
174 | while (node != null) {
175 | if (node == this.instance.overflowEl) {
176 | return true;
177 | }
178 | node = node.parentNode;
179 | }
180 | return false;
181 | }
182 |
183 | private async onScroll() {
184 | this.events.isScrolling = true;
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/src/modules/modal.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from '../cupertino-pane';
2 | import { CupertinoSettings, ModalSettings } from '../models';
3 | import { Events } from '../events/events';
4 | import { Breakpoints } from 'src/breakpoints';
5 | import { Transitions } from 'src/transitions';
6 |
7 | /**
8 | * Modal module
9 | */
10 |
11 | export class ModalModule {
12 |
13 | private static readonly BuildInTransition = {
14 | fade: {
15 | duration: 300,
16 | from: {
17 | opacity: 0
18 | },
19 | to: {
20 | opacity: 1
21 | },
22 | },
23 | zoom: {
24 | duration: 300,
25 | from: {
26 | opacity: 0,
27 | scale: 0.5
28 | },
29 | to: {
30 | opacity: 1,
31 | scale: 1
32 | },
33 | }
34 | }
35 |
36 | private static readonly ForceSettings = {
37 | fitHeight: true,
38 | touchAngle: null,
39 | showDraggable: false
40 | }
41 |
42 | // Force to use settings by module
43 | public static CollectSettings(settings) {
44 | return settings.modal
45 | ? { ...settings, ...ModalModule.ForceSettings}
46 | : settings;
47 | }
48 |
49 | public modalDefaults: ModalSettings = {
50 | transition: 'fade',
51 | flying: false,
52 | dismissOnIntense: false
53 | };
54 |
55 | private settings: CupertinoSettings;
56 | private events: Events;
57 | private breakpoints: Breakpoints;
58 | private transitions: Transitions;
59 | constructor(private instance: CupertinoPane) {
60 | this.settings = this.instance.settings;
61 | this.events = this.instance.events;
62 | this.breakpoints = this.instance.breakpoints;
63 | this.transitions = this.instance.transitions;
64 |
65 | if (!this.settings.modal) {
66 | return;
67 | }
68 |
69 | // set defaults
70 | this.settings.modal = (typeof this.settings.modal === "object")
71 | ? {...this.modalDefaults, ...this.settings.modal}
72 | : this.modalDefaults;
73 |
74 | // ehance + re-bind functions (+ get rid of recursion)
75 | this.instance['customPresent'] = this.instance['present'];
76 | this.instance['present'] = (conf) => this.present(conf);
77 | this.instance['customDestroy'] = this.instance['destroy'];
78 | this.instance['destroy'] = (conf) => this.destroy(conf);
79 |
80 | // re-bind functions
81 | this.events['handleSuperposition'] = (coords) => this.handleSuperposition(coords);
82 | this.transitions['setPaneElTransform'] = (params) => this.setPaneElTransform(params);
83 |
84 | // Hooks
85 | this.instance.on('beforeBreakHeightApplied', (ev) => {
86 | if (ev.break === 'top') {
87 | this.settings.breaks['top'].height -= this.settings.bottomOffset * 2;
88 | this.settings.breaks['top'].height += (this.instance.screen_height - this.settings.breaks['top'].height) / 2;
89 | }
90 | if (ev.break === 'bottom') {
91 | this.settings.breaks['bottom'] = { enabled: false };
92 | }
93 |
94 | this.instance.addStyle(`
95 | .cupertino-pane-wrapper .pane {
96 | transform-origin: center ${this.breakpoints.breaks[this.settings.initialBreak]}px
97 | }
98 | `);
99 | });
100 |
101 | this.instance.on('rendered', () => {
102 | this.instance.addStyle(`
103 | .cupertino-pane-wrapper .pane {
104 | border-radius: var(--cupertino-pane-border-radius, 20px)
105 | var(--cupertino-pane-border-radius, 20px)
106 | var(--cupertino-pane-border-radius, 20px)
107 | var(--cupertino-pane-border-radius, 20px);
108 | width: calc(100% - 16px) !important;
109 | margin: auto;
110 | }
111 | .cupertino-pane-wrapper .pane.modal-flying {
112 | animation: modalFlyingX 2000ms ease-in-out infinite alternate,
113 | modalFlyingY 3000ms ease-in-out infinite alternate;
114 | }
115 | @keyframes modalFlyingX {
116 | 0% { left: -10px; }
117 | 100% { left: 10px; }
118 | }
119 | @keyframes modalFlyingY {
120 | 0% { top: -10px; }
121 | 100% { top: 0px; }
122 | }
123 | `);
124 |
125 | if (this.settings.modal['flying']) {
126 | this.instance.paneEl.classList.add('modal-flying');
127 | }
128 |
129 | if (this.settings.modal['dismissOnIntense']) {
130 | this.instance.enableDrag();
131 | }
132 | });
133 | }
134 |
135 | public setPaneElTransform(params) {
136 | let closest = params.type === 'end' ? 0 : params.translateX;
137 | this.instance.paneEl.style.transform = `translateX(${closest || 0}px) translateY(${params.translateY}px) translateZ(0px)`;
138 | }
139 |
140 | /**
141 | * Private class methods
142 | */
143 |
144 | // Enhance with animation
145 | private present(conf) {
146 | let { transition } = conf;
147 | if (!transition) {
148 | transition = ModalModule.BuildInTransition[this.settings.modal['transition']];
149 | }
150 |
151 | return this.instance['customPresent']({...conf, transition });
152 | }
153 |
154 | // Enhance with animation
155 | private destroy(conf) {
156 | let { transition } = conf;
157 |
158 | if (!transition) {
159 | transition = JSON.parse(JSON.stringify({
160 | duration: ModalModule.BuildInTransition[this.settings.modal['transition']].duration,
161 | from: ModalModule.BuildInTransition[this.settings.modal['transition']].to,
162 | to: ModalModule.BuildInTransition[this.settings.modal['transition']].from
163 | }));
164 | }
165 |
166 | if (conf.fromCurrentPosition) {
167 | let computedTranslate = new WebKitCSSMatrix(window.getComputedStyle(this.instance.paneEl).transform);
168 | transition.to.transform = `translateY(${computedTranslate.m42}px) translateX(${computedTranslate.m41}px) translateZ(0px)`;
169 | }
170 |
171 | return this.instance['customDestroy']({...conf, transition });
172 | }
173 |
174 | private handleSuperposition(coords: {
175 | clientX: number, clientY: number, newVal: number,
176 | newValX: number, diffY: number, diffX: number
177 | }): {x?: number, y?: number} | false {
178 | // dismissOnIntense
179 | let intenseKoeff = 40;
180 | let differY = Math.abs(this.instance.getPanelTransformY() - this.breakpoints.topper);
181 | let differX = Math.abs(this.instance.getPanelTransformX());
182 | if (this.settings.modal['dismissOnIntense']
183 | && (differY > intenseKoeff || differX > intenseKoeff - 10)) {
184 | this.instance.disableDrag();
185 | this.destroy({animate: true, fromCurrentPosition: true});
186 | return false;
187 | }
188 |
189 | // calc new-pos
190 | let hardness = 8;
191 | const differKoefY = this.instance.getPanelTransformY() / this.breakpoints.topper / hardness;
192 | const differKoefX = this.instance.getPanelTransformX() / this.breakpoints.topper / hardness;
193 | return {
194 | y: this.instance.getPanelTransformY() + (coords.diffY * (differKoefY + differKoefX)),
195 | x: this.instance.getPanelTransformX() + (coords.diffX * (differKoefY + differKoefX))
196 | };
197 | }
198 |
199 | }
200 |
--------------------------------------------------------------------------------
/src/modules/z-stack.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from '../cupertino-pane';
2 | import { CupertinoSettings, ZStackSettings } from '../models';
3 | import { Breakpoints } from '../breakpoints';
4 |
5 | /**
6 | * Z-Stack Module
7 | */
8 |
9 | export class ZStackModule {
10 |
11 | public zStackDefaults: ZStackSettings = {
12 | pushElements: null,
13 | minPushHeight: null,
14 | cardBorderRadius: null,
15 | cardYOffset: 0,
16 | cardZScale: 0.93,
17 | cardContrast: 0.85,
18 | stackZAngle: 160,
19 | };
20 |
21 | private settings: CupertinoSettings;
22 | private breakpoints: Breakpoints;
23 | constructor(private instance: CupertinoPane) {
24 | this.breakpoints = this.instance.breakpoints;
25 | this.settings = this.instance.settings;
26 |
27 | if (!this.settings.zStack) {
28 | return;
29 | }
30 |
31 | // bind to primary instance
32 | // TODO: change binding strategy according to TypeScript
33 | // E.G. Using public module methods from modules
34 | this.instance['setZstackConfig'] = async(zStack: ZStackSettings) => this.setZstackConfig(zStack);
35 |
36 | // Assign multiplicators for push elements
37 | this.instance.on('rendered', () => {
38 | this.setZstackConfig(this.settings.zStack);
39 | this.setPushMultiplicators();
40 | });
41 |
42 | // Set initial position without animation on present
43 | this.instance.on('beforePresentTransition', (ev) => {
44 | if (!ev.animate) {
45 | this.settings.zStack.pushElements.forEach(item =>
46 | this.pushTransition(
47 | document.querySelector(item),
48 | this.breakpoints.breaks[this.settings.initialBreak], 'unset'
49 | )
50 | );
51 | }
52 | });
53 |
54 | // Move/Drag push transition for each element
55 | this.instance.on('onMoveTransitionStart', () => {
56 | this.settings.zStack.pushElements.forEach(item =>
57 | this.pushTransition(
58 | document.querySelector(item),
59 | this.instance.getPanelTransformY(), 'all 0ms linear 0ms'
60 | )
61 | );
62 | });
63 |
64 | // Main transition for pushed elements
65 | this.instance.on('onTransitionStart', (ev) => {
66 | this.settings.zStack.pushElements.forEach(item =>
67 | this.pushTransition(
68 | document.querySelector(item),
69 | ev.translateY.new,
70 | `all ${this.settings.animationDuration}ms ${this.settings.animationType} 0s`
71 | )
72 | );
73 | });
74 |
75 | }
76 |
77 | /**
78 | * Change z-stack configuration on the way
79 | */
80 | public setZstackConfig(zStack: ZStackSettings): void {
81 | // Allow user to reset config
82 | this.settings.zStack = zStack ? {...this.zStackDefaults, ...zStack} : null;
83 | }
84 |
85 | /**
86 | * Z-Stack push transitions
87 | * @param pushElement - element be pushed
88 | * @param newPaneY - translateY of new pane
89 | * @param transition - transition style
90 | * @returns
91 | */
92 | public pushTransition(pushElement: HTMLElement, newPaneY: number, transition: string) {
93 | let zStack = this.settings.zStack.pushElements;
94 | pushElement.style.transition = transition;
95 | pushElement.style.overflow = this.settings.zStack.cardBorderRadius && 'hidden';
96 | newPaneY = this.instance.screenHeightOffset - newPaneY;
97 | const topHeight = this.settings.zStack.minPushHeight
98 | ? this.settings.zStack.minPushHeight : this.instance.screenHeightOffset - this.breakpoints.bottomer;
99 | const minHeight = this.instance.screenHeightOffset - this.breakpoints.topper;
100 |
101 | // Math calculations
102 | let multiplicator = this.getPushMulitplicator(pushElement);
103 | let scaleNew = Math.pow(this.settings.zStack.cardZScale, multiplicator);
104 | let scaleNormal = Math.pow(this.settings.zStack.cardZScale, multiplicator - 1);
105 | let pushY = 6 + this.settings.zStack.cardYOffset; // 6 is iOS style offset for z-stacks
106 | let yNew = -1 * (pushY * multiplicator);
107 | let yNormal = (yNew + pushY);
108 | let contrastNew = Math.pow(this.settings.zStack.cardContrast, multiplicator);
109 | let contrastNormal = Math.pow(this.settings.zStack.cardContrast, multiplicator - 1);
110 |
111 | // Accumulated styles from each pusher to pushed
112 | const setStyles = (scale, y, contrast, border) => {
113 | let exponentAngle = Math.pow(scale, this.settings.zStack.stackZAngle / 100);
114 | pushElement.style.transform = `translateY(${y * (exponentAngle/scale)}px) scale(${scale})`;
115 | pushElement.style.borderRadius = `${border}px`;
116 | pushElement.style.filter = `contrast(${contrast})`;
117 |
118 | // When destroy transition and last item moved we reduce multiplicators
119 | let lastPushed = document.querySelector(zStack[zStack.length - 1]);
120 | if (!newPaneY && pushElement.className === lastPushed.className) {
121 | this.clearPushMultiplicators();
122 | }
123 | };
124 |
125 | // Pusher cleared or pane destroyed
126 | if (newPaneY <= topHeight) {
127 | // defaults
128 | setStyles(
129 | scaleNormal, // scale
130 | yNormal, // transformY
131 | contrastNormal, // contrast
132 | 0 // border
133 | );
134 | return;
135 | }
136 |
137 | // Pusher drag/move
138 | const getXbyY = (min, max) => {
139 | let val = (minHeight * max - topHeight * min) * -1;
140 | val -= (min - max) * newPaneY;
141 | val /= (topHeight - minHeight);
142 | if (val > max) val = max;
143 | if (val < min) val = min;
144 | return val;
145 | };
146 |
147 | setStyles(
148 | getXbyY(scaleNew, scaleNormal),
149 | getXbyY(yNew, yNormal),
150 | getXbyY(contrastNew, contrastNormal),
151 | getXbyY(this.settings.zStack.cardBorderRadius * -1, 0) * -1,
152 | );
153 | }
154 |
155 | // Z-Stack: Pushed elements multiplicators
156 | public setPushMultiplicators(): void {
157 | this.settings.zStack.pushElements.forEach((item) => {
158 | let pushElement: HTMLElement = document.querySelector(item);
159 | let multiplicator = this.getPushMulitplicator(pushElement);
160 | multiplicator = multiplicator ? multiplicator + 1 : 1;
161 | pushElement.style.setProperty('--push-multiplicator', `${multiplicator}`);
162 | });
163 | }
164 |
165 | /**
166 | * Private class methods
167 | */
168 |
169 | private getPushMulitplicator(el: HTMLElement): number {
170 | let multiplicator: (string | number) = el.style.getPropertyValue('--push-multiplicator');
171 | return parseInt(multiplicator);
172 | }
173 |
174 | private clearPushMultiplicators(): void {
175 | for (let i = 0; i < this.settings.zStack.pushElements.length; i++) {
176 | let pushElement: HTMLElement = document.querySelector(
177 | this.settings.zStack.pushElements[i]
178 | );
179 | let multiplicator = this.getPushMulitplicator(pushElement);
180 | multiplicator -= 1;
181 | if (multiplicator) {
182 | pushElement.style.setProperty('--push-multiplicator', `${multiplicator}`);
183 | } else {
184 | pushElement.style.removeProperty('--push-multiplicator');
185 | }
186 | }
187 | }
188 |
189 |
190 | }
191 |
--------------------------------------------------------------------------------
/src/public-api.ts:
--------------------------------------------------------------------------------
1 | export { CupertinoPane } from "./cupertino-pane";
2 | export { CupertinoSettings } from "./models";
--------------------------------------------------------------------------------
/src/settings.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoSettings } from './models';
2 |
3 | export class Settings {
4 |
5 | public instance: CupertinoSettings;
6 | constructor() {
7 | this.instance = {
8 | initialBreak: 'middle',
9 | horizontal: false,
10 | horizontalOffset: null,
11 | inverse: false,
12 | parentElement: null,
13 | followerElement: null,
14 | cssClass: null,
15 | fitHeight: false,
16 | maxFitHeight: null,
17 | fitScreenHeight: true,
18 | ionContentScroll: false,
19 | backdrop: false,
20 | backdropBlur: false,
21 | backdropOpacity: 0.6,
22 | animationType: 'ease',
23 | animationDuration: 300,
24 | dragBy: null,
25 | bottomOffset: 0,
26 | bottomClose: false,
27 | fastSwipeClose: false,
28 | fastSwipeSensivity: 3,
29 | freeMode: false,
30 | buttonDestroy: true,
31 | topperOverflow: true,
32 | topperOverflowOffset: 0,
33 | lowerThanBottom: true,
34 | upperThanTop: false,
35 | showDraggable: true,
36 | draggableOver: false,
37 | clickBottomOpen: true,
38 | preventClicks: true,
39 | handleKeyboard: true,
40 | simulateTouch: true,
41 | passiveListeners: true,
42 | touchMoveStopPropagation: false,
43 | touchAngle: 45,
44 | breaks: {},
45 | modal: null,
46 | zStack: null,
47 | events: null,
48 | modules: null
49 | };
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/support.ts:
--------------------------------------------------------------------------------
1 | export class Support {
2 | public static get touch() {
3 | return (window['Modernizr'] && window['Modernizr'].touch === true) || (function checkTouch() {
4 | return !!((window.navigator.maxTouchPoints > 0) || ('ontouchstart' in window) || (window['DocumentTouch'] && document instanceof window['DocumentTouch']));
5 | }());
6 | }
7 |
8 | public static get observer() {
9 | return ('MutationObserver' in window || 'WebkitMutationObserver' in window);
10 | }
11 |
12 | public static get backdropFilter() {
13 | return CSS.supports("backdrop-filter", "blur(0px)")
14 | || CSS.supports("-webkit-backdrop-filter", "blur(0px)");
15 | }
16 |
17 | public static get passiveListener() {
18 | let supportsPassive = false;
19 | try {
20 | const opts = Object.defineProperty({}, 'passive', {
21 | // eslint-disable-next-line
22 | get() {
23 | supportsPassive = true;
24 | },
25 | });
26 | window.addEventListener('testPassiveListener', null, opts);
27 | } catch (e) {
28 | // No support
29 | }
30 | return supportsPassive;
31 | }
32 |
33 | public static get gestures() {
34 | return 'ongesturestart' in window;
35 | }
36 | }
--------------------------------------------------------------------------------
/src/transitions.ts:
--------------------------------------------------------------------------------
1 | import { CupertinoPane } from './cupertino-pane';
2 | import { CupertinoSettings } from './models';
3 | import { Breakpoints } from './breakpoints';
4 |
5 | /**
6 | * Transitions class
7 | * Z-Push transitions class
8 | */
9 |
10 | // TODO: review MoveEnd can be replaced with breakpoint
11 | export enum CupertinoTransition {
12 | Present = 'present',
13 | Destroy = 'destroy',
14 | Move = 'move',
15 | Breakpoint = 'breakpoint',
16 | Hide = 'hide',
17 | TouchEnd = 'end'
18 | }
19 |
20 | export class Transitions {
21 |
22 | public isPaneHidden: boolean = false;
23 |
24 | private settings: CupertinoSettings;
25 | private breakpoints: Breakpoints;
26 | constructor(private instance: CupertinoPane) {
27 | this.settings = this.instance.settings;
28 | this.breakpoints = this.instance.breakpoints;
29 | }
30 |
31 | /***********************************
32 | * Transitions handler
33 | */
34 | public doTransition(params:any = {}): Promise {
35 | return new Promise(async (resolve) => {
36 | // touchmove simple event
37 | if (params.type === CupertinoTransition.Move) {
38 | // System event
39 | this.instance.emit('onMoveTransitionStart', {translateY: params.translateY});
40 | this.instance.paneEl.style.transition = 'all 0ms linear 0ms';
41 | this.setPaneElTransform(params);
42 | return resolve(true);
43 | }
44 |
45 | // Transition end
46 | const transitionEnd = () => {
47 | if (params.type === CupertinoTransition.Destroy) {
48 | this.instance.destroyResets();
49 | }
50 | this.instance.paneEl.style.transition = `initial`;
51 |
52 | // isHidden
53 | if (params.type === CupertinoTransition.Hide) {
54 | this.isPaneHidden = true;
55 | }
56 | if (params.type === CupertinoTransition.Breakpoint
57 | || params.type === CupertinoTransition.Present
58 | || params.type === CupertinoTransition.TouchEnd) {
59 | this.isPaneHidden = false;
60 | }
61 |
62 | // toggle ion-content scroll-y
63 | if ((params.type === CupertinoTransition.Hide
64 | || params.type === CupertinoTransition.Destroy)
65 | && this.instance.ionContent
66 | && !this.settings.ionContentScroll
67 | && !this.doesPanesExists()) {
68 | this.instance.ionContent.setAttribute('scroll-y', 'true');
69 | }
70 |
71 | // Emit event
72 | this.instance.emit('onTransitionEnd', {
73 | type: params.type,
74 | target: document.body.contains(this.instance.paneEl) ? this.instance.paneEl : null
75 | });
76 |
77 | // Remove listener
78 | this.instance.paneEl.removeEventListener('transitionend', transitionEnd);
79 | return resolve(true);
80 | };
81 |
82 | // MoveToBreak, Touchend, Present, Hide, Destroy events
83 | if (params.type === CupertinoTransition.Breakpoint
84 | || params.type === CupertinoTransition.TouchEnd
85 | || params.type === CupertinoTransition.Present
86 | || params.type === CupertinoTransition.Hide
87 | || params.type === CupertinoTransition.Destroy) {
88 |
89 | // Allow custom transitions for present/destroy
90 | // + Fix arguments mutations from re-call present
91 | let subTransition = params.conf?.transition
92 | ? JSON.parse(JSON.stringify(params.conf.transition)) : {};
93 |
94 | // freemode
95 | if (params.type === CupertinoTransition.TouchEnd && this.settings.freeMode) return resolve(true);
96 |
97 | // Get timing function && push for next
98 | const nextBreak = Object.entries(this.breakpoints.breaks).find(
99 | val => val[1] === params.translateY
100 | );
101 | let bounce = nextBreak && this.settings.breaks[nextBreak[0]]?.bounce;
102 |
103 | // transition style
104 | let buildedTransition = this.buildTransitionValue(bounce, subTransition.duration);
105 | this.instance.paneEl.style.setProperty('transition', buildedTransition);
106 |
107 | // Main transitions
108 | // Emit event
109 | this.instance.emit('onTransitionStart', {
110 | type: params.type,
111 | translateY: {new: params.translateY},
112 | transition: this.instance.paneEl.style.transition
113 | });
114 |
115 | // Move pane
116 | this.setPaneElTransform(params);
117 |
118 | /**
119 | * Custom transitions for present/destroy functions
120 | */
121 | if (subTransition.to) {
122 | if (!subTransition.to.transform) {
123 | subTransition.to.transform = `translateY(${this.breakpoints.breaks[this.settings.initialBreak]}px) translateZ(0px)`;
124 | }
125 | Object.assign(this.instance.paneEl.style, subTransition.to);
126 | }
127 |
128 | // set prev breakpoint for service needs
129 | let getNextBreakpoint = Object.entries(this.breakpoints.breaks).find(val => val[1] === params.translateY);
130 | if (getNextBreakpoint) {
131 | this.breakpoints.prevBreakpoint = getNextBreakpoint[0];
132 | }
133 |
134 | this.instance.paneEl.addEventListener('transitionend', transitionEnd);
135 | }
136 | });
137 | }
138 |
139 | private setPaneElTransform(params) {
140 | this.instance.paneEl.style.transform = `translateY(${params.translateY}px) translateZ(0px)`;
141 | }
142 |
143 | public buildTransitionValue(bounce: boolean, duration?: number): string {
144 | if (bounce) {
145 | return `all 300ms cubic-bezier(.155,1.105,.295,1.12)`;
146 | }
147 |
148 | return `all ${duration || this.settings.animationDuration}ms ${this.settings.animationType}`;
149 | }
150 |
151 |
152 | /**
153 | * Private class methods
154 | */
155 |
156 | private doesPanesExists() {
157 | return !!document.querySelector('.cupertino-pane-wrapper');
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "outDir": "./dist/",
6 | "sourceMap": false,
7 | "declaration": true,
8 | "declarationDir": "./dist/types",
9 | "module": "es2020",
10 | "target": "es2015",
11 | "moduleResolution": "node",
12 | "emitDecoratorMetadata": true,
13 | "experimentalDecorators": true,
14 | "typeRoots": [
15 | "node_modules/@types"
16 | ],
17 | "lib": [
18 | "es2018",
19 | "dom"
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "arrow-return-shorthand": true,
4 | "callable-types": true,
5 | "class-name": true,
6 | "comment-format": [
7 | true,
8 | "check-space"
9 | ],
10 | "curly": true,
11 | "deprecation": {
12 | "severity": "warn"
13 | },
14 | "eofline": true,
15 | "forin": true,
16 | "import-spacing": true,
17 | "indent": [
18 | true,
19 | "spaces"
20 | ],
21 | "interface-over-type-literal": true,
22 | "label-position": true,
23 | "max-line-length": [
24 | true,
25 | 140
26 | ],
27 | "member-access": false,
28 | "member-ordering": [
29 | true,
30 | {
31 | "order": [
32 | "static-field",
33 | "instance-field",
34 | "static-method",
35 | "instance-method"
36 | ]
37 | }
38 | ],
39 | "no-arg": true,
40 | "no-bitwise": true,
41 | "no-console": [
42 | true,
43 | "debug",
44 | "info",
45 | "time",
46 | "timeEnd",
47 | "trace"
48 | ],
49 | "no-construct": true,
50 | "no-debugger": true,
51 | "no-duplicate-super": true,
52 | "no-empty": false,
53 | "no-empty-interface": true,
54 | "no-eval": true,
55 | "no-inferrable-types": [
56 | true,
57 | "ignore-params"
58 | ],
59 | "no-misused-new": true,
60 | "no-non-null-assertion": true,
61 | "no-shadowed-variable": true,
62 | "no-string-literal": false,
63 | "no-string-throw": true,
64 | "no-switch-case-fall-through": true,
65 | "no-trailing-whitespace": true,
66 | "no-unnecessary-initializer": true,
67 | "no-unused-expression": true,
68 | "no-use-before-declare": true,
69 | "no-var-keyword": true,
70 | "object-literal-sort-keys": false,
71 | "one-line": [
72 | true,
73 | "check-open-brace",
74 | "check-catch",
75 | "check-else",
76 | "check-whitespace"
77 | ],
78 | "prefer-const": true,
79 | "quotemark": [
80 | true,
81 | "single"
82 | ],
83 | "radix": true,
84 | "semicolon": [
85 | true,
86 | "always"
87 | ],
88 | "triple-equals": [
89 | true,
90 | "allow-null-check"
91 | ],
92 | "typedef-whitespace": [
93 | true,
94 | {
95 | "call-signature": "nospace",
96 | "index-signature": "nospace",
97 | "parameter": "nospace",
98 | "property-declaration": "nospace",
99 | "variable-declaration": "nospace"
100 | }
101 | ],
102 | "unified-signatures": true,
103 | "variable-name": false,
104 | "whitespace": [
105 | true,
106 | "check-branch",
107 | "check-decl",
108 | "check-operator",
109 | "check-separator",
110 | "check-type"
111 | ],
112 | "directive-selector": [
113 | true,
114 | "attribute",
115 | "app",
116 | "camelCase"
117 | ],
118 | "component-selector": [
119 | true,
120 | "element",
121 | "app",
122 | "page",
123 | "kebab-case"
124 | ],
125 | "no-output-on-prefix": true,
126 | "use-input-property-decorator": true,
127 | "use-output-property-decorator": true,
128 | "use-host-property-decorator": true,
129 | "no-input-rename": true,
130 | "no-output-rename": true,
131 | "use-life-cycle-interface": true,
132 | "use-pipe-transform-interface": true,
133 | "directive-class-suffix": true
134 | }
135 | }
136 |
--------------------------------------------------------------------------------