vanduo-framework 1.1.8
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/LICENSE +35 -0
- package/README.md +205 -0
- package/css/components/alerts.css +224 -0
- package/css/components/avatar.css +275 -0
- package/css/components/badges.css +230 -0
- package/css/components/breadcrumbs.css +146 -0
- package/css/components/button-group.css +82 -0
- package/css/components/buttons.css +530 -0
- package/css/components/cards.css +304 -0
- package/css/components/chips.css +259 -0
- package/css/components/code-snippet.css +555 -0
- package/css/components/collapsible.css +267 -0
- package/css/components/collections.css +253 -0
- package/css/components/doc-search.css +464 -0
- package/css/components/doc-tabs.css +38 -0
- package/css/components/draggable.css +317 -0
- package/css/components/dropdown.css +266 -0
- package/css/components/footer.css +375 -0
- package/css/components/forms.css +1774 -0
- package/css/components/image-box.css +279 -0
- package/css/components/modals.css +285 -0
- package/css/components/navbar.css +530 -0
- package/css/components/pagination.css +186 -0
- package/css/components/preloader.css +340 -0
- package/css/components/progress.css +107 -0
- package/css/components/sidenav.css +301 -0
- package/css/components/skeleton.css +241 -0
- package/css/components/spinner.css +144 -0
- package/css/components/tabs.css +327 -0
- package/css/components/theme-customizer.css +835 -0
- package/css/components/toast.css +357 -0
- package/css/components/tooltips.css +270 -0
- package/css/core/colors.css +1017 -0
- package/css/core/fonts.css +266 -0
- package/css/core/grid.css +1699 -0
- package/css/core/helpers.css +2202 -0
- package/css/core/reset.css +128 -0
- package/css/core/tokens.css +213 -0
- package/css/core/typography.css +405 -0
- package/css/core/vd-aliases.css +47 -0
- package/css/effects/parallax.css +113 -0
- package/css/icons/icons-all.css +23 -0
- package/css/icons/icons.css +25 -0
- package/css/utilities/media.css +167 -0
- package/css/utilities/print.css +111 -0
- package/css/utilities/shadow.css +243 -0
- package/css/utilities/table.css +381 -0
- package/css/utilities/transforms.css +71 -0
- package/css/utilities/transitions.css +87 -0
- package/css/vanduo.css +80 -0
- package/dist/build-info.json +6 -0
- package/dist/fonts/fira-sans/fira-sans-bold.woff2 +0 -0
- package/dist/fonts/fira-sans/fira-sans-medium.woff2 +0 -0
- package/dist/fonts/fira-sans/fira-sans-regular.woff2 +0 -0
- package/dist/fonts/ibm-plex/ibm-plex-sans-bold.woff2 +0 -0
- package/dist/fonts/ibm-plex/ibm-plex-sans-medium.woff2 +0 -0
- package/dist/fonts/ibm-plex/ibm-plex-sans-regular.woff2 +0 -0
- package/dist/fonts/inter/inter-bold.woff2 +0 -0
- package/dist/fonts/inter/inter-medium.woff2 +0 -0
- package/dist/fonts/inter/inter-regular.woff2 +0 -0
- package/dist/fonts/inter/inter-semibold.woff2 +0 -0
- package/dist/fonts/jetbrains-mono/jetbrains-mono-bold.woff2 +0 -0
- package/dist/fonts/jetbrains-mono/jetbrains-mono-regular.woff2 +0 -0
- package/dist/fonts/open-sans/open-sans-bold.woff2 +0 -0
- package/dist/fonts/open-sans/open-sans-medium.woff2 +0 -0
- package/dist/fonts/open-sans/open-sans-regular.woff2 +0 -0
- package/dist/fonts/rubik/rubik-bold.woff2 +0 -0
- package/dist/fonts/rubik/rubik-medium.woff2 +0 -0
- package/dist/fonts/rubik/rubik-regular.woff2 +0 -0
- package/dist/fonts/source-sans/source-sans-bold.woff2 +0 -0
- package/dist/fonts/source-sans/source-sans-regular.woff2 +0 -0
- package/dist/fonts/source-sans/source-sans-semibold.woff2 +0 -0
- package/dist/fonts/titillium-web/titillium-web-bold.woff2 +0 -0
- package/dist/fonts/titillium-web/titillium-web-regular.woff2 +0 -0
- package/dist/fonts/titillium-web/titillium-web-semibold.woff2 +0 -0
- package/dist/fonts/ubuntu/ubuntu-bold.woff2 +0 -0
- package/dist/fonts/ubuntu/ubuntu-medium.woff2 +0 -0
- package/dist/fonts/ubuntu/ubuntu-regular.woff2 +0 -0
- package/dist/icons/phosphor/LICENSE +21 -0
- package/dist/icons/phosphor/bold/Phosphor-Bold.ttf +0 -0
- package/dist/icons/phosphor/bold/Phosphor-Bold.woff +0 -0
- package/dist/icons/phosphor/bold/Phosphor-Bold.woff2 +0 -0
- package/dist/icons/phosphor/bold/style.css +4627 -0
- package/dist/icons/phosphor/duotone/Phosphor-Duotone.ttf +0 -0
- package/dist/icons/phosphor/duotone/Phosphor-Duotone.woff +0 -0
- package/dist/icons/phosphor/duotone/Phosphor-Duotone.woff2 +0 -0
- package/dist/icons/phosphor/duotone/style.css +12115 -0
- package/dist/icons/phosphor/fill/Phosphor-Fill.ttf +0 -0
- package/dist/icons/phosphor/fill/Phosphor-Fill.woff +0 -0
- package/dist/icons/phosphor/fill/Phosphor-Fill.woff2 +0 -0
- package/dist/icons/phosphor/fill/style.css +4627 -0
- package/dist/icons/phosphor/light/Phosphor-Light.ttf +0 -0
- package/dist/icons/phosphor/light/Phosphor-Light.woff +0 -0
- package/dist/icons/phosphor/light/Phosphor-Light.woff2 +0 -0
- package/dist/icons/phosphor/light/style.css +4627 -0
- package/dist/icons/phosphor/regular/Phosphor.ttf +0 -0
- package/dist/icons/phosphor/regular/Phosphor.woff +0 -0
- package/dist/icons/phosphor/regular/Phosphor.woff2 +0 -0
- package/dist/icons/phosphor/regular/style.css +4627 -0
- package/dist/icons/phosphor/thin/Phosphor-Thin.ttf +0 -0
- package/dist/icons/phosphor/thin/Phosphor-Thin.woff +0 -0
- package/dist/icons/phosphor/thin/Phosphor-Thin.woff2 +0 -0
- package/dist/icons/phosphor/thin/style.css +4627 -0
- package/dist/vanduo.cjs.js +5569 -0
- package/dist/vanduo.cjs.js.map +7 -0
- package/dist/vanduo.cjs.min.js +48 -0
- package/dist/vanduo.cjs.min.js.map +7 -0
- package/dist/vanduo.css +60666 -0
- package/dist/vanduo.css.map +1 -0
- package/dist/vanduo.esm.js +5548 -0
- package/dist/vanduo.esm.js.map +7 -0
- package/dist/vanduo.esm.min.js +48 -0
- package/dist/vanduo.esm.min.js.map +7 -0
- package/dist/vanduo.js +5545 -0
- package/dist/vanduo.js.map +7 -0
- package/dist/vanduo.min.css +2 -0
- package/dist/vanduo.min.css.map +1 -0
- package/dist/vanduo.min.js +48 -0
- package/dist/vanduo.min.js.map +7 -0
- package/fonts/fira-sans/fira-sans-bold.woff2 +0 -0
- package/fonts/fira-sans/fira-sans-medium.woff2 +0 -0
- package/fonts/fira-sans/fira-sans-regular.woff2 +0 -0
- package/fonts/ibm-plex/ibm-plex-sans-bold.woff2 +0 -0
- package/fonts/ibm-plex/ibm-plex-sans-medium.woff2 +0 -0
- package/fonts/ibm-plex/ibm-plex-sans-regular.woff2 +0 -0
- package/fonts/inter/inter-bold.woff2 +0 -0
- package/fonts/inter/inter-medium.woff2 +0 -0
- package/fonts/inter/inter-regular.woff2 +0 -0
- package/fonts/inter/inter-semibold.woff2 +0 -0
- package/fonts/jetbrains-mono/jetbrains-mono-bold.woff2 +0 -0
- package/fonts/jetbrains-mono/jetbrains-mono-regular.woff2 +0 -0
- package/fonts/open-sans/open-sans-bold.woff2 +0 -0
- package/fonts/open-sans/open-sans-medium.woff2 +0 -0
- package/fonts/open-sans/open-sans-regular.woff2 +0 -0
- package/fonts/rubik/rubik-bold.woff2 +0 -0
- package/fonts/rubik/rubik-medium.woff2 +0 -0
- package/fonts/rubik/rubik-regular.woff2 +0 -0
- package/fonts/source-sans/source-sans-bold.woff2 +0 -0
- package/fonts/source-sans/source-sans-regular.woff2 +0 -0
- package/fonts/source-sans/source-sans-semibold.woff2 +0 -0
- package/fonts/titillium-web/titillium-web-bold.woff2 +0 -0
- package/fonts/titillium-web/titillium-web-regular.woff2 +0 -0
- package/fonts/titillium-web/titillium-web-semibold.woff2 +0 -0
- package/fonts/ubuntu/ubuntu-bold.woff2 +0 -0
- package/fonts/ubuntu/ubuntu-medium.woff2 +0 -0
- package/fonts/ubuntu/ubuntu-regular.woff2 +0 -0
- package/icons/phosphor/LICENSE +21 -0
- package/icons/phosphor/bold/Phosphor-Bold.ttf +0 -0
- package/icons/phosphor/bold/Phosphor-Bold.woff +0 -0
- package/icons/phosphor/bold/Phosphor-Bold.woff2 +0 -0
- package/icons/phosphor/bold/style.css +4627 -0
- package/icons/phosphor/duotone/Phosphor-Duotone.ttf +0 -0
- package/icons/phosphor/duotone/Phosphor-Duotone.woff +0 -0
- package/icons/phosphor/duotone/Phosphor-Duotone.woff2 +0 -0
- package/icons/phosphor/duotone/style.css +12115 -0
- package/icons/phosphor/fill/Phosphor-Fill.ttf +0 -0
- package/icons/phosphor/fill/Phosphor-Fill.woff +0 -0
- package/icons/phosphor/fill/Phosphor-Fill.woff2 +0 -0
- package/icons/phosphor/fill/style.css +4627 -0
- package/icons/phosphor/light/Phosphor-Light.ttf +0 -0
- package/icons/phosphor/light/Phosphor-Light.woff +0 -0
- package/icons/phosphor/light/Phosphor-Light.woff2 +0 -0
- package/icons/phosphor/light/style.css +4627 -0
- package/icons/phosphor/regular/Phosphor.ttf +0 -0
- package/icons/phosphor/regular/Phosphor.woff +0 -0
- package/icons/phosphor/regular/Phosphor.woff2 +0 -0
- package/icons/phosphor/regular/style.css +4627 -0
- package/icons/phosphor/thin/Phosphor-Thin.ttf +0 -0
- package/icons/phosphor/thin/Phosphor-Thin.woff +0 -0
- package/icons/phosphor/thin/Phosphor-Thin.woff2 +0 -0
- package/icons/phosphor/thin/style.css +4627 -0
- package/js/components/code-snippet.js +639 -0
- package/js/components/collapsible.js +226 -0
- package/js/components/doc-search.js +936 -0
- package/js/components/draggable.js +725 -0
- package/js/components/dropdown.js +362 -0
- package/js/components/font-switcher.js +253 -0
- package/js/components/grid.js +279 -0
- package/js/components/image-box.js +372 -0
- package/js/components/modals.js +367 -0
- package/js/components/navbar.js +264 -0
- package/js/components/pagination.js +286 -0
- package/js/components/parallax.js +216 -0
- package/js/components/preloader.js +183 -0
- package/js/components/select.js +444 -0
- package/js/components/sidenav.js +303 -0
- package/js/components/tabs.js +303 -0
- package/js/components/theme-customizer.js +784 -0
- package/js/components/theme-switcher.js +183 -0
- package/js/components/toast.js +343 -0
- package/js/components/tooltips.js +306 -0
- package/js/index.js +52 -0
- package/js/utils/helpers.js +306 -0
- package/js/utils/lifecycle.js +135 -0
- package/js/vanduo.js +120 -0
- package/package.json +78 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vanduo Framework - Pagination Component
|
|
3
|
+
* JavaScript functionality for dynamic pagination
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
(function() {
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Pagination Component
|
|
11
|
+
*/
|
|
12
|
+
const Pagination = {
|
|
13
|
+
// Store initialized paginations and their cleanup functions
|
|
14
|
+
instances: new Map(),
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Initialize pagination components
|
|
18
|
+
*/
|
|
19
|
+
init: function() {
|
|
20
|
+
const paginations = document.querySelectorAll('.vd-pagination[data-pagination]');
|
|
21
|
+
|
|
22
|
+
paginations.forEach(pagination => {
|
|
23
|
+
if (this.instances.has(pagination)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
this.initPagination(pagination);
|
|
27
|
+
});
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Initialize a pagination
|
|
32
|
+
* @param {HTMLElement} pagination - Pagination container
|
|
33
|
+
*/
|
|
34
|
+
initPagination: function(pagination) {
|
|
35
|
+
const totalPages = parseInt(pagination.dataset.totalPages) || 1;
|
|
36
|
+
const currentPage = parseInt(pagination.dataset.currentPage) || 1;
|
|
37
|
+
const maxVisible = parseInt(pagination.dataset.maxVisible) || 7;
|
|
38
|
+
|
|
39
|
+
this.render(pagination, {
|
|
40
|
+
totalPages: totalPages,
|
|
41
|
+
currentPage: currentPage,
|
|
42
|
+
maxVisible: maxVisible
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Handle clicks (event delegation)
|
|
46
|
+
const clickHandler = (e) => {
|
|
47
|
+
const link = e.target.closest('.vd-pagination-link');
|
|
48
|
+
if (!link || link.closest('.vd-pagination-item.disabled') || link.closest('.vd-pagination-item.active')) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
e.preventDefault();
|
|
53
|
+
|
|
54
|
+
const item = link.closest('.vd-pagination-item');
|
|
55
|
+
const page = item.dataset.page;
|
|
56
|
+
|
|
57
|
+
if (page) {
|
|
58
|
+
this.goToPage(pagination, parseInt(page));
|
|
59
|
+
} else if (item.classList.contains('pagination-prev')) {
|
|
60
|
+
this.prevPage(pagination);
|
|
61
|
+
} else if (item.classList.contains('pagination-next')) {
|
|
62
|
+
this.nextPage(pagination);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
pagination.addEventListener('click', clickHandler);
|
|
66
|
+
|
|
67
|
+
this.instances.set(pagination, {
|
|
68
|
+
cleanup: [() => pagination.removeEventListener('click', clickHandler)]
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Render pagination
|
|
74
|
+
* @param {HTMLElement} pagination - Pagination container
|
|
75
|
+
* @param {Object} options - Pagination options
|
|
76
|
+
*/
|
|
77
|
+
render: function(pagination, options) {
|
|
78
|
+
const { totalPages, currentPage, maxVisible } = options;
|
|
79
|
+
|
|
80
|
+
if (totalPages <= 1) {
|
|
81
|
+
pagination.innerHTML = '';
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let html = '';
|
|
86
|
+
|
|
87
|
+
// Previous button
|
|
88
|
+
html += `<li class="vd-pagination-item vd-pagination-prev pagination-item pagination-prev ${currentPage === 1 ? 'disabled' : ''}">`;
|
|
89
|
+
html += `<a class="vd-pagination-link pagination-link" href="#" aria-label="Previous">Previous</a>`;
|
|
90
|
+
html += `</li>`;
|
|
91
|
+
|
|
92
|
+
// Calculate page range
|
|
93
|
+
const pages = this.calculatePages(currentPage, totalPages, maxVisible);
|
|
94
|
+
|
|
95
|
+
// Page numbers
|
|
96
|
+
let lastPage = 0;
|
|
97
|
+
pages.forEach(page => {
|
|
98
|
+
if (page === 'ellipsis') {
|
|
99
|
+
html += `<li class="vd-pagination-item pagination-item"><span class="vd-pagination-ellipsis pagination-ellipsis">…</span></li>`;
|
|
100
|
+
} else {
|
|
101
|
+
if (page !== lastPage + 1 && lastPage > 0) {
|
|
102
|
+
html += `<li class="vd-pagination-item pagination-item"><span class="vd-pagination-ellipsis pagination-ellipsis">…</span></li>`;
|
|
103
|
+
}
|
|
104
|
+
const safePage = Number(page);
|
|
105
|
+
html += `<li class="vd-pagination-item pagination-item ${safePage === currentPage ? 'active' : ''}" data-page="${safePage}">`;
|
|
106
|
+
html += `<a class="vd-pagination-link pagination-link" href="#" aria-label="Page ${safePage}">${safePage}</a>`;
|
|
107
|
+
html += `</li>`;
|
|
108
|
+
lastPage = page;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Next button
|
|
113
|
+
html += `<li class="vd-pagination-item vd-pagination-next pagination-item pagination-next ${currentPage === totalPages ? 'disabled' : ''}">`;
|
|
114
|
+
html += `<a class="vd-pagination-link pagination-link" href="#" aria-label="Next">Next</a>`;
|
|
115
|
+
html += `</li>`;
|
|
116
|
+
|
|
117
|
+
pagination.innerHTML = html;
|
|
118
|
+
|
|
119
|
+
// Update data attributes
|
|
120
|
+
pagination.dataset.currentPage = currentPage;
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Calculate which pages to show
|
|
125
|
+
* @param {number} currentPage - Current page
|
|
126
|
+
* @param {number} totalPages - Total pages
|
|
127
|
+
* @param {number} maxVisible - Maximum visible pages
|
|
128
|
+
* @returns {Array} Array of page numbers or 'ellipsis'
|
|
129
|
+
*/
|
|
130
|
+
calculatePages: function(currentPage, totalPages, maxVisible) {
|
|
131
|
+
const pages = [];
|
|
132
|
+
const half = Math.floor(maxVisible / 2);
|
|
133
|
+
|
|
134
|
+
if (totalPages <= maxVisible) {
|
|
135
|
+
// Show all pages
|
|
136
|
+
for (let i = 1; i <= totalPages; i++) {
|
|
137
|
+
pages.push(i);
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
// Always show first page
|
|
141
|
+
pages.push(1);
|
|
142
|
+
|
|
143
|
+
let start = Math.max(2, currentPage - half);
|
|
144
|
+
let end = Math.min(totalPages - 1, currentPage + half);
|
|
145
|
+
|
|
146
|
+
// Adjust if we're near the start
|
|
147
|
+
if (currentPage <= half + 1) {
|
|
148
|
+
end = Math.min(totalPages - 1, maxVisible - 1);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Adjust if we're near the end
|
|
152
|
+
if (currentPage >= totalPages - half) {
|
|
153
|
+
start = Math.max(2, totalPages - maxVisible + 2);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Add ellipsis before if needed
|
|
157
|
+
if (start > 2) {
|
|
158
|
+
pages.push('ellipsis');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Add middle pages
|
|
162
|
+
for (let i = start; i <= end; i++) {
|
|
163
|
+
pages.push(i);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Add ellipsis after if needed
|
|
167
|
+
if (end < totalPages - 1) {
|
|
168
|
+
pages.push('ellipsis');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Always show last page
|
|
172
|
+
if (totalPages > 1) {
|
|
173
|
+
pages.push(totalPages);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return pages;
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Go to specific page
|
|
182
|
+
* @param {HTMLElement} pagination - Pagination container
|
|
183
|
+
* @param {number} page - Page number
|
|
184
|
+
*/
|
|
185
|
+
goToPage: function(pagination, page) {
|
|
186
|
+
const totalPages = parseInt(pagination.dataset.totalPages) || 1;
|
|
187
|
+
const maxVisible = parseInt(pagination.dataset.maxVisible) || 7;
|
|
188
|
+
|
|
189
|
+
if (page < 1 || page > totalPages) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
this.render(pagination, {
|
|
194
|
+
totalPages: totalPages,
|
|
195
|
+
currentPage: page,
|
|
196
|
+
maxVisible: maxVisible
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// Dispatch event
|
|
200
|
+
pagination.dispatchEvent(new CustomEvent('pagination:change', {
|
|
201
|
+
bubbles: true,
|
|
202
|
+
detail: { page, totalPages }
|
|
203
|
+
}));
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Go to previous page
|
|
208
|
+
* @param {HTMLElement} pagination - Pagination container
|
|
209
|
+
*/
|
|
210
|
+
prevPage: function(pagination) {
|
|
211
|
+
const currentPage = parseInt(pagination.dataset.currentPage) || 1;
|
|
212
|
+
if (currentPage > 1) {
|
|
213
|
+
this.goToPage(pagination, currentPage - 1);
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Go to next page
|
|
219
|
+
* @param {HTMLElement} pagination - Pagination container
|
|
220
|
+
*/
|
|
221
|
+
nextPage: function(pagination) {
|
|
222
|
+
const currentPage = parseInt(pagination.dataset.currentPage) || 1;
|
|
223
|
+
const totalPages = parseInt(pagination.dataset.totalPages) || 1;
|
|
224
|
+
if (currentPage < totalPages) {
|
|
225
|
+
this.goToPage(pagination, currentPage + 1);
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Update pagination
|
|
231
|
+
* @param {HTMLElement|string} pagination - Pagination container or selector
|
|
232
|
+
* @param {Object} options - Pagination options
|
|
233
|
+
*/
|
|
234
|
+
update: function(pagination, options) {
|
|
235
|
+
const el = typeof pagination === 'string' ? document.querySelector(pagination) : pagination;
|
|
236
|
+
if (el) {
|
|
237
|
+
if (options.totalPages !== undefined) {
|
|
238
|
+
el.dataset.totalPages = options.totalPages;
|
|
239
|
+
}
|
|
240
|
+
if (options.currentPage !== undefined) {
|
|
241
|
+
el.dataset.currentPage = options.currentPage;
|
|
242
|
+
}
|
|
243
|
+
if (options.maxVisible !== undefined) {
|
|
244
|
+
el.dataset.maxVisible = options.maxVisible;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
this.render(el, {
|
|
248
|
+
totalPages: parseInt(el.dataset.totalPages) || 1,
|
|
249
|
+
currentPage: parseInt(el.dataset.currentPage) || 1,
|
|
250
|
+
maxVisible: parseInt(el.dataset.maxVisible) || 7
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Destroy a pagination instance and clean up event listeners
|
|
257
|
+
* @param {HTMLElement} pagination - Pagination container
|
|
258
|
+
*/
|
|
259
|
+
destroy: function(pagination) {
|
|
260
|
+
const instance = this.instances.get(pagination);
|
|
261
|
+
if (!instance) return;
|
|
262
|
+
|
|
263
|
+
instance.cleanup.forEach(fn => fn());
|
|
264
|
+
this.instances.delete(pagination);
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Destroy all pagination instances
|
|
269
|
+
*/
|
|
270
|
+
destroyAll: function() {
|
|
271
|
+
this.instances.forEach((instance, pagination) => {
|
|
272
|
+
this.destroy(pagination);
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
// Register with Vanduo framework if available
|
|
278
|
+
if (typeof window.Vanduo !== 'undefined') {
|
|
279
|
+
window.Vanduo.register('pagination', Pagination);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Expose globally
|
|
283
|
+
window.VanduoPagination = Pagination;
|
|
284
|
+
|
|
285
|
+
})();
|
|
286
|
+
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vanduo Framework - Parallax Component
|
|
3
|
+
* JavaScript functionality for parallax scroll effects
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
(function () {
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Parallax Component
|
|
11
|
+
*/
|
|
12
|
+
const Parallax = {
|
|
13
|
+
parallaxElements: new Map(),
|
|
14
|
+
ticking: false,
|
|
15
|
+
isMobile: window.innerWidth < 768,
|
|
16
|
+
reducedMotion: window.matchMedia('(prefers-reduced-motion: reduce)').matches,
|
|
17
|
+
isInitialized: false,
|
|
18
|
+
_onScroll: null,
|
|
19
|
+
_onResize: null,
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Initialize parallax components
|
|
23
|
+
*/
|
|
24
|
+
init: function () {
|
|
25
|
+
if (this.isInitialized) {
|
|
26
|
+
this.refresh();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
this.isInitialized = true;
|
|
31
|
+
|
|
32
|
+
// Check for reduced motion preference
|
|
33
|
+
if (this.reducedMotion) {
|
|
34
|
+
return; // Don't initialize if user prefers reduced motion
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const parallaxElements = document.querySelectorAll('.vd-parallax');
|
|
38
|
+
|
|
39
|
+
parallaxElements.forEach(element => {
|
|
40
|
+
if (!element.dataset.parallaxInitialized) {
|
|
41
|
+
this.initParallax(element);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Handle scroll
|
|
46
|
+
this.handleScroll();
|
|
47
|
+
this._onScroll = () => {
|
|
48
|
+
this.handleScroll();
|
|
49
|
+
};
|
|
50
|
+
window.addEventListener('scroll', this._onScroll, { passive: true });
|
|
51
|
+
|
|
52
|
+
// Handle resize
|
|
53
|
+
this._onResize = () => {
|
|
54
|
+
this.isMobile = window.innerWidth < 768;
|
|
55
|
+
this.updateAll();
|
|
56
|
+
};
|
|
57
|
+
window.addEventListener('resize', this._onResize);
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Initialize a parallax element
|
|
62
|
+
* @param {HTMLElement} element - Parallax container
|
|
63
|
+
*/
|
|
64
|
+
initParallax: function (element) {
|
|
65
|
+
element.dataset.parallaxInitialized = 'true';
|
|
66
|
+
|
|
67
|
+
// Check if disabled on mobile
|
|
68
|
+
const disableMobile = element.classList.contains('parallax-disable-mobile');
|
|
69
|
+
if (disableMobile && this.isMobile) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const layers = element.querySelectorAll('.vd-parallax-layer, .vd-parallax-bg');
|
|
74
|
+
const speed = this.getSpeed(element);
|
|
75
|
+
const direction = element.classList.contains('parallax-horizontal') ? 'horizontal' : 'vertical';
|
|
76
|
+
|
|
77
|
+
this.parallaxElements.set(element, {
|
|
78
|
+
layers: Array.from(layers),
|
|
79
|
+
speed: speed,
|
|
80
|
+
direction: direction,
|
|
81
|
+
disableMobile: disableMobile
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Initial update
|
|
85
|
+
this.updateParallax(element);
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get parallax speed from element
|
|
90
|
+
* @param {HTMLElement} element - Parallax element
|
|
91
|
+
* @returns {number} Speed multiplier
|
|
92
|
+
*/
|
|
93
|
+
getSpeed: function (element) {
|
|
94
|
+
if (element.classList.contains('parallax-slow')) {
|
|
95
|
+
return 0.5;
|
|
96
|
+
} else if (element.classList.contains('parallax-fast')) {
|
|
97
|
+
return 1.5;
|
|
98
|
+
}
|
|
99
|
+
return 1; // Default medium speed
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Handle scroll event
|
|
104
|
+
*/
|
|
105
|
+
handleScroll: function () {
|
|
106
|
+
if (!this.ticking) {
|
|
107
|
+
window.requestAnimationFrame(() => {
|
|
108
|
+
this.updateAll();
|
|
109
|
+
this.ticking = false;
|
|
110
|
+
});
|
|
111
|
+
this.ticking = true;
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Update all parallax elements
|
|
117
|
+
*/
|
|
118
|
+
updateAll: function () {
|
|
119
|
+
this.parallaxElements.forEach((config, element) => {
|
|
120
|
+
// Skip if disabled on mobile
|
|
121
|
+
if (config.disableMobile && this.isMobile) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
this.updateParallax(element);
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Update parallax for a single element
|
|
131
|
+
* @param {HTMLElement} element - Parallax element
|
|
132
|
+
*/
|
|
133
|
+
updateParallax: function (element) {
|
|
134
|
+
const config = this.parallaxElements.get(element);
|
|
135
|
+
if (!config) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const rect = element.getBoundingClientRect();
|
|
140
|
+
const windowHeight = window.innerHeight;
|
|
141
|
+
const elementTop = rect.top;
|
|
142
|
+
const elementHeight = rect.height;
|
|
143
|
+
|
|
144
|
+
// Calculate scroll progress (0 to 1)
|
|
145
|
+
const scrollProgress = Math.max(0, Math.min(1,
|
|
146
|
+
(windowHeight - elementTop) / (windowHeight + elementHeight)
|
|
147
|
+
));
|
|
148
|
+
|
|
149
|
+
// Calculate offset based on speed and direction
|
|
150
|
+
const offset = (scrollProgress - 0.5) * config.speed * 100;
|
|
151
|
+
|
|
152
|
+
config.layers.forEach((layer, _index) => {
|
|
153
|
+
// Different layers can have different speeds
|
|
154
|
+
const layerSpeed = layer.dataset.parallaxSpeed ? parseFloat(layer.dataset.parallaxSpeed) : 1;
|
|
155
|
+
const layerOffset = offset * layerSpeed;
|
|
156
|
+
|
|
157
|
+
if (config.direction === 'horizontal') {
|
|
158
|
+
layer.style.transform = `translateX(${layerOffset}px)`;
|
|
159
|
+
} else {
|
|
160
|
+
layer.style.transform = `translateY(${layerOffset}px)`;
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Destroy parallax element
|
|
167
|
+
* @param {HTMLElement|string} element - Parallax element or selector
|
|
168
|
+
*/
|
|
169
|
+
destroy: function (element) {
|
|
170
|
+
const el = typeof element === 'string' ? document.querySelector(element) : element;
|
|
171
|
+
if (el && this.parallaxElements.has(el)) {
|
|
172
|
+
const config = this.parallaxElements.get(el);
|
|
173
|
+
config.layers.forEach(layer => {
|
|
174
|
+
layer.style.transform = '';
|
|
175
|
+
});
|
|
176
|
+
this.parallaxElements.delete(el);
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Refresh parallax (recalculate positions)
|
|
182
|
+
*/
|
|
183
|
+
refresh: function () {
|
|
184
|
+
this.updateAll();
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
destroyAll: function () {
|
|
188
|
+
this.parallaxElements.forEach((_config, element) => {
|
|
189
|
+
this.destroy(element);
|
|
190
|
+
});
|
|
191
|
+
this.parallaxElements.clear();
|
|
192
|
+
|
|
193
|
+
if (this._onScroll) {
|
|
194
|
+
window.removeEventListener('scroll', this._onScroll);
|
|
195
|
+
this._onScroll = null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (this._onResize) {
|
|
199
|
+
window.removeEventListener('resize', this._onResize);
|
|
200
|
+
this._onResize = null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
this.isInitialized = false;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// Register with Vanduo framework if available
|
|
208
|
+
if (typeof window.Vanduo !== 'undefined') {
|
|
209
|
+
window.Vanduo.register('parallax', Parallax);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Expose globally
|
|
213
|
+
window.VanduoParallax = Parallax;
|
|
214
|
+
|
|
215
|
+
})();
|
|
216
|
+
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vanduo Framework - Preloader Component
|
|
3
|
+
* JavaScript functionality for progress bars and loaders
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
(function() {
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Preloader Component
|
|
11
|
+
*/
|
|
12
|
+
const Preloader = {
|
|
13
|
+
/**
|
|
14
|
+
* Initialize preloader components
|
|
15
|
+
*/
|
|
16
|
+
init: function() {
|
|
17
|
+
const progressBars = document.querySelectorAll('.progress-bar[data-progress]');
|
|
18
|
+
|
|
19
|
+
progressBars.forEach(bar => {
|
|
20
|
+
if (!bar.dataset.progressInitialized) {
|
|
21
|
+
this.initProgressBar(bar);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Initialize a progress bar
|
|
28
|
+
* @param {HTMLElement} bar - Progress bar element
|
|
29
|
+
*/
|
|
30
|
+
initProgressBar: function(bar) {
|
|
31
|
+
bar.dataset.progressInitialized = 'true';
|
|
32
|
+
|
|
33
|
+
const initialValue = parseInt(bar.dataset.progress) || 0;
|
|
34
|
+
this.setProgress(bar, initialValue, false);
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Set progress value
|
|
39
|
+
* @param {HTMLElement|string} bar - Progress bar element or selector
|
|
40
|
+
* @param {number} value - Progress value (0-100)
|
|
41
|
+
* @param {boolean} animate - Whether to animate
|
|
42
|
+
*/
|
|
43
|
+
setProgress: function(bar, value, animate = true) {
|
|
44
|
+
const el = typeof bar === 'string' ? document.querySelector(bar) : bar;
|
|
45
|
+
|
|
46
|
+
if (!el) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Clamp value between 0 and 100
|
|
51
|
+
value = Math.max(0, Math.min(100, value));
|
|
52
|
+
|
|
53
|
+
// Update width
|
|
54
|
+
if (animate) {
|
|
55
|
+
el.style.transition = 'width var(--transition-duration-slow) var(--transition-ease)';
|
|
56
|
+
} else {
|
|
57
|
+
el.style.transition = 'none';
|
|
58
|
+
setTimeout(() => {
|
|
59
|
+
el.style.transition = '';
|
|
60
|
+
}, 0);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
el.style.width = value + '%';
|
|
64
|
+
el.setAttribute('aria-valuenow', value);
|
|
65
|
+
el.setAttribute('aria-valuemin', 0);
|
|
66
|
+
el.setAttribute('aria-valuemax', 100);
|
|
67
|
+
|
|
68
|
+
// Update text if exists
|
|
69
|
+
const text = el.querySelector('.progress-text');
|
|
70
|
+
if (text) {
|
|
71
|
+
text.textContent = value + '%';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Dispatch event
|
|
75
|
+
el.dispatchEvent(new CustomEvent('progress:update', {
|
|
76
|
+
bubbles: true,
|
|
77
|
+
detail: { value, max: 100 }
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
// Complete event
|
|
81
|
+
if (value >= 100) {
|
|
82
|
+
el.dispatchEvent(new CustomEvent('progress:complete', {
|
|
83
|
+
bubbles: true,
|
|
84
|
+
detail: { value, max: 100 }
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Animate progress from current to target
|
|
91
|
+
* @param {HTMLElement|string} bar - Progress bar element or selector
|
|
92
|
+
* @param {number} targetValue - Target progress value (0-100)
|
|
93
|
+
* @param {number} duration - Animation duration in ms
|
|
94
|
+
*/
|
|
95
|
+
animateProgress: function(bar, targetValue, duration = 1000) {
|
|
96
|
+
const el = typeof bar === 'string' ? document.querySelector(bar) : bar;
|
|
97
|
+
|
|
98
|
+
if (!el) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const startValue = parseInt(el.style.width) || 0;
|
|
103
|
+
const difference = targetValue - startValue;
|
|
104
|
+
const startTime = performance.now();
|
|
105
|
+
|
|
106
|
+
const animate = (currentTime) => {
|
|
107
|
+
const elapsed = currentTime - startTime;
|
|
108
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
109
|
+
|
|
110
|
+
// Easing function (ease-out)
|
|
111
|
+
const easeOut = 1 - Math.pow(1 - progress, 3);
|
|
112
|
+
const currentValue = startValue + (difference * easeOut);
|
|
113
|
+
|
|
114
|
+
this.setProgress(el, currentValue, false);
|
|
115
|
+
|
|
116
|
+
if (progress < 1) {
|
|
117
|
+
requestAnimationFrame(animate);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
requestAnimationFrame(animate);
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Show preloader
|
|
126
|
+
* @param {HTMLElement|string} preloader - Preloader element or selector
|
|
127
|
+
*/
|
|
128
|
+
show: function(preloader) {
|
|
129
|
+
const el = typeof preloader === 'string' ? document.querySelector(preloader) : preloader;
|
|
130
|
+
if (el) {
|
|
131
|
+
el.style.display = 'inline-block';
|
|
132
|
+
el.setAttribute('aria-hidden', 'false');
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Hide preloader
|
|
138
|
+
* @param {HTMLElement|string} preloader - Preloader element or selector
|
|
139
|
+
*/
|
|
140
|
+
hide: function(preloader) {
|
|
141
|
+
const el = typeof preloader === 'string' ? document.querySelector(preloader) : preloader;
|
|
142
|
+
if (el) {
|
|
143
|
+
el.style.display = 'none';
|
|
144
|
+
el.setAttribute('aria-hidden', 'true');
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Toggle preloader
|
|
150
|
+
* @param {HTMLElement|string} preloader - Preloader element or selector
|
|
151
|
+
*/
|
|
152
|
+
toggle: function(preloader) {
|
|
153
|
+
const el = typeof preloader === 'string' ? document.querySelector(preloader) : preloader;
|
|
154
|
+
if (el) {
|
|
155
|
+
if (el.style.display === 'none' || el.getAttribute('aria-hidden') === 'true') {
|
|
156
|
+
this.show(el);
|
|
157
|
+
} else {
|
|
158
|
+
this.hide(el);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Destroy all progress bar instances
|
|
165
|
+
*/
|
|
166
|
+
destroyAll: function() {
|
|
167
|
+
const progressBars = document.querySelectorAll('.progress-bar[data-progress-initialized="true"]');
|
|
168
|
+
progressBars.forEach(bar => {
|
|
169
|
+
delete bar.dataset.progressInitialized;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// Register with Vanduo framework if available
|
|
175
|
+
if (typeof window.Vanduo !== 'undefined') {
|
|
176
|
+
window.Vanduo.register('preloader', Preloader);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Expose globally
|
|
180
|
+
window.VanduoPreloader = Preloader;
|
|
181
|
+
|
|
182
|
+
})();
|
|
183
|
+
|