@kupola/kupola 1.5.2 → 1.5.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/js/message.js CHANGED
@@ -1,62 +1,75 @@
1
+ import { getMessageConfig } from './kupola-config.js';
2
+
1
3
  const Message = {
2
- normal: function(message, options = {}) {
3
- this.show(message, 'normal', options);
4
- },
5
- success: function(message, options = {}) {
6
- this.show(message, 'success', options);
7
- },
8
- error: function(message, options = {}) {
9
- this.show(message, 'error', options);
10
- },
11
- warning: function(message, options = {}) {
12
- this.show(message, 'warning', options);
13
- },
14
- info: function(message, options = {}) {
15
- this.show(message, 'info', options);
16
- },
17
- show: function(message, type = 'normal', options = {}) {
18
- const { duration = 3000 } = options;
19
-
20
- const msg = document.createElement('div');
21
- msg.className = `ds-message__item ds-message__item--${type}`;
22
-
23
- const icons = {
24
- normal: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>',
25
- success: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>',
26
- error: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>',
27
- warning: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>',
28
- info: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>'
29
- };
30
-
31
- msg.innerHTML = `
4
+ normal: function (message, options = {}) {
5
+ this.show(message, 'normal', options);
6
+ },
7
+ success: function (message, options = {}) {
8
+ this.show(message, 'success', options);
9
+ },
10
+ error: function (message, options = {}) {
11
+ this.show(message, 'error', options);
12
+ },
13
+ warning: function (message, options = {}) {
14
+ this.show(message, 'warning', options);
15
+ },
16
+ info: function (message, options = {}) {
17
+ this.show(message, 'info', options);
18
+ },
19
+ show: function (message, type = 'normal', options = {}) {
20
+ const msgConfig = getMessageConfig();
21
+ const { duration = msgConfig.duration, position = msgConfig.position } = options;
22
+ const maxCount = msgConfig.maxCount || 5;
23
+
24
+ const msg = document.createElement('div');
25
+ msg.className = `ds-message__item ds-message__item--${type}`;
26
+
27
+ const icons = {
28
+ normal: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>',
29
+ success: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>',
30
+ error: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>',
31
+ warning: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>',
32
+ info: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>',
33
+ };
34
+
35
+ msg.innerHTML = `
32
36
  <div class="ds-message__icon ds-message__icon--${type}">${icons[type]}</div>
33
37
  <div class="ds-message__content"></div>
34
38
  `;
35
- msg.querySelector('.ds-message__content').textContent = message;
36
-
37
- let container = document.querySelector('.ds-message');
38
- if (!container) {
39
- container = document.createElement('div');
40
- container.className = 'ds-message';
41
- document.body.appendChild(container);
42
- }
43
-
44
- container.appendChild(msg);
45
-
46
- setTimeout(() => {
47
- msg.classList.add('is-visible');
48
- }, 10);
49
-
50
- if (duration > 0) {
51
- setTimeout(() => {
52
- msg.classList.remove('is-visible');
53
- msg.classList.add('is-exiting');
54
- setTimeout(() => msg.remove(), 300);
55
- }, duration);
56
- }
39
+ msg.querySelector('.ds-message__content').textContent = message;
40
+
41
+ let container = document.querySelector('.ds-message');
42
+ if (!container) {
43
+ container = document.createElement('div');
44
+ container.className = `ds-message ds-message--${position}`;
45
+ document.body.appendChild(container);
46
+ }
47
+
48
+ // Remove excess messages if maxCount exceeded
49
+ const existingMessages = container.querySelectorAll('.ds-message__item');
50
+ if (existingMessages.length >= maxCount) {
51
+ const oldest = existingMessages[0];
52
+ oldest.classList.remove('is-visible');
53
+ oldest.classList.add('is-exiting');
54
+ setTimeout(() => oldest.remove(), 300);
55
+ }
56
+
57
+ container.appendChild(msg);
58
+
59
+ setTimeout(() => {
60
+ msg.classList.add('is-visible');
61
+ }, 10);
62
+
63
+ if (duration > 0) {
64
+ setTimeout(() => {
65
+ msg.classList.remove('is-visible');
66
+ msg.classList.add('is-exiting');
67
+ setTimeout(() => msg.remove(), 300);
68
+ }, duration);
57
69
  }
70
+ },
58
71
  };
59
72
 
60
73
  function initMessages() {}
61
74
 
62
- export { Message, initMessages };
75
+ export { Message, initMessages };
package/js/modal.js CHANGED
@@ -7,10 +7,13 @@ class Modal {
7
7
  this.mask = element.querySelector('.ds-modal-mask');
8
8
  this.modal = element.querySelector('.ds-modal');
9
9
  this.closeBtn = element.querySelector('.ds-modal__close');
10
-
11
- // Options
10
+
11
+ // Options - use config default for backdropClick
12
+ const uiConfig = getUiConfig();
13
+ const defaultBackdropClick = uiConfig.modal?.backdropClick !== undefined ? uiConfig.modal.backdropClick : true;
14
+
12
15
  this.fullscreen = options.fullscreen || element.hasAttribute('data-modal-fullscreen');
13
- this.closableOnMask = options.closableOnMask !== false; // default true
16
+ this.closableOnMask = options.closableOnMask !== undefined ? options.closableOnMask : defaultBackdropClick;
14
17
  this.escClose = options.escClose !== false;
15
18
  this.width = options.width || element.getAttribute('data-modal-width') || '';
16
19
  this.center = options.center !== false;
@@ -18,54 +21,54 @@ class Modal {
18
21
  this.onBeforeClose = options.onBeforeClose || null;
19
22
  this.onOpened = options.onOpened || null;
20
23
  this.onClosed = options.onClosed || null;
21
-
24
+
22
25
  this._isOpen = false; // 实例级跟踪,防止 _openCount 不匹配
23
-
26
+
24
27
  this._keydownHandler = (e) => {
25
28
  if (this.escClose && e.key === 'Escape' && this.isVisible()) {
26
29
  this.close();
27
30
  }
28
31
  };
29
-
32
+
30
33
  this._closeBtnClickHandler = () => this.close();
31
-
34
+
32
35
  this._maskClickHandler = (e) => {
33
36
  if (this.closableOnMask && e.target === this.mask) {
34
37
  this.close();
35
38
  }
36
39
  };
37
-
40
+
38
41
  this.init();
39
42
  }
40
-
43
+
41
44
  init() {
42
45
  if (this.closeBtn) {
43
46
  this.closeBtn.addEventListener('click', this._closeBtnClickHandler);
44
47
  }
45
-
48
+
46
49
  if (this.mask) {
47
50
  this.mask.addEventListener('click', this._maskClickHandler);
48
51
  }
49
-
52
+
50
53
  document.addEventListener('keydown', this._keydownHandler);
51
-
54
+
52
55
  // Apply fullscreen
53
56
  if (this.fullscreen && this.modal) {
54
57
  this.modal.classList.add('ds-modal--fullscreen');
55
58
  }
56
-
59
+
57
60
  // Apply custom width
58
61
  if (this.width && this.modal) {
59
62
  this.modal.style.maxWidth = this.width;
60
63
  }
61
64
  }
62
-
65
+
63
66
  open() {
64
67
  if (this.onBeforeOpen) {
65
68
  const result = this.onBeforeOpen();
66
- if (result === false) return;
69
+ if (result === false) {return;}
67
70
  }
68
-
71
+
69
72
  if (this.mask) {
70
73
  this.mask.classList.add('is-visible');
71
74
  this.mask.classList.add('ds-modal-fade-enter');
@@ -79,26 +82,26 @@ class Modal {
79
82
  this.modal.classList.add('ds-modal-zoom-enter-active');
80
83
  });
81
84
  }
82
-
85
+
83
86
  if (!this._isOpen) {
84
87
  Modal._openCount = (Modal._openCount || 0) + 1;
85
88
  this._isOpen = true;
86
89
  }
87
90
  document.body.style.overflow = 'hidden';
88
-
91
+
89
92
  if (this.onOpened) {
90
93
  setTimeout(() => this.onOpened(), 300);
91
94
  }
92
-
95
+
93
96
  this.element.dispatchEvent(new CustomEvent('kupola:modal-open', { bubbles: true }));
94
97
  }
95
-
98
+
96
99
  close() {
97
100
  if (this.onBeforeClose) {
98
101
  const result = this.onBeforeClose();
99
- if (result === false) return;
102
+ if (result === false) {return;}
100
103
  }
101
-
104
+
102
105
  if (this.mask) {
103
106
  this.mask.classList.remove('ds-modal-fade-enter-active');
104
107
  this.mask.classList.add('ds-modal-fade-leave-active');
@@ -107,7 +110,7 @@ class Modal {
107
110
  this.modal.classList.remove('ds-modal-zoom-enter-active');
108
111
  this.modal.classList.add('ds-modal-zoom-leave-active');
109
112
  }
110
-
113
+
111
114
  setTimeout(() => {
112
115
  if (this.mask) {
113
116
  this.mask.classList.remove('is-visible', 'ds-modal-fade-enter', 'ds-modal-fade-leave-active');
@@ -116,7 +119,7 @@ class Modal {
116
119
  this.modal.classList.remove('ds-modal-zoom-enter', 'ds-modal-zoom-leave-active');
117
120
  }
118
121
  }, 300);
119
-
122
+
120
123
  if (this._isOpen) {
121
124
  Modal._openCount = Math.max(0, (Modal._openCount || 0) - 1);
122
125
  this._isOpen = false;
@@ -124,25 +127,25 @@ class Modal {
124
127
  document.body.style.overflow = '';
125
128
  }
126
129
  }
127
-
130
+
128
131
  if (this.onClosed) {
129
132
  setTimeout(() => this.onClosed(), 300);
130
133
  }
131
-
134
+
132
135
  this.element.dispatchEvent(new CustomEvent('kupola:modal-close', { bubbles: true }));
133
136
  }
134
-
137
+
135
138
  toggleFullscreen() {
136
139
  this.fullscreen = !this.fullscreen;
137
140
  if (this.modal) {
138
141
  this.modal.classList.toggle('ds-modal--fullscreen', this.fullscreen);
139
142
  }
140
143
  }
141
-
144
+
142
145
  isVisible() {
143
146
  return this.mask && this.mask.classList.contains('is-visible');
144
147
  }
145
-
148
+
146
149
  destroy() {
147
150
  document.removeEventListener('keydown', this._keydownHandler);
148
151
  if (this.closeBtn) {
@@ -184,14 +187,14 @@ function createModal(options = {}) {
184
187
  onOpen,
185
188
  onClose,
186
189
  footer = null,
187
- size = getUiConfig().defaultSize
190
+ size = getUiConfig().defaultSize,
188
191
  } = options;
189
-
192
+
190
193
  const sizeClass = size === 'sm' ? 'ds-btn--sm' : size === 'lg' ? 'ds-btn--lg' : '';
191
-
194
+
192
195
  const container = document.createElement('div');
193
196
  container.className = 'ds-modal-container';
194
-
197
+
195
198
  let footerHTML = '';
196
199
  if (footer !== null) {
197
200
  if (typeof footer === 'string') {
@@ -203,7 +206,7 @@ function createModal(options = {}) {
203
206
  </div>`;
204
207
  }
205
208
  }
