@hortonstudio/main 1.7.2 → 1.7.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/.claude/settings.local.json +3 -1
- package/autoInit/form.js +23 -0
- package/index.js +1 -1
- package/package.json +1 -1
- package/utils/slider.js +168 -4
package/autoInit/form.js
CHANGED
|
@@ -651,6 +651,29 @@ export function init() {
|
|
|
651
651
|
finalAnimElement.click();
|
|
652
652
|
}
|
|
653
653
|
|
|
654
|
+
// Check for custom redirect before allowing natural submission
|
|
655
|
+
if (formWrapper && formWrapper.hasAttribute('data-hs-form-redirect')) {
|
|
656
|
+
const redirectUrl = formWrapper.getAttribute('data-hs-form-redirect');
|
|
657
|
+
|
|
658
|
+
if (redirectUrl && redirectUrl.trim()) {
|
|
659
|
+
// Prevent the natural form submission
|
|
660
|
+
event.preventDefault();
|
|
661
|
+
event.stopPropagation();
|
|
662
|
+
event.stopImmediatePropagation();
|
|
663
|
+
|
|
664
|
+
// Perform the redirect
|
|
665
|
+
try {
|
|
666
|
+
window.location.href = redirectUrl.trim();
|
|
667
|
+
} catch (error) {
|
|
668
|
+
console.error('Form redirect failed:', error);
|
|
669
|
+
// Fallback: allow natural form submission if redirect fails
|
|
670
|
+
form.submit();
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
654
677
|
// Prevent submission if configured to do so
|
|
655
678
|
if (shouldPreventSubmit) {
|
|
656
679
|
event.preventDefault();
|
package/index.js
CHANGED
package/package.json
CHANGED
package/utils/slider.js
CHANGED
|
@@ -56,7 +56,8 @@ const initPaginationInstance = (originalList) => {
|
|
|
56
56
|
nextBtn: container.querySelector('[data-hs-slider="pagination-next"]'),
|
|
57
57
|
prevBtn: container.querySelector('[data-hs-slider="pagination-previous"]'),
|
|
58
58
|
counter: container.querySelector('[data-hs-slider="pagination-counter"]'),
|
|
59
|
-
controls: container.querySelector('[data-hs-slider="pagination-controls"]')
|
|
59
|
+
controls: container.querySelector('[data-hs-slider="pagination-controls"]'),
|
|
60
|
+
dotsWrap: container.querySelector('[data-hs-slider="dots-wrap"]')
|
|
60
61
|
};
|
|
61
62
|
|
|
62
63
|
if (!elements.nextBtn || !elements.prevBtn) return;
|
|
@@ -69,16 +70,119 @@ const initPaginationInstance = (originalList) => {
|
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
const config = originalList.getAttribute('data-hs-config') || '';
|
|
72
|
-
const
|
|
73
|
-
const
|
|
73
|
+
const configOptions = config.split(', ').map(opt => opt.trim());
|
|
74
|
+
const isInfiniteMode = configOptions.includes('infinite');
|
|
74
75
|
|
|
75
|
-
const
|
|
76
|
+
const desktopItems = !isInfiniteMode ? (parseInt(config.match(/show-(\d+)(?!-mobile)/)?.[1]) || 6) : 0;
|
|
77
|
+
const mobileItems = !isInfiniteMode ? (parseInt(config.match(/show-(\d+)-mobile/)?.[1]) || desktopItems) : 0;
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
// Early exit for infinite mode - disable pagination completely
|
|
81
|
+
if (isInfiniteMode) {
|
|
82
|
+
if (elements.controls) {
|
|
83
|
+
elements.controls.style.display = 'none';
|
|
84
|
+
elements.controls.setAttribute('aria-hidden', 'true');
|
|
85
|
+
}
|
|
86
|
+
if (elements.dotsWrap) {
|
|
87
|
+
elements.dotsWrap.style.display = 'none';
|
|
88
|
+
elements.dotsWrap.setAttribute('aria-hidden', 'true');
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const isMobileLayout = () => {
|
|
94
|
+
const breakpoint = getComputedStyle(originalList).getPropertyValue('--data-hs-break').trim().replace(/"/g, '');
|
|
95
|
+
return breakpoint === 'mobile';
|
|
96
|
+
};
|
|
76
97
|
const allItems = Array.from(originalList.children);
|
|
77
98
|
const totalItems = allItems.length;
|
|
78
99
|
if (!totalItems) return;
|
|
79
100
|
|
|
80
101
|
const state = { totalPages: 1, currentIndex: 1, currentPage: 1, isAnimating: false, itemsPerPage: desktopItems };
|
|
81
102
|
let wrapperChildren = [];
|
|
103
|
+
let dotTemplates = { active: null, inactive: null };
|
|
104
|
+
|
|
105
|
+
const initializeDots = () => {
|
|
106
|
+
if (!elements.dotsWrap) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Find template dots
|
|
111
|
+
const existingDots = Array.from(elements.dotsWrap.children);
|
|
112
|
+
|
|
113
|
+
// Identify active and inactive templates
|
|
114
|
+
existingDots.forEach((dot) => {
|
|
115
|
+
if (dot.classList.contains('is-active')) {
|
|
116
|
+
dotTemplates.active = dot.cloneNode(true);
|
|
117
|
+
} else {
|
|
118
|
+
dotTemplates.inactive = dot.cloneNode(true);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Clear existing dots
|
|
123
|
+
elements.dotsWrap.innerHTML = '';
|
|
124
|
+
|
|
125
|
+
// Handle single page or no pages case
|
|
126
|
+
if (state.totalPages <= 1) {
|
|
127
|
+
elements.dotsWrap.style.display = 'none';
|
|
128
|
+
elements.dotsWrap.setAttribute('aria-hidden', 'true');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Show dots container
|
|
133
|
+
elements.dotsWrap.style.display = '';
|
|
134
|
+
elements.dotsWrap.removeAttribute('aria-hidden');
|
|
135
|
+
|
|
136
|
+
// Create dots for each page
|
|
137
|
+
for (let i = 1; i <= state.totalPages; i++) {
|
|
138
|
+
const template = i === 1 ? dotTemplates.active : dotTemplates.inactive;
|
|
139
|
+
if (template) {
|
|
140
|
+
const dot = template.cloneNode(true);
|
|
141
|
+
dot.setAttribute('data-page', i);
|
|
142
|
+
dot.setAttribute('aria-label', `Go to page ${i}`);
|
|
143
|
+
dot.style.cursor = 'pointer';
|
|
144
|
+
|
|
145
|
+
// Add click handler for dot navigation
|
|
146
|
+
dot.addEventListener('click', (e) => {
|
|
147
|
+
e.preventDefault();
|
|
148
|
+
const targetPage = parseInt(dot.getAttribute('data-page'));
|
|
149
|
+
navigateToPage(targetPage);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Add keyboard support
|
|
153
|
+
dot.setAttribute('tabindex', '0');
|
|
154
|
+
dot.setAttribute('role', 'button');
|
|
155
|
+
dot.addEventListener('keydown', (e) => {
|
|
156
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
157
|
+
e.preventDefault();
|
|
158
|
+
const targetPage = parseInt(dot.getAttribute('data-page'));
|
|
159
|
+
navigateToPage(targetPage);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
elements.dotsWrap.appendChild(dot);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const updateActiveDot = () => {
|
|
169
|
+
if (!elements.dotsWrap || state.totalPages <= 1) return;
|
|
170
|
+
|
|
171
|
+
const dots = Array.from(elements.dotsWrap.children);
|
|
172
|
+
|
|
173
|
+
dots.forEach((dot, index) => {
|
|
174
|
+
const pageNumber = index + 1;
|
|
175
|
+
const isActive = pageNumber === state.currentPage;
|
|
176
|
+
|
|
177
|
+
if (isActive) {
|
|
178
|
+
dot.classList.add('is-active');
|
|
179
|
+
dot.setAttribute('aria-current', 'page');
|
|
180
|
+
} else {
|
|
181
|
+
dot.classList.remove('is-active');
|
|
182
|
+
dot.removeAttribute('aria-current');
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
};
|
|
82
186
|
|
|
83
187
|
const initializePagination = (forceItemsPerPage = null) => {
|
|
84
188
|
const currentIsMobile = isMobileLayout();
|
|
@@ -97,6 +201,10 @@ const initPaginationInstance = (originalList) => {
|
|
|
97
201
|
elements.controls.style.display = 'none';
|
|
98
202
|
elements.controls.setAttribute('aria-hidden', 'true');
|
|
99
203
|
}
|
|
204
|
+
if (elements.dotsWrap) {
|
|
205
|
+
elements.dotsWrap.style.display = 'none';
|
|
206
|
+
elements.dotsWrap.setAttribute('aria-hidden', 'true');
|
|
207
|
+
}
|
|
100
208
|
Object.assign(state, { totalPages: 1, currentIndex: 1, currentPage: 1, isAnimating: false });
|
|
101
209
|
wrapper.style.cssText = `transform: translateX(0%); height: ${originalList.offsetHeight}px;`;
|
|
102
210
|
wrapperChildren = [originalList];
|
|
@@ -107,6 +215,10 @@ const initPaginationInstance = (originalList) => {
|
|
|
107
215
|
elements.controls.style.display = '';
|
|
108
216
|
elements.controls.removeAttribute('aria-hidden');
|
|
109
217
|
}
|
|
218
|
+
if (elements.dotsWrap) {
|
|
219
|
+
elements.dotsWrap.style.display = '';
|
|
220
|
+
elements.dotsWrap.removeAttribute('aria-hidden');
|
|
221
|
+
}
|
|
110
222
|
|
|
111
223
|
const pageLists = Array.from({ length: state.totalPages }, (_, page) => {
|
|
112
224
|
const pageList = originalList.cloneNode(false);
|
|
@@ -130,6 +242,7 @@ const initPaginationInstance = (originalList) => {
|
|
|
130
242
|
updateCounter();
|
|
131
243
|
updateHeight();
|
|
132
244
|
manageFocus();
|
|
245
|
+
initializeDots();
|
|
133
246
|
return state.totalPages;
|
|
134
247
|
};
|
|
135
248
|
const liveRegion = document.createElement('div');
|
|
@@ -170,6 +283,46 @@ const initPaginationInstance = (originalList) => {
|
|
|
170
283
|
resizeObserver.observe(wrapper);
|
|
171
284
|
initializePagination();
|
|
172
285
|
|
|
286
|
+
const navigateToPage = (targetPage) => {
|
|
287
|
+
if (state.isAnimating || state.totalPages <= 1 || targetPage === state.currentPage) return;
|
|
288
|
+
state.isAnimating = true;
|
|
289
|
+
|
|
290
|
+
// For multi-page jumps, we still use the same transition but update the index directly
|
|
291
|
+
state.currentIndex = targetPage;
|
|
292
|
+
state.currentPage = targetPage;
|
|
293
|
+
|
|
294
|
+
updateCounter();
|
|
295
|
+
announcePageChange();
|
|
296
|
+
updateHeight();
|
|
297
|
+
updateActiveDot();
|
|
298
|
+
|
|
299
|
+
if (isMobileLayout() && elements.controls) {
|
|
300
|
+
setTimeout(() => {
|
|
301
|
+
const controlsBottom = elements.controls.getBoundingClientRect().bottom + window.pageYOffset;
|
|
302
|
+
const clearance = 5 * 16; // 5rem in pixels
|
|
303
|
+
const targetScrollPosition = controlsBottom - window.innerHeight + clearance;
|
|
304
|
+
window.scrollTo({ top: targetScrollPosition, behavior: 'smooth' });
|
|
305
|
+
}, 50);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
wrapper.style.transition = 'transform 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94)';
|
|
309
|
+
wrapper.style.transform = `translateX(${-state.currentIndex * 100}%)`;
|
|
310
|
+
|
|
311
|
+
const handleTransitionEnd = () => {
|
|
312
|
+
wrapper.removeEventListener('transitionend', handleTransitionEnd);
|
|
313
|
+
wrapper.style.transition = '';
|
|
314
|
+
|
|
315
|
+
updateCounter();
|
|
316
|
+
announcePageChange();
|
|
317
|
+
updateHeight();
|
|
318
|
+
manageFocus();
|
|
319
|
+
updateActiveDot();
|
|
320
|
+
state.isAnimating = false;
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
wrapper.addEventListener('transitionend', handleTransitionEnd);
|
|
324
|
+
};
|
|
325
|
+
|
|
173
326
|
const navigate = (direction) => {
|
|
174
327
|
if (state.isAnimating || state.totalPages <= 1) return;
|
|
175
328
|
state.isAnimating = true;
|
|
@@ -181,6 +334,16 @@ const initPaginationInstance = (originalList) => {
|
|
|
181
334
|
updateCounter();
|
|
182
335
|
announcePageChange();
|
|
183
336
|
updateHeight();
|
|
337
|
+
updateActiveDot();
|
|
338
|
+
|
|
339
|
+
if (isMobileLayout() && elements.controls) {
|
|
340
|
+
setTimeout(() => {
|
|
341
|
+
const controlsBottom = elements.controls.getBoundingClientRect().bottom + window.pageYOffset;
|
|
342
|
+
const clearance = 5 * 16; // 5rem in pixels
|
|
343
|
+
const targetScrollPosition = controlsBottom - window.innerHeight + clearance;
|
|
344
|
+
window.scrollTo({ top: targetScrollPosition, behavior: 'smooth' });
|
|
345
|
+
}, 50);
|
|
346
|
+
}
|
|
184
347
|
|
|
185
348
|
wrapper.style.transition = 'transform 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94)';
|
|
186
349
|
wrapper.style.transform = `translateX(${-state.currentIndex * 100}%)`;
|
|
@@ -203,6 +366,7 @@ const initPaginationInstance = (originalList) => {
|
|
|
203
366
|
announcePageChange();
|
|
204
367
|
updateHeight();
|
|
205
368
|
manageFocus();
|
|
369
|
+
updateActiveDot();
|
|
206
370
|
state.isAnimating = false;
|
|
207
371
|
};
|
|
208
372
|
|