avalynx-modal 0.0.1
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 +162 -0
- package/dist/css/avalynx-modal.css +46 -0
- package/dist/js/avalynx-modal.esm.js +267 -0
- package/dist/js/avalynx-modal.js +265 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Juergen Schwind an the Avalynx-Modal Authors
|
|
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,162 @@
|
|
|
1
|
+
# AvalynxModal
|
|
2
|
+
|
|
3
|
+
AvalynxModal is a simple modal system for web applications with fullscreen support. Based on Bootstrap >=5.3 without any framework dependencies.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Customizable Modals**: Supports various customization options like fullscreen mode, custom title, body content, buttons, and more.
|
|
8
|
+
- **Bootstrap Integration**: Designed for seamless integration with Bootstrap >= 5.3.
|
|
9
|
+
- **Easy to Use**: Simple API for creating and managing modals within your web applications.
|
|
10
|
+
|
|
11
|
+
## Example
|
|
12
|
+
|
|
13
|
+
Here's a simple example of how to use AvalynxModal in your project:
|
|
14
|
+
|
|
15
|
+
* [Overview](https://avalynx-modal.jbs-newmedia.de/examples/index.html)
|
|
16
|
+
* [Simple Modal](https://avalynx-modal.jbs-newmedia.de/examples/modal.html)
|
|
17
|
+
* [Modal Permanent](https://avalynx-modal.jbs-newmedia.de/examples/modal-permanent.html)
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
To use AvalynxModal in your project, you can directly include it in your HTML file. Ensure you have Bootstrap 5.3 or higher included in your project for AvalynxModal to work correctly.
|
|
22
|
+
|
|
23
|
+
First, include Bootstrap:
|
|
24
|
+
|
|
25
|
+
```html
|
|
26
|
+
<!-- Bootstrap -->
|
|
27
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
28
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Then, include AvalynxModal:
|
|
32
|
+
|
|
33
|
+
```html
|
|
34
|
+
<link href="path/to/avalynx-modal.css" rel="stylesheet">
|
|
35
|
+
<script src="path/to/avalynx-modal.js"></script>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Replace `path/to/avalynx-modal.js` and `path/to/avalynx-modal.css` with the actual path to the files in your project.
|
|
39
|
+
|
|
40
|
+
## Installation via jsDelivr ([Link](https://cdn.jsdelivr.net/npm/avalynx-modal/))
|
|
41
|
+
|
|
42
|
+
AvalynxModal is also available via [jsDelivr](https://www.jsdelivr.com/). You can include it in your project like this:
|
|
43
|
+
|
|
44
|
+
```html
|
|
45
|
+
<link href="https://cdn.jsdelivr.net/npm/avalynx-modal@0.0.1/dist/css/avalynx-modal.css" rel="stylesheet">
|
|
46
|
+
<script src="https://cdn.jsdelivr.net/npm/avalynx-modal@0.0.1/dist/js/avalynx-modal.js"></script>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Make sure to also include Bootstrap's JS/CSS in your project to ensure AvalynxModal displays correctly.
|
|
50
|
+
|
|
51
|
+
## Installation via NPM ([Link](https://www.npmjs.com/package/avalynx-modal))
|
|
52
|
+
|
|
53
|
+
AvalynxModal is also available as a npm package. You can add it to your project with the following command:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm install avalynx-modal
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
After installing, you can import AvalynxModal into your JavaScript file like this:
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
import { AvalynxModal } from 'avalynx-modal';
|
|
63
|
+
import 'avalynx-modal/dist/css/avalynx-modal.min.css';
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Make sure to also include Bootstrap's JS/CSS in your project to ensure AvalynxModal displays correctly.
|
|
67
|
+
|
|
68
|
+
## Installation via Symfony AssetMapper
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
php bin/console importmap:require avalynx-modal
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
After installing, you can import AvalynxModal into your JavaScript file like this:
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
import { AvalynxModal } from 'avalynx-modal';
|
|
78
|
+
import 'avalynx-modal/dist/css/avalynx-modal.min.css';
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Make sure to also include Bootstrap's JS/CSS in your project to ensure AvalynxModal displays correctly.
|
|
82
|
+
|
|
83
|
+
## Installation via Composer ([Link](https://packagist.org/packages/avalynx/avalynx-modal))
|
|
84
|
+
|
|
85
|
+
AvalynxModal is also available as a Composer package. You can add it to your project with the following command:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
composer require avalynx/avalynx-modal
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
After installing, you can import AvalynxModal into your HTML file like this:
|
|
92
|
+
|
|
93
|
+
```html
|
|
94
|
+
<link href="vendor/avalynx/avalynx-modal/dist/css/avalynx-modal.css" rel="stylesheet">
|
|
95
|
+
<script src="vendor/avalynx/avalynx-modal/dist/js/avalynx-modal.js"></script>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Make sure to also include Bootstrap's JS/CSS in your project to ensure AvalynxModal displays correctly.
|
|
99
|
+
|
|
100
|
+
## Usage
|
|
101
|
+
|
|
102
|
+
To create a modal dialog, simply instantiate a new `AvalynxModal` object with the desired options:
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
new AvalynxModal("#myModal", {
|
|
106
|
+
modalFullscreen: true,
|
|
107
|
+
title: 'My Modal',
|
|
108
|
+
body: 'This is the body of my modal.',
|
|
109
|
+
buttons: [
|
|
110
|
+
{
|
|
111
|
+
label: 'Close',
|
|
112
|
+
class: 'btn btn-primary',
|
|
113
|
+
onClick: function() {
|
|
114
|
+
// Close modal
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Options
|
|
122
|
+
|
|
123
|
+
AvalynxModal allows the following options for customization:
|
|
124
|
+
|
|
125
|
+
- `id`: (string) The ID of the element to attach the modal to.
|
|
126
|
+
- `options`: An object containing the following keys:
|
|
127
|
+
- `modalFullscreen`: (boolean) Enable fullscreen mode (default: `false`).
|
|
128
|
+
- `title`: (string) The title of the modal (default: `''`).
|
|
129
|
+
- `titleIsHtml`: (boolean) Treat the title as HTML (default: `false`).
|
|
130
|
+
- `body`: (string) The body content of the modal (default: `''`).
|
|
131
|
+
- `bodyIsHtml`: (boolean) Treat the body content as HTML (default: `false`).
|
|
132
|
+
- `bodyAjaxUrl`: (string) URL to fetch the body content from (default: `''`).
|
|
133
|
+
- `buttons`: (array) An array of button objects. Each object should have a `label`, `class`, and `onClick` function (default: `[]`).
|
|
134
|
+
- `safeInstance`: (boolean) Keep the modal instance after it's closed (default: `false`).
|
|
135
|
+
- `disableFullscreen`: (boolean) Disable the fullscreen button (default: `false`).
|
|
136
|
+
- `disableClose`: (boolean) Disable the close button (default: `false`).
|
|
137
|
+
- `removeFullscreenBtn`: (boolean) Remove the fullscreen button (default: `false`).
|
|
138
|
+
- `removeCloseBtn`: (boolean) Remove the close button (default: `false`).
|
|
139
|
+
- `loader`: (object) An instance of AvalynxLoader to use as the loader for the modal (default: `null`).
|
|
140
|
+
- `onModalCreated`: (function) A callback function to execute when the modal is created (default: `null`).
|
|
141
|
+
- `onFullscreenToggled`: (function) A callback function to execute when the fullscreen mode is toggled (default: `null`).
|
|
142
|
+
- `onModalClosed`: (function) A callback function to execute when the modal is closed (default: `null`).
|
|
143
|
+
|
|
144
|
+
## Contributing
|
|
145
|
+
|
|
146
|
+
Contributions are welcome! If you'd like to contribute, please fork the repository and submit a pull request with your changes or improvements. We're looking for contributions in the following areas:
|
|
147
|
+
|
|
148
|
+
- Bug fixes
|
|
149
|
+
- Feature enhancements
|
|
150
|
+
- Documentation improvements
|
|
151
|
+
|
|
152
|
+
Before submitting your pull request, please ensure your changes are well-documented and follow the existing coding style of the project.
|
|
153
|
+
|
|
154
|
+
## License
|
|
155
|
+
|
|
156
|
+
AvalynxModal is open-sourced software licensed under the [MIT license](LICENSE).
|
|
157
|
+
|
|
158
|
+
## Contact
|
|
159
|
+
|
|
160
|
+
If you have any questions, feature requests, or issues, please open an issue on our [GitHub repository](https://github.com/avalynx/avalynx-modal/issues) or submit a pull request.
|
|
161
|
+
|
|
162
|
+
Thank you for considering AvalynxModal for your project!
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AvalynxModal
|
|
3
|
+
*
|
|
4
|
+
* AvalynxModal is a simple modal system for web applications with fullscreen support. Based on Bootstrap >=5.3 without any framework dependencies.
|
|
5
|
+
*
|
|
6
|
+
* @version 0.0.1
|
|
7
|
+
* @license MIT
|
|
8
|
+
* @author https://github.com/avalynx/avalynx-modal/graphs/contributors
|
|
9
|
+
* @website https://github.com/avalynx/
|
|
10
|
+
* @repository https://github.com/avalynx/avalynx-modal.git
|
|
11
|
+
* @bugs https://github.com/avalynx/avalynx-modal/issues
|
|
12
|
+
*
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
.avalynx-modal .avalynx-modal-dialog {
|
|
16
|
+
max-width: 95%;
|
|
17
|
+
max-height: 95%;
|
|
18
|
+
width: 800px;
|
|
19
|
+
height: 600px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.avalynx-modal .avalynx-modal-btn-fullscreen {
|
|
23
|
+
--avalynx-modal-btn-fullscreen-bg: url("data:image/svg+xml,%3c!--!Font%20Awesome%20Free%206.5.2%20by%20%40fontawesome%20-%20https%3a//fontawesome.com%20License%20-%20https%3a//fontawesome.com/license/free%20Copyright%202024%20Fonticons%2c%20Inc.--%3e%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath d='M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM96 96H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H96c-17.7 0-32-14.3-32-32s14.3-32 32-32z' fill='%23000'/%3e%3c/svg%3e");
|
|
24
|
+
background: transparent var(--avalynx-modal-btn-fullscreen-bg) center/2em auto no-repeat;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.avalynx-modal .avalynx-modal-dialog .modal-fullscreen .avalynx-modal-btn-fullscreen {
|
|
28
|
+
--avalynx-modal-btn-fullscreen-bg: url("data:image/svg+xml,%3c!--!Font%20Awesome%20Free%206.5.2%20by%20%40fontawesome%20-%20https%3a//fontawesome.com%20License%20-%20https%3a//fontawesome.com/license/free%20Copyright%202024%20Fonticons%2c%20Inc.--%3e%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath d='M32 416c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H32z'/%3e%3c/svg%3e");
|
|
29
|
+
background: transparent var(--avalynx-modal-btn-fullscreen-bg) center/2em auto no-repeat;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.avalynx-modal .avalynx-modal-btn-fullscreen:focus,
|
|
33
|
+
.avalynx-modal .avalynx-modal-btn-close:focus {
|
|
34
|
+
box-shadow: none;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.avalynx-modal .avalynx-modal-btn-close {
|
|
38
|
+
--avalynx-modal-btn-close-bg: url("data:image/svg+xml,%3c!--!Font%20Awesome%20Free%206.5.2%20by%20%40fontawesome%20-%20https%3a//fontawesome.com%20License%20-%20https%3a//fontawesome.com/license/free%20Copyright%202024%20Fonticons%2c%20Inc.--%3e%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath d='M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM175 175c9.4-9.4 24.6-9.4 33.9 0l47 47 47-47c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-47 47 47 47c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0l-47-47-47 47c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l47-47-47-47c-9.4-9.4-9.4-24.6 0-33.9z'/%3e%3c/svg%3e");
|
|
39
|
+
background: transparent var(--avalynx-modal-btn-close-bg) center/2em auto no-repeat;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.avalynx-modal .modal-header .btn-close {
|
|
43
|
+
margin-left: calc(0.5 * var(--bs-modal-header-padding-y));
|
|
44
|
+
margin-right: 0rem;
|
|
45
|
+
}
|
|
46
|
+
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AvalynxModal
|
|
3
|
+
*
|
|
4
|
+
* AvalynxModal is a simple modal system for web applications with fullscreen support. Based on Bootstrap >=5.3 without any framework dependencies.
|
|
5
|
+
*
|
|
6
|
+
* @version 0.0.1
|
|
7
|
+
* @license MIT
|
|
8
|
+
* @author https://github.com/avalynx/avalynx-modal/graphs/contributors
|
|
9
|
+
* @website https://github.com/avalynx/
|
|
10
|
+
* @repository https://github.com/avalynx/avalynx-modal.git
|
|
11
|
+
* @bugs https://github.com/avalynx/avalynx-modal/issues
|
|
12
|
+
*
|
|
13
|
+
* @param {string} id - The ID of the element to attach the modal to.
|
|
14
|
+
* @param {object} options - An object containing the following keys:
|
|
15
|
+
* @param {boolean} options.modalFullscreen - Enable fullscreen mode (default: false).
|
|
16
|
+
* @param {string} options.title - The title of the modal (default: '').
|
|
17
|
+
* @param {boolean} options.titleIsHtml - Treat the title as HTML (default: false).
|
|
18
|
+
* @param {string} options.body - The body content of the modal (default: '').
|
|
19
|
+
* @param {boolean} options.bodyIsHtml - Treat the body content as HTML (default: false).
|
|
20
|
+
* @param {string} options.bodyAjaxUrl - URL to fetch the body content from (default: '').
|
|
21
|
+
* @param {array} options.buttons - An array of button objects. Each object should have a `label`, `class`, and `onClick` function (default: []).
|
|
22
|
+
* @param {boolean} options.safeInstance - Keep the modal instance after it's closed (default: false).
|
|
23
|
+
* @param {boolean} options.disableFullscreen - Disable the fullscreen button (default: false).
|
|
24
|
+
* @param {boolean} options.disableClose - Disable the close button (default: false).
|
|
25
|
+
* @param {boolean} options.removeFullscreenBtn - Remove the fullscreen button (default: false).
|
|
26
|
+
* @param {boolean} options.removeCloseBtn - Remove the close button (default: false).
|
|
27
|
+
* @param {object} options.loader - An instance of AvalynxLoader to use as the loader for the modal (default: null).
|
|
28
|
+
* @param {function} options.onModalCreated - A callback function to execute when the modal is created (default: null).
|
|
29
|
+
* @param {function} options.onFullscreenToggled - A callback function to execute when the fullscreen mode is toggled (default: null).
|
|
30
|
+
* @param {function} options.onModalClosed - A callback function to execute when the modal is closed (default: null).
|
|
31
|
+
*
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import * as bootstrap from 'bootstrap';
|
|
35
|
+
|
|
36
|
+
export class AvalynxModal {
|
|
37
|
+
constructor(id, options = {}, language = {}) {
|
|
38
|
+
this.id = id;
|
|
39
|
+
this.options = {
|
|
40
|
+
modalFullscreen: false,
|
|
41
|
+
title: '',
|
|
42
|
+
titleIsHtml: false,
|
|
43
|
+
body: '',
|
|
44
|
+
bodyIsHtml: false,
|
|
45
|
+
bodyAjaxUrl: '',
|
|
46
|
+
buttons: [],
|
|
47
|
+
safeInstance: false,
|
|
48
|
+
disableFullscreen: false,
|
|
49
|
+
disableClose: false,
|
|
50
|
+
removeFullscreenBtn: false,
|
|
51
|
+
removeCloseBtn: false,
|
|
52
|
+
loader: null,
|
|
53
|
+
onModalCreated: null,
|
|
54
|
+
onFullscreenToggled: null,
|
|
55
|
+
onModalClosed: null,
|
|
56
|
+
...options
|
|
57
|
+
};
|
|
58
|
+
this.language = {
|
|
59
|
+
...language
|
|
60
|
+
};
|
|
61
|
+
this.init();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
init() {
|
|
65
|
+
this.ensureTemplatesExist();
|
|
66
|
+
|
|
67
|
+
const template_avalynx_modal = document.getElementById("avalynx-modal-template").content.cloneNode(true);
|
|
68
|
+
this.modal = template_avalynx_modal.querySelector('.modal');
|
|
69
|
+
this.setTitle(this.options.title, this.options.titleIsHtml);
|
|
70
|
+
|
|
71
|
+
if (this.options.body) {
|
|
72
|
+
this.setBody(this.options.body, this.options.bodyIsHtml);
|
|
73
|
+
}
|
|
74
|
+
this.setButtons(this.options.buttons);
|
|
75
|
+
|
|
76
|
+
const fullscreenBtn = this.modal.querySelector('.avalynx-modal-btn-fullscreen');
|
|
77
|
+
if (fullscreenBtn) {
|
|
78
|
+
if (this.options.removeFullscreenBtn) {
|
|
79
|
+
fullscreenBtn.remove();
|
|
80
|
+
} else if (this.options.disableFullscreen) {
|
|
81
|
+
fullscreenBtn.disabled = true;
|
|
82
|
+
} else {
|
|
83
|
+
fullscreenBtn.addEventListener('click', () => this.toggleFullscreen(this.modal));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const closeBtn = this.modal.querySelector('.avalynx-modal-btn-close');
|
|
88
|
+
if (closeBtn) {
|
|
89
|
+
if (this.options.removeCloseBtn) {
|
|
90
|
+
closeBtn.remove();
|
|
91
|
+
} else if (this.options.disableClose) {
|
|
92
|
+
closeBtn.disabled = true;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
document.body.appendChild(this.modal);
|
|
97
|
+
|
|
98
|
+
this.modalInstance = new bootstrap.Modal(this.modal, {
|
|
99
|
+
backdrop: 'static',
|
|
100
|
+
keyboard: false
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (this.options.modalFullscreen) {
|
|
104
|
+
this.toggleFullscreen(this.modal);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (this.options.onModalCreated && typeof this.options.onModalCreated === 'function') {
|
|
108
|
+
this.options.onModalCreated(this);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this.setupOverlayAndLoader();
|
|
112
|
+
|
|
113
|
+
if (this.options.bodyAjaxUrl) {
|
|
114
|
+
this.fetchData(this.options.bodyAjaxUrl, this.options.bodyIsHtml);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (this.options.safeInstance !== true) {
|
|
118
|
+
this.modal.addEventListener('hidden.bs.modal', () => {
|
|
119
|
+
this.modal.remove();
|
|
120
|
+
this.modalInstance = null;
|
|
121
|
+
if (this.options.onModalClosed && typeof this.options.onModalClosed === 'function') {
|
|
122
|
+
this.options.onModalClosed(this);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
} else {
|
|
126
|
+
this.modal.addEventListener('hidden.bs.modal', () => {
|
|
127
|
+
if (this.options.onModalClosed && typeof this.options.onModalClosed === 'function') {
|
|
128
|
+
this.options.onModalClosed(this);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async fetchData(url, isHtml) {
|
|
135
|
+
if (this.options.loader === null) {
|
|
136
|
+
const overlay = document.getElementById(`${this.id}-overlay`);
|
|
137
|
+
overlay.style.display = 'flex';
|
|
138
|
+
} else {
|
|
139
|
+
this.options.loader.load = true;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const response = await fetch(url);
|
|
144
|
+
const data = await response.text();
|
|
145
|
+
this.setBody(data, isHtml);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error('Error:', error);
|
|
148
|
+
} finally {
|
|
149
|
+
if (this.options.loader === null) {
|
|
150
|
+
const overlay = document.getElementById(`${this.id}-overlay`);
|
|
151
|
+
if (overlay) {
|
|
152
|
+
overlay.style.display = 'none';
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
this.options.loader.load = false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
ensureTemplatesExist() {
|
|
161
|
+
this.addTemplateIfMissing("avalynx-modal-template", `
|
|
162
|
+
<div class="avalynx-modal modal fade" tabindex="-1" id="${this.id}" aria-hidden="true">
|
|
163
|
+
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable avalynx-modal-dialog">
|
|
164
|
+
<div class="modal-content">
|
|
165
|
+
<div class="modal-header">
|
|
166
|
+
<h5 class="modal-title"></h5>
|
|
167
|
+
<div class="flex-grow-1"></div>
|
|
168
|
+
<button type="button" class="btn-close avalynx-modal-btn-fullscreen" aria-label="Fullscreen"></button>
|
|
169
|
+
<button type="button" class="btn-close avalynx-modal-btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
170
|
+
</div>
|
|
171
|
+
<div class="modal-body" id="${this.id}-body"></div>
|
|
172
|
+
<div class="modal-footer"></div>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
addTemplateIfMissing(id, content) {
|
|
180
|
+
if (!document.getElementById(id)) {
|
|
181
|
+
const template = document.createElement('template');
|
|
182
|
+
template.id = id;
|
|
183
|
+
template.innerHTML = content;
|
|
184
|
+
document.body.appendChild(template);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
toggleFullscreen(modal) {
|
|
189
|
+
const dialog = modal.querySelector('.modal-dialog');
|
|
190
|
+
dialog.classList.toggle('modal-fullscreen');
|
|
191
|
+
dialog.classList.toggle('avalynx-modal-dialog');
|
|
192
|
+
if (this.options.onFullscreenToggled && typeof this.options.onFullscreenToggled === 'function') {
|
|
193
|
+
this.options.onFullscreenToggled(this);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
setTitle(content, isHtml = this.options.titleIsHtml) {
|
|
198
|
+
const modalTitleElement = this.modal.querySelector('.modal-title');
|
|
199
|
+
if (this.options.titleIsHtml) {
|
|
200
|
+
modalTitleElement.innerHTML = content;
|
|
201
|
+
} else {
|
|
202
|
+
modalTitleElement.innerText = content;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
setBody(content, isHtml = this.options.bodyIsHtml) {
|
|
207
|
+
const modalBodyElement = this.modal.querySelector('.modal-body');
|
|
208
|
+
if (this.options.bodyIsHtml) {
|
|
209
|
+
modalBodyElement.innerHTML = content;
|
|
210
|
+
} else {
|
|
211
|
+
modalBodyElement.innerText = content;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
setButtons(buttons) {
|
|
216
|
+
const modalFooter = this.modal.querySelector('.modal-footer');
|
|
217
|
+
modalFooter.innerHTML = '';
|
|
218
|
+
buttons.forEach(button => {
|
|
219
|
+
const btn = document.createElement('button');
|
|
220
|
+
btn.className = `btn ${button.class}`;
|
|
221
|
+
btn.innerText = button.label;
|
|
222
|
+
if (button.onClick) {
|
|
223
|
+
btn.addEventListener('click', button.onClick);
|
|
224
|
+
}
|
|
225
|
+
modalFooter.appendChild(btn);
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
setLoader(loader) {
|
|
230
|
+
this.options.loader = loader;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
open() {
|
|
234
|
+
this.modalInstance.show();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
close() {
|
|
238
|
+
if (this.modalInstance) {
|
|
239
|
+
this.modalInstance.hide();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
setupOverlayAndLoader() {
|
|
244
|
+
if (this.options.loader === null) {
|
|
245
|
+
const overlay = document.createElement('div');
|
|
246
|
+
overlay.id = `${this.id}-overlay`;
|
|
247
|
+
overlay.style.position = 'absolute';
|
|
248
|
+
overlay.style.top = 0;
|
|
249
|
+
overlay.style.left = 0;
|
|
250
|
+
overlay.style.width = '100%';
|
|
251
|
+
overlay.style.height = '100%';
|
|
252
|
+
overlay.style.display = 'none';
|
|
253
|
+
overlay.style.alignItems = 'center';
|
|
254
|
+
overlay.style.justifyContent = 'center';
|
|
255
|
+
overlay.style.backgroundColor = 'rgba(var(--bs-body-bg-rgb, 0, 0, 0), 0.7)';
|
|
256
|
+
overlay.style.zIndex = '1000';
|
|
257
|
+
|
|
258
|
+
const spinner = document.createElement('div');
|
|
259
|
+
spinner.className = 'spinner-border text-primary';
|
|
260
|
+
spinner.role = 'status';
|
|
261
|
+
spinner.innerHTML = '<span class="visually-hidden">Loading...</span>';
|
|
262
|
+
|
|
263
|
+
overlay.appendChild(spinner);
|
|
264
|
+
this.modal.querySelector('.modal-body').appendChild(overlay);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AvalynxModal
|
|
3
|
+
*
|
|
4
|
+
* AvalynxModal is a simple modal system for web applications with fullscreen support. Based on Bootstrap >=5.3 without any framework dependencies.
|
|
5
|
+
*
|
|
6
|
+
* @version 0.0.1
|
|
7
|
+
* @license MIT
|
|
8
|
+
* @author https://github.com/avalynx/avalynx-modal/graphs/contributors
|
|
9
|
+
* @website https://github.com/avalynx/
|
|
10
|
+
* @repository https://github.com/avalynx/avalynx-modal.git
|
|
11
|
+
* @bugs https://github.com/avalynx/avalynx-modal/issues
|
|
12
|
+
*
|
|
13
|
+
* @param {string} id - The ID of the element to attach the modal to.
|
|
14
|
+
* @param {object} options - An object containing the following keys:
|
|
15
|
+
* @param {boolean} options.modalFullscreen - Enable fullscreen mode (default: false).
|
|
16
|
+
* @param {string} options.title - The title of the modal (default: '').
|
|
17
|
+
* @param {boolean} options.titleIsHtml - Treat the title as HTML (default: false).
|
|
18
|
+
* @param {string} options.body - The body content of the modal (default: '').
|
|
19
|
+
* @param {boolean} options.bodyIsHtml - Treat the body content as HTML (default: false).
|
|
20
|
+
* @param {string} options.bodyAjaxUrl - URL to fetch the body content from (default: '').
|
|
21
|
+
* @param {array} options.buttons - An array of button objects. Each object should have a `label`, `class`, and `onClick` function (default: []).
|
|
22
|
+
* @param {boolean} options.safeInstance - Keep the modal instance after it's closed (default: false).
|
|
23
|
+
* @param {boolean} options.disableFullscreen - Disable the fullscreen button (default: false).
|
|
24
|
+
* @param {boolean} options.disableClose - Disable the close button (default: false).
|
|
25
|
+
* @param {boolean} options.removeFullscreenBtn - Remove the fullscreen button (default: false).
|
|
26
|
+
* @param {boolean} options.removeCloseBtn - Remove the close button (default: false).
|
|
27
|
+
* @param {object} options.loader - An instance of AvalynxLoader to use as the loader for the modal (default: null).
|
|
28
|
+
* @param {function} options.onModalCreated - A callback function to execute when the modal is created (default: null).
|
|
29
|
+
* @param {function} options.onFullscreenToggled - A callback function to execute when the fullscreen mode is toggled (default: null).
|
|
30
|
+
* @param {function} options.onModalClosed - A callback function to execute when the modal is closed (default: null).
|
|
31
|
+
*
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
class AvalynxModal {
|
|
35
|
+
constructor(id, options = {}, language = {}) {
|
|
36
|
+
this.id = id;
|
|
37
|
+
this.options = {
|
|
38
|
+
modalFullscreen: false,
|
|
39
|
+
title: '',
|
|
40
|
+
titleIsHtml: false,
|
|
41
|
+
body: '',
|
|
42
|
+
bodyIsHtml: false,
|
|
43
|
+
bodyAjaxUrl: '',
|
|
44
|
+
buttons: [],
|
|
45
|
+
safeInstance: false,
|
|
46
|
+
disableFullscreen: false,
|
|
47
|
+
disableClose: false,
|
|
48
|
+
removeFullscreenBtn: false,
|
|
49
|
+
removeCloseBtn: false,
|
|
50
|
+
loader: null,
|
|
51
|
+
onModalCreated: null,
|
|
52
|
+
onFullscreenToggled: null,
|
|
53
|
+
onModalClosed: null,
|
|
54
|
+
...options
|
|
55
|
+
};
|
|
56
|
+
this.language = {
|
|
57
|
+
...language
|
|
58
|
+
};
|
|
59
|
+
this.init();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
init() {
|
|
63
|
+
this.ensureTemplatesExist();
|
|
64
|
+
|
|
65
|
+
const template_avalynx_modal = document.getElementById("avalynx-modal-template").content.cloneNode(true);
|
|
66
|
+
this.modal = template_avalynx_modal.querySelector('.modal');
|
|
67
|
+
this.setTitle(this.options.title, this.options.titleIsHtml);
|
|
68
|
+
|
|
69
|
+
if (this.options.body) {
|
|
70
|
+
this.setBody(this.options.body, this.options.bodyIsHtml);
|
|
71
|
+
}
|
|
72
|
+
this.setButtons(this.options.buttons);
|
|
73
|
+
|
|
74
|
+
const fullscreenBtn = this.modal.querySelector('.avalynx-modal-btn-fullscreen');
|
|
75
|
+
if (fullscreenBtn) {
|
|
76
|
+
if (this.options.removeFullscreenBtn) {
|
|
77
|
+
fullscreenBtn.remove();
|
|
78
|
+
} else if (this.options.disableFullscreen) {
|
|
79
|
+
fullscreenBtn.disabled = true;
|
|
80
|
+
} else {
|
|
81
|
+
fullscreenBtn.addEventListener('click', () => this.toggleFullscreen(this.modal));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const closeBtn = this.modal.querySelector('.avalynx-modal-btn-close');
|
|
86
|
+
if (closeBtn) {
|
|
87
|
+
if (this.options.removeCloseBtn) {
|
|
88
|
+
closeBtn.remove();
|
|
89
|
+
} else if (this.options.disableClose) {
|
|
90
|
+
closeBtn.disabled = true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
document.body.appendChild(this.modal);
|
|
95
|
+
|
|
96
|
+
this.modalInstance = new bootstrap.Modal(this.modal, {
|
|
97
|
+
backdrop: 'static',
|
|
98
|
+
keyboard: false
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (this.options.modalFullscreen) {
|
|
102
|
+
this.toggleFullscreen(this.modal);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (this.options.onModalCreated && typeof this.options.onModalCreated === 'function') {
|
|
106
|
+
this.options.onModalCreated(this);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
this.setupOverlayAndLoader();
|
|
110
|
+
|
|
111
|
+
if (this.options.bodyAjaxUrl) {
|
|
112
|
+
this.fetchData(this.options.bodyAjaxUrl, this.options.bodyIsHtml);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (this.options.safeInstance !== true) {
|
|
116
|
+
this.modal.addEventListener('hidden.bs.modal', () => {
|
|
117
|
+
this.modal.remove();
|
|
118
|
+
this.modalInstance = null;
|
|
119
|
+
if (this.options.onModalClosed && typeof this.options.onModalClosed === 'function') {
|
|
120
|
+
this.options.onModalClosed(this);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
} else {
|
|
124
|
+
this.modal.addEventListener('hidden.bs.modal', () => {
|
|
125
|
+
if (this.options.onModalClosed && typeof this.options.onModalClosed === 'function') {
|
|
126
|
+
this.options.onModalClosed(this);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async fetchData(url, isHtml) {
|
|
133
|
+
if (this.options.loader === null) {
|
|
134
|
+
const overlay = document.getElementById(`${this.id}-overlay`);
|
|
135
|
+
overlay.style.display = 'flex';
|
|
136
|
+
} else {
|
|
137
|
+
this.options.loader.load = true;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
const response = await fetch(url);
|
|
142
|
+
const data = await response.text();
|
|
143
|
+
this.setBody(data, isHtml);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error('Error:', error);
|
|
146
|
+
} finally {
|
|
147
|
+
if (this.options.loader === null) {
|
|
148
|
+
const overlay = document.getElementById(`${this.id}-overlay`);
|
|
149
|
+
if (overlay) {
|
|
150
|
+
overlay.style.display = 'none';
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
this.options.loader.load = false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
ensureTemplatesExist() {
|
|
159
|
+
this.addTemplateIfMissing("avalynx-modal-template", `
|
|
160
|
+
<div class="avalynx-modal modal fade" tabindex="-1" id="${this.id}" aria-hidden="true">
|
|
161
|
+
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable avalynx-modal-dialog">
|
|
162
|
+
<div class="modal-content">
|
|
163
|
+
<div class="modal-header">
|
|
164
|
+
<h5 class="modal-title"></h5>
|
|
165
|
+
<div class="flex-grow-1"></div>
|
|
166
|
+
<button type="button" class="btn-close avalynx-modal-btn-fullscreen" aria-label="Fullscreen"></button>
|
|
167
|
+
<button type="button" class="btn-close avalynx-modal-btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
168
|
+
</div>
|
|
169
|
+
<div class="modal-body" id="${this.id}-body"></div>
|
|
170
|
+
<div class="modal-footer"></div>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
addTemplateIfMissing(id, content) {
|
|
178
|
+
if (!document.getElementById(id)) {
|
|
179
|
+
const template = document.createElement('template');
|
|
180
|
+
template.id = id;
|
|
181
|
+
template.innerHTML = content;
|
|
182
|
+
document.body.appendChild(template);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
toggleFullscreen(modal) {
|
|
187
|
+
const dialog = modal.querySelector('.modal-dialog');
|
|
188
|
+
dialog.classList.toggle('modal-fullscreen');
|
|
189
|
+
dialog.classList.toggle('avalynx-modal-dialog');
|
|
190
|
+
if (this.options.onFullscreenToggled && typeof this.options.onFullscreenToggled === 'function') {
|
|
191
|
+
this.options.onFullscreenToggled(this);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
setTitle(content, isHtml = this.options.titleIsHtml) {
|
|
196
|
+
const modalTitleElement = this.modal.querySelector('.modal-title');
|
|
197
|
+
if (this.options.titleIsHtml) {
|
|
198
|
+
modalTitleElement.innerHTML = content;
|
|
199
|
+
} else {
|
|
200
|
+
modalTitleElement.innerText = content;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
setBody(content, isHtml = this.options.bodyIsHtml) {
|
|
205
|
+
const modalBodyElement = this.modal.querySelector('.modal-body');
|
|
206
|
+
if (this.options.bodyIsHtml) {
|
|
207
|
+
modalBodyElement.innerHTML = content;
|
|
208
|
+
} else {
|
|
209
|
+
modalBodyElement.innerText = content;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
setButtons(buttons) {
|
|
214
|
+
const modalFooter = this.modal.querySelector('.modal-footer');
|
|
215
|
+
modalFooter.innerHTML = '';
|
|
216
|
+
buttons.forEach(button => {
|
|
217
|
+
const btn = document.createElement('button');
|
|
218
|
+
btn.className = `btn ${button.class}`;
|
|
219
|
+
btn.innerText = button.label;
|
|
220
|
+
if (button.onClick) {
|
|
221
|
+
btn.addEventListener('click', button.onClick);
|
|
222
|
+
}
|
|
223
|
+
modalFooter.appendChild(btn);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
setLoader(loader) {
|
|
228
|
+
this.options.loader = loader;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
open() {
|
|
232
|
+
this.modalInstance.show();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
close() {
|
|
236
|
+
if (this.modalInstance) {
|
|
237
|
+
this.modalInstance.hide();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
setupOverlayAndLoader() {
|
|
242
|
+
if (this.options.loader === null) {
|
|
243
|
+
const overlay = document.createElement('div');
|
|
244
|
+
overlay.id = `${this.id}-overlay`;
|
|
245
|
+
overlay.style.position = 'absolute';
|
|
246
|
+
overlay.style.top = 0;
|
|
247
|
+
overlay.style.left = 0;
|
|
248
|
+
overlay.style.width = '100%';
|
|
249
|
+
overlay.style.height = '100%';
|
|
250
|
+
overlay.style.display = 'none';
|
|
251
|
+
overlay.style.alignItems = 'center';
|
|
252
|
+
overlay.style.justifyContent = 'center';
|
|
253
|
+
overlay.style.backgroundColor = 'rgba(var(--bs-body-bg-rgb, 0, 0, 0), 0.7)';
|
|
254
|
+
overlay.style.zIndex = '1000';
|
|
255
|
+
|
|
256
|
+
const spinner = document.createElement('div');
|
|
257
|
+
spinner.className = 'spinner-border text-primary';
|
|
258
|
+
spinner.role = 'status';
|
|
259
|
+
spinner.innerHTML = '<span class="visually-hidden">Loading...</span>';
|
|
260
|
+
|
|
261
|
+
overlay.appendChild(spinner);
|
|
262
|
+
this.modal.querySelector('.modal-body').appendChild(overlay);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "avalynx-modal",
|
|
3
|
+
"title": "AvalynxModal",
|
|
4
|
+
"description": "AvalynxModal is a simple modal system for web applications with fullscreen support. Based on Bootstrap >=5.3 without any framework dependencies.",
|
|
5
|
+
"version": "0.0.1",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "dist/js/avalynx-modal.js",
|
|
8
|
+
"module": "dist/js/avalynx-modal.esm.js",
|
|
9
|
+
"style": "dist/css/avalynx-modal.css",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/css/avalynx-modal.css",
|
|
12
|
+
"dist/js/avalynx-modal.js",
|
|
13
|
+
"dist/js/avalynx-modal.esm.js"
|
|
14
|
+
],
|
|
15
|
+
"keywords": [
|
|
16
|
+
"avalynx",
|
|
17
|
+
"modal",
|
|
18
|
+
"bootstrap",
|
|
19
|
+
"bootstrap5",
|
|
20
|
+
"component",
|
|
21
|
+
"popup"
|
|
22
|
+
],
|
|
23
|
+
"ignore": [
|
|
24
|
+
"package.json"
|
|
25
|
+
],
|
|
26
|
+
"author": {
|
|
27
|
+
"name": "Juergen Schwind an the Avalynx-Modal authors",
|
|
28
|
+
"url": "https://github.com/avalynx/avalynx-modal/graphs/contributors"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/avalynx/",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/avalynx/avalynx-modal.git"
|
|
34
|
+
},
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/avalynx/avalynx-modal/issues"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"test": "jest"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"bootstrap": ">=5.3"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@babel/core": "^7",
|
|
46
|
+
"@babel/preset-env": "^7",
|
|
47
|
+
"babel-jest": "^29",
|
|
48
|
+
"jest": "^29",
|
|
49
|
+
"jest-environment-jsdom": "^29",
|
|
50
|
+
"@testing-library/jest-dom": "^6"
|
|
51
|
+
}
|
|
52
|
+
}
|