206
-
209
+
207
210
  container.innerHTML = `
208
211
  <div class="ds-modal-mask">
209
212
  <div class="ds-modal${fullscreen ? ' ds-modal--fullscreen' : ''}" style="${!fullscreen ? 'max-width: ' + width : ''}">
@@ -220,13 +223,13 @@ function createModal(options = {}) {
220
223
  </div>
221
224
  </div>
222
225
  `;
223
-
226
+
224
227
  document.body.appendChild(container);
225
-
228
+
226
229
  const modal = new Modal(container, { fullscreen, closableOnMask: maskClosable });
227
-
230
+
228
231
  const titleEl = container.querySelector('.ds-modal__title');
229
- if (titleEl) titleEl.textContent = title;
232
+ if (titleEl) {titleEl.textContent = title;}
230
233
  const bodyEl = container.querySelector('.ds-modal__body');
231
234
  if (bodyEl) {
232
235
  if (html) {
@@ -235,12 +238,12 @@ function createModal(options = {}) {
235
238
  bodyEl.textContent = content;
236
239
  }
237
240
  }
238
-
241
+
239
242
  const confirmBtn = container.querySelector('[data-modal-confirm]');
240
243
  const cancelBtn = container.querySelector('[data-modal-cancel]');
241
-
244
+
242
245
  let isConfirming = false;
243
-
246
+
244
247
  const confirmHandler = async () => {
245
248
  if (onConfirm) {
246
249
  // Support async onConfirm
@@ -253,7 +256,7 @@ function createModal(options = {}) {
253
256
  confirmBtn.classList.remove('is-loading');
254
257
  return;
255
258
  }
256
- } catch(e) {
259
+ } catch (e) {
257
260
  confirmBtn.disabled = false;
258
261
  confirmBtn.classList.remove('is-loading');
259
262
  return;
@@ -262,34 +265,34 @@ function createModal(options = {}) {
262
265
  isConfirming = true;
263
266
  modal.close();
264
267
  };
265
-
268
+
266
269
  const cancelHandler = () => {
267
- if (onCancel) onCancel();
270
+ if (onCancel) {onCancel();}
268
271
  modal.close();
269
272
  };
270
-
271
- if (confirmBtn) confirmBtn.addEventListener('click', confirmHandler);
272
- if (cancelBtn) cancelBtn.addEventListener('click', cancelHandler);
273
-
273
+
274
+ if (confirmBtn) {confirmBtn.addEventListener('click', confirmHandler);}
275
+ if (cancelBtn) {cancelBtn.addEventListener('click', cancelHandler);}
276
+
274
277
  const closeAndDestroy = () => {
275
278
  setTimeout(() => {
276
- if (confirmBtn) confirmBtn.removeEventListener('click', confirmHandler);
277
- if (cancelBtn) cancelBtn.removeEventListener('click', cancelHandler);
279
+ if (confirmBtn) {confirmBtn.removeEventListener('click', confirmHandler);}
280
+ if (cancelBtn) {cancelBtn.removeEventListener('click', cancelHandler);}
278
281
  modal.destroy();
279
282
  container.remove();
280
- if (onClose) onClose(isConfirming);
283
+ if (onClose) {onClose(isConfirming);}
281
284
  }, 300);
282
285
  };
283
-
286
+
284
287
  const originalClose = modal.close.bind(modal);
285
288
  modal.close = () => {
286
289
  originalClose();
287
290
  closeAndDestroy();
288
291
  };
289
-
292
+
290
293
  modal.open();
291
- if (onOpen) setTimeout(() => onOpen(), 50);
292
-
294
+ if (onOpen) {setTimeout(() => onOpen(), 50);}
295
+
293
296
  return modal;
294
297
  }
295
298
 
@@ -301,7 +304,7 @@ function confirmModal(options) {
301
304
  return createModal({
302
305
  ...options,
303
306
  showCancel: true,
304
- showConfirm: true
307
+ showConfirm: true,
305
308
  });
306
309
  }
307
310
 
@@ -312,12 +315,12 @@ function alertModal(options) {
312
315
  return createModal({
313
316
  ...options,
314
317
  showCancel: false,
315
- showConfirm: true
318
+ showConfirm: true,
316
319
  });
317
320
  }
318
321
 
319
322
  function initModal(element) {
320
- if (element.__kupolaInitialized) return;
323
+ if (element.__kupolaInitialized) {return;}
321
324
 
322
325
  const modal = new Modal(element);
323
326
  element.__kupolaInstance = modal;
@@ -325,7 +328,7 @@ function initModal(element) {
325
328
  }
326
329
 
327
330
  function cleanupModal(element) {
328
- if (!element.__kupolaInitialized || !element.__kupolaInstance) return;
331
+ if (!element.__kupolaInitialized || !element.__kupolaInstance) {return;}
329
332
 
330
333
  const modal = element.__kupolaInstance;
331
334
  modal.destroy();
@@ -1,34 +1,37 @@
1
+ import { getNotificationConfig } from './kupola-config.js';
2
+
1
3
  const Notification = {
2
- normal: function(options) {
3
- this.show({ ...options, type: 'normal' });
4
- },
5
- success: function(options) {
6
- this.show({ ...options, type: 'success' });
7
- },
8
- error: function(options) {
9
- this.show({ ...options, type: 'error' });
10
- },
11
- warning: function(options) {
12
- this.show({ ...options, type: 'warning' });
13
- },
14
- info: function(options) {
15
- this.show({ ...options, type: 'info' });
16
- },
17
- show: function(options) {
18
- const { title, message, type = 'normal', duration = 4000 } = options;
19
-
20
- const notification = document.createElement('div');
21
- notification.className = `ds-notification__item ds-notification__item--${type}`;
22
-
23
- const icons = {
24
- normal: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>',
25
- success: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>',
26
- error: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>',
27
- warning: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>',
28
- info: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>'
29
- };
30
-
31
- notification.innerHTML = `
4
+ normal: function (options) {
5
+ this.show({ ...options, type: 'normal' });
6
+ },
7
+ success: function (options) {
8
+ this.show({ ...options, type: 'success' });
9
+ },
10
+ error: function (options) {
11
+ this.show({ ...options, type: 'error' });
12
+ },
13
+ warning: function (options) {
14
+ this.show({ ...options, type: 'warning' });
15
+ },
16
+ info: function (options) {
17
+ this.show({ ...options, type: 'info' });
18
+ },
19
+ show: function (options) {
20
+ const notifConfig = getNotificationConfig();
21
+ const { title, message, type = 'normal', duration = notifConfig.duration, position = notifConfig.position } = options;
22
+
23
+ const notification = document.createElement('div');
24
+ notification.className = `ds-notification__item ds-notification__item--${type}`;
25
+
26
+ const icons = {
27
+ normal: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>',
28
+ success: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>',
29
+ error: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>',
30
+ warning: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>',
31
+ info: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>',
32
+ };
33
+
34
+ notification.innerHTML = `
32
35
  <div class="ds-notification__icon ds-notification__icon--${type}">${icons[type]}</div>
33
36
  <div class="ds-notification__content">
34
37
  ${title ? '<div class="ds-notification__title"></div>' : ''}
@@ -38,38 +41,38 @@ const Notification = {
38
41
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
39
42
  </button>
40
43
  `;
41
- if (title) notification.querySelector('.ds-notification__title').textContent = title;
42
- if (message) notification.querySelector('.ds-notification__message').textContent = message;
43
-
44
- let container = document.querySelector('.ds-notification');
45
- if (!container) {
46
- container = document.createElement('div');
47
- container.className = 'ds-notification';
48
- document.body.appendChild(container);
49
- }
50
-
51
- container.appendChild(notification);
52
-
53
- setTimeout(() => {
54
- notification.classList.add('is-visible');
55
- }, 10);
56
-
57
- notification.querySelector('.ds-notification__close').addEventListener('click', () => {
58
- notification.classList.remove('is-visible');
59
- notification.classList.add('is-exiting');
60
- setTimeout(() => notification.remove(), 300);
61
- });
62
-
63
- if (duration > 0) {
64
- setTimeout(() => {
65
- notification.classList.remove('is-visible');
66
- notification.classList.add('is-exiting');
67
- setTimeout(() => notification.remove(), 300);
68
- }, duration);
69
- }
44
+ if (title) {notification.querySelector('.ds-notification__title').textContent = title;}
45
+ if (message) {notification.querySelector('.ds-notification__message').textContent = message;}
46
+
47
+ let container = document.querySelector('.ds-notification');
48
+ if (!container) {
49
+ container = document.createElement('div');
50
+ container.className = `ds-notification ds-notification--${position}`;
51
+ document.body.appendChild(container);
52
+ }
53
+
54
+ container.appendChild(notification);
55
+
56
+ setTimeout(() => {
57
+ notification.classList.add('is-visible');
58
+ }, 10);
59
+
60
+ notification.querySelector('.ds-notification__close').addEventListener('click', () => {
61
+ notification.classList.remove('is-visible');
62
+ notification.classList.add('is-exiting');
63
+ setTimeout(() => notification.remove(), 300);
64
+ });
65
+
66
+ if (duration > 0) {
67
+ setTimeout(() => {
68
+ notification.classList.remove('is-visible');
69
+ notification.classList.add('is-exiting');
70
+ setTimeout(() => notification.remove(), 300);
71
+ }, duration);
70
72
  }
73
+ },
71
74
  };
72
75
 
73
76
  function initNotifications() {}
74
77
 
75
- export { Notification, initNotifications };
78
+ export { Notification, initNotifications };
package/js/security.js CHANGED
@@ -58,4 +58,56 @@ export function stripHtml(html) {
58
58
  const domParser = new DOMParser();
59
59
  const doc = domParser.parseFromString(html, 'text/html');
60
60
  return doc.body.textContent || '';
61
+ }
62
+
63
+ export function maskData(value, type, options = {}) {
64
+ const securityConfig = getSecurityConfig();
65
+ const maskConfig = securityConfig?.maskData || {};
66
+
67
+ if (!maskConfig.enabled && !options.force) {
68
+ return value;
69
+ }
70
+
71
+ if (value === null || value === undefined) {
72
+ return value;
73
+ }
74
+
75
+ const patterns = options.patterns || maskConfig.patterns || {};
76
+ const pattern = patterns[type];
77
+
78
+ if (!pattern) {
79
+ return value;
80
+ }
81
+
82
+ const regex = typeof pattern.regex === 'string'
83
+ ? new RegExp(pattern.regex)
84
+ : pattern.regex;
85
+
86
+ return String(value).replace(regex, pattern.replace);
87
+ }
88
+
89
+ export function generateSecureId(length, prefix) {
90
+ const securityConfig = getSecurityConfig();
91
+ const secureIdConfig = securityConfig?.secureId || {};
92
+
93
+ const idLength = length || secureIdConfig.length || 16;
94
+ const charset = secureIdConfig.charset || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
95
+
96
+ if (typeof crypto === 'undefined' || !crypto.getRandomValues) {
97
+ let result = '';
98
+ for (let i = 0; i < idLength; i++) {
99
+ result += charset[Math.floor(Math.random() * charset.length)];
100
+ }
101
+ return prefix ? `${prefix}_${result}` : result;
102
+ }
103
+
104
+ const array = new Uint32Array(idLength);
105
+ crypto.getRandomValues(array);
106
+
107
+ let result = '';
108
+ for (let i = 0; i < idLength; i++) {
109
+ result += charset[array[i] % charset.length];
110
+ }
111
+
112
+ return prefix ? `${prefix}_${result}` : result;
61
113
  }