@hortonstudio/main 1.2.14 → 1.2.18
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 +40 -0
- package/animations/hero.js +599 -0
- package/animations/text.js +369 -0
- package/animations/transition.js +57 -0
- package/autoInit/smooth-scroll.js +89 -0
- package/debug-version.html +37 -0
- package/index.js +209 -282
- package/package.json +4 -10
- package/utils/navbar.js +214 -0
- package/utils/scroll-progress.js +29 -0
- package/utils/toc.js +77 -0
- package/HS-MAIN-DOCUMENTATION.md +0 -567
package/index.js
CHANGED
@@ -1,305 +1,232 @@
|
|
1
|
-
// ver 1.2.14
|
2
|
-
|
3
1
|
const API_NAME = 'hsmain';
|
4
2
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
const initializeHsMain = async () => {
|
4
|
+
if (window[API_NAME] && !Array.isArray(window[API_NAME]) && window[API_NAME].loaded) {
|
5
|
+
return;
|
6
|
+
}
|
7
|
+
|
8
|
+
const queuedModules = Array.isArray(window[API_NAME]) ? window[API_NAME] : [];
|
9
|
+
|
10
|
+
const animationModules = {
|
11
|
+
'data-hs-anim-text': true,
|
12
|
+
'data-hs-anim-hero': true,
|
13
|
+
'data-hs-anim-transition': true
|
14
|
+
};
|
15
|
+
|
16
|
+
const utilityModules = {
|
17
|
+
'data-hs-util-toc': true,
|
18
|
+
'data-hs-util-progress': true,
|
19
|
+
'data-hs-util-navbar': true
|
20
|
+
};
|
21
|
+
|
22
|
+
const autoInitModules = {
|
23
|
+
'smooth-scroll': true
|
24
|
+
};
|
25
|
+
|
26
|
+
const allDataAttributes = { ...animationModules, ...utilityModules };
|
27
|
+
|
28
|
+
const waitForWebflow = async () => new Promise(resolve => {
|
29
|
+
if (!window.Webflow) window.Webflow = [];
|
30
|
+
window.Webflow.push(resolve);
|
31
|
+
});
|
32
|
+
|
33
|
+
const waitForDOMContentLoaded = async () => new Promise(resolve => {
|
34
|
+
if (document.readyState === 'loading') {
|
35
|
+
document.addEventListener('DOMContentLoaded', resolve);
|
36
|
+
} else {
|
37
|
+
resolve();
|
38
|
+
}
|
39
|
+
});
|
40
|
+
|
41
|
+
const moduleMap = {
|
42
|
+
'data-hs-anim-text': () => import('./animations/text.js'),
|
43
|
+
'data-hs-anim-hero': () => import('./animations/hero.js'),
|
44
|
+
'data-hs-anim-transition': () => import('./animations/transition.js'),
|
45
|
+
'data-hs-util-toc': () => import('./utils/toc.js'),
|
46
|
+
'data-hs-util-progress': () => import('./utils/scroll-progress.js'),
|
47
|
+
'data-hs-util-navbar': () => import('./utils/navbar.js'),
|
48
|
+
'smooth-scroll': () => import('./autoInit/smooth-scroll.js')
|
49
|
+
};
|
50
|
+
|
51
|
+
let scripts = [...document.querySelectorAll(`script[type="module"][src="${import.meta.url}"]`)];
|
52
|
+
|
53
|
+
if (scripts.length === 0) {
|
54
|
+
scripts = [...document.querySelectorAll('script[type="module"][src*="@hortonstudio/main"]')]
|
55
|
+
.filter(script => {
|
56
|
+
const scriptSrc = script.src;
|
57
|
+
const currentSrc = import.meta.url;
|
58
|
+
const scriptPackage = scriptSrc.match(/@hortonstudio\/main(@[\d.]+)?/)?.[0];
|
59
|
+
const currentPackage = currentSrc.match(/@hortonstudio\/main(@[\d.]+)?/)?.[0];
|
60
|
+
return scriptPackage && currentPackage &&
|
61
|
+
scriptPackage.split('@')[0] === currentPackage.split('@')[0];
|
62
|
+
});
|
63
|
+
}
|
64
|
+
|
65
|
+
const loadModule = async (moduleName) => {
|
66
|
+
const instance = window[API_NAME];
|
67
|
+
if (instance.process.has(moduleName)) {
|
68
|
+
return instance.modules[moduleName]?.loading;
|
10
69
|
}
|
11
70
|
|
12
|
-
|
13
|
-
const existingRequests = Array.isArray(window[API_NAME]) ? window[API_NAME] : [];
|
14
|
-
|
15
|
-
// Module definitions
|
16
|
-
const animationModules = {
|
17
|
-
"data-hs-anim-text": true,
|
18
|
-
"data-hs-anim-hero": true,
|
19
|
-
"data-hs-anim-transition": true
|
20
|
-
};
|
21
|
-
|
22
|
-
const utilityModules = {
|
23
|
-
"data-hs-util-toc": true,
|
24
|
-
"data-hs-util-progress": true,
|
25
|
-
"data-hs-util-navbar": true
|
26
|
-
};
|
27
|
-
|
28
|
-
const autoInitModules = {
|
29
|
-
"smooth-scroll": true
|
30
|
-
};
|
31
|
-
|
32
|
-
// All available modules
|
33
|
-
const allModules = { ...animationModules, ...utilityModules };
|
34
|
-
|
35
|
-
// Dynamic module loader (CDN URLs)
|
36
|
-
const loadModule = async (moduleName) => {
|
37
|
-
switch (moduleName) {
|
38
|
-
case "data-hs-anim-text":
|
39
|
-
return import('https://cdn.jsdelivr.net/npm/@hortonstudio/main-anim-text@1/text.js');
|
40
|
-
case "data-hs-anim-hero":
|
41
|
-
return import('https://cdn.jsdelivr.net/npm/@hortonstudio/main-anim-hero@1/hero.js');
|
42
|
-
case "data-hs-anim-transition":
|
43
|
-
return import('https://cdn.jsdelivr.net/npm/@hortonstudio/main-anim-transition@1/transition.js');
|
44
|
-
case "data-hs-util-toc":
|
45
|
-
return import('https://cdn.jsdelivr.net/npm/@hortonstudio/main-util-toc@1/toc.js');
|
46
|
-
case "data-hs-util-progress":
|
47
|
-
return import('https://cdn.jsdelivr.net/npm/@hortonstudio/main-util-progress@1/scroll-progress.js');
|
48
|
-
case "data-hs-util-navbar":
|
49
|
-
return import('https://cdn.jsdelivr.net/npm/@hortonstudio/main-util-navbar@1/navbar.js');
|
50
|
-
case "smooth-scroll":
|
51
|
-
return import('https://cdn.jsdelivr.net/npm/@hortonstudio/main-smooth-scroll@1/smooth-scroll.js');
|
52
|
-
default:
|
53
|
-
throw new Error(`${API_NAME} module "${moduleName}" is not supported.`);
|
54
|
-
}
|
55
|
-
};
|
71
|
+
instance.process.add(moduleName);
|
56
72
|
|
57
|
-
|
58
|
-
|
59
|
-
return new Promise((resolve) => {
|
60
|
-
window.Webflow ||= [];
|
61
|
-
window.Webflow.push(resolve);
|
62
|
-
});
|
63
|
-
};
|
73
|
+
const moduleData = instance.modules[moduleName] || {};
|
74
|
+
instance.modules[moduleName] = moduleData;
|
64
75
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
document.addEventListener('DOMContentLoaded', resolve);
|
70
|
-
} else {
|
71
|
-
resolve();
|
72
|
-
}
|
73
|
-
});
|
74
|
-
};
|
76
|
+
moduleData.loading = new Promise((resolve, reject) => {
|
77
|
+
moduleData.resolve = resolve;
|
78
|
+
moduleData.reject = reject;
|
79
|
+
});
|
75
80
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
81
|
+
try {
|
82
|
+
const { init, version } = await moduleMap[moduleName]();
|
83
|
+
const result = await init();
|
84
|
+
const { result: initResult, destroy } = result || {};
|
85
|
+
|
86
|
+
moduleData.version = version;
|
87
|
+
moduleData.destroy = () => {
|
88
|
+
destroy?.();
|
89
|
+
instance.process.delete(moduleName);
|
90
|
+
};
|
91
|
+
moduleData.restart = () => {
|
92
|
+
moduleData.destroy?.();
|
93
|
+
instance.load(moduleName);
|
94
|
+
};
|
95
|
+
|
96
|
+
moduleData.resolve?.(initResult);
|
97
|
+
delete moduleData.resolve;
|
98
|
+
delete moduleData.reject;
|
99
|
+
|
100
|
+
return initResult;
|
101
|
+
} catch (error) {
|
102
|
+
moduleData.reject?.(error);
|
103
|
+
instance.process.delete(moduleName);
|
104
|
+
throw error;
|
90
105
|
}
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
106
|
+
};
|
107
|
+
|
108
|
+
const readyCallbacks = [];
|
109
|
+
|
110
|
+
window[API_NAME] = {
|
111
|
+
scripts,
|
112
|
+
modules: {},
|
113
|
+
process: new Set(),
|
114
|
+
load: loadModule,
|
115
|
+
loaded: false,
|
116
|
+
push(...items) {
|
117
|
+
for (const [moduleName, callback] of items) {
|
118
|
+
if (typeof callback === 'function') {
|
119
|
+
this.modules[moduleName]?.loading?.then(callback);
|
120
|
+
} else {
|
121
|
+
this.load(moduleName);
|
99
122
|
}
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
try {
|
115
|
-
const { init, version } = await loadModule(moduleName);
|
116
|
-
const initResult = await init();
|
117
|
-
const { result, destroy } = initResult || {};
|
118
|
-
|
119
|
-
moduleObj.version = version;
|
120
|
-
|
121
|
-
// Add destroy and restart methods
|
122
|
-
moduleObj.destroy = () => {
|
123
|
-
destroy?.();
|
124
|
-
apiInstance.process.delete(moduleName);
|
125
|
-
};
|
126
|
-
|
127
|
-
moduleObj.restart = () => {
|
128
|
-
moduleObj.destroy?.();
|
129
|
-
return apiInstance.load(moduleName);
|
130
|
-
};
|
131
|
-
|
132
|
-
moduleObj.resolve?.(result);
|
133
|
-
delete moduleObj.resolve;
|
134
|
-
delete moduleObj.reject;
|
135
|
-
|
136
|
-
return result;
|
137
|
-
|
138
|
-
} catch (error) {
|
139
|
-
moduleObj.reject?.(error);
|
140
|
-
apiInstance.process.delete(moduleName);
|
141
|
-
throw error;
|
123
|
+
}
|
124
|
+
},
|
125
|
+
destroy() {
|
126
|
+
for (const moduleName in this.modules) {
|
127
|
+
this.modules[moduleName]?.destroy?.();
|
128
|
+
}
|
129
|
+
},
|
130
|
+
afterReady(callback) {
|
131
|
+
if (typeof callback === 'function') {
|
132
|
+
if (this.loaded) {
|
133
|
+
callback();
|
134
|
+
} else {
|
135
|
+
readyCallbacks.push(callback);
|
142
136
|
}
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
137
|
+
}
|
138
|
+
},
|
139
|
+
afterWebflowReady(callback) {
|
140
|
+
if (typeof callback === 'function') {
|
141
|
+
if (this.loaded) {
|
142
|
+
callback();
|
143
|
+
} else {
|
144
|
+
readyCallbacks.push(callback);
|
145
|
+
}
|
146
|
+
}
|
147
|
+
},
|
148
|
+
status(moduleName) {
|
149
|
+
if (moduleName) {
|
150
|
+
return {
|
151
|
+
loaded: !!this.modules[moduleName],
|
152
|
+
loading: this.process.has(moduleName)
|
153
|
+
};
|
154
|
+
}
|
155
|
+
return {
|
156
|
+
loaded: Object.keys(this.modules),
|
157
|
+
loading: [...this.process],
|
158
|
+
animations: Object.keys(animationModules),
|
159
|
+
utilities: Object.keys(utilityModules),
|
160
|
+
autoInit: Object.keys(autoInitModules)
|
161
|
+
};
|
162
|
+
}
|
163
|
+
};
|
147
164
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
modules: {},
|
152
|
-
process: new Set(),
|
153
|
-
load: loadHsModule,
|
154
|
-
loaded: false,
|
155
|
-
|
156
|
-
// Push method for queuing
|
157
|
-
push(...requests) {
|
158
|
-
for (let [moduleName, callback] of requests) {
|
159
|
-
if (typeof callback === 'function') {
|
160
|
-
this.modules[moduleName]?.loading?.then(callback);
|
161
|
-
} else {
|
162
|
-
this.load(moduleName);
|
163
|
-
}
|
164
|
-
}
|
165
|
-
},
|
166
|
-
|
167
|
-
// Destroy all modules
|
168
|
-
destroy() {
|
169
|
-
for (let moduleName in this.modules) {
|
170
|
-
this.modules[moduleName]?.destroy?.();
|
171
|
-
}
|
172
|
-
},
|
165
|
+
const processModules = () => {
|
166
|
+
for (const script of scripts) {
|
167
|
+
const isAutoDetect = script.getAttribute('data-hs-auto') === 'true';
|
173
168
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
if (this.loaded) {
|
178
|
-
callback();
|
179
|
-
} else {
|
180
|
-
postWebflowCallbacks.push(callback);
|
181
|
-
}
|
182
|
-
}
|
183
|
-
},
|
184
|
-
|
185
|
-
// Legacy alias for Webflow compatibility
|
186
|
-
afterWebflowReady(callback) {
|
187
|
-
if (typeof callback === 'function') {
|
188
|
-
if (this.loaded) {
|
189
|
-
callback();
|
190
|
-
} else {
|
191
|
-
postWebflowCallbacks.push(callback);
|
192
|
-
}
|
193
|
-
}
|
194
|
-
},
|
195
|
-
|
196
|
-
// Status method
|
197
|
-
status(moduleName) {
|
198
|
-
if (!moduleName) {
|
199
|
-
return {
|
200
|
-
loaded: Object.keys(this.modules),
|
201
|
-
loading: [...this.process],
|
202
|
-
animations: Object.keys(animationModules),
|
203
|
-
utilities: Object.keys(utilityModules),
|
204
|
-
autoInit: Object.keys(autoInitModules)
|
205
|
-
};
|
206
|
-
}
|
207
|
-
return {
|
208
|
-
loaded: !!this.modules[moduleName],
|
209
|
-
loading: this.process.has(moduleName)
|
210
|
-
};
|
169
|
+
for (const attrName of Object.keys(allDataAttributes)) {
|
170
|
+
if (script.hasAttribute(attrName)) {
|
171
|
+
loadModule(attrName);
|
211
172
|
}
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
}
|
226
|
-
|
227
|
-
// Auto-discovery mode
|
228
|
-
if (autoMode) {
|
229
|
-
waitDOMReady().then(() => {
|
230
|
-
const foundModules = new Set();
|
231
|
-
const allElements = document.querySelectorAll('*');
|
232
|
-
|
233
|
-
for (const element of allElements) {
|
234
|
-
for (const attrName of element.getAttributeNames()) {
|
235
|
-
// Look for data-hs-* attributes
|
236
|
-
const match = attrName.match(/^(data-hs-(?:anim|util)-[^-=]+)/)?.[1];
|
237
|
-
if (match && allModules[match]) {
|
238
|
-
foundModules.add(match);
|
239
|
-
}
|
240
|
-
}
|
241
|
-
}
|
242
|
-
|
243
|
-
for (const moduleName of foundModules) {
|
244
|
-
loadHsModule(moduleName);
|
245
|
-
}
|
246
|
-
});
|
173
|
+
}
|
174
|
+
|
175
|
+
if (isAutoDetect) {
|
176
|
+
waitForDOMContentLoaded().then(() => {
|
177
|
+
const foundAttributes = new Set();
|
178
|
+
const allElements = document.querySelectorAll('*');
|
179
|
+
|
180
|
+
for (const element of allElements) {
|
181
|
+
for (const attrName of element.getAttributeNames()) {
|
182
|
+
const match = attrName.match(/^(data-hs-(?:anim|util)-[^-=]+)/)?.[1];
|
183
|
+
if (match && allDataAttributes[match]) {
|
184
|
+
foundAttributes.add(match);
|
185
|
+
}
|
247
186
|
}
|
248
|
-
|
249
|
-
|
250
|
-
// Always load auto-init modules
|
251
|
-
for (const moduleName of Object.keys(autoInitModules)) {
|
252
|
-
loadHsModule(moduleName);
|
253
|
-
}
|
254
|
-
|
255
|
-
// Hide .transition elements if transition module is not loaded
|
256
|
-
const hasTransition = scripts.some(script => script.hasAttribute('data-hs-anim-transition'));
|
257
|
-
if (!hasTransition) {
|
258
|
-
const transitionElements = document.querySelectorAll('.transition');
|
259
|
-
transitionElements.forEach(element => {
|
260
|
-
element.style.display = 'none';
|
261
|
-
});
|
262
|
-
}
|
263
|
-
};
|
187
|
+
}
|
264
188
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
const images = block.querySelectorAll('img');
|
269
|
-
images.forEach(img => {
|
270
|
-
img.loading = 'eager';
|
189
|
+
for (const attrName of foundAttributes) {
|
190
|
+
loadModule(attrName);
|
191
|
+
}
|
271
192
|
});
|
272
|
-
|
193
|
+
}
|
194
|
+
}
|
273
195
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
processScriptModules();
|
278
|
-
|
279
|
-
// Wait for Webflow
|
280
|
-
await waitWebflowReady();
|
281
|
-
|
282
|
-
// Mark as loaded
|
283
|
-
window[API_NAME].loaded = true;
|
284
|
-
|
285
|
-
// Run all registered post-Webflow callbacks
|
286
|
-
postWebflowCallbacks.forEach((callback) => {
|
287
|
-
try {
|
288
|
-
callback();
|
289
|
-
} catch (error) {
|
290
|
-
// Silent fail for callbacks
|
291
|
-
}
|
292
|
-
});
|
293
|
-
};
|
196
|
+
for (const moduleName of Object.keys(autoInitModules)) {
|
197
|
+
loadModule(moduleName);
|
198
|
+
}
|
294
199
|
|
295
|
-
|
296
|
-
|
200
|
+
if (!scripts.some(script => script.hasAttribute('data-hs-anim-transition'))) {
|
201
|
+
document.querySelectorAll('.transition').forEach(element => {
|
202
|
+
element.style.display = 'none';
|
203
|
+
});
|
204
|
+
}
|
205
|
+
};
|
297
206
|
|
298
|
-
|
299
|
-
|
300
|
-
|
207
|
+
document.querySelectorAll('.w-richtext').forEach(richtext => {
|
208
|
+
richtext.querySelectorAll('img').forEach(img => {
|
209
|
+
img.loading = 'eager';
|
301
210
|
});
|
211
|
+
});
|
212
|
+
|
213
|
+
const finalize = async () => {
|
214
|
+
processModules();
|
215
|
+
await waitForWebflow();
|
216
|
+
window[API_NAME].loaded = true;
|
217
|
+
readyCallbacks.forEach(callback => {
|
218
|
+
try {
|
219
|
+
callback();
|
220
|
+
} catch (error) {
|
221
|
+
// Silent fail for callbacks
|
222
|
+
}
|
223
|
+
});
|
224
|
+
};
|
225
|
+
|
226
|
+
window[API_NAME].push(...queuedModules);
|
227
|
+
finalize().catch(() => {
|
228
|
+
// Silent fail for initialization
|
229
|
+
});
|
302
230
|
};
|
303
231
|
|
304
|
-
// Run initialization
|
305
232
|
initializeHsMain();
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@hortonstudio/main",
|
3
|
-
"version": "1.2.
|
4
|
-
"description": "
|
3
|
+
"version": "1.2.18",
|
4
|
+
"description": "Animation and utility library for client websites",
|
5
5
|
"main": "index.js",
|
6
6
|
"type": "module",
|
7
7
|
"keywords": [
|
@@ -12,13 +12,7 @@
|
|
12
12
|
],
|
13
13
|
"author": "Horton Studio",
|
14
14
|
"license": "ISC",
|
15
|
-
"
|
16
|
-
"
|
17
|
-
"@hortonstudio/main-anim-text": "^1.0.0",
|
18
|
-
"@hortonstudio/main-anim-transition": "^1.0.0",
|
19
|
-
"@hortonstudio/main-util-navbar": "^1.0.0",
|
20
|
-
"@hortonstudio/main-util-toc": "^1.0.0",
|
21
|
-
"@hortonstudio/main-util-progress": "^1.0.0",
|
22
|
-
"@hortonstudio/main-smooth-scroll": "^1.0.0"
|
15
|
+
"scripts": {
|
16
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
23
17
|
}
|
24
18
|
}
|