PageSourceSearch

https://pupwife.com/projects/chatroom/scripts/components/Modal.js

js pupwife.com collected 2026-09-30 15:06:44 UTC 5,713 bytes, 201 lines download raw bytes

1/**
2 * Modal Component (Vencord-inspired)
3 * Reusable modal/dialog component
4 */
5
6window.ChatroomModal = (function() {
7    'use strict';
8
9    /**
10     * Create a modal
11     */
12    function create(options = {}) {
13        const {
14            title = '',
15            content = '',
16            footer = '',
17            onClose = null,
18            className = '',
19            closeOnOutsideClick = true,
20            closeOnEscape = true
21        } = options;
22
23        const overlay = document.createElement('div');
24        overlay.className = `modal-overlay ${className}`;
25
26        const modal = document.createElement('div');
27        modal.className = 'modal-container';
28
29        const header = document.createElement('div');
30        header.className = 'modal-header';
31        header.innerHTML = `
32            <h3>${title}</h3>
33            <button class="modal-close-btn">&times;</button>
34        `;
35
36        const body = document.createElement('div');
37        body.className = 'modal-body';
38        if (typeof content === 'string') {
39            body.innerHTML = content;
40        } else {
41            body.appendChild(content);
42        }
43
44        modal.appendChild(header);
45        modal.appendChild(body);
46
47        if (footer) {
48            const footerEl = document.createElement('div');
49            footerEl.className = 'modal-footer';
50            if (typeof footer === 'string') {
51                footerEl.innerHTML = footer;
52            } else {
53                footerEl.appendChild(footer);
54            }
55            modal.appendChild(footerEl);
56        }
57
58        overlay.appendChild(modal);
59
60        // Close handlers
61        const close = () => {
62            overlay.classList.remove('show');
63            setTimeout(() => {
64                overlay.remove();
65                if (onClose) onClose();
66            }, 300);
67        };
68
69        header.querySelector('.modal-close-btn').addEventListener('click', close);
70
71        if (closeOnOutsideClick) {
72            overlay.addEventListener('click', (e) => {
73                if (e.target === overlay) {
74                    close();
75                }
76            });
77        }
78
79        if (closeOnEscape) {
80            const escapeHandler = (e) => {
81                if (e.key === 'Escape') {
82                    close();
83                    document.removeEventListener('keydown', escapeHandler);
84                }
85            };
86            document.addEventListener('keydown', escapeHandler);
87        }
88
89        // Show modal
90        document.body.appendChild(overlay);
91        setTimeout(() => {
92            overlay.classList.add('show');
93        }, 10);
94
95        return {
96            element: overlay,
97            body: body,
98            close: close,
99            updateContent: (newContent) => {
100                body.innerHTML = '';
101                if (typeof newContent === 'string') {
102                    body.innerHTML = newContent;
103                } else {
104                    body.appendChild(newContent);
105                }
106            }
107        };
108    }
109
110    /**
111     * Create confirm dialog
112     */
113    function confirm(options = {}) {
114        const {
115            title = 'Confirm',
116            message = 'Are you sure?',
117            confirmText = 'Confirm',
118            cancelText = 'Cancel',
119            onConfirm = null,
120            onCancel = null,
121            danger = false
122        } = options;
123
124        return new Promise((resolve) => {
125            const footer = document.createElement('div');
126            footer.style.display = 'flex';
127            footer.style.gap = '10px';
128            footer.style.justifyContent = 'flex-end';
129
130            const cancelBtn = window.ChatroomButton.create({
131                text: cancelText,
132                variant: 'ghost',
133                onClick: () => {
134                    modal.close();
135                    if (onCancel) onCancel();
136                    resolve(false);
137                }
138            });
139
140            const confirmBtn = window.ChatroomButton.create({
141                text: confirmText,
142                variant: danger ? 'danger' : 'primary',
143                onClick: () => {
144                    modal.close();
145                    if (onConfirm) onConfirm();
146                    resolve(true);
147                }
148            });
149
150            footer.appendChild(cancelBtn);
151            footer.appendChild(confirmBtn);
152
153            const modal = create({
154                title,
155                content: `<p>${message}</p>`,
156                footer: footer,
157                className: danger ? 'modal-danger' : ''
158            });
159        });
160    }
161
162    /**
163     * Create alert dialog
164     */
165    function alert(options = {}) {
166        const {
167            title = 'Alert',
168            message = '',
169            buttonText = 'OK'
170        } = options;
171
172        return new Promise((resolve) => {
173            const footer = document.createElement('div');
174            footer.style.display = 'flex';
175            footer.style.justifyContent = 'flex-end';
176
177            const okBtn = window.ChatroomButton.create({
178                text: buttonText,
179                variant: 'primary',
180                onClick: () => {
181                    modal.close();
182                    resolve();
183                }
184            });
185
186            footer.appendChild(okBtn);
187
188            const modal = create({
189                title,
190                content: `<p>${message}</p>`,
191                footer: footer
192            });
193        });
194    }
195
196    return {
197        create,
198        confirm,
199        alert
200    };
201})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.