@hortonstudio/main 1.9.11 → 1.9.20
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/.prettierrc +8 -0
- package/README.md +146 -0
- package/eslint.config.js +32 -0
- package/index.ts +275 -0
- package/package.json +19 -2
- package/public/bootstrap.js +16 -0
- package/src/animations/animations.ts +93 -0
- package/src/animations/functions/counter/counter.ts +137 -0
- package/src/config.json +570 -0
- package/src/config.ts +105 -0
- package/src/modules/default/README.md +167 -0
- package/src/modules/default/default.ts +71 -0
- package/{autoInit → src/modules/default/functions}/accessibility/README.md +44 -12
- package/src/modules/default/functions/accessibility/accessibility.ts +54 -0
- package/src/modules/default/functions/accordion/README.md +451 -0
- package/src/modules/default/functions/accordion/accordion.ts +189 -0
- package/src/modules/default/functions/comparison/comparison.ts +424 -0
- package/src/modules/default/functions/marquee/marquee.ts +206 -0
- package/src/modules/default/functions/navbar/README.md +393 -0
- package/src/modules/default/functions/navbar/functions/arrow-navigation/arrow-navigation.ts +183 -0
- package/src/modules/default/functions/navbar/functions/dropdown/dropdown.ts +313 -0
- package/src/modules/default/functions/navbar/functions/menu/menu.ts +315 -0
- package/src/modules/default/functions/navbar/navbar.ts +51 -0
- package/{autoInit → src/modules/default/functions}/smooth-scroll/README.md +45 -14
- package/{autoInit/smooth-scroll/smooth-scroll.js → src/modules/default/functions/smooth-scroll/smooth-scroll.ts} +33 -38
- package/{autoInit → src/modules/default/functions}/transition/README.md +59 -32
- package/src/modules/default/functions/transition/transition.ts +290 -0
- package/src/modules/normalize/README.md +172 -0
- package/src/modules/normalize/functions/clickable/README.md +84 -0
- package/src/modules/normalize/functions/clickable/clickable.ts +43 -0
- package/src/modules/normalize/functions/clickable/functions/normalize/README.md +213 -0
- package/src/modules/normalize/functions/clickable/functions/normalize/normalize.ts +68 -0
- package/src/modules/normalize/functions/dupe/README.md +405 -0
- package/src/modules/normalize/functions/dupe/dupe.ts +197 -0
- package/src/modules/normalize/functions/sync/sync.ts +378 -0
- package/src/modules/normalize/normalize.ts +58 -0
- package/src/modules/structure/README.md +190 -0
- package/src/modules/structure/functions/form/README.md +94 -0
- package/src/modules/structure/functions/form/form.ts +54 -0
- package/src/modules/structure/functions/form/functions/honeypot/README.md +77 -0
- package/src/modules/structure/functions/form/functions/honeypot/honeypot.ts +37 -0
- package/src/modules/structure/functions/form/functions/range/README.md +410 -0
- package/src/modules/structure/functions/form/functions/range/range.ts +92 -0
- package/src/modules/structure/functions/form/functions/select/README.md +393 -0
- package/src/modules/structure/functions/form/functions/select/functions/custom-select/custom-select.ts +637 -0
- package/src/modules/structure/functions/form/functions/select/functions/states/states.ts +118 -0
- package/src/modules/structure/functions/form/functions/select/select.ts +48 -0
- package/src/modules/structure/functions/form/functions/test/test.ts +132 -0
- package/{autoInit/accessibility → src/modules/structure}/functions/pagination/README.md +147 -72
- package/{autoInit/accessibility/functions/pagination/pagination.js → src/modules/structure/functions/pagination/pagination.ts} +98 -50
- package/{autoInit → src/modules/structure/functions}/site-settings/README.md +57 -27
- package/{autoInit/site-settings/site-settings.js → src/modules/structure/functions/site-settings/site-settings.ts} +36 -32
- package/{autoInit/accessibility → src/modules/structure}/functions/toc/README.md +18 -15
- package/{autoInit/accessibility/functions/toc/toc.js → src/modules/structure/functions/toc/functions/heading-links/heading-links.ts} +43 -63
- package/src/modules/structure/functions/toc/functions/progress-bar/progress-bar.ts +101 -0
- package/src/modules/structure/functions/toc/toc.ts +35 -0
- package/{autoInit/accessibility → src/modules/structure}/functions/year-replacement/README.md +7 -6
- package/src/modules/structure/functions/year-replacement/year-replacement.ts +59 -0
- package/src/modules/structure/structure.ts +59 -0
- package/src/utils/attributeSelector.ts +78 -0
- package/src/utils/cssVariables.ts +24 -0
- package/src/utils/gsap.ts +198 -0
- package/src/utils/heightAnimator.ts +130 -0
- package/src/utils/modalManager.ts +150 -0
- package/src/utils.ts +54 -0
- package/tsconfig.json +24 -0
- package/vite.config.js +45 -0
- package/.claude/settings.local.json +0 -70
- package/archive/hero.js +0 -794
- package/archive/modal.js +0 -80
- package/archive/text.js +0 -628
- package/autoInit/accessibility/accessibility.js +0 -53
- package/autoInit/accessibility/functions/blog-remover/README.md +0 -61
- package/autoInit/accessibility/functions/blog-remover/blog-remover.js +0 -31
- package/autoInit/accessibility/functions/click-forwarding/README.md +0 -60
- package/autoInit/accessibility/functions/click-forwarding/click-forwarding.js +0 -82
- package/autoInit/accessibility/functions/dropdown/README.md +0 -212
- package/autoInit/accessibility/functions/dropdown/dropdown.js +0 -167
- package/autoInit/accessibility/functions/list-accessibility/README.md +0 -56
- package/autoInit/accessibility/functions/list-accessibility/list-accessibility.js +0 -23
- package/autoInit/accessibility/functions/text-synchronization/README.md +0 -62
- package/autoInit/accessibility/functions/text-synchronization/text-synchronization.js +0 -101
- package/autoInit/accessibility/functions/year-replacement/year-replacement.js +0 -43
- package/autoInit/button/README.md +0 -122
- package/autoInit/button/button.js +0 -51
- package/autoInit/counter/README.md +0 -274
- package/autoInit/counter/counter.js +0 -185
- package/autoInit/form/README.md +0 -338
- package/autoInit/form/form.js +0 -374
- package/autoInit/navbar/README.md +0 -366
- package/autoInit/navbar/navbar.js +0 -786
- package/autoInit/transition/transition.js +0 -116
- package/index.js +0 -305
- package/utils/before-after/README.md +0 -520
- package/utils/before-after/before-after.js +0 -653
- package/utils/css-animations/buttons/main/bgbasic/btn-main-bgbasic.html +0 -10
- package/utils/css-animations/buttons/main/bgfill/btn-main-bgfill.html +0 -29
- package/utils/css-animations/buttons/navbar/bgbasic/navbar-main-bgbasic.html +0 -17
- package/utils/css-animations/buttons/navbar/bgbasic/navbar-menu-bgbasic.html +0 -16
- package/utils/css-animations/buttons/navbar/bgfill/navbar-main-bgfill.html +0 -46
- package/utils/css-animations/buttons/navbar/bgfill/navbar-menu-bgfill.html +0 -39
- package/utils/css-animations/buttons/navbar/color/navbar-announce-color.html +0 -5
- package/utils/css-animations/buttons/navbar/color/navbar-main-color.html +0 -7
- package/utils/css-animations/buttons/navbar/color/navbar-menu-color.html +0 -7
- package/utils/css-animations/buttons/navbar/double-slide/navbar-announce-double-slide.html +0 -40
- package/utils/css-animations/buttons/navbar/double-slide/navbar-main-double-slide.html +0 -77
- package/utils/css-animations/buttons/navbar/scale/navbar-announce-scale.html +0 -6
- package/utils/css-animations/buttons/navbar/scale/navbar-main-scale.html +0 -9
- package/utils/css-animations/buttons/navbar/scale/navbar-menu-scale.html +0 -8
- package/utils/css-animations/buttons/navbar/underline/navbar-announce-underline.html +0 -32
- package/utils/css-animations/buttons/navbar/underline/navbar-main-underline.html +0 -56
- package/utils/css-animations/buttons/text/color/text-footer-color.html +0 -5
- package/utils/css-animations/buttons/text/color/text-main-color.html +0 -5
- package/utils/css-animations/buttons/text/double-slide/text-main-double-slide.html +0 -56
- package/utils/css-animations/buttons/text/scale/text-footer-scale.html +0 -6
- package/utils/css-animations/buttons/text/scale/text-main-scale.html +0 -6
- package/utils/css-animations/buttons/text/underline/text-footer-underline.html +0 -45
- package/utils/css-animations/buttons/text/underline/text-main-underline.html +0 -58
- package/utils/css-animations/cards/card-clickable.html +0 -11
- package/utils/css-animations/defaults.html +0 -69
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
# **Counter System Documentation**
|
|
2
|
-
|
|
3
|
-
## **Overview**
|
|
4
|
-
|
|
5
|
-
The counter system provides animated number counting that triggers when the counter scrolls into view. It supports prefixes, suffixes, decimals, and automatically handles accessibility by combining all text content into an aria-label.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## **Counter Setup**
|
|
10
|
-
|
|
11
|
-
### **Required Elements**
|
|
12
|
-
|
|
13
|
-
**Counter Wrapper** *(main wrapper of the counter)*
|
|
14
|
-
|
|
15
|
-
* data-hs-counter="wrapper"
|
|
16
|
-
|
|
17
|
-
**Counter Number** *(the number element to animate)*
|
|
18
|
-
|
|
19
|
-
* data-hs-anim="counter"
|
|
20
|
-
* Contains the target number with optional prefix/suffix (e.g., "20+", "$1,500", "99.9%")
|
|
21
|
-
|
|
22
|
-
**Typical element layout:**
|
|
23
|
-
|
|
24
|
-
1. Counter Wrapper (data-hs-counter="wrapper")
|
|
25
|
-
1. Counter Number (data-hs-anim="counter")
|
|
26
|
-
1. Text: "20+"
|
|
27
|
-
2. Description Text (optional)
|
|
28
|
-
1. Text: "Years of Experience"
|
|
29
|
-
|
|
30
|
-
### **What It Does**
|
|
31
|
-
|
|
32
|
-
1. **Parses numbers** - Extracts prefix, number, and suffix from counter text
|
|
33
|
-
2. **Scroll trigger** - Uses IntersectionObserver to detect when counter enters viewport
|
|
34
|
-
3. **Animates count** - Counts from 0 to target number with easeOutQuart easing
|
|
35
|
-
4. **Accessibility** - Combines ALL text within wrapper into aria-label (e.g., "20+ Years of Experience")
|
|
36
|
-
5. **Respects motion preferences** - Skips animation if user has `prefers-reduced-motion` enabled
|
|
37
|
-
|
|
38
|
-
### **When It Runs**
|
|
39
|
-
|
|
40
|
-
* Runs once on page load to set up observers
|
|
41
|
-
* Triggers animation when counter scrolls into view (threshold: 0)
|
|
42
|
-
* Each counter only animates once (tracks with `data-animated="true"`)
|
|
43
|
-
* Runs again on Barba.js page transitions (via `reinitialize()`)
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## **Number Formats**
|
|
48
|
-
|
|
49
|
-
### **Supported Formats**
|
|
50
|
-
|
|
51
|
-
The counter automatically detects and preserves:
|
|
52
|
-
|
|
53
|
-
**Prefix:**
|
|
54
|
-
```
|
|
55
|
-
$1000 � Counts from $0 to $1000
|
|
56
|
-
+500 � Counts from +0 to +500
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
**Suffix:**
|
|
60
|
-
```
|
|
61
|
-
100% � Counts from 0% to 100%
|
|
62
|
-
50+ � Counts from 0+ to 50+
|
|
63
|
-
1000K � Counts from 0K to 1000K
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
**Decimals:**
|
|
67
|
-
```
|
|
68
|
-
99.9 � Counts from 0.0 to 99.9 (preserves 1 decimal)
|
|
69
|
-
3.14159 � Counts from 0.00000 to 3.14159 (preserves 5 decimals)
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
**Combined:**
|
|
73
|
-
```
|
|
74
|
-
$99.99 � Counts from $0.00 to $99.99
|
|
75
|
-
50.5% � Counts from 0.0% to 50.5%
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
---
|
|
79
|
-
|
|
80
|
-
## **Accessibility**
|
|
81
|
-
|
|
82
|
-
### **Automatic ARIA Setup**
|
|
83
|
-
|
|
84
|
-
For each counter wrapper, the system:
|
|
85
|
-
|
|
86
|
-
1. **Combines all text** - Uses TreeWalker to find all text nodes within wrapper
|
|
87
|
-
2. **Creates aria-label** - Joins text with spaces (e.g., "20+ Years of Experience")
|
|
88
|
-
3. **Sets role** - Adds `role="img"` to wrapper
|
|
89
|
-
4. **Hides children** - Sets `aria-hidden="true"` on all descendant elements
|
|
90
|
-
|
|
91
|
-
### **Example**
|
|
92
|
-
|
|
93
|
-
```html
|
|
94
|
-
<!-- Before JS runs -->
|
|
95
|
-
<div data-hs-counter="wrapper">
|
|
96
|
-
<div data-hs-anim="counter">20+</div>
|
|
97
|
-
<div>Years of Experience</div>
|
|
98
|
-
</div>
|
|
99
|
-
|
|
100
|
-
<!-- After JS runs -->
|
|
101
|
-
<div data-hs-counter="wrapper" aria-label="20+ Years of Experience" role="img">
|
|
102
|
-
<div data-hs-anim="counter" aria-hidden="true">20+</div>
|
|
103
|
-
<div aria-hidden="true">Years of Experience</div>
|
|
104
|
-
</div>
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
Screen readers announce: "20+ Years of Experience"
|
|
108
|
-
|
|
109
|
-
---
|
|
110
|
-
|
|
111
|
-
## **Usage Examples**
|
|
112
|
-
|
|
113
|
-
### **Basic Counter**
|
|
114
|
-
|
|
115
|
-
```html
|
|
116
|
-
<div data-hs-counter="wrapper">
|
|
117
|
-
<div data-hs-anim="counter">1000</div>
|
|
118
|
-
</div>
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
**Result:** Counts from 0 to 1000 when scrolled into view
|
|
122
|
-
**aria-label:** "1000"
|
|
123
|
-
|
|
124
|
-
### **Counter with Description**
|
|
125
|
-
|
|
126
|
-
```html
|
|
127
|
-
<div data-hs-counter="wrapper">
|
|
128
|
-
<div data-hs-anim="counter">50+</div>
|
|
129
|
-
<p>Projects Completed</p>
|
|
130
|
-
</div>
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
**Result:** Counts from 0+ to 50+
|
|
134
|
-
**aria-label:** "50+ Projects Completed"
|
|
135
|
-
|
|
136
|
-
### **Currency Counter**
|
|
137
|
-
|
|
138
|
-
```html
|
|
139
|
-
<div data-hs-counter="wrapper">
|
|
140
|
-
<div data-hs-anim="counter">$99.99</div>
|
|
141
|
-
<p>Starting Price</p>
|
|
142
|
-
</div>
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
**Result:** Counts from $0.00 to $99.99
|
|
146
|
-
**aria-label:** "$99.99 Starting Price"
|
|
147
|
-
|
|
148
|
-
### **Percentage Counter**
|
|
149
|
-
|
|
150
|
-
```html
|
|
151
|
-
<div data-hs-counter="wrapper">
|
|
152
|
-
<div data-hs-anim="counter">99.9%</div>
|
|
153
|
-
<p>Uptime Guarantee</p>
|
|
154
|
-
</div>
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
**Result:** Counts from 0.0% to 99.9%
|
|
158
|
-
**aria-label:** "99.9% Uptime Guarantee"
|
|
159
|
-
|
|
160
|
-
---
|
|
161
|
-
|
|
162
|
-
## **Configuration**
|
|
163
|
-
|
|
164
|
-
### **Default Settings**
|
|
165
|
-
|
|
166
|
-
```javascript
|
|
167
|
-
{
|
|
168
|
-
duration: 3000 // Animation duration in milliseconds
|
|
169
|
-
}
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
### **Updating Config**
|
|
173
|
-
|
|
174
|
-
```javascript
|
|
175
|
-
window.hsmain.counter.updateConfig({
|
|
176
|
-
duration: 2000 // Faster animation
|
|
177
|
-
});
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
**Note:** Config changes only affect counters that haven't animated yet.
|
|
181
|
-
|
|
182
|
-
---
|
|
183
|
-
|
|
184
|
-
## **API Methods**
|
|
185
|
-
|
|
186
|
-
### **Trigger Animation Manually**
|
|
187
|
-
|
|
188
|
-
```javascript
|
|
189
|
-
window.hsmain.counter.triggerAnimation('[data-hs-counter="wrapper"]');
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
Manually triggers animation for a specific counter (bypasses scroll detection).
|
|
193
|
-
|
|
194
|
-
### **Reset All Counters**
|
|
195
|
-
|
|
196
|
-
```javascript
|
|
197
|
-
window.hsmain.counter.reset();
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
Resets all counters to original state and re-enables scroll triggers.
|
|
201
|
-
|
|
202
|
-
### **Access Counter Elements**
|
|
203
|
-
|
|
204
|
-
```javascript
|
|
205
|
-
window.hsmain.counter.counters
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
Returns array of all counter wrapper elements.
|
|
209
|
-
|
|
210
|
-
---
|
|
211
|
-
|
|
212
|
-
## **Animation Behavior**
|
|
213
|
-
|
|
214
|
-
### **Easing Function**
|
|
215
|
-
|
|
216
|
-
Uses **easeOutQuart** for smooth deceleration:
|
|
217
|
-
- Starts fast
|
|
218
|
-
- Slows down as it approaches target
|
|
219
|
-
- Creates natural counting effect
|
|
220
|
-
|
|
221
|
-
### **Reduced Motion**
|
|
222
|
-
|
|
223
|
-
If user has `prefers-reduced-motion: reduce` enabled:
|
|
224
|
-
- Skips animation entirely
|
|
225
|
-
- Shows final number immediately
|
|
226
|
-
- Respects user accessibility preferences
|
|
227
|
-
|
|
228
|
-
---
|
|
229
|
-
|
|
230
|
-
## **Key Attributes Summary**
|
|
231
|
-
|
|
232
|
-
| Attribute | Purpose | Required On |
|
|
233
|
-
| ----- | ----- | ----- |
|
|
234
|
-
| `data-hs-counter="wrapper"` | Counter container | Wrapper div |
|
|
235
|
-
| `data-hs-anim="counter"` | Number to animate | Number element |
|
|
236
|
-
|
|
237
|
-
---
|
|
238
|
-
|
|
239
|
-
## **Notes**
|
|
240
|
-
|
|
241
|
-
1. **One-time animation** - Each counter animates only once when first scrolled into view
|
|
242
|
-
2. **IntersectionObserver** - Uses `threshold: 0` (triggers as soon as any part is visible)
|
|
243
|
-
3. **Original text preserved** - Stores original value in `data-hs-original` attribute
|
|
244
|
-
4. **Barba.js compatible** - Automatically reinitializes on page transitions
|
|
245
|
-
5. **No visual component** - Works with any styling, just handles number animation and a11y
|
|
246
|
-
|
|
247
|
-
---
|
|
248
|
-
|
|
249
|
-
## **Common Issues**
|
|
250
|
-
|
|
251
|
-
**Counter not animating:**
|
|
252
|
-
|
|
253
|
-
1. Verify `data-hs-counter="wrapper"` is on container element
|
|
254
|
-
2. Check that `data-hs-anim="counter"` is on number element
|
|
255
|
-
3. Ensure number element contains parseable number
|
|
256
|
-
4. Check if `prefers-reduced-motion` is enabled (animation skips)
|
|
257
|
-
|
|
258
|
-
**Wrong number format:**
|
|
259
|
-
|
|
260
|
-
1. Counter auto-detects prefix/suffix - ensure format is: `[prefix][number][suffix]`
|
|
261
|
-
2. Decimals are auto-detected from original number
|
|
262
|
-
3. Use dot (.) for decimal separator, not comma
|
|
263
|
-
|
|
264
|
-
**Aria-label missing text:**
|
|
265
|
-
|
|
266
|
-
1. Ensure all text is within `[data-hs-counter="wrapper"]`
|
|
267
|
-
2. Check that text nodes have actual content (not just whitespace)
|
|
268
|
-
3. Verify elements aren't hidden with `display: none` when script runs
|
|
269
|
-
|
|
270
|
-
**Counter animates multiple times:**
|
|
271
|
-
|
|
272
|
-
1. This shouldn't happen - check if `reset()` is being called unintentionally
|
|
273
|
-
2. Verify `data-animated` attribute is being set correctly
|
|
274
|
-
3. Check for multiple script instances running
|
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
export function init() {
|
|
2
|
-
const config = {
|
|
3
|
-
duration: 3000,
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
let counters = [];
|
|
7
|
-
let observer = null;
|
|
8
|
-
|
|
9
|
-
function updateConfig(newConfig) {
|
|
10
|
-
Object.assign(config, newConfig);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function easeOutQuart(t) {
|
|
14
|
-
return 1 - Math.pow(1 - t, 4);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function parseText(text) {
|
|
18
|
-
const match = text.match(/([^\d.]*)(\d*\.?\d+)(.*)/);
|
|
19
|
-
return {
|
|
20
|
-
prefix: match[1] || "",
|
|
21
|
-
number: parseFloat(match[2]),
|
|
22
|
-
suffix: match[3] || "",
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function formatNumber(value, prefix, suffix, decimals = 0) {
|
|
27
|
-
const formattedNumber = decimals > 0 ? value.toFixed(decimals) : Math.round(value);
|
|
28
|
-
return `${prefix}${formattedNumber}${suffix}`;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function prefersReducedMotion() {
|
|
32
|
-
return window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function animateCounter(element, targetValue, prefix, suffix, decimals) {
|
|
36
|
-
if (prefersReducedMotion()) {
|
|
37
|
-
element.textContent = element.getAttribute("data-hs-original");
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const startTime = Date.now();
|
|
42
|
-
const startValue = 0;
|
|
43
|
-
|
|
44
|
-
const animate = () => {
|
|
45
|
-
const elapsed = Date.now() - startTime;
|
|
46
|
-
const progress = Math.min(elapsed / config.duration, 1);
|
|
47
|
-
const easedProgress = easeOutQuart(progress);
|
|
48
|
-
const currentValue = startValue + (targetValue - startValue) * easedProgress;
|
|
49
|
-
|
|
50
|
-
element.textContent = formatNumber(currentValue, prefix, suffix, decimals);
|
|
51
|
-
|
|
52
|
-
if (progress < 1) {
|
|
53
|
-
requestAnimationFrame(animate);
|
|
54
|
-
} else {
|
|
55
|
-
element.textContent = element.getAttribute("data-hs-original");
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
element.textContent = formatNumber(0, prefix, suffix, decimals);
|
|
60
|
-
requestAnimationFrame(animate);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function setupObserver() {
|
|
64
|
-
observer = new IntersectionObserver((entries) => {
|
|
65
|
-
entries.forEach((entry) => {
|
|
66
|
-
if (entry.isIntersecting && !entry.target.dataset.animated) {
|
|
67
|
-
const numberElement = entry.target.querySelector('[data-hs-anim="counter"]');
|
|
68
|
-
startAnimation(numberElement);
|
|
69
|
-
entry.target.dataset.animated = "true";
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
}, { threshold: 0 });
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function startAnimation(element) {
|
|
76
|
-
const originalText = element.getAttribute("data-hs-original");
|
|
77
|
-
const parsed = parseText(originalText);
|
|
78
|
-
const decimals = (parsed.number.toString().split(".")[1] || "").length;
|
|
79
|
-
|
|
80
|
-
animateCounter(element, parsed.number, parsed.prefix, parsed.suffix, decimals);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function setupStatsAccessibility() {
|
|
84
|
-
const statsElements = document.querySelectorAll('[data-hs-counter="wrapper"]');
|
|
85
|
-
|
|
86
|
-
statsElements.forEach((element) => {
|
|
87
|
-
// Collect all text nodes and join with spaces
|
|
88
|
-
const walker = document.createTreeWalker(
|
|
89
|
-
element,
|
|
90
|
-
NodeFilter.SHOW_TEXT,
|
|
91
|
-
null
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
const textParts = [];
|
|
95
|
-
let node;
|
|
96
|
-
while (node = walker.nextNode()) {
|
|
97
|
-
const text = node.textContent.trim();
|
|
98
|
-
if (text) {
|
|
99
|
-
textParts.push(text);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const allText = textParts.join(' ');
|
|
104
|
-
|
|
105
|
-
element.setAttribute("aria-label", allText);
|
|
106
|
-
element.setAttribute("role", "img");
|
|
107
|
-
|
|
108
|
-
const allChildren = element.querySelectorAll("*");
|
|
109
|
-
allChildren.forEach((child) => {
|
|
110
|
-
child.setAttribute("aria-hidden", "true");
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function initCounters() {
|
|
116
|
-
const wrappers = document.querySelectorAll('[data-hs-counter="wrapper"]');
|
|
117
|
-
|
|
118
|
-
wrappers.forEach((wrapper) => {
|
|
119
|
-
const numberElement = wrapper.querySelector('[data-hs-anim="counter"]');
|
|
120
|
-
const parsed = parseText(numberElement.textContent.trim());
|
|
121
|
-
|
|
122
|
-
numberElement.setAttribute("data-hs-original", numberElement.textContent.trim());
|
|
123
|
-
counters.push(wrapper);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
setupObserver();
|
|
127
|
-
|
|
128
|
-
counters.forEach((counter) => {
|
|
129
|
-
observer.observe(counter);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
setupStatsAccessibility();
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function triggerAnimation(selector) {
|
|
136
|
-
const wrapper = document.querySelector(selector);
|
|
137
|
-
const numberElement = wrapper.querySelector('[data-hs-anim="counter"]');
|
|
138
|
-
startAnimation(numberElement);
|
|
139
|
-
wrapper.dataset.animated = "true";
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function reset() {
|
|
143
|
-
counters.forEach((wrapper) => {
|
|
144
|
-
delete wrapper.dataset.animated;
|
|
145
|
-
const numberElement = wrapper.querySelector('[data-hs-anim="counter"]');
|
|
146
|
-
numberElement.textContent = numberElement.getAttribute("data-hs-original");
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
counters.forEach((counter) => {
|
|
150
|
-
observer.unobserve(counter);
|
|
151
|
-
observer.observe(counter);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
setupStatsAccessibility();
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
initCounters();
|
|
158
|
-
|
|
159
|
-
window.hsmain = window.hsmain || {};
|
|
160
|
-
window.hsmain.counter = {
|
|
161
|
-
config,
|
|
162
|
-
updateConfig,
|
|
163
|
-
triggerAnimation,
|
|
164
|
-
reset,
|
|
165
|
-
counters,
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
return {
|
|
169
|
-
result: "counter initialized",
|
|
170
|
-
destroy: () => {
|
|
171
|
-
// Disconnect observer
|
|
172
|
-
if (observer) {
|
|
173
|
-
observer.disconnect();
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Clear counters array
|
|
177
|
-
counters.length = 0;
|
|
178
|
-
|
|
179
|
-
// Remove window API
|
|
180
|
-
if (window.hsmain && window.hsmain.counter) {
|
|
181
|
-
delete window.hsmain.counter;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
}
|