@liwe3/webcomponents 1.0.14 → 1.1.10
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/dist/AIMarkdownEditor.d.ts +35 -0
- package/dist/AIMarkdownEditor.d.ts.map +1 -0
- package/dist/AIMarkdownEditor.js +412 -0
- package/dist/AIMarkdownEditor.js.map +1 -0
- package/dist/AITextEditor.d.ts +183 -0
- package/dist/AITextEditor.d.ts.map +1 -0
- package/dist/AITextEditor.js +63 -27
- package/dist/AITextEditor.js.map +1 -1
- package/dist/ButtonToolbar.d.ts +35 -0
- package/dist/ButtonToolbar.d.ts.map +1 -0
- package/dist/ButtonToolbar.js +220 -0
- package/dist/ButtonToolbar.js.map +1 -0
- package/dist/CheckList.d.ts +31 -0
- package/dist/CheckList.d.ts.map +1 -0
- package/dist/CheckList.js +336 -0
- package/dist/CheckList.js.map +1 -0
- package/dist/ChunkUploader.d.ts +125 -0
- package/dist/ChunkUploader.d.ts.map +1 -0
- package/dist/ChunkUploader.js +756 -0
- package/dist/ChunkUploader.js.map +1 -0
- package/dist/ComicBalloon.d.ts +82 -0
- package/dist/ComicBalloon.d.ts.map +1 -0
- package/dist/ComicBalloon.js +346 -0
- package/dist/ComicBalloon.js.map +1 -0
- package/dist/ContainerBox.d.ts +112 -0
- package/dist/ContainerBox.d.ts.map +1 -0
- package/dist/ContainerBox.js +359 -0
- package/dist/ContainerBox.js.map +1 -0
- package/dist/DateSelector.d.ts +103 -0
- package/dist/DateSelector.d.ts.map +1 -0
- package/dist/Dialog.d.ts +102 -0
- package/dist/Dialog.d.ts.map +1 -0
- package/dist/Dialog.js +299 -0
- package/dist/Dialog.js.map +1 -0
- package/dist/Drawer.d.ts +63 -0
- package/dist/Drawer.d.ts.map +1 -0
- package/dist/Drawer.js +340 -0
- package/dist/Drawer.js.map +1 -0
- package/dist/ImageView.d.ts +42 -0
- package/dist/ImageView.d.ts.map +1 -0
- package/dist/ImageView.js +209 -0
- package/dist/ImageView.js.map +1 -0
- package/dist/MarkdownPreview.d.ts +25 -0
- package/dist/MarkdownPreview.d.ts.map +1 -0
- package/dist/MarkdownPreview.js +147 -0
- package/dist/MarkdownPreview.js.map +1 -0
- package/dist/PopoverMenu.d.ts +103 -0
- package/dist/PopoverMenu.d.ts.map +1 -0
- package/dist/ResizableCropper.d.ts +158 -0
- package/dist/ResizableCropper.d.ts.map +1 -0
- package/dist/ResizableCropper.js +562 -0
- package/dist/ResizableCropper.js.map +1 -0
- package/dist/SmartSelect.d.ts +100 -0
- package/dist/SmartSelect.d.ts.map +1 -0
- package/dist/SmartSelect.js +45 -2
- package/dist/SmartSelect.js.map +1 -1
- package/dist/Toast.d.ts +127 -0
- package/dist/Toast.d.ts.map +1 -0
- package/dist/Toast.js +79 -49
- package/dist/Toast.js.map +1 -1
- package/dist/TreeView.d.ts +84 -0
- package/dist/TreeView.d.ts.map +1 -0
- package/dist/TreeView.js +478 -0
- package/dist/TreeView.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -14
- package/dist/index.js.map +1 -1
- package/package.json +60 -5
- package/src/AIMarkdownEditor.ts +568 -0
- package/src/AITextEditor.ts +97 -2
- package/src/ButtonToolbar.ts +302 -0
- package/src/CheckList.ts +438 -0
- package/src/ChunkUploader.ts +1135 -0
- package/src/ComicBalloon.ts +709 -0
- package/src/ContainerBox.ts +570 -0
- package/src/Dialog.ts +510 -0
- package/src/Drawer.ts +435 -0
- package/src/ImageView.ts +265 -0
- package/src/MarkdownPreview.ts +213 -0
- package/src/ResizableCropper.ts +1099 -0
- package/src/SmartSelect.ts +48 -2
- package/src/Toast.ts +96 -32
- package/src/TreeView.ts +673 -0
- package/src/index.ts +129 -27
package/src/Dialog.ts
ADDED
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dialog Web Component
|
|
3
|
+
* A customizable dialog with HTML body, custom buttons, and modal support
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type DialogButton = {
|
|
7
|
+
label : string;
|
|
8
|
+
backgroundColor? : string;
|
|
9
|
+
onclick : ( dialog? : DialogElement ) => void;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type DialogConfig = {
|
|
13
|
+
title? : string; // Default: "Dialog"
|
|
14
|
+
body : string; // HTML content
|
|
15
|
+
buttons? : DialogButton[];
|
|
16
|
+
modal? : boolean; // If true, dims background and prevents interaction outside dialog
|
|
17
|
+
escToClose? : boolean; // If true, Esc key closes dialog
|
|
18
|
+
clickToClose? : boolean; // If true, clicking outside closes dialog (like cancel)
|
|
19
|
+
fxAppear? : 'none' | 'fade' | 'slide'; // Animation effect when dialog appears (default: 'none')
|
|
20
|
+
fxSpeed? : number; // Animation duration in milliseconds (default: 1000)
|
|
21
|
+
onclose? : () => void;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export class DialogElement extends HTMLElement {
|
|
25
|
+
declare shadowRoot : ShadowRoot;
|
|
26
|
+
private config : DialogConfig = {
|
|
27
|
+
title: 'Dialog',
|
|
28
|
+
body: '',
|
|
29
|
+
modal: true,
|
|
30
|
+
escToClose: true,
|
|
31
|
+
clickToClose: true,
|
|
32
|
+
fxAppear: 'none',
|
|
33
|
+
fxSpeed: 1000,
|
|
34
|
+
};
|
|
35
|
+
private backdrop? : HTMLElement;
|
|
36
|
+
private escKeyHandler? : ( e : KeyboardEvent ) => void;
|
|
37
|
+
private eventsBound : boolean = false;
|
|
38
|
+
|
|
39
|
+
constructor () {
|
|
40
|
+
super();
|
|
41
|
+
this.attachShadow( { mode: 'open' } );
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
connectedCallback () : void {
|
|
45
|
+
this.render();
|
|
46
|
+
this.setupKeyboardListeners();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
disconnectedCallback () : void {
|
|
50
|
+
this.removeKeyboardListeners();
|
|
51
|
+
if ( this.backdrop ) {
|
|
52
|
+
this.backdrop.remove();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Shows the dialog with the given configuration
|
|
58
|
+
*/
|
|
59
|
+
show ( config : DialogConfig ) : void {
|
|
60
|
+
this.config = { ...this.config, ...config };
|
|
61
|
+
this.render();
|
|
62
|
+
this.setupKeyboardListeners();
|
|
63
|
+
|
|
64
|
+
// Create backdrop if modal is enabled
|
|
65
|
+
if ( this.config.modal ) {
|
|
66
|
+
this.createBackdrop();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Add opening animation class
|
|
70
|
+
requestAnimationFrame( () => {
|
|
71
|
+
requestAnimationFrame( () => {
|
|
72
|
+
const dialog = this.shadowRoot.querySelector( '.dialog-container' ) as HTMLElement;
|
|
73
|
+
if ( dialog ) {
|
|
74
|
+
dialog.classList.add( 'show' );
|
|
75
|
+
}
|
|
76
|
+
if ( this.backdrop ) {
|
|
77
|
+
// Use inline style instead of class since backdrop is outside shadow DOM
|
|
78
|
+
this.backdrop.style.opacity = '1';
|
|
79
|
+
}
|
|
80
|
+
} );
|
|
81
|
+
} );
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Closes the dialog
|
|
86
|
+
*/
|
|
87
|
+
close () : void {
|
|
88
|
+
const dialog = this.shadowRoot.querySelector( '.dialog-container' ) as HTMLElement;
|
|
89
|
+
|
|
90
|
+
// Add closing animation
|
|
91
|
+
if ( dialog ) {
|
|
92
|
+
dialog.classList.add( 'closing' );
|
|
93
|
+
}
|
|
94
|
+
if ( this.backdrop ) {
|
|
95
|
+
this.backdrop.remove();
|
|
96
|
+
this.backdrop = undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Wait for animation to complete
|
|
100
|
+
const animationDuration = this.config.fxAppear === 'none' ? 0 : ( this.config.fxSpeed || 1000 );
|
|
101
|
+
setTimeout( () => {
|
|
102
|
+
this.removeKeyboardListeners();
|
|
103
|
+
|
|
104
|
+
this.dispatchEvent( new CustomEvent( 'close' ) );
|
|
105
|
+
if ( this.config.onclose ) {
|
|
106
|
+
this.config.onclose();
|
|
107
|
+
}
|
|
108
|
+
this.remove();
|
|
109
|
+
}, animationDuration );
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Creates the modal backdrop
|
|
114
|
+
*/
|
|
115
|
+
private createBackdrop () : void {
|
|
116
|
+
if ( this.backdrop ) return;
|
|
117
|
+
|
|
118
|
+
this.backdrop = document.createElement( 'div' );
|
|
119
|
+
this.backdrop.className = 'dialog-backdrop';
|
|
120
|
+
this.backdrop.style.position = 'fixed';
|
|
121
|
+
this.backdrop.style.top = '0';
|
|
122
|
+
this.backdrop.style.left = '0';
|
|
123
|
+
this.backdrop.style.width = '100%';
|
|
124
|
+
this.backdrop.style.height = '100%';
|
|
125
|
+
this.backdrop.style.backgroundColor = 'rgba(0, 0, 0, 0.6)';
|
|
126
|
+
this.backdrop.style.backdropFilter = 'blur(4px)';
|
|
127
|
+
( this.backdrop.style as any ).webkitBackdropFilter = 'blur(4px)'; // Safari support
|
|
128
|
+
this.backdrop.style.zIndex = '99998';
|
|
129
|
+
this.backdrop.style.opacity = '0';
|
|
130
|
+
this.backdrop.style.transition = 'opacity 0.3s ease';
|
|
131
|
+
|
|
132
|
+
// Handle click outside to close
|
|
133
|
+
if ( this.config.clickToClose ) {
|
|
134
|
+
this.backdrop.addEventListener( 'click', () => {
|
|
135
|
+
this.close();
|
|
136
|
+
} );
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
document.body.appendChild( this.backdrop );
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Sets up keyboard event listeners
|
|
144
|
+
*/
|
|
145
|
+
private setupKeyboardListeners () : void {
|
|
146
|
+
if ( this.config.escToClose ) {
|
|
147
|
+
this.escKeyHandler = ( e : KeyboardEvent ) => {
|
|
148
|
+
if ( e.key === 'Escape' ) {
|
|
149
|
+
this.close();
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
document.addEventListener( 'keydown', this.escKeyHandler );
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Removes keyboard event listeners
|
|
158
|
+
*/
|
|
159
|
+
private removeKeyboardListeners () : void {
|
|
160
|
+
if ( this.escKeyHandler ) {
|
|
161
|
+
document.removeEventListener( 'keydown', this.escKeyHandler );
|
|
162
|
+
this.escKeyHandler = undefined;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Binds all event listeners
|
|
168
|
+
*/
|
|
169
|
+
private bindEvents () : void {
|
|
170
|
+
// Only bind events once to prevent duplicate event listeners
|
|
171
|
+
if ( this.eventsBound ) return;
|
|
172
|
+
this.eventsBound = true;
|
|
173
|
+
|
|
174
|
+
// Handle button clicks
|
|
175
|
+
this.shadowRoot.addEventListener( 'click', ( e ) => {
|
|
176
|
+
const target = e.target as HTMLElement;
|
|
177
|
+
|
|
178
|
+
if ( target.closest( '.close-button' ) ) {
|
|
179
|
+
this.close();
|
|
180
|
+
} else if ( target.closest( '.dialog-button' ) ) {
|
|
181
|
+
const buttonIndex = ( target.closest( '.dialog-button' ) as HTMLElement ).dataset.index;
|
|
182
|
+
if ( buttonIndex !== undefined ) {
|
|
183
|
+
const button = this.config.buttons?.[parseInt( buttonIndex, 10 )];
|
|
184
|
+
if ( button && button.onclick ) {
|
|
185
|
+
button.onclick( this );
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} );
|
|
190
|
+
|
|
191
|
+
// Prevent scroll propagation to body when scrolling within dialog
|
|
192
|
+
const dialogContainer = this.shadowRoot.querySelector( '.dialog-container' ) as HTMLElement;
|
|
193
|
+
if ( dialogContainer ) {
|
|
194
|
+
dialogContainer.addEventListener( 'wheel', ( e ) => {
|
|
195
|
+
e.stopPropagation();
|
|
196
|
+
}, { passive: true } );
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Renders the component
|
|
202
|
+
*/
|
|
203
|
+
private render () : void {
|
|
204
|
+
const title = this.config.title || 'Dialog';
|
|
205
|
+
const buttons = this.config.buttons || [];
|
|
206
|
+
const animationSpeed = this.config.fxSpeed || 1000;
|
|
207
|
+
const animationDuration = `${animationSpeed}ms`;
|
|
208
|
+
const fxClass = this.config.fxAppear === 'fade'
|
|
209
|
+
? 'fx-fade'
|
|
210
|
+
: this.config.fxAppear === 'slide'
|
|
211
|
+
? 'fx-slide'
|
|
212
|
+
: '';
|
|
213
|
+
|
|
214
|
+
// Determine button layout:
|
|
215
|
+
// - If only 1 button, put it on the right
|
|
216
|
+
// - If 2+ buttons, put first on left, rest on right
|
|
217
|
+
const firstButton = buttons.length > 1 ? buttons[0] : null;
|
|
218
|
+
const rightButtons = buttons.length === 1 ? buttons : buttons.slice( 1 );
|
|
219
|
+
|
|
220
|
+
this.shadowRoot.innerHTML = `
|
|
221
|
+
<style>
|
|
222
|
+
:host {
|
|
223
|
+
display: block;
|
|
224
|
+
font-family: var(--font-family, var(--liwe3-font-family, Ubuntu, sans-serif));
|
|
225
|
+
font-size: var(--font-size, 14px);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.dialog-container {
|
|
229
|
+
position: fixed;
|
|
230
|
+
top: 50%;
|
|
231
|
+
left: 50%;
|
|
232
|
+
transform: translate(-50%, -50%);
|
|
233
|
+
min-width: 400px;
|
|
234
|
+
max-width: 600px;
|
|
235
|
+
max-height: 80vh;
|
|
236
|
+
background: var(--dialog-background, var(--liwe3-surface-raised, white));
|
|
237
|
+
border-radius: var(--dialog-border-radius, var(--liwe3-border-radius, 8px));
|
|
238
|
+
box-shadow: var(--dialog-shadow, 0 10px 40px rgba(0, 0, 0, 0.2));
|
|
239
|
+
z-index: 99999;
|
|
240
|
+
display: flex;
|
|
241
|
+
flex-direction: column;
|
|
242
|
+
opacity: 1;
|
|
243
|
+
font-family: var(--font-family, var(--liwe3-font-family, Ubuntu, sans-serif));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/* Fade animation */
|
|
247
|
+
.dialog-container.fx-fade {
|
|
248
|
+
opacity: 0;
|
|
249
|
+
transition: opacity ${animationDuration} ease;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.dialog-container.fx-fade.show {
|
|
253
|
+
opacity: 1;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.dialog-container.fx-fade.closing {
|
|
257
|
+
opacity: 0;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/* Slide animation */
|
|
261
|
+
.dialog-container.fx-slide {
|
|
262
|
+
transform: translate(-50%, -100%);
|
|
263
|
+
opacity: 0;
|
|
264
|
+
transition: transform ${animationDuration} cubic-bezier(0.16, 1, 0.3, 1), opacity ${animationDuration} ease;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.dialog-container.fx-slide.show {
|
|
268
|
+
transform: translate(-50%, -50%);
|
|
269
|
+
opacity: 1;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.dialog-container.fx-slide.closing {
|
|
273
|
+
transform: translate(-50%, -100%);
|
|
274
|
+
opacity: 0;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.dialog-header {
|
|
278
|
+
padding: 0;
|
|
279
|
+
border-bottom: none;
|
|
280
|
+
flex-shrink: 0;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.dialog-title {
|
|
284
|
+
margin: 0;
|
|
285
|
+
padding: 20px 24px;
|
|
286
|
+
font-size: 20px;
|
|
287
|
+
font-weight: 600;
|
|
288
|
+
color: var(--dialog-title-color, var(--liwe3-text-inverse, white));
|
|
289
|
+
background: var(--dialog-title-background, linear-gradient(135deg,
|
|
290
|
+
var(--liwe3-mode1-500, #667eea),
|
|
291
|
+
var(--liwe3-mode4-500, #9f7aea)
|
|
292
|
+
));
|
|
293
|
+
border-bottom: 1px solid var(--dialog-border-color, var(--liwe3-border-default, #e0e0e0));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.dialog-body {
|
|
297
|
+
padding: 24px;
|
|
298
|
+
overflow-y: auto;
|
|
299
|
+
flex: 1;
|
|
300
|
+
color: var(--dialog-text-color, var(--liwe3-text-mode2, #555));
|
|
301
|
+
line-height: 1.6;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.dialog-footer {
|
|
305
|
+
padding: 16px 24px;
|
|
306
|
+
border-top: 1px solid var(--dialog-border-color, var(--liwe3-border-default, #e0e0e0));
|
|
307
|
+
display: flex;
|
|
308
|
+
justify-content: space-between;
|
|
309
|
+
align-items: center;
|
|
310
|
+
gap: 12px;
|
|
311
|
+
flex-shrink: 0;
|
|
312
|
+
background: var(--dialog-background, var(--liwe3-surface-raised, white));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.footer-left {
|
|
316
|
+
display: flex;
|
|
317
|
+
gap: 12px;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.footer-right {
|
|
321
|
+
display: flex;
|
|
322
|
+
gap: 12px;
|
|
323
|
+
margin-left: auto;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.dialog-button {
|
|
327
|
+
padding: 8px 20px;
|
|
328
|
+
border: 1px solid var(--dialog-button-border-color, var(--liwe3-border-default, #ccc));
|
|
329
|
+
border-radius: var(--dialog-button-border-radius, var(--liwe3-border-radius, 4px));
|
|
330
|
+
background: var(--dialog-button-background, var(--liwe3-surface-raised, white));
|
|
331
|
+
color: var(--dialog-button-color, var(--liwe3-text-mode1, #333));
|
|
332
|
+
font-size: 14px;
|
|
333
|
+
cursor: pointer;
|
|
334
|
+
transition: all 0.2s;
|
|
335
|
+
font-family: inherit;
|
|
336
|
+
font-weight: 500;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.dialog-button:hover {
|
|
340
|
+
background-color: var(--dialog-button-hover-background, var(--liwe3-hover-overlay, #f8f9fa));
|
|
341
|
+
border-color: var(--dialog-button-hover-border-color, var(--liwe3-border-strong, #999));
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.dialog-button:active {
|
|
345
|
+
background-color: var(--dialog-button-active-background, var(--liwe3-active-overlay, #e9ecef));
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.close-button {
|
|
349
|
+
padding: 8px 20px;
|
|
350
|
+
border: 1px solid var(--dialog-close-border-color, var(--liwe3-border-default, #ccc));
|
|
351
|
+
border-radius: var(--dialog-button-border-radius, var(--liwe3-border-radius, 4px));
|
|
352
|
+
background: var(--dialog-close-background, var(--liwe3-surface-raised, white));
|
|
353
|
+
color: var(--dialog-close-color, var(--liwe3-text-mode1, #333));
|
|
354
|
+
font-size: 14px;
|
|
355
|
+
cursor: pointer;
|
|
356
|
+
transition: all 0.2s;
|
|
357
|
+
font-family: inherit;
|
|
358
|
+
font-weight: 500;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.close-button:hover {
|
|
362
|
+
background-color: var(--dialog-close-hover-background, var(--liwe3-hover-overlay, #f8f9fa));
|
|
363
|
+
border-color: var(--dialog-close-hover-border-color, var(--liwe3-border-strong, #999));
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
@media (max-width: 768px) {
|
|
367
|
+
.dialog-container {
|
|
368
|
+
min-width: 90vw;
|
|
369
|
+
max-width: 90vw;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
</style>
|
|
373
|
+
|
|
374
|
+
<div class="dialog-container ${fxClass}">
|
|
375
|
+
<div class="dialog-header">
|
|
376
|
+
<h2 class="dialog-title">${title}</h2>
|
|
377
|
+
</div>
|
|
378
|
+
|
|
379
|
+
<div class="dialog-body">
|
|
380
|
+
${this.config.body}
|
|
381
|
+
</div>
|
|
382
|
+
|
|
383
|
+
<div class="dialog-footer">
|
|
384
|
+
${
|
|
385
|
+
firstButton
|
|
386
|
+
? `
|
|
387
|
+
<div class="footer-left">
|
|
388
|
+
<button
|
|
389
|
+
class="dialog-button"
|
|
390
|
+
data-index="0"
|
|
391
|
+
style="${firstButton.backgroundColor ? `background-color: ${firstButton.backgroundColor}; color: white; border-color: ${firstButton.backgroundColor};` : ''}"
|
|
392
|
+
>
|
|
393
|
+
${firstButton.label}
|
|
394
|
+
</button>
|
|
395
|
+
</div>
|
|
396
|
+
`
|
|
397
|
+
: ''
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
<div class="footer-right">
|
|
401
|
+
${
|
|
402
|
+
rightButtons.map( ( button, index ) => `
|
|
403
|
+
<button
|
|
404
|
+
class="dialog-button"
|
|
405
|
+
data-index="${firstButton ? index + 1 : index}"
|
|
406
|
+
style="${button.backgroundColor ? `background-color: ${button.backgroundColor}; color: white; border-color: ${button.backgroundColor};` : ''}"
|
|
407
|
+
>
|
|
408
|
+
${button.label}
|
|
409
|
+
</button>
|
|
410
|
+
` ).join( '' )
|
|
411
|
+
}
|
|
412
|
+
${buttons.length === 0 ? '<button class="close-button">Close</button>' : ''}
|
|
413
|
+
</div>
|
|
414
|
+
</div>
|
|
415
|
+
</div>
|
|
416
|
+
`;
|
|
417
|
+
|
|
418
|
+
this.bindEvents();
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Conditionally defines the custom element if in a browser environment.
|
|
424
|
+
*/
|
|
425
|
+
const defineDialog = ( tagName : string = 'liwe3-dialog' ) : void => {
|
|
426
|
+
if ( typeof window !== 'undefined' && !window.customElements.get( tagName ) ) {
|
|
427
|
+
customElements.define( tagName, DialogElement );
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
// Auto-register with default tag name
|
|
432
|
+
defineDialog();
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Container ID for dialogs
|
|
436
|
+
*/
|
|
437
|
+
const DIALOG_CONTAINER_ID = 'liwe3-dialog-container';
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Gets or creates the dialog container element
|
|
441
|
+
*/
|
|
442
|
+
const getDialogContainer = () : HTMLElement => {
|
|
443
|
+
let container = document.getElementById( DIALOG_CONTAINER_ID );
|
|
444
|
+
|
|
445
|
+
if ( !container ) {
|
|
446
|
+
container = document.createElement( 'div' );
|
|
447
|
+
container.id = DIALOG_CONTAINER_ID;
|
|
448
|
+
container.style.position = 'fixed';
|
|
449
|
+
container.style.zIndex = '99999';
|
|
450
|
+
container.style.pointerEvents = 'none';
|
|
451
|
+
document.body.appendChild( container );
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return container;
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Shows a dialog with the given configuration.
|
|
459
|
+
* This is the recommended way to display dialogs.
|
|
460
|
+
*
|
|
461
|
+
* @param config - The dialog configuration
|
|
462
|
+
* @returns The dialog element instance
|
|
463
|
+
*
|
|
464
|
+
* @example
|
|
465
|
+
* ```typescript
|
|
466
|
+
* import { dialogAdd } from '@liwe3/webcomponents';
|
|
467
|
+
*
|
|
468
|
+
* dialogAdd({
|
|
469
|
+
* title: 'Delete File',
|
|
470
|
+
* body: '<p>Are you sure you want to delete this file? This action cannot be undone.</p>',
|
|
471
|
+
* buttons: [
|
|
472
|
+
* {
|
|
473
|
+
* label: 'Delete',
|
|
474
|
+
* backgroundColor: '#dc3545',
|
|
475
|
+
* onclick: (dialog) => {
|
|
476
|
+
* console.log('File deleted');
|
|
477
|
+
* dialog.close();
|
|
478
|
+
* }
|
|
479
|
+
* },
|
|
480
|
+
* {
|
|
481
|
+
* label: 'Cancel',
|
|
482
|
+
* onclick: (dialog) => {
|
|
483
|
+
* console.log('Cancelled');
|
|
484
|
+
* dialog.close();
|
|
485
|
+
* }
|
|
486
|
+
* }
|
|
487
|
+
* ],
|
|
488
|
+
* modal: true,
|
|
489
|
+
* escToClose: true,
|
|
490
|
+
* clickToClose: true
|
|
491
|
+
* });
|
|
492
|
+
* ```
|
|
493
|
+
*/
|
|
494
|
+
const dialogAdd = ( config : DialogConfig ) : DialogElement => {
|
|
495
|
+
const container = getDialogContainer();
|
|
496
|
+
const dialog = document.createElement( 'liwe3-dialog' ) as DialogElement;
|
|
497
|
+
|
|
498
|
+
// Allow pointer events on individual dialogs
|
|
499
|
+
dialog.style.pointerEvents = 'auto';
|
|
500
|
+
|
|
501
|
+
// Show the dialog with the provided config
|
|
502
|
+
dialog.show( config );
|
|
503
|
+
|
|
504
|
+
// Add to container
|
|
505
|
+
container.appendChild( dialog );
|
|
506
|
+
|
|
507
|
+
return dialog;
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
export { defineDialog, dialogAdd };
|