@awesomeness-js/server 1.1.11 → 1.1.14
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/package.json +1 -1
- package/src/componentAndPageMemory.js +47 -19
- package/src/componentDependencies.js +211 -210
- package/tests/componentAndPageMemory.test.js +1 -0
- package/tests/componentDependencies.test.js +85 -0
- package/tests/fetchPage.test.js +2 -0
- package/tests/fixtures/site-and-components/components/app/cleanMain/index.js +26 -0
- package/tests/fixtures/site-and-components/components/app/cleanMain.js +14 -0
- package/tests/fixtures/site-and-components/components/app/index.css +4 -0
- package/tests/fixtures/site-and-components/components/app/index.js +42 -0
- package/tests/fixtures/site-and-components/components/app/insertIntoList.jquery.js +150 -0
- package/tests/fixtures/site-and-components/components/app/keyUpWithTimeout.jQuery.js +26 -0
- package/tests/fixtures/site-and-components/components/app/onEnter.jQuery.js +39 -0
- package/tests/fixtures/site-and-components/components/app/onResize.jQuery.js +64 -0
- package/tests/fixtures/site-and-components/components/app/pwa/_.css +305 -0
- package/tests/fixtures/site-and-components/components/app/pwa/index.js +235 -0
- package/tests/fixtures/site-and-components/components/app/pwa/updateProfileImage.js +7 -0
- package/tests/fixtures/site-and-components/components/app/shapes.css +3 -0
- package/tests/fixtures/site-and-components/components/app/simple/_.css +151 -0
- package/tests/fixtures/site-and-components/components/app/simple/index.js +170 -0
- package/tests/fixtures/site-and-components/components/app/start.js +165 -0
- package/tests/fixtures/site-and-components/components/app/vanilla/_.css +1 -0
- package/tests/fixtures/site-and-components/components/app/vanilla/index.js +27 -0
- package/tests/fixtures/site-and-components/components/scrollSpy/elm.js +172 -0
- package/tests/fixtures/site-and-components/components/scrollSpy/index.js +63 -0
- package/tests/fixtures/site-and-components/components/scrollSpy/observerPoolGet.js +91 -0
- package/tests/fixtures/site-and-components/components/scrollSpy/observerPoolRegistry.js +18 -0
- package/tests/fixtures/site-and-components/components/scrollSpy/observerPoolSubscribe.js +37 -0
- package/tests/fixtures/site-and-components/components/scrollSpy/observerPoolUnsubscribe.js +44 -0
- package/tests/fixtures/site-and-components/components/scrollSpy/top.js +86 -0
- package/tests/fixtures/site-and-components/sites/site-a/pages/home/js/index.js +41 -8
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import ui from '#ui';
|
|
2
|
+
|
|
3
|
+
export default ({
|
|
4
|
+
replaceApp = true,
|
|
5
|
+
navItems = [],
|
|
6
|
+
logo = 'AwesomenessJS.com',
|
|
7
|
+
fancyNavUnderline = true,
|
|
8
|
+
maxWidth = null,
|
|
9
|
+
navMaxWidth = null,
|
|
10
|
+
} = {}) => {
|
|
11
|
+
|
|
12
|
+
console.log({ navMaxWidth });
|
|
13
|
+
|
|
14
|
+
let $app = ui.app.vanilla({ replaceApp });
|
|
15
|
+
|
|
16
|
+
const themeProps = ui.theme();
|
|
17
|
+
|
|
18
|
+
const $topNavBar = $(`<div class="awesomeness-app-simple-navBar ${themeProps.text.secondary}"></div>`)
|
|
19
|
+
.appendTo($app);
|
|
20
|
+
|
|
21
|
+
const $navBarInterior = $(`<div class="awesomeness-app-simple-navBar-interior"></div>`)
|
|
22
|
+
.appendTo($topNavBar);
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if(navMaxWidth){
|
|
26
|
+
|
|
27
|
+
$navBarInterior.css('max-width', `${navMaxWidth}px`);
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const $logo = $(`<div class="awesomeness-app-simple-logo ${themeProps.text.accent}--hover">${logo}</div>`)
|
|
32
|
+
.appendTo($navBarInterior)
|
|
33
|
+
.on('click', () => {
|
|
34
|
+
|
|
35
|
+
window.location.href = '/';
|
|
36
|
+
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const $topNavItems = $(`<div class="hidden-t hidden-p awesomeness-app-simple-top-navItems"></div>`)
|
|
40
|
+
.appendTo($navBarInterior);
|
|
41
|
+
|
|
42
|
+
const $main = $(`<div id="main" class="awesomeness-app-simple-main ${themeProps.surface.base}"></div>`)
|
|
43
|
+
.appendTo($app);
|
|
44
|
+
|
|
45
|
+
if(maxWidth){
|
|
46
|
+
|
|
47
|
+
$main.css('max-width', `${maxWidth}px`);
|
|
48
|
+
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const $menuNav = $(`<i class="ico-menu awesomeness-app-simple-menu-button hidden-xl hidden-d ${themeProps.text.secondary} ${themeProps.text.accent}--hover"></i>`)
|
|
52
|
+
.appendTo($navBarInterior);
|
|
53
|
+
|
|
54
|
+
const $phoneMenu = $(`<div class="awesomeness-app-simple-phone-menu hidden-xl hidden-d ${themeProps.surface.base} ${themeProps.text.secondary} ${themeProps.text.accent}--hover pt65"></div>`);
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if(navItems.length > 0){
|
|
58
|
+
|
|
59
|
+
const $navItemsContainer = $(`<div class="flex gap-10 height-65"></div>`)
|
|
60
|
+
.appendTo($topNavItems);
|
|
61
|
+
|
|
62
|
+
navItems.forEach((item) => {
|
|
63
|
+
|
|
64
|
+
let thisFancyNavUnderline = fancyNavUnderline;
|
|
65
|
+
|
|
66
|
+
if(!item.name && item.ico) {
|
|
67
|
+
|
|
68
|
+
item.name = `<i class="ico-${item.ico}"></i>`;
|
|
69
|
+
thisFancyNavUnderline = false;
|
|
70
|
+
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
const itemContent = thisFancyNavUnderline ? `<span class="underline">${item.name}</span>` : item.name;
|
|
75
|
+
|
|
76
|
+
const $link = ui.link({
|
|
77
|
+
link: item.url,
|
|
78
|
+
}).appendTo($navItemsContainer)
|
|
79
|
+
.addClass(`flex justify-center align-center`);
|
|
80
|
+
|
|
81
|
+
const $item = $(`<div class="awesomeness-app-simple-nav-item ${themeProps.text.secondary} ${themeProps.text.accent}--hover">${itemContent}</div>`)
|
|
82
|
+
.appendTo($link);
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
$link.clone(true).appendTo($phoneMenu);
|
|
86
|
+
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Put this after you create $menuNav and $phoneMenu
|
|
92
|
+
|
|
93
|
+
let menuOpen = false;
|
|
94
|
+
|
|
95
|
+
// Ensure phone menu is in the DOM
|
|
96
|
+
$phoneMenu.appendTo($app);
|
|
97
|
+
|
|
98
|
+
// Backdrop (z-index 2)
|
|
99
|
+
const $menuBackdrop = $('<div class="awesomeness-app-simple-menu-backdrop clearfix hidden"></div>')
|
|
100
|
+
.appendTo($app);
|
|
101
|
+
|
|
102
|
+
// icon swap helpers
|
|
103
|
+
const setMenuIconOpen = () => {
|
|
104
|
+
|
|
105
|
+
$menuNav.removeClass('ico-menu').addClass('ico-x');
|
|
106
|
+
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const setMenuIconClosed = () => {
|
|
110
|
+
|
|
111
|
+
$menuNav.removeClass('ico-x').addClass('ico-menu');
|
|
112
|
+
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// open/close
|
|
116
|
+
const openMenu = () => {
|
|
117
|
+
|
|
118
|
+
if (menuOpen) return;
|
|
119
|
+
menuOpen = true;
|
|
120
|
+
|
|
121
|
+
$menuBackdrop.removeClass('hidden'); // show backdrop
|
|
122
|
+
$phoneMenu.addClass('is-open'); // slide down
|
|
123
|
+
setMenuIconOpen();
|
|
124
|
+
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const closeMenu = () => {
|
|
128
|
+
|
|
129
|
+
if (!menuOpen) return;
|
|
130
|
+
menuOpen = false;
|
|
131
|
+
|
|
132
|
+
$phoneMenu.removeClass('is-open'); // slide up
|
|
133
|
+
$menuBackdrop.addClass('hidden'); // hide backdrop
|
|
134
|
+
setMenuIconClosed();
|
|
135
|
+
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const toggleMenu = () => (menuOpen ? closeMenu() : openMenu());
|
|
139
|
+
|
|
140
|
+
// button click
|
|
141
|
+
$menuNav.on('click', (e) => {
|
|
142
|
+
|
|
143
|
+
e.preventDefault();
|
|
144
|
+
toggleMenu();
|
|
145
|
+
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// backdrop click closes
|
|
149
|
+
$menuBackdrop.on('click', (e) => {
|
|
150
|
+
|
|
151
|
+
e.preventDefault();
|
|
152
|
+
closeMenu();
|
|
153
|
+
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// clicking a link closes
|
|
157
|
+
$phoneMenu.on('click', 'a', () => closeMenu());
|
|
158
|
+
|
|
159
|
+
// defensive: if items aren't anchors
|
|
160
|
+
$phoneMenu.find('.awesomeness-app-simple-nav-item').on('click', () => closeMenu());
|
|
161
|
+
|
|
162
|
+
ui.scrollSpy.top({
|
|
163
|
+
threshold: 120,
|
|
164
|
+
className: themeProps.surface.base,
|
|
165
|
+
$applyTo: $topNavBar
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
return $app;
|
|
169
|
+
|
|
170
|
+
};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import ui from '#ui';
|
|
2
|
+
|
|
3
|
+
export default function startApp({
|
|
4
|
+
data = {},
|
|
5
|
+
style = 'simple',
|
|
6
|
+
theme = {
|
|
7
|
+
name: 'light',
|
|
8
|
+
neutralColor: 'zinc',
|
|
9
|
+
accentColor: 'cyan',
|
|
10
|
+
customColors: {},
|
|
11
|
+
},
|
|
12
|
+
navItems = [],
|
|
13
|
+
logo = null,
|
|
14
|
+
styleConfig = {
|
|
15
|
+
},
|
|
16
|
+
postSetupHook = null,
|
|
17
|
+
home = null,
|
|
18
|
+
}) {
|
|
19
|
+
|
|
20
|
+
if(!ui.app[style]){
|
|
21
|
+
|
|
22
|
+
throw new Error(`App style ${style} not found`);
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
app.theme = theme;
|
|
27
|
+
|
|
28
|
+
if(theme.customColors){
|
|
29
|
+
|
|
30
|
+
$.each(theme.customColors, (name, hexValue) => {
|
|
31
|
+
|
|
32
|
+
let inputColor;
|
|
33
|
+
let anchorShade = 600;
|
|
34
|
+
|
|
35
|
+
if(typeof hexValue === 'string'){
|
|
36
|
+
|
|
37
|
+
inputColor = hexValue;
|
|
38
|
+
|
|
39
|
+
} else if(typeof hexValue === 'object' && hexValue !== null){
|
|
40
|
+
|
|
41
|
+
inputColor = hexValue.inputColor;
|
|
42
|
+
anchorShade = hexValue.anchorShade || anchorShade;
|
|
43
|
+
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
ui.colors.custom({
|
|
47
|
+
inputColor: inputColor,
|
|
48
|
+
anchorShade: 600,
|
|
49
|
+
name
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// create HTML first
|
|
57
|
+
// could be done in parts broken down in the scripts folder
|
|
58
|
+
var $body = $('body');
|
|
59
|
+
|
|
60
|
+
$body.empty();
|
|
61
|
+
|
|
62
|
+
var $app = $('<div id="app"></div>').appendTo($body);
|
|
63
|
+
|
|
64
|
+
ui.app[style]({
|
|
65
|
+
logo,
|
|
66
|
+
navItems,
|
|
67
|
+
...styleConfig
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
app.$app = $app;
|
|
71
|
+
|
|
72
|
+
if(postSetupHook && typeof postSetupHook === 'function'){
|
|
73
|
+
|
|
74
|
+
postSetupHook({ $app });
|
|
75
|
+
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
const $main = ui.app.cleanMain();
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
if(
|
|
83
|
+
data?.goToPage
|
|
84
|
+
&& data.goToPage != 'start'
|
|
85
|
+
){
|
|
86
|
+
|
|
87
|
+
let place = app.pages;
|
|
88
|
+
const trimmedPlace = data.goToPage.replace(/^\/+|\/+$/g, '');
|
|
89
|
+
const placeParts = trimmedPlace.split('/');
|
|
90
|
+
|
|
91
|
+
for(const part of placeParts){
|
|
92
|
+
|
|
93
|
+
if(!place[part]){
|
|
94
|
+
|
|
95
|
+
place[part] = {};
|
|
96
|
+
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
place = place[part];
|
|
100
|
+
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if(place.init){
|
|
104
|
+
|
|
105
|
+
if(data.goToPage == "_md"){
|
|
106
|
+
|
|
107
|
+
// state management
|
|
108
|
+
app.state.create({
|
|
109
|
+
title: data?.pageData?.metadata?.title ?? '👋',
|
|
110
|
+
url: data?.pageData?.metadata?.url ?? data?.origLocation ?? '/' + data.goToPage,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
} else {
|
|
114
|
+
|
|
115
|
+
// state management
|
|
116
|
+
app.state.create({
|
|
117
|
+
title: place?.about?.title ?? '👋',
|
|
118
|
+
url: place?.about?.url ?? data?.origLocation ?? '/' + data.goToPage,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
place.init(data.pageData);
|
|
124
|
+
|
|
125
|
+
} else {
|
|
126
|
+
|
|
127
|
+
console.warn('No page found for', data);
|
|
128
|
+
|
|
129
|
+
if(data.errorFetchingPage?.code){
|
|
130
|
+
|
|
131
|
+
ui.statusPage(data.errorFetchingPage?.code).appendTo($app);
|
|
132
|
+
|
|
133
|
+
} else {
|
|
134
|
+
|
|
135
|
+
ui.statusPage(401).appendTo($app);
|
|
136
|
+
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
} else {
|
|
144
|
+
|
|
145
|
+
if(home && typeof home === 'function'){
|
|
146
|
+
|
|
147
|
+
home({
|
|
148
|
+
$app,
|
|
149
|
+
data,
|
|
150
|
+
$main
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
setTimeout(function(){
|
|
159
|
+
|
|
160
|
+
app.initialScroll();
|
|
161
|
+
|
|
162
|
+
}, 10);
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/* nothing */
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import ui from '#ui';
|
|
2
|
+
|
|
3
|
+
export default ({
|
|
4
|
+
replaceApp = true
|
|
5
|
+
} = {}) => {
|
|
6
|
+
|
|
7
|
+
let $app;
|
|
8
|
+
|
|
9
|
+
if (replaceApp) {
|
|
10
|
+
|
|
11
|
+
$app = $('#app');
|
|
12
|
+
$app.empty().addClass('awesomeness-app-simple');
|
|
13
|
+
|
|
14
|
+
} else {
|
|
15
|
+
|
|
16
|
+
$app = $('<div class="awesomeness-app-simple"></div>');
|
|
17
|
+
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const themeProps = ui.theme();
|
|
21
|
+
|
|
22
|
+
$app.addClass(themeProps.surface.base);
|
|
23
|
+
$('body').addClass(themeProps.surface.base);
|
|
24
|
+
|
|
25
|
+
return $app;
|
|
26
|
+
|
|
27
|
+
};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import ui from '#ui';
|
|
2
|
+
|
|
3
|
+
export default ({
|
|
4
|
+
$elm,
|
|
5
|
+
callback = () => {},
|
|
6
|
+
root = null,
|
|
7
|
+
rootMargin = "0px 0px",
|
|
8
|
+
threshold = 0.01,
|
|
9
|
+
once = true,
|
|
10
|
+
|
|
11
|
+
// auto cleanup
|
|
12
|
+
destroyOnRemove = true,
|
|
13
|
+
removeRoot = null, // defaults below
|
|
14
|
+
debug = false
|
|
15
|
+
} = {}) => {
|
|
16
|
+
|
|
17
|
+
const registry = ui.scrollSpy.observerPoolRegistry();
|
|
18
|
+
|
|
19
|
+
if (!$elm) throw new Error("observer: missing $elm");
|
|
20
|
+
|
|
21
|
+
const el = $elm?.[0] || $elm;
|
|
22
|
+
|
|
23
|
+
if (!el || !el.nodeType) {
|
|
24
|
+
|
|
25
|
+
throw new Error("observer: $elm must be a DOM element or a jQuery-like wrapper");
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!removeRoot) removeRoot = document.documentElement || document.body;
|
|
30
|
+
|
|
31
|
+
let pool = null;
|
|
32
|
+
let mo = null;
|
|
33
|
+
let connectMo = null;
|
|
34
|
+
|
|
35
|
+
let last = null;
|
|
36
|
+
let destroyed = false;
|
|
37
|
+
let started = false;
|
|
38
|
+
const subscriber = {
|
|
39
|
+
safeCall: (isIntersecting, entry) => safeCall(isIntersecting, entry)
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const log = (...args) => debug && console.log("[observer]", ...args);
|
|
43
|
+
|
|
44
|
+
const safeCall = (v, entry) => {
|
|
45
|
+
|
|
46
|
+
if (destroyed) return;
|
|
47
|
+
|
|
48
|
+
v = !!v;
|
|
49
|
+
|
|
50
|
+
if (v === last) return;
|
|
51
|
+
last = v;
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
|
|
55
|
+
callback(v, entry);
|
|
56
|
+
|
|
57
|
+
} catch (e) {}
|
|
58
|
+
|
|
59
|
+
if (once && v) destroy();
|
|
60
|
+
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const destroy = () => {
|
|
64
|
+
|
|
65
|
+
if (destroyed) return;
|
|
66
|
+
destroyed = true;
|
|
67
|
+
|
|
68
|
+
ui.scrollSpy.observerPoolUnsubscribe({
|
|
69
|
+
registry,
|
|
70
|
+
poolEntry: pool,
|
|
71
|
+
targetElm: el,
|
|
72
|
+
subscriber
|
|
73
|
+
});
|
|
74
|
+
pool = null;
|
|
75
|
+
|
|
76
|
+
if (mo) mo.disconnect();
|
|
77
|
+
mo = null;
|
|
78
|
+
|
|
79
|
+
if (connectMo) connectMo.disconnect();
|
|
80
|
+
connectMo = null;
|
|
81
|
+
|
|
82
|
+
log("destroyed");
|
|
83
|
+
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const start = () => {
|
|
87
|
+
|
|
88
|
+
if (destroyed || started) return;
|
|
89
|
+
started = true;
|
|
90
|
+
|
|
91
|
+
pool = ui.scrollSpy.observerPoolGet({
|
|
92
|
+
registry,
|
|
93
|
+
root,
|
|
94
|
+
rootMargin,
|
|
95
|
+
threshold
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
ui.scrollSpy.observerPoolSubscribe({
|
|
99
|
+
poolEntry: pool,
|
|
100
|
+
targetElm: el,
|
|
101
|
+
subscriber
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
log("started", {
|
|
105
|
+
el,
|
|
106
|
+
root,
|
|
107
|
+
rootMargin,
|
|
108
|
+
threshold,
|
|
109
|
+
sharedObserverPool: true
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// auto destroy on removal
|
|
113
|
+
if (destroyOnRemove) {
|
|
114
|
+
|
|
115
|
+
mo = new MutationObserver(() => {
|
|
116
|
+
|
|
117
|
+
if (!el.isConnected) destroy();
|
|
118
|
+
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
mo.observe(removeRoot, {
|
|
122
|
+
childList: true,
|
|
123
|
+
subtree: true
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// If it's already connected, start immediately.
|
|
131
|
+
// If not, wait until it becomes connected.
|
|
132
|
+
if (el.isConnected) {
|
|
133
|
+
|
|
134
|
+
start();
|
|
135
|
+
|
|
136
|
+
} else {
|
|
137
|
+
|
|
138
|
+
log("waiting for element to connect...");
|
|
139
|
+
|
|
140
|
+
connectMo = new MutationObserver(() => {
|
|
141
|
+
|
|
142
|
+
if (destroyed) return;
|
|
143
|
+
|
|
144
|
+
if (el.isConnected) {
|
|
145
|
+
|
|
146
|
+
connectMo.disconnect();
|
|
147
|
+
connectMo = null;
|
|
148
|
+
|
|
149
|
+
// wait 1 frame so layout exists
|
|
150
|
+
requestAnimationFrame(() => start());
|
|
151
|
+
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
connectMo.observe(removeRoot, {
|
|
157
|
+
childList: true,
|
|
158
|
+
subtree: true
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// stash destroy
|
|
164
|
+
try {
|
|
165
|
+
|
|
166
|
+
$elm?.data?.("observerDestroy", destroy);
|
|
167
|
+
|
|
168
|
+
} catch (e) {}
|
|
169
|
+
|
|
170
|
+
return { destroy };
|
|
171
|
+
|
|
172
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export default ({
|
|
2
|
+
$spyOn,
|
|
3
|
+
$applyTo,
|
|
4
|
+
deadZone = 10
|
|
5
|
+
} = {}) => {
|
|
6
|
+
|
|
7
|
+
const $classEl = ($applyTo && $applyTo.length)
|
|
8
|
+
? $applyTo
|
|
9
|
+
: $('body');
|
|
10
|
+
|
|
11
|
+
const spyTarget = ($spyOn && $spyOn.length)
|
|
12
|
+
? $spyOn.get(0)
|
|
13
|
+
: window;
|
|
14
|
+
|
|
15
|
+
const isWindow = (spyTarget === window);
|
|
16
|
+
|
|
17
|
+
const className = 'spy-scrolling-down';
|
|
18
|
+
|
|
19
|
+
let lastTop = isWindow
|
|
20
|
+
? (window.pageYOffset || document.documentElement.scrollTop || 0)
|
|
21
|
+
: (spyTarget.scrollTop || 0);
|
|
22
|
+
|
|
23
|
+
let ticking = false;
|
|
24
|
+
|
|
25
|
+
function apply(isDown) {
|
|
26
|
+
|
|
27
|
+
if (isDown) $classEl.addClass(className);
|
|
28
|
+
else $classEl.removeClass(className);
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function readTop() {
|
|
33
|
+
|
|
34
|
+
return isWindow
|
|
35
|
+
? (window.pageYOffset || document.documentElement.scrollTop || 0)
|
|
36
|
+
: (spyTarget.scrollTop || 0);
|
|
37
|
+
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function onRaf() {
|
|
41
|
+
|
|
42
|
+
ticking = false;
|
|
43
|
+
|
|
44
|
+
const topNow = readTop();
|
|
45
|
+
const delta = topNow - lastTop;
|
|
46
|
+
|
|
47
|
+
if (Math.abs(delta) < deadZone) return;
|
|
48
|
+
|
|
49
|
+
apply(delta > 0);
|
|
50
|
+
lastTop = topNow;
|
|
51
|
+
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
spyTarget.addEventListener('scroll', function() {
|
|
55
|
+
|
|
56
|
+
if (ticking) return;
|
|
57
|
+
|
|
58
|
+
ticking = true;
|
|
59
|
+
requestAnimationFrame(onRaf);
|
|
60
|
+
|
|
61
|
+
}, { passive: true });
|
|
62
|
+
|
|
63
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
export default ({
|
|
2
|
+
registry,
|
|
3
|
+
root,
|
|
4
|
+
rootMargin,
|
|
5
|
+
threshold
|
|
6
|
+
} = {}) => {
|
|
7
|
+
|
|
8
|
+
if (!registry) {
|
|
9
|
+
|
|
10
|
+
throw new Error('observerPoolGet: missing registry');
|
|
11
|
+
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const toThresholdKey = (value) => {
|
|
15
|
+
|
|
16
|
+
if (Array.isArray(value)) {
|
|
17
|
+
|
|
18
|
+
return value.join(',');
|
|
19
|
+
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return String(value);
|
|
23
|
+
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const getRootKey = (rootElm) => {
|
|
27
|
+
|
|
28
|
+
if (!rootElm) {
|
|
29
|
+
|
|
30
|
+
return 'viewport';
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!registry.rootIds.has(rootElm)) {
|
|
35
|
+
|
|
36
|
+
registry.rootSeq += 1;
|
|
37
|
+
registry.rootIds.set(rootElm, `root-${registry.rootSeq}`);
|
|
38
|
+
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return registry.rootIds.get(rootElm);
|
|
42
|
+
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const key = `${getRootKey(root)}|${rootMargin}|${toThresholdKey(threshold)}`;
|
|
46
|
+
|
|
47
|
+
if (registry.pools.has(key)) {
|
|
48
|
+
|
|
49
|
+
return registry.pools.get(key);
|
|
50
|
+
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const targets = new Map();
|
|
54
|
+
|
|
55
|
+
const observer = new IntersectionObserver((entries) => {
|
|
56
|
+
|
|
57
|
+
entries.forEach((entry) => {
|
|
58
|
+
|
|
59
|
+
const subscribers = targets.get(entry.target);
|
|
60
|
+
|
|
61
|
+
if (!subscribers || subscribers.size === 0) {
|
|
62
|
+
|
|
63
|
+
return;
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
subscribers.forEach((subscriber) => {
|
|
68
|
+
|
|
69
|
+
subscriber.safeCall(entry.isIntersecting, entry);
|
|
70
|
+
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
}, {
|
|
76
|
+
root,
|
|
77
|
+
rootMargin,
|
|
78
|
+
threshold
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const poolEntry = {
|
|
82
|
+
key,
|
|
83
|
+
observer,
|
|
84
|
+
targets
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
registry.pools.set(key, poolEntry);
|
|
88
|
+
|
|
89
|
+
return poolEntry;
|
|
90
|
+
|
|
91
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export default () => {
|
|
2
|
+
|
|
3
|
+
const OBSERVER_POOL_KEY = '__awesomenessScrollSpyElmPool__';
|
|
4
|
+
const globalScope = typeof window !== 'undefined' ? window : globalThis;
|
|
5
|
+
|
|
6
|
+
if (!globalScope[OBSERVER_POOL_KEY]) {
|
|
7
|
+
|
|
8
|
+
globalScope[OBSERVER_POOL_KEY] = {
|
|
9
|
+
pools: new Map(),
|
|
10
|
+
rootIds: new WeakMap(),
|
|
11
|
+
rootSeq: 0
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return globalScope[OBSERVER_POOL_KEY];
|
|
17
|
+
|
|
18
|
+
};
|