vanduo-framework 1.1.8 → 1.2.1
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/README.md +42 -31
- package/dist/build-info.json +3 -3
- package/dist/vanduo.cjs.js +720 -111
- package/dist/vanduo.cjs.js.map +3 -3
- package/dist/vanduo.cjs.min.js +11 -11
- package/dist/vanduo.cjs.min.js.map +4 -4
- package/dist/vanduo.css +285 -1
- package/dist/vanduo.css.map +1 -1
- package/dist/vanduo.esm.js +720 -111
- package/dist/vanduo.esm.js.map +3 -3
- package/dist/vanduo.esm.min.js +11 -11
- package/dist/vanduo.esm.min.js.map +4 -4
- package/dist/vanduo.js +720 -111
- package/dist/vanduo.js.map +3 -3
- package/dist/vanduo.min.css +1 -1
- package/dist/vanduo.min.js +11 -11
- package/dist/vanduo.min.js.map +4 -4
- package/js/components/code-snippet.js +5 -3
- package/js/components/doc-search.js +90 -73
- package/js/components/grid.js +22 -22
- package/js/components/lazy-load.js +353 -0
- package/js/components/theme-customizer.js +20 -4
- package/js/components/tooltips.js +1 -1
- package/js/index.js +1 -0
- package/js/utils/helpers.js +24 -12
- package/js/vanduo.js +14 -14
- package/package.json +3 -3
|
@@ -377,8 +377,10 @@
|
|
|
377
377
|
// Apply syntax highlighting
|
|
378
378
|
html = this.highlightHtml(html);
|
|
379
379
|
|
|
380
|
-
// Set content
|
|
381
|
-
|
|
380
|
+
// Set content via DOM API to avoid string-based HTML insertion
|
|
381
|
+
const codeEl = document.createElement('code');
|
|
382
|
+
codeEl.innerHTML = html;
|
|
383
|
+
pane.replaceChildren(codeEl);
|
|
382
384
|
pane.dataset.extracted = 'true';
|
|
383
385
|
},
|
|
384
386
|
|
|
@@ -537,7 +539,7 @@
|
|
|
537
539
|
// Wrap code content
|
|
538
540
|
const codeWrapper = document.createElement('div');
|
|
539
541
|
codeWrapper.className = 'vd-code-snippet-code';
|
|
540
|
-
codeWrapper.
|
|
542
|
+
codeWrapper.appendChild(code.cloneNode(true));
|
|
541
543
|
|
|
542
544
|
// Replace code with new structure
|
|
543
545
|
code.parentNode.removeChild(code);
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
/**
|
|
37
37
|
* Default configuration
|
|
38
38
|
*/
|
|
39
|
-
|
|
39
|
+
const DEFAULTS = {
|
|
40
40
|
// Behavior
|
|
41
41
|
minQueryLength: 2,
|
|
42
42
|
maxResults: 10,
|
|
@@ -92,10 +92,10 @@
|
|
|
92
92
|
* @returns {Object} Search instance
|
|
93
93
|
*/
|
|
94
94
|
function createSearch(options) {
|
|
95
|
-
|
|
95
|
+
const config = Object.assign({}, DEFAULTS, options || {});
|
|
96
96
|
|
|
97
97
|
// Instance state
|
|
98
|
-
|
|
98
|
+
const state = {
|
|
99
99
|
initialized: false,
|
|
100
100
|
index: [],
|
|
101
101
|
results: [],
|
|
@@ -109,6 +109,23 @@
|
|
|
109
109
|
boundHandlers: {}
|
|
110
110
|
};
|
|
111
111
|
|
|
112
|
+
function safeInvokeCallback(name, fn, ...args) {
|
|
113
|
+
try {
|
|
114
|
+
fn(...args);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.warn('[Vanduo Search] Callback error in "' + name + '":', error);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function setResultsHtml(html) {
|
|
121
|
+
if (!state.resultsContainer) return;
|
|
122
|
+
try {
|
|
123
|
+
state.resultsContainer.innerHTML = html;
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.warn('[Vanduo Search] Failed to render results:', error);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
112
129
|
/**
|
|
113
130
|
* Initialize the search component
|
|
114
131
|
* Idempotent — safe to call more than once on the same instance.
|
|
@@ -175,22 +192,22 @@
|
|
|
175
192
|
}
|
|
176
193
|
|
|
177
194
|
// Build from DOM
|
|
178
|
-
|
|
179
|
-
|
|
195
|
+
const sections = document.querySelectorAll(config.contentSelector);
|
|
196
|
+
const categoryMap = buildCategoryMap();
|
|
180
197
|
|
|
181
198
|
sections.forEach(function(section) {
|
|
182
|
-
|
|
199
|
+
const id = section.id;
|
|
183
200
|
if (!id) return;
|
|
184
201
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
202
|
+
const titleEl = section.querySelector(config.titleSelector);
|
|
203
|
+
const title = titleEl ? titleEl.textContent.replace(/v[\d.]+/g, '').trim() : id;
|
|
204
|
+
const category = categoryMap[id] || 'Documentation';
|
|
205
|
+
const content = extractContent(section);
|
|
206
|
+
const keywords = extractKeywords(section, title);
|
|
207
|
+
const iconEl = titleEl ? titleEl.querySelector('i.ph') : null;
|
|
208
|
+
let icon = '';
|
|
192
209
|
if (iconEl && iconEl.classList) {
|
|
193
|
-
for (
|
|
210
|
+
for (let ci = 0; ci < iconEl.classList.length; ci++) {
|
|
194
211
|
if (iconEl.classList[ci].indexOf('ph-') === 0) {
|
|
195
212
|
icon = iconEl.classList[ci];
|
|
196
213
|
break;
|
|
@@ -215,17 +232,17 @@
|
|
|
215
232
|
* Build a map of section IDs to their categories
|
|
216
233
|
*/
|
|
217
234
|
function buildCategoryMap() {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
235
|
+
const map = {};
|
|
236
|
+
let currentCategory = 'Documentation';
|
|
237
|
+
const navItems = document.querySelectorAll(config.navSelector + ', ' + config.sectionSelector);
|
|
221
238
|
|
|
222
239
|
navItems.forEach(function(item) {
|
|
223
240
|
if (item.classList.contains('doc-nav-section')) {
|
|
224
241
|
currentCategory = item.textContent.trim();
|
|
225
242
|
} else {
|
|
226
|
-
|
|
243
|
+
const href = item.getAttribute('href');
|
|
227
244
|
if (href && href.startsWith('#')) {
|
|
228
|
-
|
|
245
|
+
const id = href.substring(1);
|
|
229
246
|
map[id] = currentCategory;
|
|
230
247
|
}
|
|
231
248
|
}
|
|
@@ -238,14 +255,14 @@
|
|
|
238
255
|
* Extract searchable content from a section
|
|
239
256
|
*/
|
|
240
257
|
function extractContent(section) {
|
|
241
|
-
|
|
258
|
+
const clone = section.cloneNode(true);
|
|
242
259
|
|
|
243
|
-
|
|
260
|
+
const toRemove = clone.querySelectorAll(config.excludeFromContent);
|
|
244
261
|
toRemove.forEach(function(el) {
|
|
245
262
|
el.remove();
|
|
246
263
|
});
|
|
247
264
|
|
|
248
|
-
|
|
265
|
+
let text = clone.textContent || '';
|
|
249
266
|
text = text.replace(/\s+/g, ' ').trim();
|
|
250
267
|
|
|
251
268
|
return text.substring(0, config.maxContentLength);
|
|
@@ -255,7 +272,7 @@
|
|
|
255
272
|
* Extract keywords from a section
|
|
256
273
|
*/
|
|
257
274
|
function extractKeywords(section, title) {
|
|
258
|
-
|
|
275
|
+
const keywords = [];
|
|
259
276
|
|
|
260
277
|
// Add title words
|
|
261
278
|
title.toLowerCase().split(/\s+/).forEach(function(word) {
|
|
@@ -265,10 +282,10 @@
|
|
|
265
282
|
});
|
|
266
283
|
|
|
267
284
|
// Add class names from code examples
|
|
268
|
-
|
|
285
|
+
const codeBlocks = section.querySelectorAll('code');
|
|
269
286
|
codeBlocks.forEach(function(code) {
|
|
270
|
-
|
|
271
|
-
|
|
287
|
+
const text = code.textContent || '';
|
|
288
|
+
const classMatches = text.match(/\.([\w-]+)/g);
|
|
272
289
|
if (classMatches) {
|
|
273
290
|
classMatches.forEach(function(match) {
|
|
274
291
|
keywords.push(match.substring(1).toLowerCase());
|
|
@@ -277,9 +294,9 @@
|
|
|
277
294
|
});
|
|
278
295
|
|
|
279
296
|
// Add data attributes
|
|
280
|
-
|
|
297
|
+
const dataAttrs = section.querySelectorAll('[data-tooltip], [data-modal]');
|
|
281
298
|
dataAttrs.forEach(function(el) {
|
|
282
|
-
|
|
299
|
+
const attrs = el.getAttributeNames().filter(function(name) {
|
|
283
300
|
return name.startsWith('data-');
|
|
284
301
|
});
|
|
285
302
|
attrs.forEach(function(attr) {
|
|
@@ -294,7 +311,7 @@
|
|
|
294
311
|
* Extract keywords from text string
|
|
295
312
|
*/
|
|
296
313
|
function extractKeywordsFromText(text) {
|
|
297
|
-
|
|
314
|
+
const words = text.toLowerCase().split(/\s+/);
|
|
298
315
|
return words.filter(function(word) {
|
|
299
316
|
return word.length > 2;
|
|
300
317
|
});
|
|
@@ -336,9 +353,9 @@
|
|
|
336
353
|
}
|
|
337
354
|
};
|
|
338
355
|
state.boundHandlers.handleResultClick = function(e) {
|
|
339
|
-
|
|
356
|
+
const result = e.target.closest('.vd-doc-search-result');
|
|
340
357
|
if (result) {
|
|
341
|
-
|
|
358
|
+
const index = parseInt(result.dataset.index, 10);
|
|
342
359
|
select(index);
|
|
343
360
|
}
|
|
344
361
|
};
|
|
@@ -378,7 +395,7 @@
|
|
|
378
395
|
* Set up ARIA attributes
|
|
379
396
|
*/
|
|
380
397
|
function setupAria() {
|
|
381
|
-
|
|
398
|
+
const resultsId = state.resultsContainer.id || 'search-results-' + Math.random().toString(36).substr(2, 9);
|
|
382
399
|
state.resultsContainer.id = resultsId;
|
|
383
400
|
|
|
384
401
|
state.input.setAttribute('role', 'combobox');
|
|
@@ -394,7 +411,7 @@
|
|
|
394
411
|
* Handle input changes
|
|
395
412
|
*/
|
|
396
413
|
function handleInput(e) {
|
|
397
|
-
|
|
414
|
+
const query = e.target.value.trim();
|
|
398
415
|
|
|
399
416
|
if (state.debounceTimer) {
|
|
400
417
|
clearTimeout(state.debounceTimer);
|
|
@@ -415,7 +432,7 @@
|
|
|
415
432
|
|
|
416
433
|
// Callback
|
|
417
434
|
if (typeof config.onSearch === 'function') {
|
|
418
|
-
config.onSearch
|
|
435
|
+
safeInvokeCallback('onSearch', config.onSearch, query, state.results);
|
|
419
436
|
}
|
|
420
437
|
}, config.debounceMs);
|
|
421
438
|
}
|
|
@@ -469,16 +486,16 @@
|
|
|
469
486
|
* Perform search
|
|
470
487
|
*/
|
|
471
488
|
function search(query) {
|
|
472
|
-
|
|
489
|
+
const terms = query.toLowerCase().split(/\s+/).filter(function(t) {
|
|
473
490
|
return t.length > 0;
|
|
474
491
|
});
|
|
475
|
-
|
|
492
|
+
const scored = [];
|
|
476
493
|
|
|
477
494
|
state.index.forEach(function(entry) {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
495
|
+
let score = 0;
|
|
496
|
+
const titleLower = entry.title.toLowerCase();
|
|
497
|
+
const categoryLower = entry.category.toLowerCase();
|
|
498
|
+
const contentLower = entry.content.toLowerCase();
|
|
482
499
|
|
|
483
500
|
terms.forEach(function(term) {
|
|
484
501
|
// Title match - highest priority
|
|
@@ -497,7 +514,7 @@
|
|
|
497
514
|
}
|
|
498
515
|
|
|
499
516
|
// Keyword match
|
|
500
|
-
|
|
517
|
+
const keywordMatch = entry.keywords.some(function(k) {
|
|
501
518
|
return k.includes(term);
|
|
502
519
|
});
|
|
503
520
|
if (keywordMatch) {
|
|
@@ -536,16 +553,16 @@
|
|
|
536
553
|
*/
|
|
537
554
|
function render() {
|
|
538
555
|
if (state.results.length === 0) {
|
|
539
|
-
|
|
556
|
+
setResultsHtml(renderEmpty());
|
|
540
557
|
return;
|
|
541
558
|
}
|
|
542
559
|
|
|
543
|
-
|
|
560
|
+
let html = '<ul class="vd-doc-search-results-list" role="listbox">';
|
|
544
561
|
|
|
545
562
|
state.results.forEach(function(result, index) {
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
563
|
+
const isActive = index === state.activeIndex;
|
|
564
|
+
const icon = result.icon || getCategoryIcon(result.categorySlug);
|
|
565
|
+
const excerpt = getExcerpt(result.content, state.query);
|
|
549
566
|
|
|
550
567
|
html += '<li class="vd-doc-search-result' + (isActive ? ' is-active' : '') + '"' +
|
|
551
568
|
' role="option"' +
|
|
@@ -568,7 +585,7 @@
|
|
|
568
585
|
html += '</ul>';
|
|
569
586
|
html += renderFooter();
|
|
570
587
|
|
|
571
|
-
|
|
588
|
+
setResultsHtml(html);
|
|
572
589
|
}
|
|
573
590
|
|
|
574
591
|
/**
|
|
@@ -604,13 +621,13 @@
|
|
|
604
621
|
* Get excerpt from content
|
|
605
622
|
*/
|
|
606
623
|
function getExcerpt(content, query) {
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
624
|
+
const terms = query.toLowerCase().split(/\s+/);
|
|
625
|
+
const contentLower = content.toLowerCase();
|
|
626
|
+
const excerptLength = 100;
|
|
610
627
|
|
|
611
|
-
|
|
612
|
-
for (
|
|
613
|
-
|
|
628
|
+
let matchPos = -1;
|
|
629
|
+
for (let i = 0; i < terms.length; i++) {
|
|
630
|
+
const pos = contentLower.indexOf(terms[i]);
|
|
614
631
|
if (pos !== -1 && (matchPos === -1 || pos < matchPos)) {
|
|
615
632
|
matchPos = pos;
|
|
616
633
|
}
|
|
@@ -620,9 +637,9 @@
|
|
|
620
637
|
return content.substring(0, excerptLength) + '...';
|
|
621
638
|
}
|
|
622
639
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
640
|
+
const start = Math.max(0, matchPos - 30);
|
|
641
|
+
const end = Math.min(content.length, matchPos + excerptLength);
|
|
642
|
+
let excerpt = content.substring(start, end);
|
|
626
643
|
|
|
627
644
|
if (start > 0) {
|
|
628
645
|
excerpt = '...' + excerpt;
|
|
@@ -640,15 +657,15 @@
|
|
|
640
657
|
function highlight(text, query) {
|
|
641
658
|
if (!query) return escapeHtml(text);
|
|
642
659
|
|
|
643
|
-
|
|
660
|
+
const terms = query.toLowerCase().split(/\s+/).filter(function(t) {
|
|
644
661
|
return t.length > 0;
|
|
645
662
|
});
|
|
646
|
-
|
|
663
|
+
let escaped = escapeHtml(text);
|
|
647
664
|
|
|
648
665
|
terms.forEach(function(term) {
|
|
649
666
|
// Skip overly long terms to prevent ReDoS
|
|
650
667
|
if (term.length > 50) return;
|
|
651
|
-
|
|
668
|
+
const regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + ')', 'gi');
|
|
652
669
|
escaped = escaped.replace(regex, '<' + config.highlightTag + '>$1</' + config.highlightTag + '>');
|
|
653
670
|
});
|
|
654
671
|
|
|
@@ -659,7 +676,7 @@
|
|
|
659
676
|
* Escape HTML entities
|
|
660
677
|
*/
|
|
661
678
|
function escapeHtml(text) {
|
|
662
|
-
|
|
679
|
+
const div = document.createElement('div');
|
|
663
680
|
div.textContent = text;
|
|
664
681
|
return div.innerHTML;
|
|
665
682
|
}
|
|
@@ -668,7 +685,7 @@
|
|
|
668
685
|
* Navigate results with keyboard
|
|
669
686
|
*/
|
|
670
687
|
function navigate(direction) {
|
|
671
|
-
|
|
688
|
+
let newIndex = state.activeIndex + direction;
|
|
672
689
|
|
|
673
690
|
if (newIndex < 0) {
|
|
674
691
|
newIndex = state.results.length - 1;
|
|
@@ -683,7 +700,7 @@
|
|
|
683
700
|
* Set active result index
|
|
684
701
|
*/
|
|
685
702
|
function setActiveIndex(index) {
|
|
686
|
-
|
|
703
|
+
const prevActive = state.resultsContainer.querySelector('.vd-doc-search-result.is-active');
|
|
687
704
|
if (prevActive) {
|
|
688
705
|
prevActive.classList.remove('is-active');
|
|
689
706
|
prevActive.setAttribute('aria-selected', 'false');
|
|
@@ -691,7 +708,7 @@
|
|
|
691
708
|
|
|
692
709
|
state.activeIndex = index;
|
|
693
710
|
|
|
694
|
-
|
|
711
|
+
const newActive = state.resultsContainer.querySelector('[data-index="' + index + '"]');
|
|
695
712
|
if (newActive) {
|
|
696
713
|
newActive.classList.add('is-active');
|
|
697
714
|
newActive.setAttribute('aria-selected', 'true');
|
|
@@ -704,7 +721,7 @@
|
|
|
704
721
|
* Select a result
|
|
705
722
|
*/
|
|
706
723
|
function select(index) {
|
|
707
|
-
|
|
724
|
+
const result = state.results[index];
|
|
708
725
|
if (!result) return;
|
|
709
726
|
|
|
710
727
|
// Close search
|
|
@@ -714,12 +731,12 @@
|
|
|
714
731
|
|
|
715
732
|
// Custom callback
|
|
716
733
|
if (typeof config.onSelect === 'function') {
|
|
717
|
-
config.onSelect
|
|
734
|
+
safeInvokeCallback('onSelect', config.onSelect, result);
|
|
718
735
|
return;
|
|
719
736
|
}
|
|
720
737
|
|
|
721
738
|
// Default behavior: navigate to section
|
|
722
|
-
|
|
739
|
+
const section = document.querySelector(result.url);
|
|
723
740
|
if (section) {
|
|
724
741
|
section.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
725
742
|
window.history.pushState(null, '', result.url);
|
|
@@ -731,7 +748,7 @@
|
|
|
731
748
|
* Update sidebar navigation active state
|
|
732
749
|
*/
|
|
733
750
|
function updateSidebarActive(sectionId) {
|
|
734
|
-
|
|
751
|
+
const navLinks = document.querySelectorAll(config.navSelector);
|
|
735
752
|
navLinks.forEach(function(link) {
|
|
736
753
|
link.classList.remove('active');
|
|
737
754
|
if (link.getAttribute('href') === '#' + sectionId) {
|
|
@@ -751,7 +768,7 @@
|
|
|
751
768
|
state.input.setAttribute('aria-expanded', 'true');
|
|
752
769
|
|
|
753
770
|
if (typeof config.onOpen === 'function') {
|
|
754
|
-
config.onOpen
|
|
771
|
+
safeInvokeCallback('onOpen', config.onOpen);
|
|
755
772
|
}
|
|
756
773
|
}
|
|
757
774
|
|
|
@@ -768,7 +785,7 @@
|
|
|
768
785
|
state.input.removeAttribute('aria-activedescendant');
|
|
769
786
|
|
|
770
787
|
if (typeof config.onClose === 'function') {
|
|
771
|
-
config.onClose
|
|
788
|
+
safeInvokeCallback('onClose', config.onClose);
|
|
772
789
|
}
|
|
773
790
|
}
|
|
774
791
|
|
|
@@ -789,7 +806,7 @@
|
|
|
789
806
|
}
|
|
790
807
|
|
|
791
808
|
if (state.resultsContainer) {
|
|
792
|
-
|
|
809
|
+
setResultsHtml('');
|
|
793
810
|
}
|
|
794
811
|
}
|
|
795
812
|
|
|
@@ -822,7 +839,7 @@
|
|
|
822
839
|
}
|
|
823
840
|
|
|
824
841
|
// Public instance API
|
|
825
|
-
|
|
842
|
+
const instance = {
|
|
826
843
|
init: init,
|
|
827
844
|
destroy: destroy,
|
|
828
845
|
rebuild: rebuild,
|
|
@@ -840,12 +857,12 @@
|
|
|
840
857
|
/**
|
|
841
858
|
* Search Component (singleton for backward compatibility)
|
|
842
859
|
*/
|
|
843
|
-
|
|
860
|
+
const Search = {
|
|
844
861
|
// Factory method — creates and auto-initializes a new independent instance.
|
|
845
862
|
// Always returns the instance so callers retain a reference even if the
|
|
846
863
|
// DOM container is not yet available (they can retry init() later).
|
|
847
864
|
create: function(options) {
|
|
848
|
-
|
|
865
|
+
const instance = createSearch(options);
|
|
849
866
|
if (instance) {
|
|
850
867
|
instance.init();
|
|
851
868
|
}
|
package/js/components/grid.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
(function () {
|
|
8
8
|
'use strict';
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
const supportsHas = (function () {
|
|
11
11
|
try {
|
|
12
12
|
return CSS.supports('selector(:has(*))');
|
|
13
13
|
} catch (_e) {
|
|
@@ -18,14 +18,14 @@
|
|
|
18
18
|
/**
|
|
19
19
|
* Grid Layout Component
|
|
20
20
|
*/
|
|
21
|
-
|
|
21
|
+
const GridLayout = {
|
|
22
22
|
instances: new Map(),
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Initialize all grid layout containers
|
|
26
26
|
*/
|
|
27
27
|
init: function () {
|
|
28
|
-
|
|
28
|
+
const containers = document.querySelectorAll('[data-layout-mode]');
|
|
29
29
|
|
|
30
30
|
containers.forEach(function (container) {
|
|
31
31
|
if (this.instances.has(container)) {
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
* @param {HTMLElement} container - Element with data-layout-mode
|
|
43
43
|
*/
|
|
44
44
|
initContainer: function (container) {
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
const mode = container.getAttribute('data-layout-mode') || 'standard';
|
|
46
|
+
const cleanupFunctions = [];
|
|
47
47
|
|
|
48
48
|
this.applyMode(container, mode);
|
|
49
49
|
|
|
@@ -60,17 +60,17 @@
|
|
|
60
60
|
* Initialize toggle buttons that target grid containers
|
|
61
61
|
*/
|
|
62
62
|
initToggleButtons: function () {
|
|
63
|
-
|
|
63
|
+
const toggleButtons = document.querySelectorAll('[data-grid-toggle]');
|
|
64
64
|
|
|
65
65
|
toggleButtons.forEach(function (button) {
|
|
66
66
|
if (button.getAttribute('data-grid-initialized') === 'true') {
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
const clickHandler = function (e) {
|
|
71
71
|
e.preventDefault();
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
const targetSelector = button.getAttribute('data-grid-toggle');
|
|
73
|
+
let target;
|
|
74
74
|
|
|
75
75
|
if (targetSelector) {
|
|
76
76
|
target = document.querySelector(targetSelector);
|
|
@@ -102,10 +102,10 @@
|
|
|
102
102
|
applyFibFallback: function (container) {
|
|
103
103
|
if (supportsHas) return;
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
const rows = container.querySelectorAll('.vd-row, .row');
|
|
106
106
|
rows.forEach(function (row) {
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
const cols = row.querySelectorAll(':scope > [class*="vd-col-"], :scope > [class*="col-"]');
|
|
108
|
+
const count = cols.length;
|
|
109
109
|
|
|
110
110
|
if (count === 1) {
|
|
111
111
|
row.style.gridTemplateColumns = '1fr';
|
|
@@ -126,7 +126,7 @@
|
|
|
126
126
|
* @param {HTMLElement} container - Grid container
|
|
127
127
|
*/
|
|
128
128
|
removeFibFallback: function (container) {
|
|
129
|
-
|
|
129
|
+
const rows = container.querySelectorAll('.vd-row, .row');
|
|
130
130
|
rows.forEach(function (row) {
|
|
131
131
|
row.style.gridTemplateColumns = '';
|
|
132
132
|
});
|
|
@@ -152,11 +152,11 @@
|
|
|
152
152
|
container.setAttribute('aria-label', 'Grid layout: ' + mode + ' mode');
|
|
153
153
|
|
|
154
154
|
// Update associated toggle button states
|
|
155
|
-
|
|
155
|
+
const toggleButtons = document.querySelectorAll('[data-grid-toggle]');
|
|
156
156
|
toggleButtons.forEach(function (btn) {
|
|
157
|
-
|
|
157
|
+
const targetSelector = btn.getAttribute('data-grid-toggle');
|
|
158
158
|
if (targetSelector && container.matches(targetSelector)) {
|
|
159
|
-
|
|
159
|
+
const isActive = (mode === 'fibonacci');
|
|
160
160
|
if (isActive) {
|
|
161
161
|
btn.classList.add('is-active');
|
|
162
162
|
} else {
|
|
@@ -167,13 +167,13 @@
|
|
|
167
167
|
});
|
|
168
168
|
|
|
169
169
|
// Store mode in instance
|
|
170
|
-
|
|
170
|
+
const instance = this.instances.get(container);
|
|
171
171
|
if (instance) {
|
|
172
172
|
instance.mode = mode;
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
// Dispatch custom event
|
|
176
|
-
|
|
176
|
+
let event;
|
|
177
177
|
try {
|
|
178
178
|
event = new CustomEvent('grid:modechange', {
|
|
179
179
|
bubbles: true,
|
|
@@ -202,8 +202,8 @@
|
|
|
202
202
|
}
|
|
203
203
|
if (!container) return;
|
|
204
204
|
|
|
205
|
-
|
|
206
|
-
|
|
205
|
+
const currentMode = container.getAttribute('data-layout-mode') || 'standard';
|
|
206
|
+
const newMode = (currentMode === 'fibonacci') ? 'standard' : 'fibonacci';
|
|
207
207
|
this.applyMode(container, newMode);
|
|
208
208
|
},
|
|
209
209
|
|
|
@@ -240,7 +240,7 @@
|
|
|
240
240
|
* @param {HTMLElement} container - Grid container
|
|
241
241
|
*/
|
|
242
242
|
destroy: function (container) {
|
|
243
|
-
|
|
243
|
+
const instance = this.instances.get(container);
|
|
244
244
|
if (!instance) return;
|
|
245
245
|
|
|
246
246
|
instance.cleanup.forEach(function (fn) { fn(); });
|
|
@@ -258,7 +258,7 @@
|
|
|
258
258
|
this.destroy(container);
|
|
259
259
|
}.bind(this));
|
|
260
260
|
|
|
261
|
-
|
|
261
|
+
const toggleButtons = document.querySelectorAll('[data-grid-initialized="true"]');
|
|
262
262
|
toggleButtons.forEach(function (button) {
|
|
263
263
|
if (button._gridCleanup) {
|
|
264
264
|
button._gridCleanup();
|