flowbite-svelte 0.26.31 → 0.27.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/CHANGELOG.md +26 -0
- package/modals/Modal.svelte +85 -86
- package/modals/Modal.svelte.d.ts +1 -0
- package/package.json +1 -1
- package/utils/focusTrap.d.ts +1 -2
- package/utils/focusTrap.js +37 -50
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [0.27.1](https://github.com/themesberg/flowbite-svelte/compare/v0.27.0...v0.27.1) (2022-09-24)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* modal block background scroll ([f3eb627](https://github.com/themesberg/flowbite-svelte/commit/f3eb62732dc899d6f1483d9f86a5c644c8d2edd2))
|
|
11
|
+
* modal internal scrolling ([7ca08ea](https://github.com/themesberg/flowbite-svelte/commit/7ca08ea939e0116241c564738ec237bd501ca32c))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* placement and padding ([169c749](https://github.com/themesberg/flowbite-svelte/commit/169c749004d16d9b7c1aef46448608beefd3caf7))
|
|
17
|
+
|
|
18
|
+
## [0.27.0](https://github.com/themesberg/flowbite-svelte/compare/v0.26.31...v0.27.0) (2022-09-24)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Features
|
|
22
|
+
|
|
23
|
+
* add server hooks to redirect component pages and add redirect.spec test ([9e2cb72](https://github.com/themesberg/flowbite-svelte/commit/9e2cb72653cb2de16f77c30d73bd69a69bd73bd5))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Bug Fixes
|
|
27
|
+
|
|
28
|
+
* add other dir ([dc5e73f](https://github.com/themesberg/flowbite-svelte/commit/dc5e73feaa44c3fc7f020967f2e342b08bafccdf))
|
|
29
|
+
* add redirect to all other directories ([6ca9ce0](https://github.com/themesberg/flowbite-svelte/commit/6ca9ce0c38c2a417fc1bcd88520e609a0e1b2b0e))
|
|
30
|
+
|
|
5
31
|
### [0.26.31](https://github.com/themesberg/flowbite-svelte/compare/v0.26.30...v0.26.31) (2022-09-23)
|
|
6
32
|
|
|
7
33
|
|
package/modals/Modal.svelte
CHANGED
|
@@ -1,61 +1,53 @@
|
|
|
1
|
-
<script>import
|
|
1
|
+
<script>import classNames from 'classnames';
|
|
2
|
+
import Frame from '../utils/Frame.svelte';
|
|
2
3
|
import { createEventDispatcher } from 'svelte';
|
|
3
4
|
import CloseButton from '../utils/CloseButton.svelte';
|
|
4
5
|
import focusTrap from '../utils/focusTrap';
|
|
5
6
|
export let open = false;
|
|
6
|
-
export let title =
|
|
7
|
+
export let title = '';
|
|
7
8
|
export let size = 'md';
|
|
8
9
|
export let placement = 'center';
|
|
9
10
|
export let autoclose = true;
|
|
10
|
-
export let backdropClasses = 'bg-gray-900 bg-opacity-50 dark:bg-opacity-80
|
|
11
|
+
export let backdropClasses = 'bg-gray-900 bg-opacity-50 dark:bg-opacity-80';
|
|
11
12
|
const dispatch = createEventDispatcher();
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
function blockEvent(e) {
|
|
14
|
+
e.preventDefault();
|
|
15
|
+
e.stopPropagation();
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
function contentInit(node) {
|
|
19
|
+
function keydownHandler(e) {
|
|
20
|
+
if (open && e.key === 'Escape')
|
|
21
|
+
return hide();
|
|
22
|
+
const target = e.target;
|
|
23
|
+
if (node.contains(target))
|
|
24
|
+
return true;
|
|
25
|
+
if (e.key === 'ArrowDown' || e.key === 'ArrowUp')
|
|
26
|
+
return blockEvent(e);
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
document.addEventListener('keydown', keydownHandler, { passive: false });
|
|
30
|
+
node.focus();
|
|
24
31
|
return {
|
|
25
|
-
update(_open) {
|
|
26
|
-
allPlacementClasses.map((c) => node.classList.remove(c));
|
|
27
|
-
getPlacementClasses().map((c) => node.classList.add(c));
|
|
28
|
-
_open ? createBackdrop(node) : destroyBackdrop(node);
|
|
29
|
-
},
|
|
30
32
|
destroy() {
|
|
31
|
-
|
|
33
|
+
document.removeEventListener('keydown', keydownHandler);
|
|
32
34
|
}
|
|
33
35
|
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const createBackdrop = (node) => {
|
|
40
|
-
if (!backdropEl) {
|
|
41
|
-
backdropEl = document.createElement('div');
|
|
42
|
-
backdropEl.classList.add(...backdropClasses.split(' '));
|
|
43
|
-
const body = document.querySelector('body');
|
|
44
|
-
body.append(backdropEl);
|
|
45
|
-
body.style.overflow = 'hidden';
|
|
46
|
-
document.addEventListener('keydown', handleEscape, true);
|
|
36
|
+
}
|
|
37
|
+
function init(node) {
|
|
38
|
+
function preventScroll(e) {
|
|
39
|
+
if (e.target === node)
|
|
40
|
+
return blockEvent(e);
|
|
47
41
|
}
|
|
42
|
+
node.addEventListener('wheel', preventScroll, { passive: false });
|
|
48
43
|
dispatch('show', node);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
document.removeEventListener('keydown', handleEscape, true);
|
|
57
|
-
dispatch('hide', node);
|
|
58
|
-
};
|
|
44
|
+
return {
|
|
45
|
+
destroy() {
|
|
46
|
+
node.removeEventListener('wheel', preventScroll);
|
|
47
|
+
dispatch('hide', node);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
59
51
|
const getPlacementClasses = () => {
|
|
60
52
|
switch (placement) {
|
|
61
53
|
// top
|
|
@@ -90,53 +82,60 @@ const sizes = {
|
|
|
90
82
|
lg: 'max-w-4xl',
|
|
91
83
|
xl: 'max-w-7xl'
|
|
92
84
|
};
|
|
93
|
-
const
|
|
94
|
-
|
|
85
|
+
const onAutoClose = (e) => {
|
|
86
|
+
const target = e.target;
|
|
87
|
+
if (autoclose && target?.tagName === 'BUTTON')
|
|
95
88
|
open = !open;
|
|
96
89
|
};
|
|
97
90
|
const hide = () => {
|
|
98
91
|
open = false;
|
|
99
92
|
};
|
|
93
|
+
let mainClass;
|
|
94
|
+
$: mainClass = classNames('flex overflow-hidden fixed top-0 right-0 left-0 z-50 w-full md:inset-0 h-modal md:h-full', backdropClasses, getPlacementClasses().join(' '), $$props.class);
|
|
100
95
|
</script>
|
|
101
96
|
|
|
102
97
|
<!-- Main modal -->
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
98
|
+
{#if open}
|
|
99
|
+
<div
|
|
100
|
+
tabindex="-1"
|
|
101
|
+
class={mainClass}
|
|
102
|
+
aria-modal="true"
|
|
103
|
+
role="dialog"
|
|
104
|
+
use:init
|
|
105
|
+
use:focusTrap
|
|
106
|
+
on:click={autoclose ? onAutoClose : null}>
|
|
107
|
+
<div class="flex p-4 w-full {sizes[size]} h-full md:h-auto max-h-screen">
|
|
108
|
+
<!-- Modal content -->
|
|
109
|
+
<Frame rounded shadow class="relative flex flex-col w-full h-full md:h-auto">
|
|
110
|
+
<!-- Modal header -->
|
|
111
|
+
{#if $$slots.header || title}
|
|
112
|
+
<div
|
|
113
|
+
class="flex justify-between items-center p-4 rounded-t border-b dark:border-gray-600">
|
|
114
|
+
<slot name="header">
|
|
115
|
+
<h3 class="text-xl font-semibold text-gray-900 dark:text-white p-0">
|
|
116
|
+
{title}
|
|
117
|
+
</h3>
|
|
118
|
+
</slot>
|
|
119
|
+
<CloseButton name="Close modal" on:click={hide} />
|
|
120
|
+
</div>
|
|
121
|
+
{:else}
|
|
122
|
+
<CloseButton name="Close modal" class="absolute top-3 right-2.5" on:click={hide} />
|
|
123
|
+
{/if}
|
|
124
|
+
<!-- Modal body -->
|
|
125
|
+
<div
|
|
126
|
+
class="p-6 space-y-6 flex-1 overflow-y-auto overscroll-contain"
|
|
127
|
+
tabindex="0"
|
|
128
|
+
use:contentInit>
|
|
129
|
+
<slot />
|
|
130
|
+
</div>
|
|
131
|
+
<!-- Modal footer -->
|
|
132
|
+
{#if $$slots.footer}
|
|
133
|
+
<div
|
|
134
|
+
class="flex items-center p-6 space-x-2 rounded-b border-t border-gray-200 dark:border-gray-600">
|
|
135
|
+
<slot name="footer" />
|
|
136
|
+
</div>
|
|
137
|
+
{/if}
|
|
138
|
+
</Frame>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
{/if}
|
package/modals/Modal.svelte.d.ts
CHANGED
package/package.json
CHANGED
package/utils/focusTrap.d.ts
CHANGED
package/utils/focusTrap.js
CHANGED
|
@@ -4,58 +4,45 @@
|
|
|
4
4
|
|
|
5
5
|
// add all the elements inside modal which you want to make focusable
|
|
6
6
|
const focusableElements =
|
|
7
|
-
|
|
7
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
|
|
8
8
|
|
|
9
9
|
function getFocusable(node) {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const focusableContent = node.querySelectorAll(focusableElements);
|
|
11
|
+
return [focusableContent[0], focusableContent[focusableContent.length - 1]];
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export default function focusTrap(node
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (_open) {
|
|
49
|
-
[firstFocusableElement, lastFocusableElement] = getFocusable(node);
|
|
50
|
-
document.addEventListener('keydown', handleFocusTrap, true);
|
|
51
|
-
firstFocusableElement.focus();
|
|
52
|
-
} else {
|
|
53
|
-
document.removeEventListener('keydown', handleFocusTrap, true);
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
destroy() {
|
|
58
|
-
document.removeEventListener('keydown', handleFocusTrap, true);
|
|
59
|
-
}
|
|
60
|
-
};
|
|
14
|
+
export default function focusTrap(node) {
|
|
15
|
+
let firstFocusableElement, lastFocusableElement; // first and last element to be focused inside modal
|
|
16
|
+
|
|
17
|
+
function handleFocusTrap(e) {
|
|
18
|
+
let isTabPressed = e.key === 'Tab' || e.keyCode === 9;
|
|
19
|
+
|
|
20
|
+
if (!isTabPressed) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (e.shiftKey) {
|
|
25
|
+
// if shift key pressed for shift + tab combination
|
|
26
|
+
if (document.activeElement === firstFocusableElement) {
|
|
27
|
+
lastFocusableElement.focus(); // add focus for the last focusable element
|
|
28
|
+
e.preventDefault();
|
|
29
|
+
}
|
|
30
|
+
} else {
|
|
31
|
+
// if tab key is pressed
|
|
32
|
+
if (document.activeElement === lastFocusableElement) {
|
|
33
|
+
// if focused has reached to last focusable element then focus first focusable element after pressing tab
|
|
34
|
+
firstFocusableElement.focus(); // add focus for the first focusable element
|
|
35
|
+
e.preventDefault();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
[firstFocusableElement, lastFocusableElement] = getFocusable(node);
|
|
41
|
+
document.addEventListener('keydown', handleFocusTrap, true);
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
destroy() {
|
|
45
|
+
document.removeEventListener('keydown', handleFocusTrap, true);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
61
48
|
}
|