prettier-modals 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +179 -0
- package/dist/PrettyModal.cjs +62 -0
- package/dist/PrettyModal.cjs.map +1 -0
- package/dist/PrettyModal.d.cts +67 -0
- package/dist/PrettyModal.d.ts +67 -0
- package/dist/PrettyModal.js +62 -0
- package/dist/PrettyModal.js.map +1 -0
- package/package.json +53 -0
- package/src/PrettyModal.js +278 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 srdavo
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Prettier Modals
|
|
2
|
+
|
|
3
|
+
A tiny JavaScript class that brings beautiful open/close animations to native `<dialog>` elements using [GSAP](https://gsap.com/) and its [Flip plugin](https://gsap.com/docs/v3/Plugins/Flip/).
|
|
4
|
+
|
|
5
|
+
The modal morphs **from** the trigger button and collapses **back into it** when closed — with elastic easing, blur, and fade effects. No frameworks, no dependencies beyond GSAP.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Uses the native HTML `<dialog>` element (accessible by default)
|
|
10
|
+
- Smooth elastic open animation with blur fade-in
|
|
11
|
+
- Closing animation with border-radius morph, blur, and fade-out
|
|
12
|
+
- Automatic style injection (no extra CSS file needed)
|
|
13
|
+
- Respects `prefers-reduced-motion`
|
|
14
|
+
- SSR-safe, ships ESM + CJS + TypeScript types
|
|
15
|
+
- Lightweight — GSAP is the only (peer) dependency
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install prettier-modals gsap
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
GSAP is a **peer dependency** (`>=3.12`) — install it alongside Prettier Modals. The package ships ESM, CJS, and TypeScript types.
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### 1. Register the GSAP plugins
|
|
28
|
+
|
|
29
|
+
Prettier Modals imports GSAP, `Flip`, and `CustomEase` internally, but GSAP requires the plugins to be registered once in your app:
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
import gsap from 'gsap'
|
|
33
|
+
import { Flip } from 'gsap/Flip'
|
|
34
|
+
import { CustomEase } from 'gsap/CustomEase'
|
|
35
|
+
|
|
36
|
+
gsap.registerPlugin(Flip, CustomEase)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
> With a bundler (Vite, webpack, Angular CLI…) this resolves GSAP to a single instance automatically. See [Running without a bundler](#running-without-a-bundler) for the CDN setup.
|
|
40
|
+
|
|
41
|
+
### 2. Create the instance
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
import { PrettyModal } from 'prettier-modals'
|
|
45
|
+
|
|
46
|
+
const prettyModal = new PrettyModal()
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 3. Add a `<dialog>` and a trigger
|
|
50
|
+
|
|
51
|
+
The trigger element is **explicit** — pass the element the modal should morph from. From an inline handler, use `this`:
|
|
52
|
+
|
|
53
|
+
```html
|
|
54
|
+
<button onclick="prettyModal.open('my-modal', { trigger: this })">Open</button>
|
|
55
|
+
|
|
56
|
+
<dialog id="my-modal">
|
|
57
|
+
<h1>Hello world!</h1>
|
|
58
|
+
<button onclick="prettyModal.close('my-modal')">Close</button>
|
|
59
|
+
</dialog>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
You can pass either an **id string** or an **`HTMLElement`** for both the dialog and the trigger — handy from frameworks where you hold element references:
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
prettyModal.open(dialogEl, { trigger: buttonEl, anchor: 'origin' })
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## API
|
|
69
|
+
|
|
70
|
+
### `new PrettyModal(options?)`
|
|
71
|
+
|
|
72
|
+
| Option | Type | Default | Description |
|
|
73
|
+
|---|---|---|---|
|
|
74
|
+
| `anchor` | `'center' \| 'origin'` | `'center'` | Where the modal opens from. `origin` positions it near the trigger. |
|
|
75
|
+
| `duration` | `number` | `0.4` | Flip animation duration (seconds). |
|
|
76
|
+
| `ease` | `string` | elastic | `CustomEase` SVG path for the Flip tween. |
|
|
77
|
+
| `respectReducedMotion` | `boolean` | `true` | Skip animation when the user prefers reduced motion. |
|
|
78
|
+
| `onOpen` | `(dialog) => void` | — | Called after the open animation completes. |
|
|
79
|
+
| `onClose` | `(dialog) => void` | — | Called after the close animation completes. |
|
|
80
|
+
|
|
81
|
+
These act as defaults; any of them (plus `trigger`) can be overridden per call via the second argument of `open`/`close`.
|
|
82
|
+
|
|
83
|
+
### Methods
|
|
84
|
+
|
|
85
|
+
| Method | Description |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `open(dialogRef, options?)` | Opens the `<dialog>` (id or element), morphing from `options.trigger`. |
|
|
88
|
+
| `close(dialogRef, options?)` | Closes the `<dialog>`, morphing back into its trigger. |
|
|
89
|
+
| `destroy()` | Removes the injected `<style>` tag. Call when tearing down. |
|
|
90
|
+
|
|
91
|
+
## How it works
|
|
92
|
+
|
|
93
|
+
1. **Open** — Pairs the trigger and dialog with a shared `data-flip-id`, captures the trigger's position with `Flip.getState()`, calls `dialog.showModal()`, then uses `Flip.from()` to morph the dialog out of the trigger with an elastic ease.
|
|
94
|
+
2. **Close** — Uses `Flip.to()` to morph the dialog back into its trigger with blur and fade, then calls `dialog.close()`.
|
|
95
|
+
|
|
96
|
+
CSS keyframe animations handle the blur/fade/border-radius effects during transitions. Styles are auto-injected on instantiation (once per page) so you don't need to import any CSS. The library is SSR-safe — it touches `document`/`window` only in the browser.
|
|
97
|
+
|
|
98
|
+
## Demo
|
|
99
|
+
|
|
100
|
+
The demo lives in `demo/` and loads the source directly via an [import map](demo/index.html) (no build step). Because ES modules and import maps require HTTP (not `file://`), serve it with any static server:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
git clone https://github.com/antuuanyf/prettier-modals.git
|
|
104
|
+
cd prettier-modals
|
|
105
|
+
|
|
106
|
+
# any static server works, e.g.:
|
|
107
|
+
npx serve .
|
|
108
|
+
# or
|
|
109
|
+
python3 -m http.server 8000
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Then open `http://localhost:8000/demo/` (adjust the port) and click the buttons. The demo shows both `anchor: 'origin'` and `anchor: 'center'`.
|
|
113
|
+
|
|
114
|
+
## Running without a bundler
|
|
115
|
+
|
|
116
|
+
In a plain `<script type="module">` setup, point an import map at GSAP's combined ESM bundle so `gsap`, `Flip`, and `CustomEase` share **one instance** (separate per-plugin CDN bundles each ship their own GSAP copy and break Flip):
|
|
117
|
+
|
|
118
|
+
```html
|
|
119
|
+
<script type="importmap">
|
|
120
|
+
{
|
|
121
|
+
"imports": {
|
|
122
|
+
"gsap": "https://cdn.jsdelivr.net/npm/gsap@3.14.1/all.js/+esm",
|
|
123
|
+
"gsap/Flip": "https://cdn.jsdelivr.net/npm/gsap@3.14.1/all.js/+esm",
|
|
124
|
+
"gsap/CustomEase": "https://cdn.jsdelivr.net/npm/gsap@3.14.1/all.js/+esm"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
</script>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Customization
|
|
131
|
+
|
|
132
|
+
Style your `<dialog>` however you want with regular CSS. Prettier Modals only handles the animation — layout, colors, and sizing are up to you.
|
|
133
|
+
|
|
134
|
+
```css
|
|
135
|
+
dialog {
|
|
136
|
+
border: none;
|
|
137
|
+
border-radius: 24px;
|
|
138
|
+
width: 100%;
|
|
139
|
+
height: 100%;
|
|
140
|
+
max-width: 400px;
|
|
141
|
+
max-height: 400px;
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Browser Support
|
|
146
|
+
|
|
147
|
+
Works in all modern browsers that support `<dialog>` and ES modules (Chrome, Firefox, Safari, Edge).
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
MIT License
|
|
152
|
+
|
|
153
|
+
Copyright (c) 2026 srdavo
|
|
154
|
+
|
|
155
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
156
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
157
|
+
in the Software without restriction, including without limitation the rights
|
|
158
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
159
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
160
|
+
furnished to do so, subject to the following conditions:
|
|
161
|
+
|
|
162
|
+
The above copyright notice and this permission notice shall be included in all
|
|
163
|
+
copies or substantial portions of the Software.
|
|
164
|
+
|
|
165
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
166
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
167
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
168
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
169
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
170
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
171
|
+
SOFTWARE.
|
|
172
|
+
|
|
173
|
+
## Contributing
|
|
174
|
+
|
|
175
|
+
Contributions are welcome! Feel free to open an issue or submit a pull request.
|
|
176
|
+
|
|
177
|
+
## Credits
|
|
178
|
+
|
|
179
|
+
Maintained by [Antonio Monreal Diaz](https://github.com/antuuanyf), based on the original work by [srdavo](https://github.com/srdavo). Powered by [GSAP](https://gsap.com/).
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';var gsap=require('gsap'),Flip=require('gsap/Flip'),CustomEase=require('gsap/CustomEase');var y=s=>{throw TypeError(s)};var S=(s,t,o)=>t.has(s)||y("Cannot "+o);var b=(s,t,o)=>t.has(s)?y("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(s):t.set(s,o);var l=(s,t,o)=>(S(s,t,"access private method"),o);gsap.gsap.registerPlugin(Flip.Flip,CustomEase.CustomEase);var m="pretty-modal-styles",I="M0,0 C0.305,0.206 0.116,0.567 0.3,0.8 0.394,0.921 0.491,1 1,1",i,g,x,f,w,h=class{constructor(t={}){b(this,i);this.defaults={anchor:"center",duration:.4,ease:I,respectReducedMotion:true,onOpen:null,onClose:null,...t},this.state=new WeakMap,typeof document<"u"&&(this.ease=CustomEase.CustomEase.create("pretty-modal-ease",this.defaults.ease),this.injectStyles());}open(t,o={}){let e=l(this,i,g).call(this,t);if(!e||e.open)return;let n={...this.defaults,...o},r=l(this,i,x).call(this,o.trigger);if(!r){console.warn("[PrettyModal] No trigger element found. Pass { trigger } explicitly.");return}if(this.state.get(e)?.animating)return;if(this.state.set(e,{trigger:r,anchor:n.anchor,animating:true}),l(this,i,f).call(this,n)){e.showModal(),this.state.set(e,{trigger:r,anchor:n.anchor,animating:false}),n.onOpen?.(e);return}let a=this.state.get(e).flipId||Math.random().toString(16).slice(2);r.dataset.flipId=a,e.dataset.flipId=a,e.dataset.anchor=n.anchor,this.state.get(e).flipId=a;let d=Flip.Flip.getState(r);e.showModal(),n.anchor==="origin"&&l(this,i,w).call(this,e,r),Flip.Flip.from(d,{targets:e,scale:true,ease:this.ease,toggleClass:"pretty-modal-opening",duration:n.duration,onComplete:()=>{let u=this.state.get(e);u&&(u.animating=false),n.onOpen?.(e);}});}close(t,o={}){let e=l(this,i,g).call(this,t);if(!e||!e.open)return;let n={...this.defaults,...o},r=this.state.get(e),c=r?.trigger;if(l(this,i,f).call(this,n)||!c){e.close(),e.setAttribute("style",""),r&&(r.animating=false),n.onClose?.(e);return}r&&(r.animating=true);let a=Flip.Flip.getState(c);Flip.Flip.to(a,{targets:e,scale:true,ease:this.ease,toggleClass:"pretty-modal-closing",duration:n.duration,onComplete:()=>{e.setAttribute("style",""),e.close(),r&&(r.animating=false),n.onClose?.(e);}});}destroy(){typeof document>"u"||document.getElementById(m)?.remove();}injectStyles(){if(document.getElementById(m))return;let t=`
|
|
2
|
+
.pretty-modal-opening {
|
|
3
|
+
animation: pretty-modal-opening 500ms cubic-bezier(.56,.27,0,1);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
@keyframes pretty-modal-opening {
|
|
7
|
+
from { opacity: 0; filter: blur(8px); } to { opacity: 1; filter: blur(0px); }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.pretty-modal-closing {
|
|
11
|
+
animation:
|
|
12
|
+
pretty-modal-closing-border-radius 500ms cubic-bezier(.56,.27,0,1),
|
|
13
|
+
pretty-modal-closing-blur 500ms cubic-bezier(.37,.35,0,1),
|
|
14
|
+
pretty-modal-closing-fade 700ms cubic-bezier(.56,.27,0,1)
|
|
15
|
+
;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@keyframes pretty-modal-closing-border-radius {
|
|
19
|
+
to { border-radius: 400px; }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@keyframes pretty-modal-closing-blur {
|
|
23
|
+
0% { filter: blur(0); } 100% { filter: blur(32px); }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@keyframes pretty-modal-closing-fade {
|
|
27
|
+
from { opacity: 1; } to { opacity: 0; }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
dialog[open]::backdrop {
|
|
31
|
+
background: rgba(0,0,0,0.2);
|
|
32
|
+
backdrop-filter: blur(2px);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
dialog.pretty-modal-opening::backdrop {
|
|
36
|
+
animation: pretty-modal-backdrop-in 400ms cubic-bezier(.56,.27,0,1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
dialog.pretty-modal-closing::backdrop {
|
|
40
|
+
animation: pretty-modal-backdrop-out 400ms cubic-bezier(.56,.27,0,1) forwards;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@keyframes pretty-modal-backdrop-in {
|
|
44
|
+
from { background: rgba(0,0,0,0); backdrop-filter: blur(0px); }
|
|
45
|
+
to { background: rgba(0,0,0,0.2); backdrop-filter: blur(2px); }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@keyframes pretty-modal-backdrop-out {
|
|
49
|
+
from { background: rgba(0,0,0,0.2); backdrop-filter: blur(2px); }
|
|
50
|
+
to { background: rgba(0,0,0,0); backdrop-filter: blur(0px); }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@media (prefers-reduced-motion: reduce) {
|
|
54
|
+
.pretty-modal-opening,
|
|
55
|
+
.pretty-modal-closing,
|
|
56
|
+
dialog.pretty-modal-opening::backdrop,
|
|
57
|
+
dialog.pretty-modal-closing::backdrop {
|
|
58
|
+
animation: none;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
`,o=document.createElement("style");o.id=m,o.textContent=t,document.head.appendChild(o);}};i=new WeakSet,g=function(t){return t?typeof t=="string"?document.getElementById(t):t:null},x=function(t){let o=l(this,i,g).call(this,t);return o||(typeof event<"u"&&event?.currentTarget?event.currentTarget:null)},f=function(t){return !t.respectReducedMotion||typeof window>"u"||!window.matchMedia?false:window.matchMedia("(prefers-reduced-motion: reduce)").matches},w=function(t,o){let e=o.getBoundingClientRect(),n=window.innerWidth,r=window.innerHeight;t.style.margin="0",t.style.position="fixed",t.style.inset="auto";let c=t.getBoundingClientRect(),a=c.width,d=c.height,u=n-e.left,C=e.right,M=r-e.top,v=e.bottom;a<=u?t.style.left=`${e.left}px`:a<=C?t.style.right=`${n-e.right}px`:t.style.left=`${Math.max(0,(n-a)/2)}px`,d<=M?t.style.top=`${e.top}px`:d<=v?t.style.bottom=`${r-e.bottom}px`:t.style.top=`${Math.max(0,(r-d)/2)}px`;};exports.PrettyModal=h;//# sourceMappingURL=PrettyModal.cjs.map
|
|
62
|
+
//# sourceMappingURL=PrettyModal.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/PrettyModal.js"],"names":["gsap","Flip","CustomEase","STYLE_ID","EASE","_PrettyModal_instances","resolveEl_fn","resolveTrigger_fn","reducedMotion_fn","positionAtOrigin_fn","PrettyModal","options","__privateAdd","dialogRef","dialog","__privateMethod","opts","trigger","flipId","originState","e","entry","styles","styleSheet","ref","resolved","origin","originRect","vw","vh","dialogRect","dialogW","dialogH","spaceRight","spaceLeft","spaceBelow","spaceAbove"],"mappings":"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","file":"PrettyModal.cjs","sourcesContent":["import { gsap } from 'gsap'\nimport { Flip } from 'gsap/Flip'\nimport { CustomEase } from 'gsap/CustomEase'\n\ngsap.registerPlugin(Flip, CustomEase)\n\nconst STYLE_ID = 'pretty-modal-styles'\nconst EASE = 'M0,0 C0.305,0.206 0.116,0.567 0.3,0.8 0.394,0.921 0.491,1 1,1'\n\nexport class PrettyModal {\n /**\n * @param {Object} [options]\n * @param {'center'|'origin'} [options.anchor='center'] Where the modal opens from.\n * @param {number} [options.duration=0.4] Animation duration in seconds.\n * @param {string} [options.ease] CustomEase SVG path used for the Flip tween.\n * @param {boolean} [options.respectReducedMotion=true] Skip animation when the user prefers reduced motion.\n * @param {(dialog: HTMLDialogElement) => void} [options.onOpen]\n * @param {(dialog: HTMLDialogElement) => void} [options.onClose]\n */\n constructor(options = {}) {\n this.defaults = {\n anchor: 'center',\n duration: 0.4,\n ease: EASE,\n respectReducedMotion: true,\n onOpen: null,\n onClose: null,\n ...options,\n }\n\n // Per-dialog state: trigger element + whether it is mid-animation.\n this.state = new WeakMap()\n\n if (typeof document !== 'undefined') {\n this.ease = CustomEase.create('pretty-modal-ease', this.defaults.ease)\n this.injectStyles()\n }\n }\n\n /**\n * Open a dialog, morphing from the trigger element.\n * @param {string|HTMLDialogElement} dialogRef Dialog element or its id.\n * @param {Object} [options]\n * @param {string|HTMLElement} [options.trigger] Element to animate from. Defaults to `event.currentTarget` when called from an inline handler.\n * @param {'center'|'origin'} [options.anchor]\n * @param {number} [options.duration]\n * @param {boolean} [options.respectReducedMotion]\n * @param {(dialog: HTMLDialogElement) => void} [options.onOpen]\n * @param {(dialog: HTMLDialogElement) => void} [options.onClose]\n */\n open(dialogRef, options = {}) {\n const dialog = this.#resolveEl(dialogRef)\n if (!dialog || dialog.open) return\n\n const opts = { ...this.defaults, ...options }\n const trigger = this.#resolveTrigger(options.trigger)\n if (!trigger) {\n console.warn('[PrettyModal] No trigger element found. Pass { trigger } explicitly.')\n return\n }\n\n const entry = this.state.get(dialog)\n if (entry?.animating) return\n\n this.state.set(dialog, { trigger, anchor: opts.anchor, animating: true })\n\n if (this.#reducedMotion(opts)) {\n dialog.showModal()\n this.state.set(dialog, { trigger, anchor: opts.anchor, animating: false })\n opts.onOpen?.(dialog)\n return\n }\n\n // Flip morphs elements that share a data-flip-id, so pair trigger and dialog.\n const flipId = this.state.get(dialog).flipId || Math.random().toString(16).slice(2)\n trigger.dataset.flipId = flipId\n dialog.dataset.flipId = flipId\n dialog.dataset.anchor = opts.anchor\n this.state.get(dialog).flipId = flipId\n\n const originState = Flip.getState(trigger)\n dialog.showModal()\n\n if (opts.anchor === 'origin') {\n this.#positionAtOrigin(dialog, trigger)\n }\n\n Flip.from(originState, {\n targets: dialog,\n scale: true,\n ease: this.ease,\n toggleClass: 'pretty-modal-opening',\n duration: opts.duration,\n onComplete: () => {\n const e = this.state.get(dialog)\n if (e) e.animating = false\n opts.onOpen?.(dialog)\n },\n })\n }\n\n /**\n * Close a dialog, morphing back into its trigger element.\n * @param {string|HTMLDialogElement} dialogRef Dialog element or its id.\n * @param {Object} [options]\n * @param {number} [options.duration]\n * @param {boolean} [options.respectReducedMotion]\n * @param {(dialog: HTMLDialogElement) => void} [options.onClose]\n */\n close(dialogRef, options = {}) {\n const dialog = this.#resolveEl(dialogRef)\n if (!dialog || !dialog.open) return\n\n const opts = { ...this.defaults, ...options }\n const entry = this.state.get(dialog)\n const trigger = entry?.trigger\n\n if (this.#reducedMotion(opts) || !trigger) {\n dialog.close()\n dialog.setAttribute('style', '')\n if (entry) entry.animating = false\n opts.onClose?.(dialog)\n return\n }\n\n if (entry) entry.animating = true\n\n const originState = Flip.getState(trigger)\n\n Flip.to(originState, {\n targets: dialog,\n scale: true,\n ease: this.ease,\n toggleClass: 'pretty-modal-closing',\n duration: opts.duration,\n onComplete: () => {\n dialog.setAttribute('style', '')\n dialog.close()\n if (entry) entry.animating = false\n opts.onClose?.(dialog)\n },\n })\n }\n\n /** Remove injected styles. Call when tearing down. */\n destroy() {\n if (typeof document === 'undefined') return\n document.getElementById(STYLE_ID)?.remove()\n }\n\n // --- internals ---------------------------------------------------------\n\n #resolveEl(ref) {\n if (!ref) return null\n if (typeof ref === 'string') return document.getElementById(ref)\n return ref\n }\n\n #resolveTrigger(trigger) {\n const resolved = this.#resolveEl(trigger)\n if (resolved) return resolved\n // Fallback for inline onclick handlers (Chromium exposes a global event).\n if (typeof event !== 'undefined' && event?.currentTarget) return event.currentTarget\n return null\n }\n\n #reducedMotion(opts) {\n if (!opts.respectReducedMotion) return false\n if (typeof window === 'undefined' || !window.matchMedia) return false\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches\n }\n\n #positionAtOrigin(dialog, origin) {\n const originRect = origin.getBoundingClientRect()\n const vw = window.innerWidth\n const vh = window.innerHeight\n\n dialog.style.margin = '0'\n dialog.style.position = 'fixed'\n dialog.style.inset = 'auto'\n\n const dialogRect = dialog.getBoundingClientRect()\n const dialogW = dialogRect.width\n const dialogH = dialogRect.height\n\n const spaceRight = vw - originRect.left\n const spaceLeft = originRect.right\n const spaceBelow = vh - originRect.top\n const spaceAbove = originRect.bottom\n\n if (dialogW <= spaceRight) {\n dialog.style.left = `${originRect.left}px`\n } else if (dialogW <= spaceLeft) {\n dialog.style.right = `${vw - originRect.right}px`\n } else {\n dialog.style.left = `${Math.max(0, (vw - dialogW) / 2)}px`\n }\n\n if (dialogH <= spaceBelow) {\n dialog.style.top = `${originRect.top}px`\n } else if (dialogH <= spaceAbove) {\n dialog.style.bottom = `${vh - originRect.bottom}px`\n } else {\n dialog.style.top = `${Math.max(0, (vh - dialogH) / 2)}px`\n }\n }\n\n injectStyles() {\n if (document.getElementById(STYLE_ID)) return\n\n const styles = `\n .pretty-modal-opening {\n animation: pretty-modal-opening 500ms cubic-bezier(.56,.27,0,1);\n }\n\n @keyframes pretty-modal-opening {\n from { opacity: 0; filter: blur(8px); } to { opacity: 1; filter: blur(0px); }\n }\n\n .pretty-modal-closing {\n animation:\n pretty-modal-closing-border-radius 500ms cubic-bezier(.56,.27,0,1),\n pretty-modal-closing-blur 500ms cubic-bezier(.37,.35,0,1),\n pretty-modal-closing-fade 700ms cubic-bezier(.56,.27,0,1)\n ;\n }\n\n @keyframes pretty-modal-closing-border-radius {\n to { border-radius: 400px; }\n }\n\n @keyframes pretty-modal-closing-blur {\n 0% { filter: blur(0); } 100% { filter: blur(32px); }\n }\n\n @keyframes pretty-modal-closing-fade {\n from { opacity: 1; } to { opacity: 0; }\n }\n\n dialog[open]::backdrop {\n background: rgba(0,0,0,0.2);\n backdrop-filter: blur(2px);\n }\n\n dialog.pretty-modal-opening::backdrop {\n animation: pretty-modal-backdrop-in 400ms cubic-bezier(.56,.27,0,1);\n }\n\n dialog.pretty-modal-closing::backdrop {\n animation: pretty-modal-backdrop-out 400ms cubic-bezier(.56,.27,0,1) forwards;\n }\n\n @keyframes pretty-modal-backdrop-in {\n from { background: rgba(0,0,0,0); backdrop-filter: blur(0px); }\n to { background: rgba(0,0,0,0.2); backdrop-filter: blur(2px); }\n }\n\n @keyframes pretty-modal-backdrop-out {\n from { background: rgba(0,0,0,0.2); backdrop-filter: blur(2px); }\n to { background: rgba(0,0,0,0); backdrop-filter: blur(0px); }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .pretty-modal-opening,\n .pretty-modal-closing,\n dialog.pretty-modal-opening::backdrop,\n dialog.pretty-modal-closing::backdrop {\n animation: none;\n }\n }\n `\n\n const styleSheet = document.createElement('style')\n styleSheet.id = STYLE_ID\n styleSheet.textContent = styles\n document.head.appendChild(styleSheet)\n }\n}\n"]}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
declare class PrettyModal {
|
|
2
|
+
/**
|
|
3
|
+
* @param {Object} [options]
|
|
4
|
+
* @param {'center'|'origin'} [options.anchor='center'] Where the modal opens from.
|
|
5
|
+
* @param {number} [options.duration=0.4] Animation duration in seconds.
|
|
6
|
+
* @param {string} [options.ease] CustomEase SVG path used for the Flip tween.
|
|
7
|
+
* @param {boolean} [options.respectReducedMotion=true] Skip animation when the user prefers reduced motion.
|
|
8
|
+
* @param {(dialog: HTMLDialogElement) => void} [options.onOpen]
|
|
9
|
+
* @param {(dialog: HTMLDialogElement) => void} [options.onClose]
|
|
10
|
+
*/
|
|
11
|
+
constructor(options?: {
|
|
12
|
+
anchor?: "center" | "origin" | undefined;
|
|
13
|
+
duration?: number | undefined;
|
|
14
|
+
ease?: string | undefined;
|
|
15
|
+
respectReducedMotion?: boolean | undefined;
|
|
16
|
+
onOpen?: ((dialog: HTMLDialogElement) => void) | undefined;
|
|
17
|
+
onClose?: ((dialog: HTMLDialogElement) => void) | undefined;
|
|
18
|
+
});
|
|
19
|
+
defaults: {
|
|
20
|
+
anchor: string;
|
|
21
|
+
duration: number;
|
|
22
|
+
ease: string;
|
|
23
|
+
respectReducedMotion: boolean;
|
|
24
|
+
onOpen: ((dialog: HTMLDialogElement) => void) | null;
|
|
25
|
+
onClose: ((dialog: HTMLDialogElement) => void) | null;
|
|
26
|
+
};
|
|
27
|
+
state: WeakMap<WeakKey, any>;
|
|
28
|
+
ease: EaseFunction | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Open a dialog, morphing from the trigger element.
|
|
31
|
+
* @param {string|HTMLDialogElement} dialogRef Dialog element or its id.
|
|
32
|
+
* @param {Object} [options]
|
|
33
|
+
* @param {string|HTMLElement} [options.trigger] Element to animate from. Defaults to `event.currentTarget` when called from an inline handler.
|
|
34
|
+
* @param {'center'|'origin'} [options.anchor]
|
|
35
|
+
* @param {number} [options.duration]
|
|
36
|
+
* @param {boolean} [options.respectReducedMotion]
|
|
37
|
+
* @param {(dialog: HTMLDialogElement) => void} [options.onOpen]
|
|
38
|
+
* @param {(dialog: HTMLDialogElement) => void} [options.onClose]
|
|
39
|
+
*/
|
|
40
|
+
open(dialogRef: string | HTMLDialogElement, options?: {
|
|
41
|
+
trigger?: string | HTMLElement | undefined;
|
|
42
|
+
anchor?: "center" | "origin" | undefined;
|
|
43
|
+
duration?: number | undefined;
|
|
44
|
+
respectReducedMotion?: boolean | undefined;
|
|
45
|
+
onOpen?: ((dialog: HTMLDialogElement) => void) | undefined;
|
|
46
|
+
onClose?: ((dialog: HTMLDialogElement) => void) | undefined;
|
|
47
|
+
}): void;
|
|
48
|
+
/**
|
|
49
|
+
* Close a dialog, morphing back into its trigger element.
|
|
50
|
+
* @param {string|HTMLDialogElement} dialogRef Dialog element or its id.
|
|
51
|
+
* @param {Object} [options]
|
|
52
|
+
* @param {number} [options.duration]
|
|
53
|
+
* @param {boolean} [options.respectReducedMotion]
|
|
54
|
+
* @param {(dialog: HTMLDialogElement) => void} [options.onClose]
|
|
55
|
+
*/
|
|
56
|
+
close(dialogRef: string | HTMLDialogElement, options?: {
|
|
57
|
+
duration?: number | undefined;
|
|
58
|
+
respectReducedMotion?: boolean | undefined;
|
|
59
|
+
onClose?: ((dialog: HTMLDialogElement) => void) | undefined;
|
|
60
|
+
}): void;
|
|
61
|
+
/** Remove injected styles. Call when tearing down. */
|
|
62
|
+
destroy(): void;
|
|
63
|
+
injectStyles(): void;
|
|
64
|
+
#private;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { PrettyModal };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
declare class PrettyModal {
|
|
2
|
+
/**
|
|
3
|
+
* @param {Object} [options]
|
|
4
|
+
* @param {'center'|'origin'} [options.anchor='center'] Where the modal opens from.
|
|
5
|
+
* @param {number} [options.duration=0.4] Animation duration in seconds.
|
|
6
|
+
* @param {string} [options.ease] CustomEase SVG path used for the Flip tween.
|
|
7
|
+
* @param {boolean} [options.respectReducedMotion=true] Skip animation when the user prefers reduced motion.
|
|
8
|
+
* @param {(dialog: HTMLDialogElement) => void} [options.onOpen]
|
|
9
|
+
* @param {(dialog: HTMLDialogElement) => void} [options.onClose]
|
|
10
|
+
*/
|
|
11
|
+
constructor(options?: {
|
|
12
|
+
anchor?: "center" | "origin" | undefined;
|
|
13
|
+
duration?: number | undefined;
|
|
14
|
+
ease?: string | undefined;
|
|
15
|
+
respectReducedMotion?: boolean | undefined;
|
|
16
|
+
onOpen?: ((dialog: HTMLDialogElement) => void) | undefined;
|
|
17
|
+
onClose?: ((dialog: HTMLDialogElement) => void) | undefined;
|
|
18
|
+
});
|
|
19
|
+
defaults: {
|
|
20
|
+
anchor: string;
|
|
21
|
+
duration: number;
|
|
22
|
+
ease: string;
|
|
23
|
+
respectReducedMotion: boolean;
|
|
24
|
+
onOpen: ((dialog: HTMLDialogElement) => void) | null;
|
|
25
|
+
onClose: ((dialog: HTMLDialogElement) => void) | null;
|
|
26
|
+
};
|
|
27
|
+
state: WeakMap<WeakKey, any>;
|
|
28
|
+
ease: EaseFunction | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Open a dialog, morphing from the trigger element.
|
|
31
|
+
* @param {string|HTMLDialogElement} dialogRef Dialog element or its id.
|
|
32
|
+
* @param {Object} [options]
|
|
33
|
+
* @param {string|HTMLElement} [options.trigger] Element to animate from. Defaults to `event.currentTarget` when called from an inline handler.
|
|
34
|
+
* @param {'center'|'origin'} [options.anchor]
|
|
35
|
+
* @param {number} [options.duration]
|
|
36
|
+
* @param {boolean} [options.respectReducedMotion]
|
|
37
|
+
* @param {(dialog: HTMLDialogElement) => void} [options.onOpen]
|
|
38
|
+
* @param {(dialog: HTMLDialogElement) => void} [options.onClose]
|
|
39
|
+
*/
|
|
40
|
+
open(dialogRef: string | HTMLDialogElement, options?: {
|
|
41
|
+
trigger?: string | HTMLElement | undefined;
|
|
42
|
+
anchor?: "center" | "origin" | undefined;
|
|
43
|
+
duration?: number | undefined;
|
|
44
|
+
respectReducedMotion?: boolean | undefined;
|
|
45
|
+
onOpen?: ((dialog: HTMLDialogElement) => void) | undefined;
|
|
46
|
+
onClose?: ((dialog: HTMLDialogElement) => void) | undefined;
|
|
47
|
+
}): void;
|
|
48
|
+
/**
|
|
49
|
+
* Close a dialog, morphing back into its trigger element.
|
|
50
|
+
* @param {string|HTMLDialogElement} dialogRef Dialog element or its id.
|
|
51
|
+
* @param {Object} [options]
|
|
52
|
+
* @param {number} [options.duration]
|
|
53
|
+
* @param {boolean} [options.respectReducedMotion]
|
|
54
|
+
* @param {(dialog: HTMLDialogElement) => void} [options.onClose]
|
|
55
|
+
*/
|
|
56
|
+
close(dialogRef: string | HTMLDialogElement, options?: {
|
|
57
|
+
duration?: number | undefined;
|
|
58
|
+
respectReducedMotion?: boolean | undefined;
|
|
59
|
+
onClose?: ((dialog: HTMLDialogElement) => void) | undefined;
|
|
60
|
+
}): void;
|
|
61
|
+
/** Remove injected styles. Call when tearing down. */
|
|
62
|
+
destroy(): void;
|
|
63
|
+
injectStyles(): void;
|
|
64
|
+
#private;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { PrettyModal };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {gsap}from'gsap';import {Flip}from'gsap/Flip';import {CustomEase}from'gsap/CustomEase';var y=s=>{throw TypeError(s)};var S=(s,t,o)=>t.has(s)||y("Cannot "+o);var b=(s,t,o)=>t.has(s)?y("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(s):t.set(s,o);var l=(s,t,o)=>(S(s,t,"access private method"),o);gsap.registerPlugin(Flip,CustomEase);var m="pretty-modal-styles",I="M0,0 C0.305,0.206 0.116,0.567 0.3,0.8 0.394,0.921 0.491,1 1,1",i,g,x,f,w,h=class{constructor(t={}){b(this,i);this.defaults={anchor:"center",duration:.4,ease:I,respectReducedMotion:true,onOpen:null,onClose:null,...t},this.state=new WeakMap,typeof document<"u"&&(this.ease=CustomEase.create("pretty-modal-ease",this.defaults.ease),this.injectStyles());}open(t,o={}){let e=l(this,i,g).call(this,t);if(!e||e.open)return;let n={...this.defaults,...o},r=l(this,i,x).call(this,o.trigger);if(!r){console.warn("[PrettyModal] No trigger element found. Pass { trigger } explicitly.");return}if(this.state.get(e)?.animating)return;if(this.state.set(e,{trigger:r,anchor:n.anchor,animating:true}),l(this,i,f).call(this,n)){e.showModal(),this.state.set(e,{trigger:r,anchor:n.anchor,animating:false}),n.onOpen?.(e);return}let a=this.state.get(e).flipId||Math.random().toString(16).slice(2);r.dataset.flipId=a,e.dataset.flipId=a,e.dataset.anchor=n.anchor,this.state.get(e).flipId=a;let d=Flip.getState(r);e.showModal(),n.anchor==="origin"&&l(this,i,w).call(this,e,r),Flip.from(d,{targets:e,scale:true,ease:this.ease,toggleClass:"pretty-modal-opening",duration:n.duration,onComplete:()=>{let u=this.state.get(e);u&&(u.animating=false),n.onOpen?.(e);}});}close(t,o={}){let e=l(this,i,g).call(this,t);if(!e||!e.open)return;let n={...this.defaults,...o},r=this.state.get(e),c=r?.trigger;if(l(this,i,f).call(this,n)||!c){e.close(),e.setAttribute("style",""),r&&(r.animating=false),n.onClose?.(e);return}r&&(r.animating=true);let a=Flip.getState(c);Flip.to(a,{targets:e,scale:true,ease:this.ease,toggleClass:"pretty-modal-closing",duration:n.duration,onComplete:()=>{e.setAttribute("style",""),e.close(),r&&(r.animating=false),n.onClose?.(e);}});}destroy(){typeof document>"u"||document.getElementById(m)?.remove();}injectStyles(){if(document.getElementById(m))return;let t=`
|
|
2
|
+
.pretty-modal-opening {
|
|
3
|
+
animation: pretty-modal-opening 500ms cubic-bezier(.56,.27,0,1);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
@keyframes pretty-modal-opening {
|
|
7
|
+
from { opacity: 0; filter: blur(8px); } to { opacity: 1; filter: blur(0px); }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.pretty-modal-closing {
|
|
11
|
+
animation:
|
|
12
|
+
pretty-modal-closing-border-radius 500ms cubic-bezier(.56,.27,0,1),
|
|
13
|
+
pretty-modal-closing-blur 500ms cubic-bezier(.37,.35,0,1),
|
|
14
|
+
pretty-modal-closing-fade 700ms cubic-bezier(.56,.27,0,1)
|
|
15
|
+
;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@keyframes pretty-modal-closing-border-radius {
|
|
19
|
+
to { border-radius: 400px; }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@keyframes pretty-modal-closing-blur {
|
|
23
|
+
0% { filter: blur(0); } 100% { filter: blur(32px); }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@keyframes pretty-modal-closing-fade {
|
|
27
|
+
from { opacity: 1; } to { opacity: 0; }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
dialog[open]::backdrop {
|
|
31
|
+
background: rgba(0,0,0,0.2);
|
|
32
|
+
backdrop-filter: blur(2px);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
dialog.pretty-modal-opening::backdrop {
|
|
36
|
+
animation: pretty-modal-backdrop-in 400ms cubic-bezier(.56,.27,0,1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
dialog.pretty-modal-closing::backdrop {
|
|
40
|
+
animation: pretty-modal-backdrop-out 400ms cubic-bezier(.56,.27,0,1) forwards;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@keyframes pretty-modal-backdrop-in {
|
|
44
|
+
from { background: rgba(0,0,0,0); backdrop-filter: blur(0px); }
|
|
45
|
+
to { background: rgba(0,0,0,0.2); backdrop-filter: blur(2px); }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@keyframes pretty-modal-backdrop-out {
|
|
49
|
+
from { background: rgba(0,0,0,0.2); backdrop-filter: blur(2px); }
|
|
50
|
+
to { background: rgba(0,0,0,0); backdrop-filter: blur(0px); }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@media (prefers-reduced-motion: reduce) {
|
|
54
|
+
.pretty-modal-opening,
|
|
55
|
+
.pretty-modal-closing,
|
|
56
|
+
dialog.pretty-modal-opening::backdrop,
|
|
57
|
+
dialog.pretty-modal-closing::backdrop {
|
|
58
|
+
animation: none;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
`,o=document.createElement("style");o.id=m,o.textContent=t,document.head.appendChild(o);}};i=new WeakSet,g=function(t){return t?typeof t=="string"?document.getElementById(t):t:null},x=function(t){let o=l(this,i,g).call(this,t);return o||(typeof event<"u"&&event?.currentTarget?event.currentTarget:null)},f=function(t){return !t.respectReducedMotion||typeof window>"u"||!window.matchMedia?false:window.matchMedia("(prefers-reduced-motion: reduce)").matches},w=function(t,o){let e=o.getBoundingClientRect(),n=window.innerWidth,r=window.innerHeight;t.style.margin="0",t.style.position="fixed",t.style.inset="auto";let c=t.getBoundingClientRect(),a=c.width,d=c.height,u=n-e.left,C=e.right,M=r-e.top,v=e.bottom;a<=u?t.style.left=`${e.left}px`:a<=C?t.style.right=`${n-e.right}px`:t.style.left=`${Math.max(0,(n-a)/2)}px`,d<=M?t.style.top=`${e.top}px`:d<=v?t.style.bottom=`${r-e.bottom}px`:t.style.top=`${Math.max(0,(r-d)/2)}px`;};export{h as PrettyModal};//# sourceMappingURL=PrettyModal.js.map
|
|
62
|
+
//# sourceMappingURL=PrettyModal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/PrettyModal.js"],"names":["gsap","Flip","CustomEase","STYLE_ID","EASE","_PrettyModal_instances","resolveEl_fn","resolveTrigger_fn","reducedMotion_fn","positionAtOrigin_fn","PrettyModal","options","__privateAdd","dialogRef","dialog","__privateMethod","opts","trigger","flipId","originState","e","entry","styles","styleSheet","ref","resolved","origin","originRect","vw","vh","dialogRect","dialogW","dialogH","spaceRight","spaceLeft","spaceBelow","spaceAbove"],"mappings":"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","file":"PrettyModal.js","sourcesContent":["import { gsap } from 'gsap'\nimport { Flip } from 'gsap/Flip'\nimport { CustomEase } from 'gsap/CustomEase'\n\ngsap.registerPlugin(Flip, CustomEase)\n\nconst STYLE_ID = 'pretty-modal-styles'\nconst EASE = 'M0,0 C0.305,0.206 0.116,0.567 0.3,0.8 0.394,0.921 0.491,1 1,1'\n\nexport class PrettyModal {\n /**\n * @param {Object} [options]\n * @param {'center'|'origin'} [options.anchor='center'] Where the modal opens from.\n * @param {number} [options.duration=0.4] Animation duration in seconds.\n * @param {string} [options.ease] CustomEase SVG path used for the Flip tween.\n * @param {boolean} [options.respectReducedMotion=true] Skip animation when the user prefers reduced motion.\n * @param {(dialog: HTMLDialogElement) => void} [options.onOpen]\n * @param {(dialog: HTMLDialogElement) => void} [options.onClose]\n */\n constructor(options = {}) {\n this.defaults = {\n anchor: 'center',\n duration: 0.4,\n ease: EASE,\n respectReducedMotion: true,\n onOpen: null,\n onClose: null,\n ...options,\n }\n\n // Per-dialog state: trigger element + whether it is mid-animation.\n this.state = new WeakMap()\n\n if (typeof document !== 'undefined') {\n this.ease = CustomEase.create('pretty-modal-ease', this.defaults.ease)\n this.injectStyles()\n }\n }\n\n /**\n * Open a dialog, morphing from the trigger element.\n * @param {string|HTMLDialogElement} dialogRef Dialog element or its id.\n * @param {Object} [options]\n * @param {string|HTMLElement} [options.trigger] Element to animate from. Defaults to `event.currentTarget` when called from an inline handler.\n * @param {'center'|'origin'} [options.anchor]\n * @param {number} [options.duration]\n * @param {boolean} [options.respectReducedMotion]\n * @param {(dialog: HTMLDialogElement) => void} [options.onOpen]\n * @param {(dialog: HTMLDialogElement) => void} [options.onClose]\n */\n open(dialogRef, options = {}) {\n const dialog = this.#resolveEl(dialogRef)\n if (!dialog || dialog.open) return\n\n const opts = { ...this.defaults, ...options }\n const trigger = this.#resolveTrigger(options.trigger)\n if (!trigger) {\n console.warn('[PrettyModal] No trigger element found. Pass { trigger } explicitly.')\n return\n }\n\n const entry = this.state.get(dialog)\n if (entry?.animating) return\n\n this.state.set(dialog, { trigger, anchor: opts.anchor, animating: true })\n\n if (this.#reducedMotion(opts)) {\n dialog.showModal()\n this.state.set(dialog, { trigger, anchor: opts.anchor, animating: false })\n opts.onOpen?.(dialog)\n return\n }\n\n // Flip morphs elements that share a data-flip-id, so pair trigger and dialog.\n const flipId = this.state.get(dialog).flipId || Math.random().toString(16).slice(2)\n trigger.dataset.flipId = flipId\n dialog.dataset.flipId = flipId\n dialog.dataset.anchor = opts.anchor\n this.state.get(dialog).flipId = flipId\n\n const originState = Flip.getState(trigger)\n dialog.showModal()\n\n if (opts.anchor === 'origin') {\n this.#positionAtOrigin(dialog, trigger)\n }\n\n Flip.from(originState, {\n targets: dialog,\n scale: true,\n ease: this.ease,\n toggleClass: 'pretty-modal-opening',\n duration: opts.duration,\n onComplete: () => {\n const e = this.state.get(dialog)\n if (e) e.animating = false\n opts.onOpen?.(dialog)\n },\n })\n }\n\n /**\n * Close a dialog, morphing back into its trigger element.\n * @param {string|HTMLDialogElement} dialogRef Dialog element or its id.\n * @param {Object} [options]\n * @param {number} [options.duration]\n * @param {boolean} [options.respectReducedMotion]\n * @param {(dialog: HTMLDialogElement) => void} [options.onClose]\n */\n close(dialogRef, options = {}) {\n const dialog = this.#resolveEl(dialogRef)\n if (!dialog || !dialog.open) return\n\n const opts = { ...this.defaults, ...options }\n const entry = this.state.get(dialog)\n const trigger = entry?.trigger\n\n if (this.#reducedMotion(opts) || !trigger) {\n dialog.close()\n dialog.setAttribute('style', '')\n if (entry) entry.animating = false\n opts.onClose?.(dialog)\n return\n }\n\n if (entry) entry.animating = true\n\n const originState = Flip.getState(trigger)\n\n Flip.to(originState, {\n targets: dialog,\n scale: true,\n ease: this.ease,\n toggleClass: 'pretty-modal-closing',\n duration: opts.duration,\n onComplete: () => {\n dialog.setAttribute('style', '')\n dialog.close()\n if (entry) entry.animating = false\n opts.onClose?.(dialog)\n },\n })\n }\n\n /** Remove injected styles. Call when tearing down. */\n destroy() {\n if (typeof document === 'undefined') return\n document.getElementById(STYLE_ID)?.remove()\n }\n\n // --- internals ---------------------------------------------------------\n\n #resolveEl(ref) {\n if (!ref) return null\n if (typeof ref === 'string') return document.getElementById(ref)\n return ref\n }\n\n #resolveTrigger(trigger) {\n const resolved = this.#resolveEl(trigger)\n if (resolved) return resolved\n // Fallback for inline onclick handlers (Chromium exposes a global event).\n if (typeof event !== 'undefined' && event?.currentTarget) return event.currentTarget\n return null\n }\n\n #reducedMotion(opts) {\n if (!opts.respectReducedMotion) return false\n if (typeof window === 'undefined' || !window.matchMedia) return false\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches\n }\n\n #positionAtOrigin(dialog, origin) {\n const originRect = origin.getBoundingClientRect()\n const vw = window.innerWidth\n const vh = window.innerHeight\n\n dialog.style.margin = '0'\n dialog.style.position = 'fixed'\n dialog.style.inset = 'auto'\n\n const dialogRect = dialog.getBoundingClientRect()\n const dialogW = dialogRect.width\n const dialogH = dialogRect.height\n\n const spaceRight = vw - originRect.left\n const spaceLeft = originRect.right\n const spaceBelow = vh - originRect.top\n const spaceAbove = originRect.bottom\n\n if (dialogW <= spaceRight) {\n dialog.style.left = `${originRect.left}px`\n } else if (dialogW <= spaceLeft) {\n dialog.style.right = `${vw - originRect.right}px`\n } else {\n dialog.style.left = `${Math.max(0, (vw - dialogW) / 2)}px`\n }\n\n if (dialogH <= spaceBelow) {\n dialog.style.top = `${originRect.top}px`\n } else if (dialogH <= spaceAbove) {\n dialog.style.bottom = `${vh - originRect.bottom}px`\n } else {\n dialog.style.top = `${Math.max(0, (vh - dialogH) / 2)}px`\n }\n }\n\n injectStyles() {\n if (document.getElementById(STYLE_ID)) return\n\n const styles = `\n .pretty-modal-opening {\n animation: pretty-modal-opening 500ms cubic-bezier(.56,.27,0,1);\n }\n\n @keyframes pretty-modal-opening {\n from { opacity: 0; filter: blur(8px); } to { opacity: 1; filter: blur(0px); }\n }\n\n .pretty-modal-closing {\n animation:\n pretty-modal-closing-border-radius 500ms cubic-bezier(.56,.27,0,1),\n pretty-modal-closing-blur 500ms cubic-bezier(.37,.35,0,1),\n pretty-modal-closing-fade 700ms cubic-bezier(.56,.27,0,1)\n ;\n }\n\n @keyframes pretty-modal-closing-border-radius {\n to { border-radius: 400px; }\n }\n\n @keyframes pretty-modal-closing-blur {\n 0% { filter: blur(0); } 100% { filter: blur(32px); }\n }\n\n @keyframes pretty-modal-closing-fade {\n from { opacity: 1; } to { opacity: 0; }\n }\n\n dialog[open]::backdrop {\n background: rgba(0,0,0,0.2);\n backdrop-filter: blur(2px);\n }\n\n dialog.pretty-modal-opening::backdrop {\n animation: pretty-modal-backdrop-in 400ms cubic-bezier(.56,.27,0,1);\n }\n\n dialog.pretty-modal-closing::backdrop {\n animation: pretty-modal-backdrop-out 400ms cubic-bezier(.56,.27,0,1) forwards;\n }\n\n @keyframes pretty-modal-backdrop-in {\n from { background: rgba(0,0,0,0); backdrop-filter: blur(0px); }\n to { background: rgba(0,0,0,0.2); backdrop-filter: blur(2px); }\n }\n\n @keyframes pretty-modal-backdrop-out {\n from { background: rgba(0,0,0,0.2); backdrop-filter: blur(2px); }\n to { background: rgba(0,0,0,0); backdrop-filter: blur(0px); }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .pretty-modal-opening,\n .pretty-modal-closing,\n dialog.pretty-modal-opening::backdrop,\n dialog.pretty-modal-closing::backdrop {\n animation: none;\n }\n }\n `\n\n const styleSheet = document.createElement('style')\n styleSheet.id = STYLE_ID\n styleSheet.textContent = styles\n document.head.appendChild(styleSheet)\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "prettier-modals",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Beautiful open/close animations for native <dialog> elements, powered by GSAP Flip.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/PrettyModal.cjs",
|
|
7
|
+
"module": "./dist/PrettyModal.js",
|
|
8
|
+
"types": "./dist/PrettyModal.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/PrettyModal.d.ts",
|
|
12
|
+
"import": "./dist/PrettyModal.js",
|
|
13
|
+
"require": "./dist/PrettyModal.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"src",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"dev": "tsup --watch",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"test:watch": "vitest",
|
|
27
|
+
"prepublishOnly": "npm run build"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"modal",
|
|
31
|
+
"dialog",
|
|
32
|
+
"gsap",
|
|
33
|
+
"flip",
|
|
34
|
+
"animation",
|
|
35
|
+
"vanilla"
|
|
36
|
+
],
|
|
37
|
+
"author": "Antonio Monreal Diaz",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/antuuanyf/prettier-modals.git"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"gsap": ">=3.12"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"gsap": "^3.15.0",
|
|
48
|
+
"happy-dom": "^20.9.0",
|
|
49
|
+
"tsup": "^8.5.0",
|
|
50
|
+
"typescript": "^5.7.0",
|
|
51
|
+
"vitest": "^4.1.7"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { gsap } from 'gsap'
|
|
2
|
+
import { Flip } from 'gsap/Flip'
|
|
3
|
+
import { CustomEase } from 'gsap/CustomEase'
|
|
4
|
+
|
|
5
|
+
gsap.registerPlugin(Flip, CustomEase)
|
|
6
|
+
|
|
7
|
+
const STYLE_ID = 'pretty-modal-styles'
|
|
8
|
+
const EASE = 'M0,0 C0.305,0.206 0.116,0.567 0.3,0.8 0.394,0.921 0.491,1 1,1'
|
|
9
|
+
|
|
10
|
+
export class PrettyModal {
|
|
11
|
+
/**
|
|
12
|
+
* @param {Object} [options]
|
|
13
|
+
* @param {'center'|'origin'} [options.anchor='center'] Where the modal opens from.
|
|
14
|
+
* @param {number} [options.duration=0.4] Animation duration in seconds.
|
|
15
|
+
* @param {string} [options.ease] CustomEase SVG path used for the Flip tween.
|
|
16
|
+
* @param {boolean} [options.respectReducedMotion=true] Skip animation when the user prefers reduced motion.
|
|
17
|
+
* @param {(dialog: HTMLDialogElement) => void} [options.onOpen]
|
|
18
|
+
* @param {(dialog: HTMLDialogElement) => void} [options.onClose]
|
|
19
|
+
*/
|
|
20
|
+
constructor(options = {}) {
|
|
21
|
+
this.defaults = {
|
|
22
|
+
anchor: 'center',
|
|
23
|
+
duration: 0.4,
|
|
24
|
+
ease: EASE,
|
|
25
|
+
respectReducedMotion: true,
|
|
26
|
+
onOpen: null,
|
|
27
|
+
onClose: null,
|
|
28
|
+
...options,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Per-dialog state: trigger element + whether it is mid-animation.
|
|
32
|
+
this.state = new WeakMap()
|
|
33
|
+
|
|
34
|
+
if (typeof document !== 'undefined') {
|
|
35
|
+
this.ease = CustomEase.create('pretty-modal-ease', this.defaults.ease)
|
|
36
|
+
this.injectStyles()
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Open a dialog, morphing from the trigger element.
|
|
42
|
+
* @param {string|HTMLDialogElement} dialogRef Dialog element or its id.
|
|
43
|
+
* @param {Object} [options]
|
|
44
|
+
* @param {string|HTMLElement} [options.trigger] Element to animate from. Defaults to `event.currentTarget` when called from an inline handler.
|
|
45
|
+
* @param {'center'|'origin'} [options.anchor]
|
|
46
|
+
* @param {number} [options.duration]
|
|
47
|
+
* @param {boolean} [options.respectReducedMotion]
|
|
48
|
+
* @param {(dialog: HTMLDialogElement) => void} [options.onOpen]
|
|
49
|
+
* @param {(dialog: HTMLDialogElement) => void} [options.onClose]
|
|
50
|
+
*/
|
|
51
|
+
open(dialogRef, options = {}) {
|
|
52
|
+
const dialog = this.#resolveEl(dialogRef)
|
|
53
|
+
if (!dialog || dialog.open) return
|
|
54
|
+
|
|
55
|
+
const opts = { ...this.defaults, ...options }
|
|
56
|
+
const trigger = this.#resolveTrigger(options.trigger)
|
|
57
|
+
if (!trigger) {
|
|
58
|
+
console.warn('[PrettyModal] No trigger element found. Pass { trigger } explicitly.')
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const entry = this.state.get(dialog)
|
|
63
|
+
if (entry?.animating) return
|
|
64
|
+
|
|
65
|
+
this.state.set(dialog, { trigger, anchor: opts.anchor, animating: true })
|
|
66
|
+
|
|
67
|
+
if (this.#reducedMotion(opts)) {
|
|
68
|
+
dialog.showModal()
|
|
69
|
+
this.state.set(dialog, { trigger, anchor: opts.anchor, animating: false })
|
|
70
|
+
opts.onOpen?.(dialog)
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Flip morphs elements that share a data-flip-id, so pair trigger and dialog.
|
|
75
|
+
const flipId = this.state.get(dialog).flipId || Math.random().toString(16).slice(2)
|
|
76
|
+
trigger.dataset.flipId = flipId
|
|
77
|
+
dialog.dataset.flipId = flipId
|
|
78
|
+
dialog.dataset.anchor = opts.anchor
|
|
79
|
+
this.state.get(dialog).flipId = flipId
|
|
80
|
+
|
|
81
|
+
const originState = Flip.getState(trigger)
|
|
82
|
+
dialog.showModal()
|
|
83
|
+
|
|
84
|
+
if (opts.anchor === 'origin') {
|
|
85
|
+
this.#positionAtOrigin(dialog, trigger)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
Flip.from(originState, {
|
|
89
|
+
targets: dialog,
|
|
90
|
+
scale: true,
|
|
91
|
+
ease: this.ease,
|
|
92
|
+
toggleClass: 'pretty-modal-opening',
|
|
93
|
+
duration: opts.duration,
|
|
94
|
+
onComplete: () => {
|
|
95
|
+
const e = this.state.get(dialog)
|
|
96
|
+
if (e) e.animating = false
|
|
97
|
+
opts.onOpen?.(dialog)
|
|
98
|
+
},
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Close a dialog, morphing back into its trigger element.
|
|
104
|
+
* @param {string|HTMLDialogElement} dialogRef Dialog element or its id.
|
|
105
|
+
* @param {Object} [options]
|
|
106
|
+
* @param {number} [options.duration]
|
|
107
|
+
* @param {boolean} [options.respectReducedMotion]
|
|
108
|
+
* @param {(dialog: HTMLDialogElement) => void} [options.onClose]
|
|
109
|
+
*/
|
|
110
|
+
close(dialogRef, options = {}) {
|
|
111
|
+
const dialog = this.#resolveEl(dialogRef)
|
|
112
|
+
if (!dialog || !dialog.open) return
|
|
113
|
+
|
|
114
|
+
const opts = { ...this.defaults, ...options }
|
|
115
|
+
const entry = this.state.get(dialog)
|
|
116
|
+
const trigger = entry?.trigger
|
|
117
|
+
|
|
118
|
+
if (this.#reducedMotion(opts) || !trigger) {
|
|
119
|
+
dialog.close()
|
|
120
|
+
dialog.setAttribute('style', '')
|
|
121
|
+
if (entry) entry.animating = false
|
|
122
|
+
opts.onClose?.(dialog)
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (entry) entry.animating = true
|
|
127
|
+
|
|
128
|
+
const originState = Flip.getState(trigger)
|
|
129
|
+
|
|
130
|
+
Flip.to(originState, {
|
|
131
|
+
targets: dialog,
|
|
132
|
+
scale: true,
|
|
133
|
+
ease: this.ease,
|
|
134
|
+
toggleClass: 'pretty-modal-closing',
|
|
135
|
+
duration: opts.duration,
|
|
136
|
+
onComplete: () => {
|
|
137
|
+
dialog.setAttribute('style', '')
|
|
138
|
+
dialog.close()
|
|
139
|
+
if (entry) entry.animating = false
|
|
140
|
+
opts.onClose?.(dialog)
|
|
141
|
+
},
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Remove injected styles. Call when tearing down. */
|
|
146
|
+
destroy() {
|
|
147
|
+
if (typeof document === 'undefined') return
|
|
148
|
+
document.getElementById(STYLE_ID)?.remove()
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// --- internals ---------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
#resolveEl(ref) {
|
|
154
|
+
if (!ref) return null
|
|
155
|
+
if (typeof ref === 'string') return document.getElementById(ref)
|
|
156
|
+
return ref
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
#resolveTrigger(trigger) {
|
|
160
|
+
const resolved = this.#resolveEl(trigger)
|
|
161
|
+
if (resolved) return resolved
|
|
162
|
+
// Fallback for inline onclick handlers (Chromium exposes a global event).
|
|
163
|
+
if (typeof event !== 'undefined' && event?.currentTarget) return event.currentTarget
|
|
164
|
+
return null
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
#reducedMotion(opts) {
|
|
168
|
+
if (!opts.respectReducedMotion) return false
|
|
169
|
+
if (typeof window === 'undefined' || !window.matchMedia) return false
|
|
170
|
+
return window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
#positionAtOrigin(dialog, origin) {
|
|
174
|
+
const originRect = origin.getBoundingClientRect()
|
|
175
|
+
const vw = window.innerWidth
|
|
176
|
+
const vh = window.innerHeight
|
|
177
|
+
|
|
178
|
+
dialog.style.margin = '0'
|
|
179
|
+
dialog.style.position = 'fixed'
|
|
180
|
+
dialog.style.inset = 'auto'
|
|
181
|
+
|
|
182
|
+
const dialogRect = dialog.getBoundingClientRect()
|
|
183
|
+
const dialogW = dialogRect.width
|
|
184
|
+
const dialogH = dialogRect.height
|
|
185
|
+
|
|
186
|
+
const spaceRight = vw - originRect.left
|
|
187
|
+
const spaceLeft = originRect.right
|
|
188
|
+
const spaceBelow = vh - originRect.top
|
|
189
|
+
const spaceAbove = originRect.bottom
|
|
190
|
+
|
|
191
|
+
if (dialogW <= spaceRight) {
|
|
192
|
+
dialog.style.left = `${originRect.left}px`
|
|
193
|
+
} else if (dialogW <= spaceLeft) {
|
|
194
|
+
dialog.style.right = `${vw - originRect.right}px`
|
|
195
|
+
} else {
|
|
196
|
+
dialog.style.left = `${Math.max(0, (vw - dialogW) / 2)}px`
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (dialogH <= spaceBelow) {
|
|
200
|
+
dialog.style.top = `${originRect.top}px`
|
|
201
|
+
} else if (dialogH <= spaceAbove) {
|
|
202
|
+
dialog.style.bottom = `${vh - originRect.bottom}px`
|
|
203
|
+
} else {
|
|
204
|
+
dialog.style.top = `${Math.max(0, (vh - dialogH) / 2)}px`
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
injectStyles() {
|
|
209
|
+
if (document.getElementById(STYLE_ID)) return
|
|
210
|
+
|
|
211
|
+
const styles = `
|
|
212
|
+
.pretty-modal-opening {
|
|
213
|
+
animation: pretty-modal-opening 500ms cubic-bezier(.56,.27,0,1);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@keyframes pretty-modal-opening {
|
|
217
|
+
from { opacity: 0; filter: blur(8px); } to { opacity: 1; filter: blur(0px); }
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.pretty-modal-closing {
|
|
221
|
+
animation:
|
|
222
|
+
pretty-modal-closing-border-radius 500ms cubic-bezier(.56,.27,0,1),
|
|
223
|
+
pretty-modal-closing-blur 500ms cubic-bezier(.37,.35,0,1),
|
|
224
|
+
pretty-modal-closing-fade 700ms cubic-bezier(.56,.27,0,1)
|
|
225
|
+
;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
@keyframes pretty-modal-closing-border-radius {
|
|
229
|
+
to { border-radius: 400px; }
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
@keyframes pretty-modal-closing-blur {
|
|
233
|
+
0% { filter: blur(0); } 100% { filter: blur(32px); }
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
@keyframes pretty-modal-closing-fade {
|
|
237
|
+
from { opacity: 1; } to { opacity: 0; }
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
dialog[open]::backdrop {
|
|
241
|
+
background: rgba(0,0,0,0.2);
|
|
242
|
+
backdrop-filter: blur(2px);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
dialog.pretty-modal-opening::backdrop {
|
|
246
|
+
animation: pretty-modal-backdrop-in 400ms cubic-bezier(.56,.27,0,1);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
dialog.pretty-modal-closing::backdrop {
|
|
250
|
+
animation: pretty-modal-backdrop-out 400ms cubic-bezier(.56,.27,0,1) forwards;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
@keyframes pretty-modal-backdrop-in {
|
|
254
|
+
from { background: rgba(0,0,0,0); backdrop-filter: blur(0px); }
|
|
255
|
+
to { background: rgba(0,0,0,0.2); backdrop-filter: blur(2px); }
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
@keyframes pretty-modal-backdrop-out {
|
|
259
|
+
from { background: rgba(0,0,0,0.2); backdrop-filter: blur(2px); }
|
|
260
|
+
to { background: rgba(0,0,0,0); backdrop-filter: blur(0px); }
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
@media (prefers-reduced-motion: reduce) {
|
|
264
|
+
.pretty-modal-opening,
|
|
265
|
+
.pretty-modal-closing,
|
|
266
|
+
dialog.pretty-modal-opening::backdrop,
|
|
267
|
+
dialog.pretty-modal-closing::backdrop {
|
|
268
|
+
animation: none;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
`
|
|
272
|
+
|
|
273
|
+
const styleSheet = document.createElement('style')
|
|
274
|
+
styleSheet.id = STYLE_ID
|
|
275
|
+
styleSheet.textContent = styles
|
|
276
|
+
document.head.appendChild(styleSheet)
|
|
277
|
+
}
|
|
278
|
+
}
|