@hortonstudio/main 1.0.1 → 1.1.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/.claude/settings.local.json +19 -0
- package/CLAUDE.md +45 -0
- package/animations/hero.js +530 -0
- package/animations/text.js +305 -0
- package/animations/{modules/transition.js → transition.js} +2 -1
- package/autoInit/smooth-scroll.js +89 -0
- package/index.js +133 -49
- package/package.json +1 -1
- package/styles.css +10 -3
- package/test-cdn.html +29 -0
- package/utils/navbar.js +203 -0
- package/utils/scroll-progress.js +29 -0
- package/utils/toc.js +54 -0
- package/animations/READ +0 -0
- package/animations/modules/hero.js +0 -154
- package/animations/modules/text.js +0 -165
- package/utils/READ +0 -0
- package/utils/modules/toc.js +0 -0
@@ -0,0 +1,305 @@
|
|
1
|
+
const API_NAME = 'hsmain';
|
2
|
+
|
3
|
+
const config = {
|
4
|
+
global: {
|
5
|
+
animationDelay: 0
|
6
|
+
},
|
7
|
+
wordSplit: {
|
8
|
+
duration: 1.5,
|
9
|
+
stagger: 0.075,
|
10
|
+
yPercent: 110,
|
11
|
+
ease: "power4.out",
|
12
|
+
start: "top 97%"
|
13
|
+
},
|
14
|
+
lineSplit: {
|
15
|
+
duration: 1.5,
|
16
|
+
stagger: 0.1,
|
17
|
+
yPercent: 110,
|
18
|
+
ease: "power4.out",
|
19
|
+
start: "top 97%"
|
20
|
+
},
|
21
|
+
charSplit: {
|
22
|
+
duration: 1.2,
|
23
|
+
stagger: 0.03,
|
24
|
+
yPercent: 110,
|
25
|
+
ease: "power4.out",
|
26
|
+
start: "top 97%"
|
27
|
+
},
|
28
|
+
appear: {
|
29
|
+
y: 50,
|
30
|
+
duration: 1.5,
|
31
|
+
ease: "power3.out",
|
32
|
+
start: "top 97%"
|
33
|
+
}
|
34
|
+
};
|
35
|
+
|
36
|
+
function updateConfig(newConfig) {
|
37
|
+
function deepMerge(target, source) {
|
38
|
+
for (const key in source) {
|
39
|
+
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
40
|
+
target[key] = target[key] || {};
|
41
|
+
deepMerge(target[key], source[key]);
|
42
|
+
} else {
|
43
|
+
target[key] = source[key];
|
44
|
+
}
|
45
|
+
}
|
46
|
+
return target;
|
47
|
+
}
|
48
|
+
|
49
|
+
deepMerge(config, newConfig);
|
50
|
+
}
|
51
|
+
|
52
|
+
function killTextAnimations() {
|
53
|
+
activeAnimations.forEach(({ timeline, element }) => {
|
54
|
+
if (timeline) {
|
55
|
+
timeline.kill();
|
56
|
+
}
|
57
|
+
if (element?.splitTextInstance) {
|
58
|
+
element.splitTextInstance.revert();
|
59
|
+
}
|
60
|
+
});
|
61
|
+
activeAnimations.length = 0;
|
62
|
+
}
|
63
|
+
|
64
|
+
function startTextAnimations() {
|
65
|
+
setInitialStates().then(() => {
|
66
|
+
initAnimations();
|
67
|
+
});
|
68
|
+
}
|
69
|
+
|
70
|
+
let activeAnimations = [];
|
71
|
+
|
72
|
+
function waitForFonts() {
|
73
|
+
return document.fonts.ready;
|
74
|
+
}
|
75
|
+
|
76
|
+
const CharSplitAnimations = {
|
77
|
+
async initial() {
|
78
|
+
await waitForFonts();
|
79
|
+
|
80
|
+
const elements = document.querySelectorAll(".a-char-split > *:first-child");
|
81
|
+
|
82
|
+
elements.forEach((textElement, index) => {
|
83
|
+
|
84
|
+
const split = SplitText.create(textElement, {
|
85
|
+
type: "chars",
|
86
|
+
mask: "chars",
|
87
|
+
charsClass: "char",
|
88
|
+
});
|
89
|
+
textElement.splitTextInstance = split;
|
90
|
+
|
91
|
+
gsap.set(split.chars, {
|
92
|
+
yPercent: config.charSplit.yPercent
|
93
|
+
});
|
94
|
+
gsap.set(textElement, { autoAlpha: 1 });
|
95
|
+
});
|
96
|
+
},
|
97
|
+
|
98
|
+
async animate() {
|
99
|
+
await waitForFonts();
|
100
|
+
|
101
|
+
document.querySelectorAll(".a-char-split > *:first-child").forEach((textElement) => {
|
102
|
+
const chars = textElement.querySelectorAll('.char');
|
103
|
+
const tl = gsap.timeline({
|
104
|
+
scrollTrigger: {
|
105
|
+
trigger: textElement,
|
106
|
+
start: config.charSplit.start,
|
107
|
+
invalidateOnRefresh: true,
|
108
|
+
},
|
109
|
+
onComplete: () => {
|
110
|
+
textElement.splitTextInstance.revert();
|
111
|
+
}
|
112
|
+
});
|
113
|
+
|
114
|
+
tl.to(chars, {
|
115
|
+
yPercent: 0,
|
116
|
+
duration: config.charSplit.duration,
|
117
|
+
stagger: config.charSplit.stagger,
|
118
|
+
ease: config.charSplit.ease,
|
119
|
+
});
|
120
|
+
|
121
|
+
activeAnimations.push({ timeline: tl, element: textElement });
|
122
|
+
});
|
123
|
+
}
|
124
|
+
};
|
125
|
+
|
126
|
+
const WordSplitAnimations = {
|
127
|
+
async initial() {
|
128
|
+
await waitForFonts();
|
129
|
+
|
130
|
+
const elements = document.querySelectorAll(".a-word-split > *:first-child");
|
131
|
+
|
132
|
+
elements.forEach((textElement, index) => {
|
133
|
+
|
134
|
+
const split = SplitText.create(textElement, {
|
135
|
+
type: "words",
|
136
|
+
mask: "words",
|
137
|
+
wordsClass: "word",
|
138
|
+
});
|
139
|
+
textElement.splitTextInstance = split;
|
140
|
+
|
141
|
+
gsap.set(split.words, {
|
142
|
+
yPercent: config.wordSplit.yPercent
|
143
|
+
});
|
144
|
+
gsap.set(textElement, { autoAlpha: 1 });
|
145
|
+
});
|
146
|
+
},
|
147
|
+
|
148
|
+
async animate() {
|
149
|
+
await waitForFonts();
|
150
|
+
|
151
|
+
document.querySelectorAll(".a-word-split > *:first-child").forEach((textElement) => {
|
152
|
+
const words = textElement.querySelectorAll('.word');
|
153
|
+
const tl = gsap.timeline({
|
154
|
+
scrollTrigger: {
|
155
|
+
trigger: textElement,
|
156
|
+
start: config.wordSplit.start,
|
157
|
+
invalidateOnRefresh: true,
|
158
|
+
},
|
159
|
+
onComplete: () => {
|
160
|
+
textElement.splitTextInstance.revert();
|
161
|
+
}
|
162
|
+
});
|
163
|
+
|
164
|
+
tl.to(words, {
|
165
|
+
yPercent: 0,
|
166
|
+
duration: config.wordSplit.duration,
|
167
|
+
stagger: config.wordSplit.stagger,
|
168
|
+
ease: config.wordSplit.ease,
|
169
|
+
});
|
170
|
+
|
171
|
+
activeAnimations.push({ timeline: tl, element: textElement });
|
172
|
+
});
|
173
|
+
}
|
174
|
+
};
|
175
|
+
|
176
|
+
const LineSplitAnimations = {
|
177
|
+
async initial() {
|
178
|
+
await waitForFonts();
|
179
|
+
|
180
|
+
const elements = document.querySelectorAll(".a-line-split > *:first-child");
|
181
|
+
|
182
|
+
elements.forEach((textElement, index) => {
|
183
|
+
|
184
|
+
const split = SplitText.create(textElement, {
|
185
|
+
type: "lines",
|
186
|
+
mask: "lines",
|
187
|
+
linesClass: "line",
|
188
|
+
});
|
189
|
+
textElement.splitTextInstance = split;
|
190
|
+
|
191
|
+
gsap.set(split.lines, {
|
192
|
+
yPercent: config.lineSplit.yPercent
|
193
|
+
});
|
194
|
+
gsap.set(textElement, { autoAlpha: 1 });
|
195
|
+
});
|
196
|
+
},
|
197
|
+
|
198
|
+
async animate() {
|
199
|
+
await waitForFonts();
|
200
|
+
|
201
|
+
document.querySelectorAll(".a-line-split > *:first-child").forEach((textElement) => {
|
202
|
+
const lines = textElement.querySelectorAll('.line');
|
203
|
+
const tl = gsap.timeline({
|
204
|
+
scrollTrigger: {
|
205
|
+
trigger: textElement,
|
206
|
+
start: config.lineSplit.start,
|
207
|
+
invalidateOnRefresh: true,
|
208
|
+
},
|
209
|
+
onComplete: () => {
|
210
|
+
textElement.splitTextInstance.revert();
|
211
|
+
}
|
212
|
+
});
|
213
|
+
|
214
|
+
tl.to(lines, {
|
215
|
+
yPercent: 0,
|
216
|
+
duration: config.lineSplit.duration,
|
217
|
+
stagger: config.lineSplit.stagger,
|
218
|
+
ease: config.lineSplit.ease,
|
219
|
+
});
|
220
|
+
|
221
|
+
activeAnimations.push({ timeline: tl, element: textElement });
|
222
|
+
});
|
223
|
+
}
|
224
|
+
};
|
225
|
+
|
226
|
+
const AppearAnimations = {
|
227
|
+
async initial() {
|
228
|
+
await waitForFonts();
|
229
|
+
|
230
|
+
const elements = document.querySelectorAll('.a-appear');
|
231
|
+
elements.forEach(element => {
|
232
|
+
gsap.set(element, {
|
233
|
+
y: config.appear.y,
|
234
|
+
opacity: 0
|
235
|
+
});
|
236
|
+
});
|
237
|
+
},
|
238
|
+
|
239
|
+
async animate() {
|
240
|
+
await waitForFonts();
|
241
|
+
|
242
|
+
document.querySelectorAll('.a-appear').forEach(element => {
|
243
|
+
const tl = gsap.timeline({
|
244
|
+
scrollTrigger: {
|
245
|
+
trigger: element,
|
246
|
+
start: config.appear.start,
|
247
|
+
invalidateOnRefresh: true,
|
248
|
+
}
|
249
|
+
});
|
250
|
+
|
251
|
+
tl.to(element, {
|
252
|
+
y: 0,
|
253
|
+
opacity: 1,
|
254
|
+
duration: config.appear.duration,
|
255
|
+
ease: config.appear.ease
|
256
|
+
});
|
257
|
+
|
258
|
+
activeAnimations.push({ timeline: tl, element: element });
|
259
|
+
});
|
260
|
+
|
261
|
+
}
|
262
|
+
};
|
263
|
+
|
264
|
+
async function setInitialStates() {
|
265
|
+
await Promise.all([
|
266
|
+
CharSplitAnimations.initial(),
|
267
|
+
WordSplitAnimations.initial(),
|
268
|
+
LineSplitAnimations.initial(),
|
269
|
+
AppearAnimations.initial()
|
270
|
+
]);
|
271
|
+
}
|
272
|
+
|
273
|
+
async function initAnimations() {
|
274
|
+
if (config.global.animationDelay > 0) {
|
275
|
+
await new Promise(resolve => setTimeout(resolve, config.global.animationDelay * 1000));
|
276
|
+
}
|
277
|
+
|
278
|
+
await Promise.all([
|
279
|
+
CharSplitAnimations.animate(),
|
280
|
+
WordSplitAnimations.animate(),
|
281
|
+
LineSplitAnimations.animate(),
|
282
|
+
AppearAnimations.animate()
|
283
|
+
]);
|
284
|
+
}
|
285
|
+
|
286
|
+
export async function init() {
|
287
|
+
await setInitialStates();
|
288
|
+
initAnimations();
|
289
|
+
|
290
|
+
window.addEventListener('resize', ScrollTrigger.refresh());
|
291
|
+
|
292
|
+
const api = window[API_NAME] || {};
|
293
|
+
api.textAnimations = {
|
294
|
+
config: config,
|
295
|
+
updateConfig: updateConfig,
|
296
|
+
start: startTextAnimations,
|
297
|
+
kill: killTextAnimations,
|
298
|
+
restart: () => {
|
299
|
+
killTextAnimations();
|
300
|
+
startTextAnimations();
|
301
|
+
}
|
302
|
+
};
|
303
|
+
|
304
|
+
return { result: 'anim-text initialized' };
|
305
|
+
}
|
@@ -1,4 +1,5 @@
|
|
1
1
|
// Page Transition Module
|
2
|
+
const API_NAME = 'hsmain';
|
2
3
|
export async function init() {
|
3
4
|
|
4
5
|
// Your original code with minimal changes
|
@@ -36,5 +37,5 @@ export async function init() {
|
|
36
37
|
setTimeout(() => {$(".transition").css("display", "none");}, 50);});
|
37
38
|
}, introDurationMS);
|
38
39
|
|
39
|
-
return { result: 'transition
|
40
|
+
return { result: 'anim-transition initialized' };
|
40
41
|
}
|
@@ -0,0 +1,89 @@
|
|
1
|
+
const API_NAME = 'hsmain';
|
2
|
+
|
3
|
+
export async function init() {
|
4
|
+
const api = window[API_NAME];
|
5
|
+
api.afterWebflowReady(() => {
|
6
|
+
if (typeof $ !== 'undefined') {
|
7
|
+
$(document).off('click.wf-scroll');
|
8
|
+
}
|
9
|
+
});
|
10
|
+
|
11
|
+
// Disable CSS smooth scrolling
|
12
|
+
document.documentElement.style.scrollBehavior = 'auto';
|
13
|
+
document.body.style.scrollBehavior = 'auto';
|
14
|
+
|
15
|
+
// Check if user prefers reduced motion
|
16
|
+
function prefersReducedMotion() {
|
17
|
+
return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
18
|
+
}
|
19
|
+
|
20
|
+
function getScrollOffset() {
|
21
|
+
const offsetValue = getComputedStyle(document.documentElement)
|
22
|
+
.getPropertyValue('--misc--scroll-offset').trim();
|
23
|
+
return parseInt(offsetValue) || 0;
|
24
|
+
}
|
25
|
+
|
26
|
+
// Smooth scroll to element with offset
|
27
|
+
function scrollToElement(target, offset = 0) {
|
28
|
+
if (!target) return;
|
29
|
+
|
30
|
+
// Skip animation if user prefers reduced motion
|
31
|
+
if (prefersReducedMotion()) {
|
32
|
+
const targetPosition = target.getBoundingClientRect().top + window.scrollY - offset;
|
33
|
+
window.scrollTo(0, targetPosition);
|
34
|
+
target.setAttribute('tabindex', '-1');
|
35
|
+
target.focus({ preventScroll: true });
|
36
|
+
return;
|
37
|
+
}
|
38
|
+
|
39
|
+
gsap.to(window, {
|
40
|
+
duration: 1,
|
41
|
+
scrollTo: {
|
42
|
+
y: target,
|
43
|
+
offsetY: offset
|
44
|
+
},
|
45
|
+
ease: "power2.out",
|
46
|
+
onComplete: function() {
|
47
|
+
target.setAttribute('tabindex', '-1');
|
48
|
+
target.focus({ preventScroll: true });
|
49
|
+
}
|
50
|
+
});
|
51
|
+
}
|
52
|
+
|
53
|
+
// Handle anchor link clicks and keyboard activation
|
54
|
+
function handleAnchorClicks() {
|
55
|
+
document.addEventListener('click', handleAnchorActivation);
|
56
|
+
document.addEventListener('keydown', function(e) {
|
57
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
58
|
+
handleAnchorActivation(e);
|
59
|
+
}
|
60
|
+
});
|
61
|
+
}
|
62
|
+
|
63
|
+
function handleAnchorActivation(e) {
|
64
|
+
const link = e.target.closest('a[href^="#"]');
|
65
|
+
if (!link) return;
|
66
|
+
|
67
|
+
const href = link.getAttribute('href');
|
68
|
+
if (!href || href === '#') return;
|
69
|
+
|
70
|
+
const targetId = href.substring(1);
|
71
|
+
const targetElement = document.getElementById(targetId);
|
72
|
+
|
73
|
+
if (targetElement) {
|
74
|
+
e.preventDefault();
|
75
|
+
if (history.replaceState) {
|
76
|
+
history.replaceState(null, null, `#${targetElement.id}`);
|
77
|
+
}
|
78
|
+
const offset = getScrollOffset();
|
79
|
+
scrollToElement(targetElement, offset);
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
// Initialize anchor link handling
|
84
|
+
handleAnchorClicks();
|
85
|
+
|
86
|
+
return {
|
87
|
+
result: 'autoInit-smooth-scroll initialized'
|
88
|
+
};
|
89
|
+
}
|
package/index.js
CHANGED
@@ -1,100 +1,184 @@
|
|
1
|
-
|
1
|
+
// Configuration: Change this variable to rename the API
|
2
|
+
const API_NAME = 'hsmain';
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
]);
|
4
|
+
window[API_NAME] = window[API_NAME] || {};
|
5
|
+
window[API_NAME].loaded = false
|
6
6
|
|
7
|
-
const
|
8
|
-
"hs-
|
9
|
-
"hs-
|
10
|
-
"hs-
|
11
|
-
|
7
|
+
const animationModules = {
|
8
|
+
"data-hs-anim-text": "./animations/text.js",
|
9
|
+
"data-hs-anim-hero": "./animations/hero.js",
|
10
|
+
"data-hs-anim-transition": "./animations/transition.js"
|
11
|
+
};
|
12
|
+
|
13
|
+
const utilityModules = {
|
14
|
+
"data-hs-util-toc": "./utils/toc.js",
|
15
|
+
"data-hs-util-progress": "./utils/scroll-progress.js",
|
16
|
+
"data-hs-util-navbar": "./utils/navbar.js"
|
17
|
+
};
|
18
|
+
|
19
|
+
// Modules that auto-initialize
|
20
|
+
const autoInitModules = {
|
21
|
+
"smooth-scroll": "./autoInit/smooth-scroll.js"
|
22
|
+
};
|
23
|
+
|
24
|
+
// Store callbacks to run after Webflow.ready()
|
25
|
+
const postWebflowCallbacks = [];
|
26
|
+
|
27
|
+
// Get the base URL from the current script's location
|
28
|
+
const getBaseUrl = () => {
|
29
|
+
const currentScript = document.querySelector('script[data-hs-main]');
|
30
|
+
if (currentScript && currentScript.src) {
|
31
|
+
// Extract directory path from script URL
|
32
|
+
return currentScript.src.substring(0, currentScript.src.lastIndexOf('/') + 1);
|
33
|
+
}
|
34
|
+
// Fallback to current origin + path for relative imports
|
35
|
+
return new URL('./', import.meta.url).href;
|
12
36
|
};
|
13
37
|
|
14
38
|
const loadModule = (moduleName) => {
|
15
|
-
|
39
|
+
// Check manual modules first
|
40
|
+
let modulePath = animationModules[moduleName] || utilityModules[moduleName];
|
41
|
+
|
42
|
+
// Then check auto-init modules
|
43
|
+
if (!modulePath) {
|
44
|
+
modulePath = autoInitModules[moduleName];
|
45
|
+
}
|
46
|
+
|
16
47
|
if (!modulePath) {
|
17
48
|
throw new Error(`HortonStudio module "${moduleName}" is not supported.`);
|
18
49
|
}
|
19
|
-
|
50
|
+
|
51
|
+
// Convert relative path to absolute URL
|
52
|
+
const baseUrl = getBaseUrl();
|
53
|
+
const absoluteUrl = new URL(modulePath, baseUrl).href;
|
54
|
+
|
55
|
+
return import(absoluteUrl);
|
20
56
|
};
|
21
57
|
|
22
58
|
const findCurrentScriptTag = () => {
|
23
|
-
const scriptTag = document.querySelector('script[hs-main]');
|
59
|
+
const scriptTag = document.querySelector('script[data-hs-main]');
|
24
60
|
return scriptTag || null;
|
25
61
|
};
|
26
62
|
|
27
|
-
const processModules = (scriptTag) => {
|
28
|
-
|
29
|
-
return;
|
30
|
-
}
|
63
|
+
const processModules = async (scriptTag) => {
|
64
|
+
const modulePromises = [];
|
31
65
|
|
32
|
-
|
33
|
-
|
34
|
-
|
66
|
+
// Load manual modules based on attributes
|
67
|
+
for (const moduleName of Object.keys({ ...animationModules, ...utilityModules })) {
|
68
|
+
if (scriptTag && scriptTag.hasAttribute(moduleName)) {
|
69
|
+
modulePromises.push(loadHsModule(moduleName));
|
35
70
|
}
|
36
71
|
}
|
37
|
-
};
|
38
|
-
|
39
|
-
const loadHsModule = async (moduleName) => {
|
40
|
-
const { hsMain } = window;
|
41
72
|
|
42
|
-
|
43
|
-
|
73
|
+
// Load auto-init modules
|
74
|
+
for (const moduleName of Object.keys(autoInitModules)) {
|
75
|
+
modulePromises.push(loadHsModule(moduleName));
|
44
76
|
}
|
45
77
|
|
46
|
-
|
78
|
+
// Wait for ALL modules to finish loading
|
79
|
+
await Promise.all(modulePromises);
|
47
80
|
|
48
|
-
|
49
|
-
|
81
|
+
// Always refresh Webflow after all modules are loaded
|
82
|
+
refreshWebflow();
|
83
|
+
};
|
84
|
+
|
85
|
+
const refreshWebflow = () => {
|
50
86
|
|
87
|
+
setTimeout(() => {
|
88
|
+
if (window.Webflow && window.Webflow.ready) {
|
89
|
+
window.Webflow.ready();
|
90
|
+
|
91
|
+
// Run all registered post-Webflow callbacks
|
92
|
+
setTimeout(() => {
|
93
|
+
postWebflowCallbacks.forEach(callback => {
|
94
|
+
try {
|
95
|
+
callback();
|
96
|
+
} catch (error) {
|
97
|
+
}
|
98
|
+
});
|
99
|
+
window[API_NAME].loaded = true;
|
100
|
+
},);
|
101
|
+
}
|
102
|
+
},);
|
103
|
+
};
|
104
|
+
|
105
|
+
const loadHsModule = async (moduleName) => {
|
106
|
+
const apiInstance = window[API_NAME];
|
107
|
+
|
108
|
+
if (apiInstance.process.has(moduleName)) {
|
109
|
+
return apiInstance.modules[moduleName]?.loading;
|
110
|
+
}
|
111
|
+
|
112
|
+
apiInstance.process.add(moduleName);
|
113
|
+
|
114
|
+
const moduleObj = apiInstance.modules[moduleName] || {};
|
115
|
+
apiInstance.modules[moduleName] = moduleObj;
|
116
|
+
|
51
117
|
moduleObj.loading = new Promise((resolve, reject) => {
|
52
118
|
moduleObj.resolve = resolve;
|
53
119
|
moduleObj.reject = reject;
|
54
120
|
});
|
55
|
-
|
121
|
+
|
56
122
|
try {
|
57
123
|
const { init, version } = await loadModule(moduleName);
|
58
124
|
const initResult = await init();
|
59
125
|
const { result } = initResult || {};
|
60
|
-
|
126
|
+
|
61
127
|
moduleObj.version = version;
|
62
128
|
moduleObj.restart = () => {
|
63
|
-
|
129
|
+
apiInstance.process.delete(moduleName);
|
64
130
|
return loadHsModule(moduleName);
|
65
131
|
};
|
66
|
-
|
132
|
+
|
67
133
|
moduleObj.resolve?.(result);
|
68
134
|
delete moduleObj.resolve;
|
69
135
|
delete moduleObj.reject;
|
70
|
-
|
136
|
+
|
71
137
|
return result;
|
72
|
-
|
138
|
+
|
73
139
|
} catch (error) {
|
74
140
|
moduleObj.reject?.(error);
|
75
|
-
|
141
|
+
apiInstance.process.delete(moduleName);
|
76
142
|
throw error;
|
77
143
|
}
|
78
144
|
};
|
79
145
|
|
80
|
-
const
|
81
|
-
const
|
82
|
-
|
83
|
-
const existingRequests = Array.isArray(
|
146
|
+
const initializeAPI = () => {
|
147
|
+
const apiInstance = window[API_NAME];
|
148
|
+
|
149
|
+
const existingRequests = Array.isArray(apiInstance) ? apiInstance : [];
|
84
150
|
const scriptTag = findCurrentScriptTag();
|
85
|
-
|
86
|
-
|
151
|
+
const richTextBlocks = document.querySelectorAll('.w-richtext');
|
152
|
+
richTextBlocks.forEach(block => {
|
153
|
+
const images = block.querySelectorAll('img');
|
154
|
+
images.forEach(img => {
|
155
|
+
img.loading = 'eager';
|
156
|
+
});
|
157
|
+
});
|
158
|
+
|
159
|
+
window[API_NAME] = {
|
87
160
|
scriptTag,
|
88
161
|
modules: {},
|
89
162
|
process: new Set(),
|
90
|
-
|
163
|
+
|
91
164
|
load: loadHsModule,
|
92
|
-
|
165
|
+
|
166
|
+
// API function for scripts to register post-Webflow callbacks
|
167
|
+
afterWebflowReady: (callback) => {
|
168
|
+
if (typeof callback === 'function') {
|
169
|
+
postWebflowCallbacks.push(callback);
|
170
|
+
} else {
|
171
|
+
}
|
172
|
+
},
|
173
|
+
|
93
174
|
status(moduleName) {
|
94
175
|
if (!moduleName) {
|
95
176
|
return {
|
96
177
|
loaded: Object.keys(this.modules),
|
97
|
-
loading: [...this.process]
|
178
|
+
loading: [...this.process],
|
179
|
+
animations: Object.keys(animationModules),
|
180
|
+
utilities: Object.keys(utilityModules),
|
181
|
+
autoInit: Object.keys(autoInitModules)
|
98
182
|
};
|
99
183
|
}
|
100
184
|
return {
|
@@ -103,20 +187,20 @@ const initializeHsMain = () => {
|
|
103
187
|
};
|
104
188
|
}
|
105
189
|
};
|
106
|
-
|
190
|
+
|
107
191
|
processModules(scriptTag);
|
108
|
-
|
192
|
+
|
109
193
|
if (existingRequests.length > 0) {
|
110
194
|
existingRequests.forEach(request => {
|
111
195
|
if (typeof request === 'string') {
|
112
|
-
window.
|
196
|
+
window[API_NAME].load(request);
|
113
197
|
}
|
114
198
|
});
|
115
199
|
}
|
116
200
|
};
|
117
201
|
|
118
202
|
if (document.readyState === 'loading') {
|
119
|
-
document.addEventListener('DOMContentLoaded',
|
203
|
+
document.addEventListener('DOMContentLoaded', initializeAPI);
|
120
204
|
} else {
|
121
|
-
|
205
|
+
initializeAPI();
|
122
206
|
}
|
package/package.json
CHANGED
package/styles.css
CHANGED
@@ -4,7 +4,14 @@ body .transition {display: block}
|
|
4
4
|
.no-scroll-transition {overflow: hidden; position: relative;}
|
5
5
|
|
6
6
|
/* splittext */
|
7
|
-
.line-mask, .word-mask, .char-mask {
|
8
|
-
padding: .1em
|
9
|
-
margin: -.1em
|
7
|
+
.line-mask, .word-mask, .char-mask {
|
8
|
+
padding-bottom: .1em;
|
9
|
+
margin-bottom: -.1em;
|
10
|
+
padding-inline: .1em;
|
11
|
+
margin-inline: -.1em;
|
12
|
+
}
|
13
|
+
/* scroll cleanliness */
|
14
|
+
html, body {
|
15
|
+
overscroll-behavior: none;
|
16
|
+
scrollbar-gutter: stable;
|
10
17
|
}
|