@doyosi/laraisy 1.0.2 → 1.0.3

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.
Files changed (51) hide show
  1. package/LICENSE +1 -1
  2. package/package.json +1 -1
  3. package/src/CodeInput.js +48 -48
  4. package/src/DSAlert.js +352 -352
  5. package/src/DSAvatar.js +207 -207
  6. package/src/DSDelete.js +274 -274
  7. package/src/DSForm.js +568 -568
  8. package/src/DSGridOrTable.js +453 -453
  9. package/src/DSLocaleSwitcher.js +239 -239
  10. package/src/DSLogout.js +293 -293
  11. package/src/DSNotifications.js +365 -365
  12. package/src/DSRestore.js +181 -181
  13. package/src/DSSelect.js +1071 -1071
  14. package/src/DSSelectBox.js +563 -563
  15. package/src/DSSimpleSlider.js +517 -517
  16. package/src/DSSvgFetch.js +69 -69
  17. package/src/DSTable/DSTableExport.js +68 -68
  18. package/src/DSTable/DSTableFilter.js +224 -224
  19. package/src/DSTable/DSTablePagination.js +136 -136
  20. package/src/DSTable/DSTableSearch.js +40 -40
  21. package/src/DSTable/DSTableSelection.js +192 -192
  22. package/src/DSTable/DSTableSort.js +58 -58
  23. package/src/DSTable.js +353 -353
  24. package/src/DSTabs.js +488 -488
  25. package/src/DSUpload.js +887 -887
  26. package/dist/CodeInput.d.ts +0 -10
  27. package/dist/DSAlert.d.ts +0 -112
  28. package/dist/DSAvatar.d.ts +0 -45
  29. package/dist/DSDelete.d.ts +0 -61
  30. package/dist/DSForm.d.ts +0 -151
  31. package/dist/DSGridOrTable/DSGOTRenderer.d.ts +0 -60
  32. package/dist/DSGridOrTable/DSGOTViewToggle.d.ts +0 -26
  33. package/dist/DSGridOrTable.d.ts +0 -296
  34. package/dist/DSLocaleSwitcher.d.ts +0 -71
  35. package/dist/DSLogout.d.ts +0 -76
  36. package/dist/DSNotifications.d.ts +0 -54
  37. package/dist/DSRestore.d.ts +0 -56
  38. package/dist/DSSelect.d.ts +0 -221
  39. package/dist/DSSelectBox.d.ts +0 -123
  40. package/dist/DSSimpleSlider.d.ts +0 -136
  41. package/dist/DSSvgFetch.d.ts +0 -17
  42. package/dist/DSTable/DSTableExport.d.ts +0 -11
  43. package/dist/DSTable/DSTableFilter.d.ts +0 -40
  44. package/dist/DSTable/DSTablePagination.d.ts +0 -12
  45. package/dist/DSTable/DSTableSearch.d.ts +0 -8
  46. package/dist/DSTable/DSTableSelection.d.ts +0 -46
  47. package/dist/DSTable/DSTableSort.d.ts +0 -8
  48. package/dist/DSTable.d.ts +0 -116
  49. package/dist/DSTabs.d.ts +0 -156
  50. package/dist/DSUpload.d.ts +0 -220
  51. package/dist/index.d.ts +0 -17
