@awesomeness-js/server 1.1.11 → 1.1.13

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.
Files changed (30) hide show
  1. package/package.json +1 -1
  2. package/src/componentAndPageMemory.js +47 -19
  3. package/src/componentDependencies.js +2 -4
  4. package/tests/componentAndPageMemory.test.js +1 -0
  5. package/tests/fetchPage.test.js +2 -0
  6. package/tests/fixtures/site-and-components/components/app/cleanMain/index.js +26 -0
  7. package/tests/fixtures/site-and-components/components/app/cleanMain.js +14 -0
  8. package/tests/fixtures/site-and-components/components/app/index.css +4 -0
  9. package/tests/fixtures/site-and-components/components/app/index.js +42 -0
  10. package/tests/fixtures/site-and-components/components/app/insertIntoList.jquery.js +150 -0
  11. package/tests/fixtures/site-and-components/components/app/keyUpWithTimeout.jQuery.js +26 -0
  12. package/tests/fixtures/site-and-components/components/app/onEnter.jQuery.js +39 -0
  13. package/tests/fixtures/site-and-components/components/app/onResize.jQuery.js +64 -0
  14. package/tests/fixtures/site-and-components/components/app/pwa/_.css +305 -0
  15. package/tests/fixtures/site-and-components/components/app/pwa/index.js +235 -0
  16. package/tests/fixtures/site-and-components/components/app/pwa/updateProfileImage.js +7 -0
  17. package/tests/fixtures/site-and-components/components/app/shapes.css +3 -0
  18. package/tests/fixtures/site-and-components/components/app/simple/_.css +151 -0
  19. package/tests/fixtures/site-and-components/components/app/simple/index.js +170 -0
  20. package/tests/fixtures/site-and-components/components/app/start.js +165 -0
  21. package/tests/fixtures/site-and-components/components/app/vanilla/_.css +1 -0
  22. package/tests/fixtures/site-and-components/components/app/vanilla/index.js +27 -0
  23. package/tests/fixtures/site-and-components/components/scrollSpy/elm.js +172 -0
  24. package/tests/fixtures/site-and-components/components/scrollSpy/index.js +63 -0
  25. package/tests/fixtures/site-and-components/components/scrollSpy/observerPoolGet.js +91 -0
  26. package/tests/fixtures/site-and-components/components/scrollSpy/observerPoolRegistry.js +18 -0
  27. package/tests/fixtures/site-and-components/components/scrollSpy/observerPoolSubscribe.js +37 -0
  28. package/tests/fixtures/site-and-components/components/scrollSpy/observerPoolUnsubscribe.js +44 -0
  29. package/tests/fixtures/site-and-components/components/scrollSpy/top.js +86 -0
  30. 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,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
+ };