@nuralyui/modal 0.0.3 → 0.0.4
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/bundle.js +1363 -0
- package/index.d.ts +8 -0
- package/index.js +8 -0
- package/index.js.map +1 -1
- package/modal-manager.d.ts +72 -0
- package/modal-manager.js +171 -0
- package/modal-manager.js.map +1 -0
- package/modal.component.d.ts +117 -18
- package/modal.component.js +403 -157
- package/modal.component.js.map +1 -1
- package/modal.style.d.ts +9 -0
- package/modal.style.js +369 -0
- package/modal.style.js.map +1 -0
- package/modal.types.d.ts +105 -0
- package/modal.types.js +93 -0
- package/modal.types.js.map +1 -0
- package/package.json +16 -2
- package/react.d.ts +8 -2
- package/react.js +9 -5
- package/react.js.map +1 -1
- package/demo/modal-demo.d.ts +0 -20
- package/demo/modal-demo.d.ts.map +0 -1
- package/demo/modal-demo.js +0 -104
- package/demo/modal-demo.js.map +0 -1
- package/index.d.ts.map +0 -1
- package/modal.component.d.ts.map +0 -1
- package/react.d.ts.map +0 -1
package/index.d.ts
CHANGED
|
@@ -1,2 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2023 Nuraly, Laabidi Aymen
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
1
6
|
export * from './modal.component.js';
|
|
7
|
+
export * from './modal.types.js';
|
|
8
|
+
export * from './modal-manager.js';
|
|
9
|
+
export * from './controllers/index.js';
|
|
2
10
|
//# sourceMappingURL=index.d.ts.map
|
package/index.js
CHANGED
|
@@ -1,2 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2023 Nuraly, Laabidi Aymen
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
1
6
|
export * from './modal.component.js';
|
|
7
|
+
export * from './modal.types.js';
|
|
8
|
+
export * from './modal-manager.js';
|
|
9
|
+
export * from './controllers/index.js';
|
|
2
10
|
//# sourceMappingURL=index.js.map
|
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/modal/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC","sourcesContent":["
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/modal/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2023 Nuraly, Laabidi Aymen\n * SPDX-License-Identifier: MIT\n */\n\nexport * from './modal.component.js';\nexport * from './modal.types.js';\nexport * from './modal-manager.js';\nexport * from './controllers/index.js';\n"]}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2023 Nuraly, Laabidi Aymen
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Modal stack item interface
|
|
8
|
+
*/
|
|
9
|
+
export interface ModalStackItem {
|
|
10
|
+
modal: any;
|
|
11
|
+
previousFocus: Element | null;
|
|
12
|
+
zIndex: number;
|
|
13
|
+
id: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Modal Manager - Singleton class to handle nested modals
|
|
17
|
+
* Manages the modal stack, z-index allocation, and focus management
|
|
18
|
+
*/
|
|
19
|
+
declare class ModalManagerClass {
|
|
20
|
+
private modalStack;
|
|
21
|
+
private baseZIndex;
|
|
22
|
+
private zIndexIncrement;
|
|
23
|
+
private bodyScrollDisabled;
|
|
24
|
+
private originalBodyOverflow;
|
|
25
|
+
/**
|
|
26
|
+
* Opens a modal and adds it to the stack
|
|
27
|
+
*/
|
|
28
|
+
openModal(modal: any): number;
|
|
29
|
+
/**
|
|
30
|
+
* Closes a modal and removes it from the stack
|
|
31
|
+
*/
|
|
32
|
+
closeModal(modal: any): void;
|
|
33
|
+
/**
|
|
34
|
+
* Gets the current z-index for a modal
|
|
35
|
+
*/
|
|
36
|
+
getModalZIndex(modal: any): number;
|
|
37
|
+
/**
|
|
38
|
+
* Checks if a modal is the top modal in the stack
|
|
39
|
+
*/
|
|
40
|
+
isTopModal(modal: any): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Gets the number of open modals
|
|
43
|
+
*/
|
|
44
|
+
getStackDepth(): number;
|
|
45
|
+
/**
|
|
46
|
+
* Closes all modals
|
|
47
|
+
*/
|
|
48
|
+
closeAllModals(): void;
|
|
49
|
+
/**
|
|
50
|
+
* Gets all open modal IDs
|
|
51
|
+
*/
|
|
52
|
+
getOpenModalIds(): string[];
|
|
53
|
+
/**
|
|
54
|
+
* Checks if any modals are open
|
|
55
|
+
*/
|
|
56
|
+
hasOpenModals(): boolean;
|
|
57
|
+
private generateModalId;
|
|
58
|
+
private updateModalZIndex;
|
|
59
|
+
private disableBodyScroll;
|
|
60
|
+
private restoreBodyScroll;
|
|
61
|
+
/**
|
|
62
|
+
* Handle Escape key for nested modals - only close the top modal
|
|
63
|
+
*/
|
|
64
|
+
handleEscapeKey(): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Handle backdrop clicks - only close if it's the top modal
|
|
67
|
+
*/
|
|
68
|
+
handleBackdropClick(modal: any): boolean;
|
|
69
|
+
}
|
|
70
|
+
export declare const ModalManager: ModalManagerClass;
|
|
71
|
+
export {};
|
|
72
|
+
//# sourceMappingURL=modal-manager.d.ts.map
|
package/modal-manager.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2023 Nuraly, Laabidi Aymen
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Modal Manager - Singleton class to handle nested modals
|
|
8
|
+
* Manages the modal stack, z-index allocation, and focus management
|
|
9
|
+
*/
|
|
10
|
+
class ModalManagerClass {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.modalStack = [];
|
|
13
|
+
this.baseZIndex = 1000;
|
|
14
|
+
this.zIndexIncrement = 10;
|
|
15
|
+
this.bodyScrollDisabled = false;
|
|
16
|
+
this.originalBodyOverflow = '';
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Opens a modal and adds it to the stack
|
|
20
|
+
*/
|
|
21
|
+
openModal(modal) {
|
|
22
|
+
const id = this.generateModalId();
|
|
23
|
+
const zIndex = this.baseZIndex + (this.modalStack.length * this.zIndexIncrement);
|
|
24
|
+
// Store the currently focused element
|
|
25
|
+
const previousFocus = document.activeElement;
|
|
26
|
+
// Add to stack
|
|
27
|
+
const stackItem = {
|
|
28
|
+
modal,
|
|
29
|
+
previousFocus,
|
|
30
|
+
zIndex,
|
|
31
|
+
id
|
|
32
|
+
};
|
|
33
|
+
this.modalStack.push(stackItem);
|
|
34
|
+
// Disable body scroll for the first modal
|
|
35
|
+
if (this.modalStack.length === 1) {
|
|
36
|
+
this.disableBodyScroll();
|
|
37
|
+
}
|
|
38
|
+
// Update modal z-index
|
|
39
|
+
this.updateModalZIndex(modal, zIndex);
|
|
40
|
+
console.log(`Modal opened. Stack depth: ${this.modalStack.length}, Z-Index: ${zIndex}`);
|
|
41
|
+
return zIndex;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Closes a modal and removes it from the stack
|
|
45
|
+
*/
|
|
46
|
+
closeModal(modal) {
|
|
47
|
+
const index = this.modalStack.findIndex(item => item.modal === modal);
|
|
48
|
+
if (index === -1) {
|
|
49
|
+
console.warn('Attempting to close a modal that is not in the stack');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const stackItem = this.modalStack[index];
|
|
53
|
+
// If this is not the top modal, close all modals above it first
|
|
54
|
+
if (index < this.modalStack.length - 1) {
|
|
55
|
+
const modalsToClose = this.modalStack.slice(index + 1);
|
|
56
|
+
modalsToClose.forEach(item => {
|
|
57
|
+
if (item.modal && typeof item.modal.closeModal === 'function') {
|
|
58
|
+
item.modal.closeModal();
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
// Remove from stack
|
|
63
|
+
this.modalStack.splice(index);
|
|
64
|
+
// Restore focus to the previous element
|
|
65
|
+
if (stackItem.previousFocus instanceof HTMLElement) {
|
|
66
|
+
// Use setTimeout to ensure the modal DOM changes are complete
|
|
67
|
+
setTimeout(() => {
|
|
68
|
+
if (stackItem.previousFocus instanceof HTMLElement) {
|
|
69
|
+
stackItem.previousFocus.focus();
|
|
70
|
+
}
|
|
71
|
+
}, 100);
|
|
72
|
+
}
|
|
73
|
+
// If this was the last modal, restore body scroll
|
|
74
|
+
if (this.modalStack.length === 0) {
|
|
75
|
+
this.restoreBodyScroll();
|
|
76
|
+
}
|
|
77
|
+
console.log(`Modal closed. Stack depth: ${this.modalStack.length}`);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Gets the current z-index for a modal
|
|
81
|
+
*/
|
|
82
|
+
getModalZIndex(modal) {
|
|
83
|
+
const stackItem = this.modalStack.find(item => item.modal === modal);
|
|
84
|
+
return stackItem ? stackItem.zIndex : this.baseZIndex;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Checks if a modal is the top modal in the stack
|
|
88
|
+
*/
|
|
89
|
+
isTopModal(modal) {
|
|
90
|
+
if (this.modalStack.length === 0)
|
|
91
|
+
return false;
|
|
92
|
+
return this.modalStack[this.modalStack.length - 1].modal === modal;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Gets the number of open modals
|
|
96
|
+
*/
|
|
97
|
+
getStackDepth() {
|
|
98
|
+
return this.modalStack.length;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Closes all modals
|
|
102
|
+
*/
|
|
103
|
+
closeAllModals() {
|
|
104
|
+
// Close from top to bottom to maintain proper order
|
|
105
|
+
const modalsToClose = [...this.modalStack].reverse();
|
|
106
|
+
modalsToClose.forEach(item => {
|
|
107
|
+
if (item.modal && typeof item.modal.closeModal === 'function') {
|
|
108
|
+
item.modal.closeModal();
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Gets all open modal IDs
|
|
114
|
+
*/
|
|
115
|
+
getOpenModalIds() {
|
|
116
|
+
return this.modalStack.map(item => item.id);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Checks if any modals are open
|
|
120
|
+
*/
|
|
121
|
+
hasOpenModals() {
|
|
122
|
+
return this.modalStack.length > 0;
|
|
123
|
+
}
|
|
124
|
+
generateModalId() {
|
|
125
|
+
return `modal-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
126
|
+
}
|
|
127
|
+
updateModalZIndex(modal, zIndex) {
|
|
128
|
+
if (modal && modal.style) {
|
|
129
|
+
modal.style.setProperty('--nuraly-z-modal-backdrop', zIndex.toString());
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
disableBodyScroll() {
|
|
133
|
+
if (!this.bodyScrollDisabled) {
|
|
134
|
+
this.originalBodyOverflow = document.body.style.overflow;
|
|
135
|
+
document.body.style.overflow = 'hidden';
|
|
136
|
+
this.bodyScrollDisabled = true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
restoreBodyScroll() {
|
|
140
|
+
if (this.bodyScrollDisabled) {
|
|
141
|
+
document.body.style.overflow = this.originalBodyOverflow;
|
|
142
|
+
this.bodyScrollDisabled = false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Handle Escape key for nested modals - only close the top modal
|
|
147
|
+
*/
|
|
148
|
+
handleEscapeKey() {
|
|
149
|
+
if (this.modalStack.length === 0)
|
|
150
|
+
return false;
|
|
151
|
+
const topModal = this.modalStack[this.modalStack.length - 1].modal;
|
|
152
|
+
if (topModal && typeof topModal.closeModal === 'function' && topModal.closable !== false) {
|
|
153
|
+
topModal.closeModal();
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Handle backdrop clicks - only close if it's the top modal
|
|
160
|
+
*/
|
|
161
|
+
handleBackdropClick(modal) {
|
|
162
|
+
return this.isTopModal(modal);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Export singleton instance
|
|
166
|
+
export const ModalManager = new ModalManagerClass();
|
|
167
|
+
// Make it available globally for debugging
|
|
168
|
+
if (typeof window !== 'undefined') {
|
|
169
|
+
window.ModalManager = ModalManager;
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=modal-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modal-manager.js","sourceRoot":"","sources":["../../../src/components/modal/modal-manager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license\n * Copyright 2023 Nuraly, Laabidi Aymen\n * SPDX-License-Identifier: MIT\n */\n\n/**\n * Modal stack item interface\n */\nexport interface ModalStackItem {\n modal: any; // The modal element\n previousFocus: Element | null; // The element that had focus before this modal\n zIndex: number; // The z-index for this modal\n id: string; // Unique identifier\n}\n\n/**\n * Modal Manager - Singleton class to handle nested modals\n * Manages the modal stack, z-index allocation, and focus management\n */\nclass ModalManagerClass {\n private modalStack: ModalStackItem[] = [];\n private baseZIndex = 1000;\n private zIndexIncrement = 10;\n private bodyScrollDisabled = false;\n private originalBodyOverflow = '';\n\n /**\n * Opens a modal and adds it to the stack\n */\n openModal(modal: any): number {\n const id = this.generateModalId();\n const zIndex = this.baseZIndex + (this.modalStack.length * this.zIndexIncrement);\n \n // Store the currently focused element\n const previousFocus = document.activeElement;\n \n // Add to stack\n const stackItem: ModalStackItem = {\n modal,\n previousFocus,\n zIndex,\n id\n };\n \n this.modalStack.push(stackItem);\n \n // Disable body scroll for the first modal\n if (this.modalStack.length === 1) {\n this.disableBodyScroll();\n }\n \n // Update modal z-index\n this.updateModalZIndex(modal, zIndex);\n \n console.log(`Modal opened. Stack depth: ${this.modalStack.length}, Z-Index: ${zIndex}`);\n \n return zIndex;\n }\n\n /**\n * Closes a modal and removes it from the stack\n */\n closeModal(modal: any): void {\n const index = this.modalStack.findIndex(item => item.modal === modal);\n \n if (index === -1) {\n console.warn('Attempting to close a modal that is not in the stack');\n return;\n }\n \n const stackItem = this.modalStack[index];\n \n // If this is not the top modal, close all modals above it first\n if (index < this.modalStack.length - 1) {\n const modalsToClose = this.modalStack.slice(index + 1);\n modalsToClose.forEach(item => {\n if (item.modal && typeof item.modal.closeModal === 'function') {\n item.modal.closeModal();\n }\n });\n }\n \n // Remove from stack\n this.modalStack.splice(index);\n \n // Restore focus to the previous element\n if (stackItem.previousFocus instanceof HTMLElement) {\n // Use setTimeout to ensure the modal DOM changes are complete\n setTimeout(() => {\n if (stackItem.previousFocus instanceof HTMLElement) {\n stackItem.previousFocus.focus();\n }\n }, 100);\n }\n \n // If this was the last modal, restore body scroll\n if (this.modalStack.length === 0) {\n this.restoreBodyScroll();\n }\n \n console.log(`Modal closed. Stack depth: ${this.modalStack.length}`);\n }\n\n /**\n * Gets the current z-index for a modal\n */\n getModalZIndex(modal: any): number {\n const stackItem = this.modalStack.find(item => item.modal === modal);\n return stackItem ? stackItem.zIndex : this.baseZIndex;\n }\n\n /**\n * Checks if a modal is the top modal in the stack\n */\n isTopModal(modal: any): boolean {\n if (this.modalStack.length === 0) return false;\n return this.modalStack[this.modalStack.length - 1].modal === modal;\n }\n\n /**\n * Gets the number of open modals\n */\n getStackDepth(): number {\n return this.modalStack.length;\n }\n\n /**\n * Closes all modals\n */\n closeAllModals(): void {\n // Close from top to bottom to maintain proper order\n const modalsToClose = [...this.modalStack].reverse();\n modalsToClose.forEach(item => {\n if (item.modal && typeof item.modal.closeModal === 'function') {\n item.modal.closeModal();\n }\n });\n }\n\n /**\n * Gets all open modal IDs\n */\n getOpenModalIds(): string[] {\n return this.modalStack.map(item => item.id);\n }\n\n /**\n * Checks if any modals are open\n */\n hasOpenModals(): boolean {\n return this.modalStack.length > 0;\n }\n\n private generateModalId(): string {\n return `modal-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n }\n\n private updateModalZIndex(modal: any, zIndex: number): void {\n if (modal && modal.style) {\n modal.style.setProperty('--nuraly-z-modal-backdrop', zIndex.toString());\n }\n }\n\n private disableBodyScroll(): void {\n if (!this.bodyScrollDisabled) {\n this.originalBodyOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n this.bodyScrollDisabled = true;\n }\n }\n\n private restoreBodyScroll(): void {\n if (this.bodyScrollDisabled) {\n document.body.style.overflow = this.originalBodyOverflow;\n this.bodyScrollDisabled = false;\n }\n }\n\n /**\n * Handle Escape key for nested modals - only close the top modal\n */\n handleEscapeKey(): boolean {\n if (this.modalStack.length === 0) return false;\n \n const topModal = this.modalStack[this.modalStack.length - 1].modal;\n if (topModal && typeof topModal.closeModal === 'function' && topModal.closable !== false) {\n topModal.closeModal();\n return true;\n }\n \n return false;\n }\n\n /**\n * Handle backdrop clicks - only close if it's the top modal\n */\n handleBackdropClick(modal: any): boolean {\n return this.isTopModal(modal);\n }\n}\n\n// Export singleton instance\nexport const ModalManager = new ModalManagerClass();\n\n// Make it available globally for debugging\nif (typeof window !== 'undefined') {\n (window as any).ModalManager = ModalManager;\n}"]}
|
package/modal.component.d.ts
CHANGED
|
@@ -1,24 +1,123 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2023 Nuraly, Laabidi Aymen
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { LitElement, nothing, PropertyValues } from 'lit';
|
|
7
|
+
import { ModalSize, ModalPosition, ModalAnimation, ModalBackdrop } from './modal.types.js';
|
|
8
|
+
import '../icon/icon.component.js';
|
|
9
|
+
import '../button/button.component.js';
|
|
10
|
+
import { ModalDragHost, ModalKeyboardHost } from './controllers/index.js';
|
|
11
|
+
declare const NrModalElement_base: (new (...args: any[]) => import("../../shared/dependency-mixin.js").DependencyAware) & (new (...args: any[]) => import("../../shared/theme-mixin.js").ThemeAware) & (new (...args: any[]) => import("../../shared/event-handler-mixin.js").EventHandlerCapable) & typeof LitElement;
|
|
12
|
+
/**
|
|
13
|
+
* Versatile modal component with multiple sizes, animations, and enhanced functionality.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```html
|
|
17
|
+
* <!-- Simple usage -->
|
|
18
|
+
* <nr-modal open title="My Modal">
|
|
19
|
+
* <p>Modal content goes here</p>
|
|
20
|
+
* </nr-modal>
|
|
21
|
+
*
|
|
22
|
+
* <!-- With custom configuration -->
|
|
23
|
+
* <nr-modal
|
|
24
|
+
* open
|
|
25
|
+
* size="large"
|
|
26
|
+
* position="top"
|
|
27
|
+
* animation="zoom"
|
|
28
|
+
* backdrop="static"
|
|
29
|
+
* draggable>
|
|
30
|
+
* <div slot="header">
|
|
31
|
+
* <nr-icon name="info"></nr-icon>
|
|
32
|
+
* <span>Custom Header</span>
|
|
33
|
+
* </div>
|
|
34
|
+
* <p>Modal content</p>
|
|
35
|
+
* <div slot="footer">
|
|
36
|
+
* <nr-button type="secondary">Cancel</nr-button>
|
|
37
|
+
* <nr-button type="primary">OK</nr-button>
|
|
38
|
+
* </div>
|
|
39
|
+
* </nr-modal>
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @fires modal-open - Modal opened
|
|
43
|
+
* @fires modal-close - Modal closed
|
|
44
|
+
* @fires modal-before-close - Before modal closes (cancelable)
|
|
45
|
+
* @fires modal-after-open - After modal opens
|
|
46
|
+
* @fires modal-escape - Escape key pressed
|
|
47
|
+
*
|
|
48
|
+
* @slot default - Modal body content
|
|
49
|
+
* @slot header - Custom header content
|
|
50
|
+
* @slot footer - Custom footer content
|
|
51
|
+
*/
|
|
52
|
+
export declare class NrModalElement extends NrModalElement_base implements ModalDragHost, ModalKeyboardHost {
|
|
3
53
|
static styles: import("lit").CSSResult;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
54
|
+
/** Whether the modal is open */
|
|
55
|
+
open: boolean;
|
|
56
|
+
/** Modal size (small, medium, large, xl) */
|
|
57
|
+
size: ModalSize;
|
|
58
|
+
/** Modal position (center, top, bottom) */
|
|
59
|
+
position: ModalPosition;
|
|
60
|
+
/** Animation type */
|
|
61
|
+
animation: ModalAnimation;
|
|
62
|
+
/** Backdrop behavior */
|
|
63
|
+
backdrop: ModalBackdrop;
|
|
64
|
+
/** Whether the modal can be closed */
|
|
65
|
+
closable: boolean;
|
|
66
|
+
/** Whether the modal can be dragged */
|
|
67
|
+
modalDraggable: boolean;
|
|
68
|
+
/** Whether the modal is resizable */
|
|
69
|
+
resizable: boolean;
|
|
70
|
+
/** Whether the modal is fullscreen */
|
|
71
|
+
fullscreen: boolean;
|
|
72
|
+
/** Modal title */
|
|
73
|
+
modalTitle: string;
|
|
74
|
+
/** Show close button in header */
|
|
75
|
+
showCloseButton: boolean;
|
|
76
|
+
/** Header icon */
|
|
77
|
+
headerIcon: string;
|
|
78
|
+
/** Z-index for the modal */
|
|
79
|
+
zIndex: number;
|
|
80
|
+
/** Custom width */
|
|
81
|
+
width: string;
|
|
82
|
+
/** Custom height */
|
|
83
|
+
height: string;
|
|
84
|
+
/** Dragging state */
|
|
85
|
+
isDragging: boolean;
|
|
86
|
+
/** Current X offset for dragging */
|
|
87
|
+
offsetX: number;
|
|
88
|
+
/** Current Y offset for dragging */
|
|
89
|
+
offsetY: number;
|
|
90
|
+
/** Animation state */
|
|
91
|
+
private animationState;
|
|
92
|
+
/** Previous focus element */
|
|
93
|
+
private previousActiveElement;
|
|
94
|
+
requiredComponents: string[];
|
|
95
|
+
private dragController;
|
|
96
|
+
private keyboardController;
|
|
11
97
|
connectedCallback(): void;
|
|
12
98
|
disconnectedCallback(): void;
|
|
13
|
-
|
|
14
|
-
private
|
|
15
|
-
private
|
|
16
|
-
private
|
|
17
|
-
private
|
|
99
|
+
willUpdate(changedProperties: PropertyValues): void;
|
|
100
|
+
private handleOpen;
|
|
101
|
+
private startOpenAnimation;
|
|
102
|
+
private getAnimationKeyframes;
|
|
103
|
+
private handleClose;
|
|
104
|
+
/**
|
|
105
|
+
* Opens the modal
|
|
106
|
+
*/
|
|
107
|
+
openModal(): void;
|
|
108
|
+
/**
|
|
109
|
+
* Closes the modal
|
|
110
|
+
*/
|
|
18
111
|
closeModal(): void;
|
|
19
|
-
private
|
|
20
|
-
|
|
21
|
-
private
|
|
22
|
-
|
|
112
|
+
private handleBackdropClick;
|
|
113
|
+
private getBackdropClasses;
|
|
114
|
+
private getModalClasses;
|
|
115
|
+
private getModalStyles;
|
|
116
|
+
private renderHeader;
|
|
117
|
+
private renderFooter;
|
|
118
|
+
updated(): void;
|
|
119
|
+
private updateDataTheme;
|
|
120
|
+
render(): import("lit").TemplateResult<1> | typeof nothing;
|
|
23
121
|
}
|
|
122
|
+
export {};
|
|
24
123
|
//# sourceMappingURL=modal.component.d.ts.map
|