@@ -1,365 +1,365 @@
1
- /**
2
- * DSNotifications Plugin
3
- * Handles notification drawer, fetching, and actions
4
- */
5
- import { DSAlert } from './DSAlert.js';
6
-
7
- export class DSNotifications {
8
- constructor(options = {}) {
9
- this.options = {
10
- drawerToggleId: 'notification-drawer-toggle',
11
- buttonId: 'user-notification-button',
12
- listContainerId: 'notification-items',
13
- loadingId: 'notification-loading',
14
- emptyId: 'notification-empty',
15
- badgeId: 'notification-badge',
16
- topbarBadgeSelector: '#user-notification-button .indicator-item',
17
- markAllReadBtnId: 'mark-all-read-btn',
18
- templateId: 'notification-item-template',
19
- fetchUrl: '/dashboard/notifications/list',
20
- readUrl: '/dashboard/notifications/{id}/read',
21
- readAllUrl: '/dashboard/notifications/read-all',
22
- deleteUrl: '/dashboard/notifications/{id}',
23
- unreadCountUrl: '/dashboard/notifications/unread-count',
24
- refreshInterval: 60000, // 1 minute
25
- ...options
26
- };
27
-
28
- this.notifications = [];
29
- this.unreadCount = 0;
30
- this.isLoading = false;
31
- this.refreshTimer = null;
32
-
33
- this.init();
34
- }
35
-
36
- init() {
37
- this.cacheElements();
38
- this.bindEvents();
39
- this.fetchUnreadCount();
40
- this.startAutoRefresh();
41
- }
42
-
43
- cacheElements() {
44
- this.drawerToggle = document.getElementById(this.options.drawerToggleId);
45
- this.button = document.getElementById(this.options.buttonId);
46
- this.listContainer = document.getElementById(this.options.listContainerId);
47
- this.loading = document.getElementById(this.options.loadingId);
48
- this.empty = document.getElementById(this.options.emptyId);
49
- this.badge = document.getElementById(this.options.badgeId);
50
- this.topbarBadge = document.querySelector(this.options.topbarBadgeSelector);
51
- this.markAllReadBtn = document.getElementById(this.options.markAllReadBtnId);
52
- this.template = document.getElementById(this.options.templateId);
53
- }
54
-
55
- bindEvents() {
56
- // Open drawer on button click
57
- this.button?.addEventListener('click', () => {
58
- this.openDrawer();
59
- });
60
-
61
- // Mark all as read
62
- this.markAllReadBtn?.addEventListener('click', () => {
63
- this.markAllAsRead();
64
- });
65
-
66
- // Drawer open/close events
67
- this.drawerToggle?.addEventListener('change', (e) => {
68
- if (e.target.checked) {
69
- this.fetchNotifications();
70
- }
71
- });
72
- }
73
-
74
- openDrawer() {
75
- if (this.drawerToggle) {
76
- this.drawerToggle.checked = true;
77
- this.fetchNotifications();
78
- }
79
- }
80
-
81
- closeDrawer() {
82
- if (this.drawerToggle) {
83
- this.drawerToggle.checked = false;
84
- }
85
- }
86
-
87
- async fetchNotifications() {
88
- if (this.isLoading) return;
89
-
90
- this.isLoading = true;
91
- this.showLoading();
92
-
93
- try {
94
- const response = await axios.get(this.options.fetchUrl);
95
- const data = response.data;
96
-
97
- if (data.success) {
98
- this.notifications = data.data;
99
- this.unreadCount = data.unread_count;
100
- this.renderNotifications();
101
- this.updateBadge();
102
- }
103
- } catch (error) {
104
- console.error('Failed to fetch notifications:', error);
105
- this.showEmpty();
106
- } finally {
107
- this.isLoading = false;
108
- }
109
- }
110
-
111
- async fetchUnreadCount() {
112
- try {
113
- const response = await axios.get(this.options.unreadCountUrl);
114
- if (response.data.success) {
115
- this.unreadCount = response.data.count;
116
- this.updateBadge();
117
- }
118
- } catch (error) {
119
- console.error('Failed to fetch unread count:', error);
120
- }
121
- }
122
-
123
- async markAsRead(id) {
124
- try {
125
- const url = this.options.readUrl.replace('{id}', id);
126
- const response = await axios.post(url);
127
-
128
- if (response.data.success) {
129
- // Update local state
130
- const notification = this.notifications.find(n => n.id === id);
131
- if (notification) {
132
- notification.read = true;
133
- this.unreadCount = Math.max(0, this.unreadCount - 1);
134
- }
135
- this.renderNotifications();
136
- this.updateBadge();
137
-
138
- if (window.DSAlert) {
139
- DSAlert.fire({
140
- toast: true,
141
- position: 'top-end',
142
- icon: 'success',
143
- title: response.data.message,
144
- showConfirmButton: false,
145
- timer: 2000
146
- });
147
- }
148
- }
149
- } catch (error) {
150
- console.error('Failed to mark as read:', error);
151
- }
152
- }
153
-
154
- async markAllAsRead() {
155
- try {
156
- const response = await axios.post(this.options.readAllUrl);
157
-
158
- if (response.data.success) {
159
- this.notifications.forEach(n => n.read = true);
160
- this.unreadCount = 0;
161
- this.renderNotifications();
162
- this.updateBadge();
163
-
164
- if (window.DSAlert) {
165
- DSAlert.fire({
166
- toast: true,
167
- position: 'top-end',
168
- icon: 'success',
169
- title: response.data.message,
170
- showConfirmButton: false,
171
- timer: 2000
172
- });
173
- }
174
- }
175
- } catch (error) {
176
- console.error('Failed to mark all as read:', error);
177
- }
178
- }
179
-
180
- async deleteNotification(id) {
181
- try {
182
- const url = this.options.deleteUrl.replace('{id}', id);
183
- const response = await axios.delete(url);
184
-
185
- if (response.data.success) {
186
- // Remove from local state
187
- const index = this.notifications.findIndex(n => n.id === id);
188
- if (index !== -1) {
189
- const notification = this.notifications[index];
190
- if (!notification.read) {
191
- this.unreadCount = Math.max(0, this.unreadCount - 1);
192
- }
193
- this.notifications.splice(index, 1);
194
- }
195
- this.renderNotifications();
196
- this.updateBadge();
197
-
198
- if (window.DSAlert) {
199
- DSAlert.fire({
200
- toast: true,
201
- position: 'top-end',
202
- icon: 'success',
203
- title: response.data.message,
204
- showConfirmButton: false,
205
- timer: 2000
206
- });
207
- }
208
- }
209
- } catch (error) {
210
- console.error('Failed to delete notification:', error);
211
- }
212
- }
213
-
214
- renderNotifications() {
215
- this.hideLoading();
216
-
217
- if (this.notifications.length === 0) {
218
- this.showEmpty();
219
- return;
220
- }
221
-
222
- this.hideEmpty();
223
- this.listContainer.innerHTML = '';
224
-
225
- this.notifications.forEach(notification => {
226
- const item = this.createNotificationItem(notification);
227
- this.listContainer.appendChild(item);
228
- });
229
- }
230
-
231
- createNotificationItem(notification) {
232
- const template = this.template.content.cloneNode(true);
233
- const item = template.querySelector('.notification-item');
234
-
235
- item.dataset.id = notification.id;
236
-
237
- // Apply read/unread styling
238
- if (!notification.read) {
239
- item.classList.add('bg-primary/5', 'border-l-4', 'border-l-primary');
240
- }
241
-
242
- // Icon
243
- const iconWrapper = item.querySelector('.notification-icon');
244
- iconWrapper.classList.add(this.getColorClass(notification.color, 'bg'), this.getColorClass(notification.color, 'text'));
245
- item.querySelector('.notification-icon .material-symbols-outlined').textContent = notification.icon;
246
-
247
- // Content
248
- item.querySelector('.notification-title').textContent = notification.title;
249
- item.querySelector('.notification-message').textContent = notification.message;
250
- item.querySelector('.notification-time').textContent = notification.time_ago;
251
-
252
- // Hide read button if already read
253
- const readBtn = item.querySelector('.notification-read-btn');
254
- if (notification.read) {
255
- readBtn.classList.add('hidden');
256
- }
257
-
258
- // Bind events
259
- readBtn.addEventListener('click', (e) => {
260
- e.stopPropagation();
261
- this.markAsRead(notification.id);
262
- });
263
-
264
- item.querySelector('.notification-delete-btn').addEventListener('click', (e) => {
265
- e.stopPropagation();
266
- this.deleteNotification(notification.id);
267
- });
268
-
269
- // Click to navigate
270
- item.addEventListener('click', () => {
271
- if (notification.url) {
272
- if (!notification.read) {
273
- this.markAsRead(notification.id);
274
- }
275
- window.location.href = notification.url;
276
- }
277
- });
278
-
279
- return item;
280
- }
281
-
282
- getColorClass(color, type) {
283
- const colors = {
284
- primary: { bg: 'bg-primary/10', text: 'text-primary' },
285
- success: { bg: 'bg-success/10', text: 'text-success' },
286
- warning: { bg: 'bg-warning/10', text: 'text-warning' },
287
- error: { bg: 'bg-error/10', text: 'text-error' },
288
- info: { bg: 'bg-info/10', text: 'text-info' },
289
- neutral: { bg: 'bg-base-200', text: 'text-base-content' },
290
- };
291
-
292
- return colors[color]?.[type] || colors.neutral[type];
293
- }
294
-
295
- updateBadge() {
296
- // Drawer badge
297
- if (this.badge) {
298
- if (this.unreadCount > 0) {
299
- this.badge.textContent = this.unreadCount > 99 ? '99+' : this.unreadCount;
300
- this.badge.classList.remove('hidden');
301
- } else {
302
- this.badge.classList.add('hidden');
303
- }
304
- }
305
-
306
- // Topbar badge
307
- if (this.topbarBadge) {
308
- if (this.unreadCount > 0) {
309
- this.topbarBadge.classList.remove('hidden');
310
- } else {
311
- this.topbarBadge.classList.add('hidden');
312
- }
313
- }
314
- }
315
-
316
- showLoading() {
317
- this.loading?.classList.remove('hidden');
318
- this.empty?.classList.add('hidden');
319
- this.listContainer.innerHTML = '';
320
- }
321
-
322
- hideLoading() {
323
- this.loading?.classList.add('hidden');
324
- }
325
-
326
- showEmpty() {
327
- this.empty?.classList.remove('hidden');
328
- this.listContainer.innerHTML = '';
329
- }
330
-
331
- hideEmpty() {
332
- this.empty?.classList.add('hidden');
333
- }
334
-
335
- startAutoRefresh() {
336
- if (this.options.refreshInterval > 0) {
337
- this.refreshTimer = setInterval(() => {
338
- this.fetchUnreadCount();
339
- }, this.options.refreshInterval);
340
- }
341
- }
342
-
343
- stopAutoRefresh() {
344
- if (this.refreshTimer) {
345
- clearInterval(this.refreshTimer);
346
- this.refreshTimer = null;
347
- }
348
- }
349
-
350
- destroy() {
351
- this.stopAutoRefresh();
352
- }
353
- }
354
-
355
- // Auto-initialize if not imported as module
356
- if (typeof window !== 'undefined') {
357
- window.DSNotifications = DSNotifications;
358
-
359
- document.addEventListener('DOMContentLoaded', () => {
360
- // Auto-init if drawer exists
361
- if (document.getElementById('notification-drawer')) {
362
- window.notificationsInstance = new DSNotifications();
363
- }
364
- });
365
- }
1
+ /**
2
+ * DSNotifications Plugin
3
+ * Handles notification drawer, fetching, and actions
4
+ */
5
+ import { DSAlert } from './DSAlert.js';
6
+
7
+ export class DSNotifications {
8
+ constructor(options = {}) {
9
+ this.options = {
10
+ drawerToggleId: 'notification-drawer-toggle',
11
+ buttonId: 'user-notification-button',
12
+ listContainerId: 'notification-items',
13
+ loadingId: 'notification-loading',
14
+ emptyId: 'notification-empty',
15
+ badgeId: 'notification-badge',
16
+ topbarBadgeSelector: '#user-notification-button .indicator-item',
17
+ markAllReadBtnId: 'mark-all-read-btn',
18
+ templateId: 'notification-item-template',
19
+ fetchUrl: '/dashboard/notifications/list',
20
+ readUrl: '/dashboard/notifications/{id}/read',
21
+ readAllUrl: '/dashboard/notifications/read-all',
22
+ deleteUrl: '/dashboard/notifications/{id}',
23
+ unreadCountUrl: '/dashboard/notifications/unread-count',
24
+ refreshInterval: 60000, // 1 minute
25
+ ...options
26
+ };
27
+
28
+ this.notifications = [];
29
+ this.unreadCount = 0;
30
+ this.isLoading = false;
31
+ this.refreshTimer = null;
32
+
33
+ this.init();
34
+ }
35
+
36
+ init() {
37
+ this.cacheElements();
38
+ this.bindEvents();
39
+ this.fetchUnreadCount();
40
+ this.startAutoRefresh();
41
+ }
42
+
43
+ cacheElements() {
44
+ this.drawerToggle = document.getElementById(this.options.drawerToggleId);
45
+ this.button = document.getElementById(this.options.buttonId);
46
+ this.listContainer = document.getElementById(this.options.listContainerId);
47
+ this.loading = document.getElementById(this.options.loadingId);
48
+ this.empty = document.getElementById(this.options.emptyId);
49
+ this.badge = document.getElementById(this.options.badgeId);
50
+ this.topbarBadge = document.querySelector(this.options.topbarBadgeSelector);
51
+ this.markAllReadBtn = document.getElementById(this.options.markAllReadBtnId);
52
+ this.template = document.getElementById(this.options.templateId);
53
+ }
54
+
55
+ bindEvents() {
56
+ // Open drawer on button click
57
+ this.button?.addEventListener('click', () => {
58
+ this.openDrawer();
59
+ });
60
+
61
+ // Mark all as read
62
+ this.markAllReadBtn?.addEventListener('click', () => {
63
+ this.markAllAsRead();
64
+ });
65
+
66
+ // Drawer open/close events
67
+ this.drawerToggle?.addEventListener('change', (e) => {
68
+ if (e.target.checked) {
69
+ this.fetchNotifications();
70
+ }
71
+ });
72
+ }
73
+
74
+ openDrawer() {
75
+ if (this.drawerToggle) {
76
+ this.drawerToggle.checked = true;
77
+ this.fetchNotifications();
78
+ }
79
+ }
80
+
81
+ closeDrawer() {
82
+ if (this.drawerToggle) {
83
+ this.drawerToggle.checked = false;
84
+ }
85
+ }
86
+
87
+ async fetchNotifications() {
88
+ if (this.isLoading) return;
89
+
90
+ this.isLoading = true;
91
+ this.showLoading();
92
+
93
+ try {
94
+ const response = await axios.get(this.options.fetchUrl);
95
+ const data = response.data;
96
+
97
+ if (data.success) {
98
+ this.notifications = data.data;
99
+ this.unreadCount = data.unread_count;
100
+ this.renderNotifications();
101
+ this.updateBadge();
102
+ }
103
+ } catch (error) {
104
+ console.error('Failed to fetch notifications:', error);
105
+ this.showEmpty();
106
+ } finally {
107
+ this.isLoading = false;
108
+ }
109
+ }
110
+
111
+ async fetchUnreadCount() {
112
+ try {
113
+ const response = await axios.get(this.options.unreadCountUrl);
114
+ if (response.data.success) {
115
+ this.unreadCount = response.data.count;
116
+ this.updateBadge();
117
+ }
118
+ } catch (error) {
119
+ console.error('Failed to fetch unread count:', error);
120
+ }
121
+ }
122
+
123
+ async markAsRead(id) {
124
+ try {
125
+ const url = this.options.readUrl.replace('{id}', id);
126
+ const response = await axios.post(url);
127
+
128
+ if (response.data.success) {
129
+ // Update local state
130
+ const notification = this.notifications.find(n => n.id === id);
131
+ if (notification) {
132
+ notification.read = true;
133
+ this.unreadCount = Math.max(0, this.unreadCount - 1);
134
+ }
135
+ this.renderNotifications();
136
+ this.updateBadge();
137
+
138
+ if (window.DSAlert) {
139
+ DSAlert.fire({
140
+ toast: true,
141
+ position: 'top-end',
142
+ icon: 'success',
143
+ title: response.data.message,
144
+ showConfirmButton: false,
145
+ timer: 2000
146
+ });
147
+ }
148
+ }
149
+ } catch (error) {
150
+ console.error('Failed to mark as read:', error);
151
+ }
152
+ }
153
+
154
+ async markAllAsRead() {
155
+ try {
156
+ const response = await axios.post(this.options.readAllUrl);
157
+
158
+ if (response.data.success) {
159
+ this.notifications.forEach(n => n.read = true);
160
+ this.unreadCount = 0;
161
+ this.renderNotifications();
162
+ this.updateBadge();
163
+
164
+ if (window.DSAlert) {
165
+ DSAlert.fire({
166
+ toast: true,
167
+ position: 'top-end',
168
+ icon: 'success',
169
+ title: response.data.message,
170
+ showConfirmButton: false,
171
+ timer: 2000
172
+ });
173
+ }
174
+ }
175
+ } catch (error) {
176
+ console.error('Failed to mark all as read:', error);
177
+ }
178
+ }
179
+
180
+ async deleteNotification(id) {
181
+ try {
182
+ const url = this.options.deleteUrl.replace('{id}', id);
183
+ const response = await axios.delete(url);
184
+
185
+ if (response.data.success) {
186
+ // Remove from local state
187
+ const index = this.notifications.findIndex(n => n.id === id);
188
+ if (index !== -1) {
189
+ const notification = this.notifications[index];
190
+ if (!notification.read) {
191
+ this.unreadCount = Math.max(0, this.unreadCount - 1);
192
+ }
193
+ this.notifications.splice(index, 1);
194
+ }
195
+ this.renderNotifications();
196
+ this.updateBadge();
197
+
198
+ if (window.DSAlert) {
199
+ DSAlert.fire({
200
+ toast: true,
201
+ position: 'top-end',
202
+ icon: 'success',
203
+ title: response.data.message,
204
+ showConfirmButton: false,
205
+ timer: 2000
206
+ });
207
+ }
208
+ }
209
+ } catch (error) {
210
+ console.error('Failed to delete notification:', error);
211
+ }
212
+ }
213
+
214
+ renderNotifications() {
215
+ this.hideLoading();
216
+
217
+ if (this.notifications.length === 0) {
218
+ this.showEmpty();
219
+ return;
220
+ }
221
+
222
+ this.hideEmpty();
223
+ this.listContainer.innerHTML = '';
224
+
225
+ this.notifications.forEach(notification => {
226
+ const item = this.createNotificationItem(notification);
227
+ this.listContainer.appendChild(item);
228
+ });
229
+ }
230
+
231
+ createNotificationItem(notification) {
232
+ const template = this.template.content.cloneNode(true);
233
+ const item = template.querySelector('.notification-item');
234
+
235
+ item.dataset.id = notification.id;
236
+
237
+ // Apply read/unread styling
238
+ if (!notification.read) {
239
+ item.classList.add('bg-primary/5', 'border-l-4', 'border-l-primary');
240
+ }
241
+
242
+ // Icon
243
+ const iconWrapper = item.querySelector('.notification-icon');
244
+ iconWrapper.classList.add(this.getColorClass(notification.color, 'bg'), this.getColorClass(notification.color, 'text'));
245
+ item.querySelector('.notification-icon .material-symbols-outlined').textContent = notification.icon;
246
+
247
+ // Content
248
+ item.querySelector('.notification-title').textContent = notification.title;
249
+ item.querySelector('.notification-message').textContent = notification.message;
250
+ item.querySelector('.notification-time').textContent = notification.time_ago;
251
+
252
+ // Hide read button if already read
253
+ const readBtn = item.querySelector('.notification-read-btn');
254
+ if (notification.read) {
255
+ readBtn.classList.add('hidden');
256
+ }
257
+
258
+ // Bind events
259
+ readBtn.addEventListener('click', (e) => {
260
+ e.stopPropagation();
261
+ this.markAsRead(notification.id);
262
+ });
263
+
264
+ item.querySelector('.notification-delete-btn').addEventListener('click', (e) => {
265
+ e.stopPropagation();
266
+ this.deleteNotification(notification.id);
267
+ });
268
+
269
+ // Click to navigate
270
+ item.addEventListener('click', () => {
271
+ if (notification.url) {
272
+ if (!notification.read) {
273
+ this.markAsRead(notification.id);
274
+ }
275
+ window.location.href = notification.url;
276
+ }
277
+ });
278
+
279
+ return item;
280
+ }
281
+
282
+ getColorClass(color, type) {
283
+ const colors = {
284
+ primary: { bg: 'bg-primary/10', text: 'text-primary' },
285
+ success: { bg: 'bg-success/10', text: 'text-success' },
286
+ warning: { bg: 'bg-warning/10', text: 'text-warning' },
287
+ error: { bg: 'bg-error/10', text: 'text-error' },
288
+ info: { bg: 'bg-info/10', text: 'text-info' },
289
+ neutral: { bg: 'bg-base-200', text: 'text-base-content' },
290
+ };
291
+
292
+ return colors[color]?.[type] || colors.neutral[type];
293
+ }
294
+
295
+ updateBadge() {
296
+ // Drawer badge
297
+ if (this.badge) {
298
+ if (this.unreadCount > 0) {
299
+ this.badge.textContent = this.unreadCount > 99 ? '99+' : this.unreadCount;
300
+ this.badge.classList.remove('hidden');
301
+ } else {
302
+ this.badge.classList.add('hidden');
303
+ }
304
+ }
305
+
306
+ // Topbar badge
307
+ if (this.topbarBadge) {
308
+ if (this.unreadCount > 0) {
309
+ this.topbarBadge.classList.remove('hidden');
310
+ } else {
311
+ this.topbarBadge.classList.add('hidden');
312
+ }
313
+ }
314
+ }
315
+
316
+ showLoading() {
317
+ this.loading?.classList.remove('hidden');
318
+ this.empty?.classList.add('hidden');
319
+ this.listContainer.innerHTML = '';
320
+ }
321
+
322
+ hideLoading() {
323
+ this.loading?.classList.add('hidden');
324
+ }
325
+
326
+ showEmpty() {
327
+ this.empty?.classList.remove('hidden');
328
+ this.listContainer.innerHTML = '';
329
+ }
330
+
331
+ hideEmpty() {
332
+ this.empty?.classList.add('hidden');
333
+ }
334
+
335
+ startAutoRefresh() {
336
+ if (this.options.refreshInterval > 0) {
337
+ this.refreshTimer = setInterval(() => {
338
+ this.fetchUnreadCount();
339
+ }, this.options.refreshInterval);
340
+ }
341
+ }
342
+
343
+ stopAutoRefresh() {
344
+ if (this.refreshTimer) {
345
+ clearInterval(this.refreshTimer);
346
+ this.refreshTimer = null;
347
+ }
348
+ }
349
+
350
+ destroy() {
351
+ this.stopAutoRefresh();
352
+ }
353
+ }
354
+
355
+ // Auto-initialize if not imported as module
356
+ if (typeof window !== 'undefined') {
357
+ window.DSNotifications = DSNotifications;
358
+
359
+ document.addEventListener('DOMContentLoaded', () => {
360
+ // Auto-init if drawer exists
361
+ if (document.getElementById('notification-drawer')) {
362
+ window.notificationsInstance = new DSNotifications();
363
+ }
364
+ });
365
+ }