neo.mjs 6.9.12 → 6.10.0
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/BACKERS.md +0 -30
- package/apps/ServiceWorker.mjs +2 -2
- package/apps/learnneo/index.html +8 -4
- package/apps/learnneo/view/LivePreview.mjs +171 -0
- package/apps/learnneo/view/home/ContentTreeList.mjs +91 -8
- package/apps/learnneo/view/home/MainContainerController.mjs +5 -20
- package/examples/ServiceWorker.mjs +2 -2
- package/examples/button/base/neo-config.json +1 -2
- package/package.json +4 -8
- package/resources/data/deck/learnneo/p/2023-10-01T18-29-19-158Z.md +14 -16
- package/resources/data/deck/learnneo/p/2023-10-07T19-18-28-517Z.md +9 -17
- package/resources/data/deck/learnneo/p/2023-10-08T20-20-07-934Z.md +7 -5
- package/resources/data/deck/learnneo/p/2023-10-14T19-25-08-153Z.md +18 -22
- package/resources/data/deck/learnneo/p/2023-10-31T13-59-37-550Z.md +31 -0
- package/resources/data/deck/learnneo/p/MainThreadAddonExample.md +15 -0
- package/resources/data/deck/learnneo/p/MainThreadAddonIntro.md +46 -0
- package/resources/data/deck/learnneo/p/TestLivePreview.md +10 -0
- package/resources/data/deck/learnneo/p/stylesheet.md +3 -7
- package/resources/data/deck/learnneo/t.json +126 -56
- package/resources/images/logos/Github-logo-black.svg +1 -0
- package/resources/images/logos/Slack-logo-black.svg +17 -0
- package/resources/scss/src/apps/learnneo/home/ContentView.scss +6 -0
- package/resources/scss/src/list/Base.scss +5 -1
- package/resources/scss/theme-neo-light/Global.scss +7 -5
- package/resources/scss/theme-neo-light/button/Base.scss +46 -45
- package/resources/scss/theme-neo-light/design-tokens/Component.scss +4 -0
- package/resources/scss/theme-neo-light/design-tokens/Core.scss +23 -22
- package/resources/scss/theme-neo-light/design-tokens/Semantic.scss +5 -2
- package/resources/scss/theme-neo-light/list/Base.scss +2 -4
- package/resources/scss/theme-neo-light/tab/header/Button.scss +1 -1
- package/src/DefaultConfig.mjs +2 -2
- package/src/component/StatusBadge.mjs +194 -246
- package/src/component/Video.mjs +19 -25
- package/src/controller/Base.mjs +3 -4
- package/src/core/Base.mjs +2 -2
- package/src/form/field/Text.mjs +2 -2
- package/src/form/field/TextArea.mjs +3 -3
- package/src/main/DomAccess.mjs +64 -70
- package/src/main/DomEvents.mjs +1 -1
- package/src/main/addon/HighlightJS.mjs +16 -1
- package/src/main/addon/Mwc.mjs +6 -1
- package/src/worker/Manager.mjs +8 -3
- package/apps/learnneo/view/home/ContentComponent.mjs +0 -24
- package/examples/container/dialog/MainContainer.mjs +0 -68
- package/examples/container/dialog/MainContainerController.mjs +0 -84
- package/examples/container/dialog/app.mjs +0 -6
- package/examples/container/dialog/index.html +0 -11
- package/examples/container/dialog/neo-config.json +0 -7
- package/resources/scss/src/apps/learnneo/home/ContentComponent.scss +0 -61
- package/resources/scss/src/apps/newwebsite/MainContainer.css +0 -33
- package/resources/scss/theme-neo-light/design-tokens/Components.scss +0 -3
- package/src/container/Dialog.mjs +0 -205
- package/src/main/addon/Dialog.mjs +0 -68
package/BACKERS.md
CHANGED
@@ -21,36 +21,6 @@ to an entire new level.
|
|
21
21
|
|
22
22
|
This is something I would love to share with you!
|
23
23
|
|
24
|
-
<h3 align="center">Generous Backers</h3>
|
25
|
-
<!--generous backers start-->
|
26
|
-
<table>
|
27
|
-
<tbody>
|
28
|
-
<tr>
|
29
|
-
<td align="center" valign="middle">
|
30
|
-
<a href="https://github.com/keckeroo">
|
31
|
-
<img width="40px" src="https://avatars.githubusercontent.com/u/1653769?v=4">
|
32
|
-
</a>
|
33
|
-
</td>
|
34
|
-
</tr>
|
35
|
-
</tbody>
|
36
|
-
</table>
|
37
|
-
<!--generous backers end-->
|
38
|
-
|
39
|
-
<h3 align="center">Backers</h3>
|
40
|
-
<!--backers start-->
|
41
|
-
<table>
|
42
|
-
<tbody>
|
43
|
-
<tr>
|
44
|
-
<td align="center" valign="middle">
|
45
|
-
<a href="https://github.com/pyccyp">
|
46
|
-
<img width="40px" src="https://avatars.githubusercontent.com/u/66266683?v=4">
|
47
|
-
</a>
|
48
|
-
</td>
|
49
|
-
</tr>
|
50
|
-
</tbody>
|
51
|
-
</table>
|
52
|
-
<!--backers end-->
|
53
|
-
|
54
24
|
## Special Offer
|
55
25
|
The first 4 Bronze, and the first 4 Silver tier sponsors will get kept on the main Readme file for an additional year,
|
56
26
|
after the day the 4th sponsor does sign up (*).
|
package/apps/ServiceWorker.mjs
CHANGED
package/apps/learnneo/index.html
CHANGED
@@ -5,13 +5,17 @@
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
6
6
|
<meta charset="UTF-8">
|
7
7
|
<title>LearnNeo</title>
|
8
|
-
<link
|
9
|
-
|
10
|
-
|
8
|
+
<link
|
9
|
+
href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&family=Source+Sans+3:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,700;1,800&family=Source+Serif+4:ital,opsz,wght@0,8..60,400;0,8..60,500;0,8..60,600;0,8..60,700;1,8..60,400;1,8..60,500;1,8..60,600;1,8..60,700&display=swap"
|
10
|
+
rel="stylesheet">
|
11
|
+
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
12
|
+
<script>
|
13
|
+
|
14
|
+
</script>
|
11
15
|
</head>
|
12
16
|
|
13
17
|
<body>
|
14
18
|
<script src="../../src/MicroLoader.mjs" type="module"></script>
|
15
19
|
</body>
|
16
20
|
|
17
|
-
</html>
|
21
|
+
</html>
|
@@ -0,0 +1,171 @@
|
|
1
|
+
import Base from '../../../src/container/Base.mjs';
|
2
|
+
import TabContainer from '../../../src/tab/Container.mjs';
|
3
|
+
import TextArea from '../../../src/form/field/TextArea.mjs';
|
4
|
+
|
5
|
+
/**
|
6
|
+
* @class LearnNeo.view.LivePreview
|
7
|
+
* @extends Neo.container.Base
|
8
|
+
*/
|
9
|
+
class LivePreview extends Base {
|
10
|
+
static config = {
|
11
|
+
/**
|
12
|
+
* @member {String} className='LearnNeo.view.LivePreview'
|
13
|
+
* @protected
|
14
|
+
*/
|
15
|
+
className: 'LearnNeo.view.LivePreview',
|
16
|
+
value_: null,
|
17
|
+
autoMount: true,
|
18
|
+
autoRender: true,
|
19
|
+
height: 400,
|
20
|
+
layout: 'fit',
|
21
|
+
/**
|
22
|
+
* @member {Object[]} items
|
23
|
+
*/
|
24
|
+
items: [{
|
25
|
+
module: TabContainer,
|
26
|
+
items: [{
|
27
|
+
module: TextArea,
|
28
|
+
hideLabel: true,
|
29
|
+
style: {height: '100%'},
|
30
|
+
reference: 'textArea',
|
31
|
+
tabButtonConfig: {
|
32
|
+
text: 'Editor'
|
33
|
+
},
|
34
|
+
}, {
|
35
|
+
tabButtonConfig: {
|
36
|
+
text: 'Preview'
|
37
|
+
},
|
38
|
+
reference: 'preview',
|
39
|
+
ntype: 'container'
|
40
|
+
}]
|
41
|
+
}],
|
42
|
+
listeners: {
|
43
|
+
activeIndexChange: () => console.log('activeIndexChange')
|
44
|
+
}
|
45
|
+
}
|
46
|
+
onConstructed() {
|
47
|
+
console.log('constructed');
|
48
|
+
this.on('activeIndexChange', () => console.log('onConstructed'));
|
49
|
+
super.onConstructed();
|
50
|
+
}
|
51
|
+
|
52
|
+
afterSetValue(value, oldValue) {
|
53
|
+
if (value) {
|
54
|
+
this.getItem('textArea').value = value;
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
doIt(button) {
|
59
|
+
let source = this.getReference('source').getValue();
|
60
|
+
|
61
|
+
this.getReference('codePreview').src = source;
|
62
|
+
|
63
|
+
const importRegex = /import\s+([\w-]+)\s+from\s+['"]([^'"]+)['"]/;
|
64
|
+
const exportRegex = /export\s+(?:default\s+)?(?:const|let|var|class|function|async\s+function|generator\s+function|async\s+generator\s+function|(\{[\s\S]*?\}))/g;
|
65
|
+
|
66
|
+
|
67
|
+
const cleanLines = [];
|
68
|
+
const importPromises = [];
|
69
|
+
const importModuleNames = [];
|
70
|
+
|
71
|
+
const moduleNameAndPath = [];
|
72
|
+
|
73
|
+
source.split('\n').forEach(line => {
|
74
|
+
let importMatch = line.match(importRegex);
|
75
|
+
if (importMatch) {
|
76
|
+
let moduleName = importMatch[1];
|
77
|
+
let path = importMatch[2];
|
78
|
+
moduleNameAndPath.push({
|
79
|
+
moduleName,
|
80
|
+
path
|
81
|
+
});
|
82
|
+
// importPromises.push(import(path));
|
83
|
+
// importPromises.push(import(path).then(module => {
|
84
|
+
// eval(`const ${moduleName} = module.default;`)
|
85
|
+
// }));
|
86
|
+
importModuleNames.push(moduleName);
|
87
|
+
} else if (line.match(exportRegex)) {
|
88
|
+
// Skip export statements
|
89
|
+
} else {
|
90
|
+
cleanLines.push(line);
|
91
|
+
}
|
92
|
+
});
|
93
|
+
var params = [];
|
94
|
+
var vars = [];
|
95
|
+
// Figure out the parts of the source we'll be running.
|
96
|
+
// o The promises/import() corresponding to the user's import statements
|
97
|
+
// o The vars holding the name of the imported module based on the module name for each import
|
98
|
+
// o The rest of the user-provided source
|
99
|
+
// It'll end up looking like this:
|
100
|
+
// Promise.all([
|
101
|
+
// import('../../../node_modules/neo.mjs/src/container/Base.mjs'),
|
102
|
+
// import('../../../node_modules/neo.mjs/src/button/Base.mjs')
|
103
|
+
// ]).then(([BaseModule, ButtonModule]) => {
|
104
|
+
// const Base = BaseModule.default;
|
105
|
+
// const Button = ButtonModule.default;
|
106
|
+
// // Class declaration goes here...
|
107
|
+
// });
|
108
|
+
// Making the promise part of the eval seems weird, but it made it easier to
|
109
|
+
// set up the import vars.
|
110
|
+
|
111
|
+
let promises = moduleNameAndPath.map(item => {
|
112
|
+
params.push(`${item.moduleName}Module`);
|
113
|
+
vars.push(`const ${item.moduleName} = ${item.moduleName}Module.default`);
|
114
|
+
return `import("${item.path}")`;
|
115
|
+
});
|
116
|
+
const codeString = `
|
117
|
+
Promise.all([
|
118
|
+
${promises.join(',\n')}
|
119
|
+
])
|
120
|
+
.then(([${params.join(', ')}]) => {
|
121
|
+
${vars.join('\n')}
|
122
|
+
${cleanLines.join('\n')}
|
123
|
+
container.add({module:Bar});
|
124
|
+
})
|
125
|
+
.catch(error=>container.add({ntype:'component',html:error.message}));
|
126
|
+
`;
|
127
|
+
|
128
|
+
// console.log(codeString);
|
129
|
+
|
130
|
+
const container = this.getReference('preview');
|
131
|
+
container.removeAll();
|
132
|
+
try {
|
133
|
+
const dynamicCode = new Function('container', codeString);
|
134
|
+
dynamicCode(container);
|
135
|
+
} catch (error) {
|
136
|
+
container.add({
|
137
|
+
ntype: 'component',
|
138
|
+
html: error.message
|
139
|
+
})
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
/**
|
144
|
+
* @param {String} reference
|
145
|
+
* @returns {Object|Neo.component.Base|null}
|
146
|
+
*/
|
147
|
+
getItem(reference, items = this.items) {
|
148
|
+
let i = 0,
|
149
|
+
len = items.length,
|
150
|
+
item,
|
151
|
+
childItem;
|
152
|
+
|
153
|
+
for (; i < len; i++) {
|
154
|
+
item = items[i];
|
155
|
+
if (item.reference === reference) {
|
156
|
+
return item
|
157
|
+
} else if (item.items) {
|
158
|
+
childItem = this.getItem(reference, item.items);
|
159
|
+
|
160
|
+
if (childItem) {
|
161
|
+
return childItem;
|
162
|
+
}
|
163
|
+
}
|
164
|
+
}
|
165
|
+
return null
|
166
|
+
}
|
167
|
+
}
|
168
|
+
|
169
|
+
Neo.applyClassConfig(LivePreview);
|
170
|
+
|
171
|
+
export default LivePreview;
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import ContentStore from '../../store/Content.mjs'
|
2
|
-
import TreeList
|
2
|
+
import TreeList from '../../../../src/tree/List.mjs';
|
3
|
+
import LivePreview from '../LivePreview.mjs';
|
3
4
|
|
4
5
|
/**
|
5
6
|
* @class LearnNeo.view.home.ContentTreeList
|
@@ -35,27 +36,103 @@ class ContentTreeList extends TreeList {
|
|
35
36
|
* @returns {Promise<void>}
|
36
37
|
*/
|
37
38
|
async doFetchContent(record) {
|
38
|
-
let me
|
39
|
+
let me = this,
|
39
40
|
path = `${me.contentPath}`;
|
40
41
|
|
41
42
|
path += record.path ? `/pages/${record.path}` : `/p/${record.id}.md`;
|
42
43
|
|
43
44
|
if (record.isLeaf && path) {
|
44
|
-
const data
|
45
|
+
const data = await fetch(path);
|
45
46
|
const content = await data.text();
|
47
|
+
let modifiedHtml = await this.highlightPreContent(content);
|
46
48
|
|
47
|
-
|
49
|
+
// Replace <pre data-neo></neo> with <div id='neo-preview-1'/>
|
50
|
+
// and creaet a map keyed by ID, whose value is the javascript
|
51
|
+
// from the <pre>
|
52
|
+
let neoDivs = {};
|
53
|
+
|
54
|
+
modifiedHtml = this.extractNeoContent(modifiedHtml, neoDivs);
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
await Neo.main.addon.Markdown.markdownToHtml(modifiedHtml)
|
48
59
|
.then(
|
49
60
|
html => me.fire('contentChange', {
|
50
61
|
component: me,
|
51
62
|
html,
|
52
63
|
record,
|
53
|
-
isLab
|
64
|
+
isLab: record.name?.startsWith('Lab:')
|
54
65
|
}),
|
55
|
-
() => me.fire('contentChange', {component: me}))
|
66
|
+
() => me.fire('contentChange', {component: me}));
|
67
|
+
await this.timeout(50);
|
68
|
+
Object.keys(neoDivs).forEach(key => {
|
69
|
+
// Create LivePreview for each iteration, set value to neoDivs[key]
|
70
|
+
let foo = Neo.create(LivePreview, {
|
71
|
+
value: neoDivs[key],
|
72
|
+
parentId: key,
|
73
|
+
appName: this.appName
|
74
|
+
})
|
75
|
+
console.log(foo);
|
76
|
+
});
|
56
77
|
}
|
57
78
|
}
|
58
79
|
|
80
|
+
extractNeoContent(htmlString, map) {
|
81
|
+
// 1. Replace <pre data-neo> with <div id='neo-preview-2'/>
|
82
|
+
// and update map with key/value pairs, where the key is the ID and the value is the <pre> contents.
|
83
|
+
|
84
|
+
// Define a regular expression to match <pre data-javascript> tags
|
85
|
+
const preRegex = /<pre\s+data-neo\s*>([\s\S]*?)<\/pre>/g;
|
86
|
+
|
87
|
+
let count = 0;
|
88
|
+
// Replace the content with tokens, and create a promise to update the corresponding content
|
89
|
+
var updatedHtml = htmlString.replace(preRegex, (match, preContent) => {
|
90
|
+
const key = `pre-live-preview-${Neo.core.IdGenerator.getId()}-${count++}`;
|
91
|
+
map[key] = preContent;
|
92
|
+
return `<div id="${key}"/>`;
|
93
|
+
});
|
94
|
+
return updatedHtml;
|
95
|
+
|
96
|
+
}
|
97
|
+
|
98
|
+
async highlightPreContent(htmlString) {
|
99
|
+
// 1. Replace <pre data-javascript> with unique tokens and create a HighlightJS.highlightAuto promise for each
|
100
|
+
// 2. When all promises are resolved, use their values to replace the tokens.
|
101
|
+
|
102
|
+
// Note that if we were to import HighlightJS directly, we wouldn't need all this async code.
|
103
|
+
|
104
|
+
// Define a regular expression to match <pre data-javascript> tags
|
105
|
+
const preRegex = /<pre\s+data-javascript\s*>([\s\S]*?)<\/pre>/g;
|
106
|
+
|
107
|
+
// Create an array to store promises for each replacement
|
108
|
+
const replacementPromises = [];
|
109
|
+
let count = 0;
|
110
|
+
// Replace the content with tokens, and create a promise to update the corresponding content
|
111
|
+
var updatedHtml = htmlString.replace(preRegex, (match, preContent) => {
|
112
|
+
const token = `__NEO-PRE-TOKEN-${++count}__`;
|
113
|
+
replacementPromises.push(this.getHighlightPromise(preContent, token, `pre-preview-${Neo.core.IdGenerator.getId()}`));
|
114
|
+
return token;
|
115
|
+
});
|
116
|
+
|
117
|
+
// Assert: updateHtml is the original, but with <pre data-javascript> replaced with tokens.
|
118
|
+
|
119
|
+
// Wait for all replacement promises to resolve
|
120
|
+
return Promise.all(replacementPromises)
|
121
|
+
.then(replacements => {
|
122
|
+
// Replace each token with the resolved content
|
123
|
+
replacements.forEach((replacement) => updatedHtml = updatedHtml.replace(replacement.token, replacement.after));
|
124
|
+
|
125
|
+
// Return the final updated HTML string
|
126
|
+
return updatedHtml;
|
127
|
+
});
|
128
|
+
}
|
129
|
+
|
130
|
+
getHighlightPromise(preContent, token, id) {
|
131
|
+
// Resolves to an object of the form {after, token}, where after is the updated <pre> tag content
|
132
|
+
return Neo.main.addon.HighlightJS.highlightAuto(preContent)
|
133
|
+
.then(highlight => ({after: `<pre data-javascript id="${id}">${highlight.value}</pre>`, token}));
|
134
|
+
}
|
135
|
+
|
59
136
|
/**
|
60
137
|
*
|
61
138
|
*/
|
@@ -79,11 +156,17 @@ class ContentTreeList extends TreeList {
|
|
79
156
|
|
80
157
|
let me = this;
|
81
158
|
|
159
|
+
Neo.main.addon.HighlightJS.loadLibrary({
|
160
|
+
appName: me.appName,
|
161
|
+
highlightJsPath: '../../docs/resources/highlight/highlight.pack.js',
|
162
|
+
themePath: '../../docs/resources/highlightjs-custom-github-theme.css'
|
163
|
+
});
|
164
|
+
|
82
165
|
Neo.Main.getByPath({path: 'location.search'})
|
83
166
|
.then(data => {
|
84
167
|
const searchString = data?.substr(1) || '';
|
85
|
-
const search
|
86
|
-
me.deck
|
168
|
+
const search = searchString ? JSON.parse(`{"${decodeURI(searchString.replace(/&/g, "\",\"").replace(/=/g, "\":\""))}"}`) : {};
|
169
|
+
me.deck = search.deck || 'learnneo';
|
87
170
|
|
88
171
|
me.doLoadStore();
|
89
172
|
console.log(search);
|
@@ -28,9 +28,9 @@ class MainContainerController extends Controller {
|
|
28
28
|
|
29
29
|
fetch('../../../../resources/data/deck/EditorConfig.json')
|
30
30
|
.then(response => response.json()
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
.then(data =>
|
32
|
+
this.getModel().setData('editorConfig', data)
|
33
|
+
))
|
34
34
|
}
|
35
35
|
|
36
36
|
/**
|
@@ -38,29 +38,14 @@ class MainContainerController extends Controller {
|
|
38
38
|
* @returns {Promise<void>}
|
39
39
|
*/
|
40
40
|
async onContentChange(data) {
|
41
|
-
let me
|
41
|
+
let me = this,
|
42
42
|
content = me.getReference('content');
|
43
43
|
|
44
44
|
content.toggleCls('lab', data.isLab);
|
45
45
|
|
46
|
-
content.html
|
46
|
+
content.html = data.html;
|
47
47
|
content.record = data.record;
|
48
48
|
|
49
|
-
await me.timeout(200);
|
50
|
-
|
51
|
-
// todo: we need to add the links as neo configs
|
52
|
-
await Neo.main.addon.HighlightJS.loadLibrary({
|
53
|
-
appName : me.appName,
|
54
|
-
highlightJsPath: '../../docs/resources/highlight/highlight.pack.js',
|
55
|
-
themePath : '../../docs/resources/highlightjs-custom-github-theme.css'
|
56
|
-
});
|
57
|
-
|
58
|
-
await me.timeout(200);
|
59
|
-
|
60
|
-
Neo.main.addon.HighlightJS.syntaxHighlightInit({
|
61
|
-
appName: me.appName,
|
62
|
-
vnodeId: content.id
|
63
|
-
})
|
64
49
|
}
|
65
50
|
|
66
51
|
/**
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "neo.mjs",
|
3
|
-
"version": "6.
|
3
|
+
"version": "6.10.0",
|
4
4
|
"description": "The webworkers driven UI framework",
|
5
5
|
"type": "module",
|
6
6
|
"repository": {
|
@@ -41,10 +41,8 @@
|
|
41
41
|
"url": "https://github.com/neomjs/neo/issues"
|
42
42
|
},
|
43
43
|
"homepage": "https://neomjs.github.io/pages/",
|
44
|
-
"
|
44
|
+
"devDependencies": {
|
45
45
|
"@fortawesome/fontawesome-free": "^6.4.2",
|
46
|
-
"@material/mwc-button": "^0.27.0",
|
47
|
-
"@material/mwc-textfield": "^0.27.0",
|
48
46
|
"autoprefixer": "^10.4.16",
|
49
47
|
"chalk": "^5.3.0",
|
50
48
|
"clean-webpack-plugin": "^4.0.0",
|
@@ -58,17 +56,15 @@
|
|
58
56
|
"neo-jsdoc-x": "1.0.5",
|
59
57
|
"postcss": "^8.4.31",
|
60
58
|
"sass": "^1.69.5",
|
59
|
+
"siesta-lite": "5.5.2",
|
61
60
|
"showdown": "^2.1.0",
|
61
|
+
"url": "^0.11.3",
|
62
62
|
"webpack": "^5.89.0",
|
63
63
|
"webpack-cli": "^5.1.4",
|
64
64
|
"webpack-dev-server": "4.15.1",
|
65
65
|
"webpack-hook-plugin": "^1.0.7",
|
66
66
|
"webpack-node-externals": "^3.0.0"
|
67
67
|
},
|
68
|
-
"devDependencies": {
|
69
|
-
"siesta-lite": "5.5.2",
|
70
|
-
"url": "^0.11.3"
|
71
|
-
},
|
72
68
|
"funding": {
|
73
69
|
"type": "GitHub Sponsors",
|
74
70
|
"url": "https://github.com/sponsors/tobiu"
|
@@ -3,29 +3,27 @@ Neo.mjs is a framework used to create <mark>browser-based</mark> applications.
|
|
3
3
|
|
4
4
|
# Some key features and benefits of Neo.mjs are:
|
5
5
|
|
6
|
-
<code><A code snippet></code>
|
7
|
-
|
8
6
|
<details>
|
9
|
-
<summary>Multi-Threaded</summary>
|
7
|
+
<summary><h2>Multi-Threaded</h2></summary>
|
10
8
|
<p>
|
11
9
|
When a Neo.mjs application starts, the framework spawns three web-workers, in addition
|
12
10
|
to the main browser thread, resulting in:
|
13
11
|
<ol>
|
14
12
|
<li>The <b>main</b> browser thread, where DOM updates are applied
|
15
|
-
<li>An <b>application</b> web-worker where normal application
|
13
|
+
<li>An <b>application</b> web-worker where normal application logic is run
|
16
14
|
<li>A <b>data</b> web-worker were HTTP and socket calls are run
|
17
15
|
<li>A <b>view</b> web-worker that manages delta updates
|
18
16
|
</ol>
|
19
17
|
</details>
|
20
18
|
<details>
|
21
|
-
<summary>Extreme Speed</summary>
|
19
|
+
<summary><h2>Extreme Speed</h2></summary>
|
22
20
|
<p>
|
23
|
-
Web-worker
|
21
|
+
Web-worker processes are automatically run in parallel, on separate CPU cores.
|
24
22
|
</p>
|
25
23
|
<p>
|
26
24
|
By contrast, other JavaScript frameworks run in a single thread. That means
|
27
25
|
in a typical framework all business logic, data handling, and DOM rendering compete for
|
28
|
-
CPU
|
26
|
+
CPU resources.
|
29
27
|
</p>
|
30
28
|
<p>
|
31
29
|
This means Neo.mjs applications run and render faster. This is
|
@@ -39,14 +37,14 @@ to run other specialized logic.
|
|
39
37
|
</p>
|
40
38
|
</details>
|
41
39
|
<details>
|
42
|
-
<summary>Quick Application Development</summary>
|
40
|
+
<summary><h2>Quick Application Development</h2></summary>
|
43
41
|
<p>
|
44
|
-
Neo.
|
42
|
+
Neo.mjs classes let you specify properties in a way that allows code to detect "before" and "after"
|
45
43
|
changes. This makes it easy to handle value validation and transformation, and react to changes.
|
46
44
|
</p>
|
47
45
|
<p>
|
48
46
|
Neo.mjs also has elegant yet powerful state management features that make it easy to create shared,
|
49
|
-
bindable data. For example, if two components are bound to the same
|
47
|
+
bindable data. For example, if two components are bound to the same property, a change to the
|
50
48
|
property will automatically be applied to both components.
|
51
49
|
</p>
|
52
50
|
<p>
|
@@ -57,7 +55,7 @@ update its properties ‐ these updates are immediately reflected in the runn
|
|
57
55
|
</p>
|
58
56
|
</details>
|
59
57
|
<details>
|
60
|
-
<summary>Multi-Window Applications</summary>
|
58
|
+
<summary><h2>Multi-Window Applications</h2></summary>
|
61
59
|
<p>
|
62
60
|
Neo.mjs applications can also launch as <i>shared web workers</i>, which allows you to have a single
|
63
61
|
application run in multiple browser windows; those windows could be moved to multiple monitors.
|
@@ -69,10 +67,10 @@ across windows, running seamlessly as a single application.
|
|
69
67
|
</p>
|
70
68
|
</details>
|
71
69
|
<details>
|
72
|
-
<summary>Open-Source and Standards-Based</summary>
|
70
|
+
<summary><h2>Open-Source and Standards-Based</h2></summary>
|
73
71
|
<p>
|
74
|
-
Neo.mjs is an open-source
|
75
|
-
|
72
|
+
Neo.mjs is an open-source framework. Features needed for the community can be added to the
|
73
|
+
framework via pull-requests. And since Neo.mjs uses the standard JavaScript class system,
|
76
74
|
all Neo.mjs classes can be extended.
|
77
75
|
</p>
|
78
76
|
<p>
|
@@ -80,11 +78,11 @@ Neo.mjs uses standard modular JavaScript, so developers don't need to learn non-
|
|
80
78
|
syntax, and there's no need for special pre-compilers or WebPack modules.
|
81
79
|
That means fewer dependencies and easier configuration. Furthermore, the use of
|
82
80
|
standard JavaScript makes debugging easier: any statement you write while developing your
|
83
|
-
|
81
|
+
application can also be run in the debugging console.
|
84
82
|
</p>
|
85
83
|
</details>
|
86
84
|
<details>
|
87
|
-
<summary>Scalable</summary>
|
85
|
+
<summary><h2>Scalable</h2></summary>
|
88
86
|
<p>
|
89
87
|
Applications can become exponentially difficult to implement as application
|
90
88
|
complexity increases. In contrast, the effort to code applications in Neo.mjs
|
@@ -1,9 +1,8 @@
|
|
1
1
|
Neo.mjs classes are standard JavaScript classes. Every source file
|
2
2
|
you write will be a class definition, extending some Neo.mjs
|
3
|
-
class.
|
3
|
+
class.
|
4
4
|
|
5
|
-
<pre>
|
6
|
-
<code class="javascript">
|
5
|
+
<pre data-javascript>
|
7
6
|
import Base from '../../../node_modules/neo.mjs/src/core/Base.mjs';
|
8
7
|
|
9
8
|
class Mammal extends Base {
|
@@ -14,15 +13,14 @@ class Mammal extends Base {
|
|
14
13
|
|
15
14
|
const myMammal = Neo.create(Mammal);
|
16
15
|
|
17
|
-
Neo.applyClassConfig(Mammal); // Where Neo.mjs
|
16
|
+
Neo.applyClassConfig(Mammal); // Where Neo.mjs initializes the class config.
|
18
17
|
export default Mammal; // Makes the class available elsewhere.
|
19
|
-
</code>
|
20
18
|
</pre>
|
21
19
|
|
22
20
|
In the example above, we're extending the Neo.mjs base class. The static
|
23
|
-
config block
|
24
|
-
|
25
|
-
|
21
|
+
config block is unique to Neo.mjs; it provides a way of defining special
|
22
|
+
properties, such as the `className` seen above. Over time we'll go into more detail
|
23
|
+
on the static config property.
|
26
24
|
|
27
25
|
The `const myMammal = Neo.create(Mammal);` statement creates an instance of
|
28
26
|
our class. For the sake of our discussion we're putting that statement in the same source
|
@@ -31,8 +29,7 @@ and create instances as needed.
|
|
31
29
|
|
32
30
|
Let's add a `name` propery to the class.
|
33
31
|
|
34
|
-
<pre>
|
35
|
-
<code class="javascript">
|
32
|
+
<pre data-javascript>
|
36
33
|
import Base from '../../../node_modules/neo.mjs/src/core/Base.mjs';
|
37
34
|
|
38
35
|
class Mammal extends Base {
|
@@ -50,7 +47,6 @@ console.log(myMammal.name); // Logs "Herbert"
|
|
50
47
|
Neo.applyClassConfig(Mammal);
|
51
48
|
|
52
49
|
export default Mammal;
|
53
|
-
</code>
|
54
50
|
</pre>
|
55
51
|
|
56
52
|
In Neo.mjs, instance properties are usually added in the `static config` block.
|
@@ -65,13 +61,11 @@ anywhere in the class hierarchy.
|
|
65
61
|
Since our class defines a `name` property, we can specify that when creating
|
66
62
|
the instance, using the second argument to the `create` method.
|
67
63
|
|
68
|
-
<pre>
|
69
|
-
<code class="javascript">
|
64
|
+
<pre data-javascript>
|
70
65
|
const myMammal = Neo.create(Mammal, {
|
71
66
|
name: 'Creature'
|
72
67
|
});
|
73
68
|
console.log(myMammal.name); // Logs "Creature"
|
74
|
-
</code>
|
75
69
|
</pre>
|
76
70
|
|
77
71
|
|
@@ -79,8 +73,7 @@ Since _you_ define those properties, you can
|
|
79
73
|
look for them in class methods and use them as needed.
|
80
74
|
Let's add a `speak()` method that uses the `name` property.
|
81
75
|
|
82
|
-
<pre>
|
83
|
-
<code class="javascript">
|
76
|
+
<pre data-javascript>
|
84
77
|
import Base from '../../../node_modules/neo.mjs/src/core/Base.mjs';
|
85
78
|
|
86
79
|
class Mammal extends Base {
|
@@ -103,7 +96,6 @@ myMammal.speak(); // Logs "Creature is grunting."
|
|
103
96
|
Neo.applyClassConfig(Mammal);
|
104
97
|
|
105
98
|
export default Mammal;
|
106
|
-
</code>
|
107
99
|
</pre>
|
108
100
|
|
109
101
|
|