@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,517 +1,517 @@
1
- /**
2
- * DSSimpleSlider
3
- *
4
- * A flexible, customizable slider plugin supporting multiple data sources
5
- * (ajax, json, html) with auto-play, hover pause, and timer border animation.
6
- *
7
- * @example
8
- * // AJAX Mode
9
- * const slider = new DSSimpleSlider('#premiumSlider', {
10
- * source: 'ajax',
11
- * ajax_url: '/api/featured-domains',
12
- * ajax_method: 'GET',
13
- * autoPlay: true,
14
- * interval: 5000,
15
- * pauseOnHover: true
16
- * });
17
- *
18
- * // JSON Mode
19
- * const slider = new DSSimpleSlider('#slider', {
20
- * source: 'json',
21
- * data: [
22
- * { title: 'crypto.com', subtitle: '$12,000' },
23
- * { title: 'ai.io', subtitle: '$8,500' }
24
- * ]
25
- * });
26
- *
27
- * // HTML Mode (reads existing content)
28
- * const slider = new DSSimpleSlider('#slider', { source: 'html' });
29
- */
30
- class DSSimpleSlider {
31
- static defaults = {
32
- // Data source: 'ajax', 'json', 'html'
33
- source: 'json',
34
-
35
- // AJAX settings
36
- ajax_url: null,
37
- ajax_method: 'GET',
38
- ajax_data: {},
39
- ajax_function: 'axios', // 'axios', 'fetch', 'xhr'
40
-
41
- // JSON data (for source: 'json')
42
- data: [],
43
-
44
- // Data mapping (how to extract title/subtitle from data)
45
- dataMap: {
46
- title: 'title', // property name for title
47
- subtitle: 'subtitle', // property name for subtitle
48
- url: 'url' // property name for link URL (optional)
49
- },
50
-
51
- // Auto-play settings
52
- autoPlay: true,
53
- interval: 5000, // ms between slides
54
- pauseOnHover: true,
55
-
56
- // Timer border animation
57
- showTimerBorder: true, // set to false to disable
58
- timerBorderSelector: '.ds-slider-badge', // element to animate border
59
- timerBorderColor: 'rgba(251, 191, 36, 1)', // amber-400
60
- timerBorderWidth: 2,
61
-
62
- // Navigation buttons
63
- showPrevButton: true,
64
- showNextButton: true,
65
- prevButtonSelector: '.ds-slider-prev',
66
- nextButtonSelector: '.ds-slider-next',
67
-
68
- // Content selectors
69
- contentSelector: '.ds-slider-content',
70
- titleSelector: '.ds-slider-title',
71
- subtitleSelector: '.ds-slider-subtitle',
72
-
73
- // Disabled state
74
- disabledClass: 'opacity-50 pointer-events-none',
75
-
76
- // Animation
77
- fadeClass: 'transition-opacity duration-300',
78
-
79
- // Callbacks
80
- onSlideChange: null,
81
- onDataLoad: null,
82
- onEmpty: null
83
- };
84
-
85
- constructor(wrapper, options = {}) {
86
- this.wrapper = typeof wrapper === 'string' ? document.querySelector(wrapper) : wrapper;
87
- if (!this.wrapper) {
88
- console.warn('DSSimpleSlider: Wrapper element not found');
89
- return;
90
- }
91
-
92
- this.config = { ...DSSimpleSlider.defaults, ...options };
93
- this.slides = [];
94
- this.currentIndex = 0;
95
- this.isPlaying = false;
96
- this.isPaused = false;
97
- this.timer = null;
98
- this.timerAnimation = null;
99
-
100
- this._init();
101
- }
102
-
103
- async _init() {
104
- this._cacheElements();
105
- await this._loadData();
106
- this._bindEvents();
107
-
108
- if (this.slides.length > 0) {
109
- this._render();
110
- if (this.config.autoPlay) {
111
- this.play();
112
- }
113
- } else {
114
- this._handleEmpty();
115
- }
116
- }
117
-
118
- _cacheElements() {
119
- this.contentEl = this.wrapper.querySelector(this.config.contentSelector);
120
- this.titleEl = this.wrapper.querySelector(this.config.titleSelector);
121
- this.subtitleEl = this.wrapper.querySelector(this.config.subtitleSelector);
122
- this.prevBtn = this.wrapper.querySelector(this.config.prevButtonSelector);
123
- this.nextBtn = this.wrapper.querySelector(this.config.nextButtonSelector);
124
- this.badgeEl = this.wrapper.querySelector(this.config.timerBorderSelector);
125
- }
126
-
127
- async _loadData() {
128
- try {
129
- switch (this.config.source) {
130
- case 'ajax':
131
- await this._loadFromAjax();
132
- break;
133
- case 'json':
134
- this.slides = this.config.data || [];
135
- break;
136
- case 'html':
137
- this._loadFromHtml();
138
- break;
139
- default:
140
- this.slides = [];
141
- }
142
-
143
- if (this.config.onDataLoad) {
144
- this.config.onDataLoad(this.slides);
145
- }
146
- } catch (error) {
147
- console.error('DSSimpleSlider: Error loading data', error);
148
- this.slides = [];
149
- }
150
- }
151
-
152
- async _loadFromAjax() {
153
- const { ajax_url, ajax_method, ajax_data, ajax_function } = this.config;
154
-
155
- if (!ajax_url) {
156
- console.warn('DSSimpleSlider: ajax_url is required for ajax source');
157
- return;
158
- }
159
-
160
- try {
161
- let response;
162
-
163
- if (ajax_function === 'axios' && window.axios) {
164
- response = await window.axios({
165
- method: ajax_method,
166
- url: ajax_url,
167
- params: ajax_method === 'GET' ? ajax_data : undefined,
168
- data: ajax_method !== 'GET' ? ajax_data : undefined
169
- });
170
- this.slides = response.data?.data || response.data || [];
171
- } else if (ajax_function === 'fetch' || window.fetch) {
172
- const queryString = new URLSearchParams(ajax_data).toString();
173
- const fetchUrl = ajax_method === 'GET' && queryString ? `${ajax_url}?${queryString}` : ajax_url;
174
- const options = {
175
- method: ajax_method,
176
- headers: { 'Accept': 'application/json' }
177
- };
178
- if (ajax_method !== 'GET') {
179
- options.headers['Content-Type'] = 'application/json';
180
- options.body = JSON.stringify(ajax_data);
181
- }
182
- const res = await fetch(fetchUrl, options);
183
- const json = await res.json();
184
- this.slides = json?.data || json || [];
185
- } else {
186
- // XHR fallback
187
- this.slides = await this._loadFromXhr();
188
- }
189
- } catch (error) {
190
- console.error('DSSimpleSlider: AJAX error', error);
191
- this.slides = [];
192
- }
193
- }
194
-
195
- _loadFromXhr() {
196
- return new Promise((resolve, reject) => {
197
- const { ajax_url, ajax_method, ajax_data } = this.config;
198
- const xhr = new XMLHttpRequest();
199
-
200
- let url = ajax_url;
201
- if (ajax_method === 'GET' && Object.keys(ajax_data).length) {
202
- url += '?' + new URLSearchParams(ajax_data).toString();
203
- }
204
-
205
- xhr.open(ajax_method, url, true);
206
- xhr.setRequestHeader('Accept', 'application/json');
207
-
208
- if (ajax_method !== 'GET') {
209
- xhr.setRequestHeader('Content-Type', 'application/json');
210
- }
211
-
212
- xhr.onload = () => {
213
- if (xhr.status >= 200 && xhr.status < 300) {
214
- try {
215
- const json = JSON.parse(xhr.responseText);
216
- resolve(json?.data || json || []);
217
- } catch (e) {
218
- reject(e);
219
- }
220
- } else {
221
- reject(new Error(xhr.statusText));
222
- }
223
- };
224
-
225
- xhr.onerror = () => reject(new Error('Network error'));
226
- xhr.send(ajax_method !== 'GET' ? JSON.stringify(ajax_data) : null);
227
- });
228
- }
229
-
230
- _loadFromHtml() {
231
- // Read existing content as the first slide
232
- if (this.titleEl && this.subtitleEl) {
233
- const title = this.titleEl.textContent.trim();
234
- const subtitle = this.subtitleEl.textContent.trim();
235
- if (title) {
236
- this.slides = [{ title, subtitle }];
237
- }
238
- }
239
- }
240
-
241
- _bindEvents() {
242
- // Navigation buttons
243
- if (this.prevBtn && this.config.showPrevButton) {
244
- this.prevBtn.addEventListener('click', (e) => {
245
- e.preventDefault();
246
- this.prev();
247
- });
248
- }
249
-
250
- if (this.nextBtn && this.config.showNextButton) {
251
- this.nextBtn.addEventListener('click', (e) => {
252
- e.preventDefault();
253
- this.next();
254
- });
255
- }
256
-
257
- // Hover pause
258
- if (this.config.pauseOnHover) {
259
- this.wrapper.addEventListener('mouseenter', () => this.pause());
260
- this.wrapper.addEventListener('mouseleave', () => this.resume());
261
- }
262
- }
263
-
264
- _render() {
265
- const slide = this.slides[this.currentIndex];
266
- if (!slide) return;
267
-
268
- const { dataMap } = this.config;
269
- const title = this._getNestedValue(slide, dataMap.title) || '';
270
- const subtitle = this._getNestedValue(slide, dataMap.subtitle) || '';
271
- const url = this._getNestedValue(slide, dataMap.url) || null;
272
-
273
- // Apply fade effect
274
- if (this.contentEl) {
275
- this.contentEl.classList.add('opacity-0');
276
-
277
- setTimeout(() => {
278
- if (this.titleEl) this.titleEl.textContent = title;
279
- if (this.subtitleEl) this.subtitleEl.textContent = subtitle;
280
-
281
- // Handle URL wrapping
282
- if (url && this.contentEl) {
283
- this.contentEl.style.cursor = 'pointer';
284
- this.contentEl.onclick = () => window.location.href = url;
285
- } else if (this.contentEl) {
286
- this.contentEl.style.cursor = 'default';
287
- this.contentEl.onclick = null;
288
- }
289
-
290
- this.contentEl.classList.remove('opacity-0');
291
- }, 150);
292
- } else {
293
- if (this.titleEl) this.titleEl.textContent = title;
294
- if (this.subtitleEl) this.subtitleEl.textContent = subtitle;
295
- }
296
-
297
- // Callback
298
- if (this.config.onSlideChange) {
299
- this.config.onSlideChange(slide, this.currentIndex, this.slides.length);
300
- }
301
-
302
- // Emit event
303
- this.wrapper.dispatchEvent(new CustomEvent('dsslider:change', {
304
- detail: { slide, index: this.currentIndex, total: this.slides.length }
305
- }));
306
- }
307
-
308
- _handleEmpty() {
309
- // Disable buttons
310
- if (this.prevBtn) {
311
- this.prevBtn.classList.add(...this.config.disabledClass.split(' '));
312
- this.prevBtn.disabled = true;
313
- }
314
- if (this.nextBtn) {
315
- this.nextBtn.classList.add(...this.config.disabledClass.split(' '));
316
- this.nextBtn.disabled = true;
317
- }
318
-
319
- // Clear content
320
- if (this.titleEl) this.titleEl.textContent = '';
321
- if (this.subtitleEl) this.subtitleEl.textContent = '';
322
-
323
- // Callback
324
- if (this.config.onEmpty) {
325
- this.config.onEmpty();
326
- }
327
-
328
- // Emit event
329
- this.wrapper.dispatchEvent(new CustomEvent('dsslider:empty'));
330
- }
331
-
332
- _startTimer() {
333
- this._stopTimer();
334
-
335
- if (this.config.showTimerBorder && this.badgeEl) {
336
- this._startTimerBorderAnimation();
337
- }
338
-
339
- this.timer = setTimeout(() => {
340
- this.next();
341
- }, this.config.interval);
342
- }
343
-
344
- _stopTimer() {
345
- if (this.timer) {
346
- clearTimeout(this.timer);
347
- this.timer = null;
348
- }
349
- this._stopTimerBorderAnimation();
350
- }
351
-
352
- _startTimerBorderAnimation() {
353
- if (!this.badgeEl) return;
354
-
355
- const { interval, timerBorderColor, timerBorderWidth } = this.config;
356
-
357
- // Create a pseudo-element style for the border animation
358
- // We'll use a conic gradient that animates around the element
359
- this.badgeEl.style.setProperty('--timer-duration', `${interval}ms`);
360
- this.badgeEl.style.setProperty('--timer-color', timerBorderColor);
361
-
362
- // Add animation class
363
- this.badgeEl.classList.add('ds-slider-timer-active');
364
-
365
- // Reset animation by forcing reflow
366
- this.badgeEl.style.animation = 'none';
367
- this.badgeEl.offsetHeight; // Trigger reflow
368
- this.badgeEl.style.animation = `ds-slider-timer-border ${interval}ms linear`;
369
- }
370
-
371
- _stopTimerBorderAnimation() {
372
- if (this.badgeEl) {
373
- this.badgeEl.classList.remove('ds-slider-timer-active');
374
- this.badgeEl.style.animation = '';
375
- }
376
- }
377
-
378
- // Public methods
379
- next() {
380
- if (this.slides.length === 0) return;
381
- this.currentIndex = (this.currentIndex + 1) % this.slides.length;
382
- this._render();
383
-
384
- if (this.isPlaying && !this.isPaused) {
385
- this._startTimer();
386
- }
387
- }
388
-
389
- prev() {
390
- if (this.slides.length === 0) return;
391
- this.currentIndex = (this.currentIndex - 1 + this.slides.length) % this.slides.length;
392
- this._render();
393
-
394
- if (this.isPlaying && !this.isPaused) {
395
- this._startTimer();
396
- }
397
- }
398
-
399
- goTo(index) {
400
- if (index < 0 || index >= this.slides.length) return;
401
- this.currentIndex = index;
402
- this._render();
403
-
404
- if (this.isPlaying && !this.isPaused) {
405
- this._startTimer();
406
- }
407
- }
408
-
409
- play() {
410
- if (this.slides.length <= 1) return;
411
- this.isPlaying = true;
412
- this.isPaused = false;
413
- this._startTimer();
414
- }
415
-
416
- pause() {
417
- this.isPaused = true;
418
- this._stopTimer();
419
- }
420
-
421
- resume() {
422
- if (this.isPlaying) {
423
- this.isPaused = false;
424
- this._startTimer();
425
- }
426
- }
427
-
428
- stop() {
429
- this.isPlaying = false;
430
- this.isPaused = false;
431
- this._stopTimer();
432
- }
433
-
434
- // Reload data
435
- async reload(newData = null) {
436
- if (newData) {
437
- this.slides = newData;
438
- } else {
439
- await this._loadData();
440
- }
441
-
442
- this.currentIndex = 0;
443
-
444
- if (this.slides.length > 0) {
445
- this._render();
446
- if (this.config.autoPlay) {
447
- this.play();
448
- }
449
- } else {
450
- this._handleEmpty();
451
- }
452
- }
453
-
454
- // Set AJAX params and reload
455
- setParams(params) {
456
- this.config.ajax_data = { ...this.config.ajax_data, ...params };
457
- return this;
458
- }
459
-
460
- // Get current slide
461
- getCurrentSlide() {
462
- return this.slides[this.currentIndex] || null;
463
- }
464
-
465
- // Get all slides
466
- getSlides() {
467
- return this.slides;
468
- }
469
-
470
- // Destroy
471
- destroy() {
472
- this.stop();
473
-
474
- // Remove event listeners
475
- if (this.prevBtn) {
476
- this.prevBtn.replaceWith(this.prevBtn.cloneNode(true));
477
- }
478
- if (this.nextBtn) {
479
- this.nextBtn.replaceWith(this.nextBtn.cloneNode(true));
480
- }
481
-
482
- // Remove wrapper listeners
483
- this.wrapper.replaceWith(this.wrapper.cloneNode(true));
484
- }
485
-
486
- // Helper to get nested value from object
487
- _getNestedValue(obj, path) {
488
- if (!path) return obj;
489
- return path.split('.').reduce((acc, part) => acc && acc[part], obj);
490
- }
491
- }
492
-
493
- // Add CSS for timer border animation
494
- if (typeof document !== 'undefined') {
495
- const style = document.createElement('style');
496
- style.textContent = `
497
- @keyframes ds-slider-timer-border {
498
- 0% {
499
- box-shadow: inset 0 0 0 0 var(--timer-color, rgba(251, 191, 36, 0.5));
500
- }
501
- 100% {
502
- box-shadow: inset 0 0 0 2px var(--timer-color, rgba(251, 191, 36, 0.5));
503
- }
504
- }
505
-
506
- .ds-slider-timer-active {
507
- animation: ds-slider-timer-border 5000ms linear;
508
- }
509
-
510
- .ds-slider-content {
511
- transition: opacity 0.15s ease-in-out;
512
- }
513
- `;
514
- document.head.appendChild(style);
515
- }
516
-
517
- export default DSSimpleSlider;
1
+ /**
2
+ * DSSimpleSlider
3
+ *
4
+ * A flexible, customizable slider plugin supporting multiple data sources
5
+ * (ajax, json, html) with auto-play, hover pause, and timer border animation.
6
+ *
7
+ * @example
8
+ * // AJAX Mode
9
+ * const slider = new DSSimpleSlider('#premiumSlider', {
10
+ * source: 'ajax',
11
+ * ajax_url: '/api/featured-domains',
12
+ * ajax_method: 'GET',
13
+ * autoPlay: true,
14
+ * interval: 5000,
15
+ * pauseOnHover: true
16
+ * });
17
+ *
18
+ * // JSON Mode
19
+ * const slider = new DSSimpleSlider('#slider', {
20
+ * source: 'json',
21
+ * data: [
22
+ * { title: 'crypto.com', subtitle: '$12,000' },
23
+ * { title: 'ai.io', subtitle: '$8,500' }
24
+ * ]
25
+ * });
26
+ *
27
+ * // HTML Mode (reads existing content)
28
+ * const slider = new DSSimpleSlider('#slider', { source: 'html' });
29
+ */
30
+ class DSSimpleSlider {
31
+ static defaults = {
32
+ // Data source: 'ajax', 'json', 'html'
33
+ source: 'json',
34
+
35
+ // AJAX settings
36
+ ajax_url: null,
37
+ ajax_method: 'GET',
38
+ ajax_data: {},
39
+ ajax_function: 'axios', // 'axios', 'fetch', 'xhr'
40
+
41
+ // JSON data (for source: 'json')
42
+ data: [],
43
+
44
+ // Data mapping (how to extract title/subtitle from data)
45
+ dataMap: {
46
+ title: 'title', // property name for title
47
+ subtitle: 'subtitle', // property name for subtitle
48
+ url: 'url' // property name for link URL (optional)
49
+ },
50
+
51
+ // Auto-play settings
52
+ autoPlay: true,
53
+ interval: 5000, // ms between slides
54
+ pauseOnHover: true,
55
+
56
+ // Timer border animation
57
+ showTimerBorder: true, // set to false to disable
58
+ timerBorderSelector: '.ds-slider-badge', // element to animate border
59
+ timerBorderColor: 'rgba(251, 191, 36, 1)', // amber-400
60
+ timerBorderWidth: 2,
61
+
62
+ // Navigation buttons
63
+ showPrevButton: true,
64
+ showNextButton: true,
65
+ prevButtonSelector: '.ds-slider-prev',
66
+ nextButtonSelector: '.ds-slider-next',
67
+
68
+ // Content selectors
69
+ contentSelector: '.ds-slider-content',
70
+ titleSelector: '.ds-slider-title',
71
+ subtitleSelector: '.ds-slider-subtitle',
72
+
73
+ // Disabled state
74
+ disabledClass: 'opacity-50 pointer-events-none',
75
+
76
+ // Animation
77
+ fadeClass: 'transition-opacity duration-300',
78
+
79
+ // Callbacks
80
+ onSlideChange: null,
81
+ onDataLoad: null,
82
+ onEmpty: null
83
+ };
84
+
85
+ constructor(wrapper, options = {}) {
86
+ this.wrapper = typeof wrapper === 'string' ? document.querySelector(wrapper) : wrapper;
87
+ if (!this.wrapper) {
88
+ console.warn('DSSimpleSlider: Wrapper element not found');
89
+ return;
90
+ }
91
+
92
+ this.config = { ...DSSimpleSlider.defaults, ...options };
93
+ this.slides = [];
94
+ this.currentIndex = 0;
95
+ this.isPlaying = false;
96
+ this.isPaused = false;
97
+ this.timer = null;
98
+ this.timerAnimation = null;
99
+
100
+ this._init();
101
+ }
102
+
103
+ async _init() {
104
+ this._cacheElements();
105
+ await this._loadData();
106
+ this._bindEvents();
107
+
108
+ if (this.slides.length > 0) {
109
+ this._render();
110
+ if (this.config.autoPlay) {
111
+ this.play();
112
+ }
113
+ } else {
114
+ this._handleEmpty();
115
+ }
116
+ }
117
+
118
+ _cacheElements() {
119
+ this.contentEl = this.wrapper.querySelector(this.config.contentSelector);
120
+ this.titleEl = this.wrapper.querySelector(this.config.titleSelector);
121
+ this.subtitleEl = this.wrapper.querySelector(this.config.subtitleSelector);
122
+ this.prevBtn = this.wrapper.querySelector(this.config.prevButtonSelector);
123
+ this.nextBtn = this.wrapper.querySelector(this.config.nextButtonSelector);
124
+ this.badgeEl = this.wrapper.querySelector(this.config.timerBorderSelector);
125
+ }
126
+
127
+ async _loadData() {
128
+ try {
129
+ switch (this.config.source) {
130
+ case 'ajax':
131
+ await this._loadFromAjax();
132
+ break;
133
+ case 'json':
134
+ this.slides = this.config.data || [];
135
+ break;
136
+ case 'html':
137
+ this._loadFromHtml();
138
+ break;
139
+ default:
140
+ this.slides = [];
141
+ }
142
+
143
+ if (this.config.onDataLoad) {
144
+ this.config.onDataLoad(this.slides);
145
+ }
146
+ } catch (error) {
147
+ console.error('DSSimpleSlider: Error loading data', error);
148
+ this.slides = [];
149
+ }
150
+ }
151
+
152
+ async _loadFromAjax() {
153
+ const { ajax_url, ajax_method, ajax_data, ajax_function } = this.config;
154
+
155
+ if (!ajax_url) {
156
+ console.warn('DSSimpleSlider: ajax_url is required for ajax source');
157
+ return;
158
+ }
159
+
160
+ try {
161
+ let response;
162
+
163
+ if (ajax_function === 'axios' && window.axios) {
164
+ response = await window.axios({
165
+ method: ajax_method,
166
+ url: ajax_url,
167
+ params: ajax_method === 'GET' ? ajax_data : undefined,
168
+ data: ajax_method !== 'GET' ? ajax_data : undefined
169
+ });
170
+ this.slides = response.data?.data || response.data || [];
171
+ } else if (ajax_function === 'fetch' || window.fetch) {
172
+ const queryString = new URLSearchParams(ajax_data).toString();
173
+ const fetchUrl = ajax_method === 'GET' && queryString ? `${ajax_url}?${queryString}` : ajax_url;
174
+ const options = {
175
+ method: ajax_method,
176
+ headers: { 'Accept': 'application/json' }
177
+ };
178
+ if (ajax_method !== 'GET') {
179
+ options.headers['Content-Type'] = 'application/json';
180
+ options.body = JSON.stringify(ajax_data);
181
+ }
182
+ const res = await fetch(fetchUrl, options);
183
+ const json = await res.json();
184
+ this.slides = json?.data || json || [];
185
+ } else {
186
+ // XHR fallback
187
+ this.slides = await this._loadFromXhr();
188
+ }
189
+ } catch (error) {
190
+ console.error('DSSimpleSlider: AJAX error', error);
191
+ this.slides = [];
192
+ }
193
+ }
194
+
195
+ _loadFromXhr() {
196
+ return new Promise((resolve, reject) => {
197
+ const { ajax_url, ajax_method, ajax_data } = this.config;
198
+ const xhr = new XMLHttpRequest();
199
+
200
+ let url = ajax_url;
201
+ if (ajax_method === 'GET' && Object.keys(ajax_data).length) {
202
+ url += '?' + new URLSearchParams(ajax_data).toString();
203
+ }
204
+
205
+ xhr.open(ajax_method, url, true);
206
+ xhr.setRequestHeader('Accept', 'application/json');
207
+
208
+ if (ajax_method !== 'GET') {
209
+ xhr.setRequestHeader('Content-Type', 'application/json');
210
+ }
211
+
212
+ xhr.onload = () => {
213
+ if (xhr.status >= 200 && xhr.status < 300) {
214
+ try {
215
+ const json = JSON.parse(xhr.responseText);
216
+ resolve(json?.data || json || []);
217
+ } catch (e) {
218
+ reject(e);
219
+ }
220
+ } else {
221
+ reject(new Error(xhr.statusText));
222
+ }
223
+ };
224
+
225
+ xhr.onerror = () => reject(new Error('Network error'));
226
+ xhr.send(ajax_method !== 'GET' ? JSON.stringify(ajax_data) : null);
227
+ });
228
+ }
229
+
230
+ _loadFromHtml() {
231
+ // Read existing content as the first slide
232
+ if (this.titleEl && this.subtitleEl) {
233
+ const title = this.titleEl.textContent.trim();
234
+ const subtitle = this.subtitleEl.textContent.trim();
235
+ if (title) {
236
+ this.slides = [{ title, subtitle }];
237
+ }
238
+ }
239
+ }
240
+
241
+ _bindEvents() {
242
+ // Navigation buttons
243
+ if (this.prevBtn && this.config.showPrevButton) {
244
+ this.prevBtn.addEventListener('click', (e) => {
245
+ e.preventDefault();
246
+ this.prev();
247
+ });
248
+ }
249
+
250
+ if (this.nextBtn && this.config.showNextButton) {
251
+ this.nextBtn.addEventListener('click', (e) => {
252
+ e.preventDefault();
253
+ this.next();
254
+ });
255
+ }
256
+
257
+ // Hover pause
258
+ if (this.config.pauseOnHover) {
259
+ this.wrapper.addEventListener('mouseenter', () => this.pause());
260
+ this.wrapper.addEventListener('mouseleave', () => this.resume());
261
+ }
262
+ }
263
+
264
+ _render() {
265
+ const slide = this.slides[this.currentIndex];
266
+ if (!slide) return;
267
+
268
+ const { dataMap } = this.config;
269
+ const title = this._getNestedValue(slide, dataMap.title) || '';
270
+ const subtitle = this._getNestedValue(slide, dataMap.subtitle) || '';
271
+ const url = this._getNestedValue(slide, dataMap.url) || null;
272
+
273
+ // Apply fade effect
274
+ if (this.contentEl) {
275
+ this.contentEl.classList.add('opacity-0');
276
+
277
+ setTimeout(() => {
278
+ if (this.titleEl) this.titleEl.textContent = title;
279
+ if (this.subtitleEl) this.subtitleEl.textContent = subtitle;
280
+
281
+ // Handle URL wrapping
282
+ if (url && this.contentEl) {
283
+ this.contentEl.style.cursor = 'pointer';
284
+ this.contentEl.onclick = () => window.location.href = url;
285
+ } else if (this.contentEl) {
286
+ this.contentEl.style.cursor = 'default';
287
+ this.contentEl.onclick = null;
288
+ }
289
+
290
+ this.contentEl.classList.remove('opacity-0');
291
+ }, 150);
292
+ } else {
293
+ if (this.titleEl) this.titleEl.textContent = title;
294
+ if (this.subtitleEl) this.subtitleEl.textContent = subtitle;
295
+ }
296
+
297
+ // Callback
298
+ if (this.config.onSlideChange) {
299
+ this.config.onSlideChange(slide, this.currentIndex, this.slides.length);
300
+ }
301
+
302
+ // Emit event
303
+ this.wrapper.dispatchEvent(new CustomEvent('dsslider:change', {
304
+ detail: { slide, index: this.currentIndex, total: this.slides.length }
305
+ }));
306
+ }
307
+
308
+ _handleEmpty() {
309
+ // Disable buttons
310
+ if (this.prevBtn) {
311
+ this.prevBtn.classList.add(...this.config.disabledClass.split(' '));
312
+ this.prevBtn.disabled = true;
313
+ }
314
+ if (this.nextBtn) {
315
+ this.nextBtn.classList.add(...this.config.disabledClass.split(' '));
316
+ this.nextBtn.disabled = true;
317
+ }
318
+
319
+ // Clear content
320
+ if (this.titleEl) this.titleEl.textContent = '';
321
+ if (this.subtitleEl) this.subtitleEl.textContent = '';
322
+
323
+ // Callback
324
+ if (this.config.onEmpty) {
325
+ this.config.onEmpty();
326
+ }
327
+
328
+ // Emit event
329
+ this.wrapper.dispatchEvent(new CustomEvent('dsslider:empty'));
330
+ }
331
+
332
+ _startTimer() {
333
+ this._stopTimer();
334
+
335
+ if (this.config.showTimerBorder && this.badgeEl) {
336
+ this._startTimerBorderAnimation();
337
+ }
338
+
339
+ this.timer = setTimeout(() => {
340
+ this.next();
341
+ }, this.config.interval);
342
+ }
343
+
344
+ _stopTimer() {
345
+ if (this.timer) {
346
+ clearTimeout(this.timer);
347
+ this.timer = null;
348
+ }
349
+ this._stopTimerBorderAnimation();
350
+ }
351
+
352
+ _startTimerBorderAnimation() {
353
+ if (!this.badgeEl) return;
354
+
355
+ const { interval, timerBorderColor, timerBorderWidth } = this.config;
356
+
357
+ // Create a pseudo-element style for the border animation
358
+ // We'll use a conic gradient that animates around the element
359
+ this.badgeEl.style.setProperty('--timer-duration', `${interval}ms`);
360
+ this.badgeEl.style.setProperty('--timer-color', timerBorderColor);
361
+
362
+ // Add animation class
363
+ this.badgeEl.classList.add('ds-slider-timer-active');
364
+
365
+ // Reset animation by forcing reflow
366
+ this.badgeEl.style.animation = 'none';
367
+ this.badgeEl.offsetHeight; // Trigger reflow
368
+ this.badgeEl.style.animation = `ds-slider-timer-border ${interval}ms linear`;
369
+ }
370
+
371
+ _stopTimerBorderAnimation() {
372
+ if (this.badgeEl) {
373
+ this.badgeEl.classList.remove('ds-slider-timer-active');
374
+ this.badgeEl.style.animation = '';
375
+ }
376
+ }
377
+
378
+ // Public methods
379
+ next() {
380
+ if (this.slides.length === 0) return;
381
+ this.currentIndex = (this.currentIndex + 1) % this.slides.length;
382
+ this._render();
383
+
384
+ if (this.isPlaying && !this.isPaused) {
385
+ this._startTimer();
386
+ }
387
+ }
388
+
389
+ prev() {
390
+ if (this.slides.length === 0) return;
391
+ this.currentIndex = (this.currentIndex - 1 + this.slides.length) % this.slides.length;
392
+ this._render();
393
+
394
+ if (this.isPlaying && !this.isPaused) {
395
+ this._startTimer();
396
+ }
397
+ }
398
+
399
+ goTo(index) {
400
+ if (index < 0 || index >= this.slides.length) return;
401
+ this.currentIndex = index;
402
+ this._render();
403
+
404
+ if (this.isPlaying && !this.isPaused) {
405
+ this._startTimer();
406
+ }
407
+ }
408
+
409
+ play() {
410
+ if (this.slides.length <= 1) return;
411
+ this.isPlaying = true;
412
+ this.isPaused = false;
413
+ this._startTimer();
414
+ }
415
+
416
+ pause() {
417
+ this.isPaused = true;
418
+ this._stopTimer();
419
+ }
420
+
421
+ resume() {
422
+ if (this.isPlaying) {
423
+ this.isPaused = false;
424
+ this._startTimer();
425
+ }
426
+ }
427
+
428
+ stop() {
429
+ this.isPlaying = false;
430
+ this.isPaused = false;
431
+ this._stopTimer();
432
+ }
433
+
434
+ // Reload data
435
+ async reload(newData = null) {
436
+ if (newData) {
437
+ this.slides = newData;
438
+ } else {
439
+ await this._loadData();
440
+ }
441
+
442
+ this.currentIndex = 0;
443
+
444
+ if (this.slides.length > 0) {
445
+ this._render();
446
+ if (this.config.autoPlay) {
447
+ this.play();
448
+ }
449
+ } else {
450
+ this._handleEmpty();
451
+ }
452
+ }
453
+
454
+ // Set AJAX params and reload
455
+ setParams(params) {
456
+ this.config.ajax_data = { ...this.config.ajax_data, ...params };
457
+ return this;
458
+ }
459
+
460
+ // Get current slide
461
+ getCurrentSlide() {
462
+ return this.slides[this.currentIndex] || null;
463
+ }
464
+
465
+ // Get all slides
466
+ getSlides() {
467
+ return this.slides;
468
+ }
469
+
470
+ // Destroy
471
+ destroy() {
472
+ this.stop();
473
+
474
+ // Remove event listeners
475
+ if (this.prevBtn) {
476
+ this.prevBtn.replaceWith(this.prevBtn.cloneNode(true));
477
+ }
478
+ if (this.nextBtn) {
479
+ this.nextBtn.replaceWith(this.nextBtn.cloneNode(true));
480
+ }
481
+
482
+ // Remove wrapper listeners
483
+ this.wrapper.replaceWith(this.wrapper.cloneNode(true));
484
+ }
485
+
486
+ // Helper to get nested value from object
487
+ _getNestedValue(obj, path) {
488
+ if (!path) return obj;
489
+ return path.split('.').reduce((acc, part) => acc && acc[part], obj);
490
+ }
491
+ }
492
+
493
+ // Add CSS for timer border animation
494
+ if (typeof document !== 'undefined') {
495
+ const style = document.createElement('style');
496
+ style.textContent = `
497
+ @keyframes ds-slider-timer-border {
498
+ 0% {
499
+ box-shadow: inset 0 0 0 0 var(--timer-color, rgba(251, 191, 36, 0.5));
500
+ }
501
+ 100% {
502
+ box-shadow: inset 0 0 0 2px var(--timer-color, rgba(251, 191, 36, 0.5));
503
+ }
504
+ }
505
+
506
+ .ds-slider-timer-active {
507
+ animation: ds-slider-timer-border 5000ms linear;
508
+ }
509
+
510
+ .ds-slider-content {
511
+ transition: opacity 0.15s ease-in-out;
512
+ }
513
+ `;
514
+ document.head.appendChild(style);
515
+ }
516
+
517
+ export default DSSimpleSlider;