neo.mjs 10.0.0-alpha.5 → 10.0.0-beta.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.
Files changed (72) hide show
  1. package/ServiceWorker.mjs +2 -2
  2. package/apps/portal/index.html +1 -1
  3. package/apps/portal/resources/data/examples_devmode.json +26 -27
  4. package/apps/portal/resources/data/examples_dist_dev.json +26 -27
  5. package/apps/portal/resources/data/examples_dist_esm.json +26 -27
  6. package/apps/portal/resources/data/examples_dist_prod.json +27 -28
  7. package/apps/portal/view/home/FooterContainer.mjs +1 -1
  8. package/apps/portal/view/learn/ContentComponent.mjs +102 -111
  9. package/examples/table/cellEditing/MainContainer.mjs +1 -1
  10. package/examples/table/container/MainContainer.mjs +3 -3
  11. package/examples/table/nestedRecordFields/Viewport.mjs +6 -6
  12. package/examples/tableFiltering/MainContainer.mjs +1 -1
  13. package/examples/tablePerformance/MainContainer.mjs +1 -1
  14. package/examples/tablePerformance/MainContainer2.mjs +1 -1
  15. package/examples/tablePerformance/MainContainer3.mjs +2 -2
  16. package/examples/tableStore/MainContainer.mjs +2 -2
  17. package/learn/UsingTheseTopics.md +2 -2
  18. package/learn/benefits/ConfigSystem.md +2 -2
  19. package/learn/benefits/FormsEngine.md +14 -14
  20. package/learn/benefits/MultiWindow.md +2 -2
  21. package/learn/benefits/OffTheMainThread.md +2 -2
  22. package/learn/benefits/Speed.md +2 -2
  23. package/learn/gettingstarted/ComponentModels.md +4 -4
  24. package/learn/gettingstarted/Config.md +6 -6
  25. package/learn/gettingstarted/DescribingTheUI.md +4 -4
  26. package/learn/gettingstarted/Events.md +6 -6
  27. package/learn/gettingstarted/Extending.md +4 -4
  28. package/learn/gettingstarted/References.md +6 -6
  29. package/learn/gettingstarted/Workspaces.md +6 -6
  30. package/learn/guides/ApplicationBootstrap.md +26 -26
  31. package/learn/guides/ComponentsAndContainers.md +12 -12
  32. package/learn/guides/CustomComponents.md +2 -2
  33. package/learn/guides/MainThreadAddonIntro.md +2 -2
  34. package/learn/guides/PortalApp.md +2 -2
  35. package/learn/guides/StateProviders.md +12 -12
  36. package/learn/guides/events/CustomEvents.md +16 -16
  37. package/learn/guides/events/DomEvents.md +12 -12
  38. package/learn/javascript/ClassFeatures.md +3 -2
  39. package/learn/javascript/Classes.md +8 -8
  40. package/learn/javascript/NewNode.md +4 -4
  41. package/learn/javascript/Overrides.md +8 -8
  42. package/learn/javascript/Super.md +10 -8
  43. package/learn/tutorials/Earthquakes.md +54 -57
  44. package/learn/tutorials/TodoList.md +4 -4
  45. package/package.json +1 -1
  46. package/resources/scss/src/apps/portal/learn/ContentComponent.scss +12 -0
  47. package/resources/scss/src/table/{View.scss → Body.scss} +1 -1
  48. package/resources/scss/src/table/plugin/CellEditing.scss +1 -1
  49. package/resources/scss/theme-dark/table/{View.scss → Body.scss} +1 -1
  50. package/resources/scss/theme-light/table/{View.scss → Body.scss} +1 -1
  51. package/resources/scss/theme-neo-light/Global.scss +1 -2
  52. package/resources/scss/theme-neo-light/table/{View.scss → Body.scss} +1 -1
  53. package/src/DefaultConfig.mjs +2 -2
  54. package/src/core/Base.mjs +59 -12
  55. package/src/core/Util.mjs +14 -2
  56. package/src/draggable/grid/header/toolbar/SortZone.mjs +21 -21
  57. package/src/draggable/table/header/toolbar/SortZone.mjs +1 -1
  58. package/src/grid/Body.mjs +2 -3
  59. package/src/grid/_export.mjs +1 -1
  60. package/src/main/addon/AmCharts.mjs +8 -11
  61. package/src/main/addon/Base.mjs +150 -42
  62. package/src/main/addon/GoogleMaps.mjs +9 -16
  63. package/src/main/addon/HighlightJS.mjs +2 -13
  64. package/src/main/addon/IntersectionObserver.mjs +21 -21
  65. package/src/main/addon/MonacoEditor.mjs +1 -6
  66. package/src/table/{View.mjs → Body.mjs} +19 -20
  67. package/src/table/Container.mjs +43 -43
  68. package/src/table/_export.mjs +2 -2
  69. package/src/table/plugin/CellEditing.mjs +19 -19
  70. package/src/vdom/Helper.mjs +9 -12
  71. package/src/worker/Base.mjs +1 -1
  72. package/src/worker/mixin/RemoteMethodAccess.mjs +1 -1
@@ -1,145 +1,144 @@
1
1
  [{
2
2
  "id" : 24,
3
3
  "environments": ["Desktop", "Mobile"],
4
- "image" : "devmode/liquid-glass.png",
5
- "name" : "Liquid Glass effect",
6
- "sourceUrl" : "https://github.com/neomjs/liquid-glass-demo/blob/main/apps/myapp/view/GlassComponent.mjs",
7
- "url" : "https://neomjs.github.io/pages2/workspace/neo-liquid-glass-demo/dist/production/apps/myapp/index.html"
8
- }, {
9
- "id" : 23,
10
- "environments": ["Desktop", "Mobile"],
11
4
  "image" : "devmode/bigData.png",
12
5
  "name" : "Buffered Data Grid with 5M cells",
13
6
  "sourceUrl" : "examples/grid/bigData",
14
7
  "url" : "dist/production/examples/grid/bigData/index.html"
15
8
  }, {
16
- "id" : 22,
9
+ "id" : 23,
17
10
  "image" : "devmode/sharedcovid.png",
18
11
  "name" : "Multi Window Covid App",
19
12
  "sharedWorkers": true,
20
13
  "sourceUrl" : "apps/sharedcovid",
21
14
  "url" : "dist/production/apps/sharedcovid/index.html#mainview=table"
22
15
  }, {
23
- "id" : 21,
16
+ "id" : 22,
24
17
  "image" : "devmode/calendar-preview.png",
25
18
  "name" : "Calendar",
26
19
  "sourceUrl": "src/calendar",
27
20
  "url" : "dist/production/examples/calendar/basic/index.html"
28
21
  }, {
29
- "id" : 20,
22
+ "id" : 21,
30
23
  "image" : "devmode/helix.png",
31
24
  "name" : "component.Helix",
32
25
  "sourceUrl": "examples/component/helix",
33
26
  "url" : "dist/production/examples/component/helix/index.html"
34
27
  }, {
35
- "id" : 19,
28
+ "id" : 20,
36
29
  "image" : "devmode/mwCoronaGallery.png",
37
30
  "name" : "Multi Window Covid Gallery",
38
31
  "sharedWorkers": true,
39
32
  "sourceUrl" : "examples/component/multiWindowCoronaGallery",
40
33
  "url" : "dist/production/examples/component/multiWindowCoronaGallery/index.html"
41
34
  }, {
42
- "id" : 18,
35
+ "id" : 19,
43
36
  "image" : "devmode/offscreenCanvas.png",
44
37
  "name" : "OffscreenCanvas",
45
38
  "sourceUrl": "https://github.com/neomjs/offscreen-canvas/tree/main/apps/myapp",
46
39
  "url" : "https://neomjs.github.io/pages2/workspace/neo-offscreen-canvas-demo/dist/production/apps/myapp/index.html"
47
40
  }, {
48
- "id" : 17,
41
+ "id" : 18,
49
42
  "image" : "devmode/sharedOffscreenCanvas.png",
50
43
  "name" : "Multi-Window OffscreenCanvas",
51
44
  "sharedWorkers": true,
52
45
  "sourceUrl" : "https://github.com/neomjs/shared-offscreen",
53
46
  "url" : "https://neomjs.github.io/pages2/workspace/neo-shared-offscreen-canvas-demo/dist/production/apps/mainapp/index.html"
54
47
  }, {
55
- "id" : 16,
48
+ "id" : 17,
56
49
  "image" : "devmode/multi_window_dd.png",
57
50
  "name" : "Multi-Window Drag&Drop",
58
51
  "sharedWorkers": true,
59
52
  "sourceUrl" : "apps/shareddialog",
60
53
  "url" : "dist/production/apps/shareddialog/index.html"
61
54
  }, {
62
- "id" : 15,
55
+ "id" : 16,
63
56
  "image" : "devmode/tabContainer.png",
64
57
  "name" : "tab.Container",
65
58
  "sourceUrl": "examples/tab/container",
66
59
  "url" : "dist/production/examples/tab/container/index.html"
67
60
  }, {
68
- "id" : 14,
61
+ "id" : 15,
69
62
  "image" : "devmode/model-component-example.png",
70
63
  "name" : "State Provider Example",
71
64
  "sourceUrl": "examples/stateProvider/advanced",
72
65
  "url" : "dist/production/examples/stateProvider/advanced/index.html"
73
66
  }, {
74
- "id" : 13,
67
+ "id" : 14,
75
68
  "image" : "devmode/dragdrop.png",
76
69
  "name" : "Dialog drag&drop",
77
70
  "sourceUrl": "examples/dialog",
78
71
  "url" : "dist/production/examples/dialog/"
79
72
  }, {
80
- "id" : 12,
73
+ "id" : 13,
81
74
  "image" : "dist_prod/colorsApp.png",
82
75
  "name" : "Colors Dashboard",
83
76
  "sharedWorkers": true,
84
77
  "sourceUrl" : "apps/colors",
85
78
  "url" : "dist/production/apps/colors/index.html"
86
79
  }, {
87
- "id" : 11,
80
+ "id" : 12,
88
81
  "image" : "devmode/covidDashboard.png",
89
82
  "name" : "Covid Dashboard",
90
83
  "sourceUrl": "apps/covid",
91
84
  "url" : "dist/production/apps/covid/index.html#mainview=table"
92
85
  }, {
93
- "id" : 10,
86
+ "id" : 11,
94
87
  "image" : "devmode/coronaHelix.png",
95
88
  "name" : "COVID-19 Helix",
96
89
  "sourceUrl": "examples/component/coronaHelix",
97
90
  "url" : "dist/production/examples/component/coronaHelix/index.html"
98
91
  }, {
99
- "id" : 9,
92
+ "id" : 10,
100
93
  "image" : "devmode/coronaGallery.png",
101
94
  "name" : "COVID-19 Gallery",
102
95
  "sourceUrl": "examples/component/coronaGallery",
103
96
  "url" : "dist/production/examples/component/coronaGallery/index.html"
104
97
  }, {
105
- "id" : 8,
98
+ "id" : 9,
106
99
  "image" : "devmode/gallery.png",
107
100
  "name" : "component.Gallery",
108
101
  "sourceUrl": "examples/component/gallery",
109
102
  "url" : "dist/production/examples/component/gallery/index.html"
110
103
  }, {
111
- "id" : 7,
104
+ "id" : 8,
112
105
  "image" : "devmode/tableFiltering.png",
113
106
  "name" : "Table Filtering",
114
107
  "sourceUrl": "examples/tableFiltering",
115
108
  "url" : "dist/production/examples/tableFiltering/"
116
109
  }, {
117
- "id" : 6,
110
+ "id" : 7,
118
111
  "image" : "devmode/dateSelector.png",
119
112
  "name" : "component.DateSelector",
120
113
  "sourceUrl": "examples/component/dateSelector",
121
114
  "url" : "dist/production/examples/component/dateSelector/index.html"
122
115
  }, {
123
- "id" : 5,
116
+ "id" : 6,
124
117
  "image" : "devmode/dateField.png",
125
118
  "name" : "form.field.Date",
126
119
  "sourceUrl": "examples/form/field/date",
127
120
  "url" : "dist/production/examples/form/field/date/index.html"
128
121
  }, {
129
- "id" : 4,
122
+ "id" : 5,
130
123
  "image" : "devmode/selectField.png",
131
124
  "name" : "form.field.ComboBox",
132
125
  "sourceUrl": "examples/form/field/combobox",
133
126
  "url" : "dist/production/examples/form/field/combobox/index.html"
134
127
  }, {
135
- "id" : 3,
128
+ "id" : 4,
136
129
  "environments" : ["Desktop", "Mobile"],
137
130
  "image" : "dist_prod/portalApp.png",
138
131
  "name" : "Portal App",
139
132
  "sharedWorkers": true,
140
133
  "sourceUrl" : "apps/portal",
141
134
  "url" : "dist/production/apps/portal/index.html"
142
- }, {
135
+ },{
136
+ "id" : 3,
137
+ "image" : "devmode/liquid-glass.png",
138
+ "name" : "Liquid Glass effect",
139
+ "sourceUrl": "https://github.com/neomjs/liquid-glass-demo/blob/main/apps/myapp/view/GlassComponent.mjs",
140
+ "url" : "https://neomjs.github.io/pages2/workspace/neo-liquid-glass-demo/dist/production/apps/myapp/index.html"
141
+ }, , {
143
142
  "hidden" : true,
144
143
  "id" : 2,
145
144
  "image" : "devmode/realworldApp.png",
@@ -107,7 +107,7 @@ class FooterContainer extends Container {
107
107
  }, {
108
108
  module: Component,
109
109
  cls : ['neo-version'],
110
- html : 'v10.0.0-alpha.5'
110
+ html : 'v10.0.0-beta.1'
111
111
  }]
112
112
  }],
113
113
  /**
@@ -5,9 +5,9 @@ import {marked} from '../../../../node_modules/marked/lib/marked.esm.js';
5
5
  const
6
6
  labCloseRegex = /<!--\s*\/lab\s*-->/g,
7
7
  labOpenRegex = /<!--\s*lab\s*-->/g,
8
- preLivePreviewRegex = /<pre\s+data-code-livepreview\s*>([\s\S]*?)<\/pre>/g,
9
- preJsRegex = /<pre\s+data-code-readonly\s*>([\s\S]*?)<\/pre>/g,
10
- preNeoComponentRegex = /<pre\s+data-neo-component\s*>([\s\S]*?)<\/pre>/g;
8
+ regexLivePreview = /```(javascript|html|css|json)\s+live-preview\s*\n([\s\S]*?)\n```/g,
9
+ regexNeoComponent = /```json\s+neo-component\s*\n([\s\S]*?)\n```/g,
10
+ regexReadonly = /```(javascript|html|css|json)\s+readonly\s*\n([\s\S]*?)\n```/g;
11
11
 
12
12
  /**
13
13
  * @class Portal.view.learn.ContentComponent
@@ -164,48 +164,48 @@ class ContentComponent extends Component {
164
164
  {appName, windowId} = me,
165
165
  path = me.getStateProvider().getData('contentPath'),
166
166
  pagesFolder = path.includes('/learn/') ? '' : 'pages/',
167
- baseConfigs, content, data, html, instance, modifiedHtml, neoComponents, neoDivs;
167
+ baseConfigs, content, data, html, instance, neoComponents, neoDivs;
168
168
 
169
169
  path += `${pagesFolder + record.id.replaceAll('.', '/')}.md`;
170
170
 
171
171
  if (record.isLeaf && path) {
172
- baseConfigs = {appName, autoMount: true, autoRender: true, parentComponent: me, windowId};
173
- data = await fetch(path);
174
- content = await data.text();
175
- content = me.updateContentSectionsStore(content); // also replaces ## with h2 tags
176
- content = `<h1 class='neo-h1'>${record.name}</h1>\n${content}`;
177
- modifiedHtml = await me.highlightPreContent(content);
172
+ baseConfigs = {appName, autoMount: true, autoRender: true, parentComponent: me, windowId};
173
+ data = await fetch(path);
174
+ content = await data.text();
175
+ // Update content sections (modifies markdown content with h2/h3 tags and IDs)
176
+ content = me.updateContentSectionsStore(content);
177
+ content = `<h1 class='neo-h1'>${record.name}</h1>\n${content}`;
178
+ // Initialize maps for custom components and live previews
178
179
  neoComponents = {};
179
180
  neoDivs = {};
180
-
181
- // Replace <pre neo-component></pre> with <div id='neo-component-x'/>
182
- // and create a map keyed by ID, whose value is the javascript
183
- // from the <pre>
184
- modifiedHtml = me.extractNeoComponents(modifiedHtml, neoComponents);
185
-
186
- // Replace <pre data-neo></pre> with <div id='neo-preview-1'/>
187
- // and create a map keyed by ID, whose value is the javascript
188
- // from the <pre>
189
- modifiedHtml = me.extractLivePreviewContent(modifiedHtml, neoDivs);
190
-
191
- html = marked.parse(modifiedHtml);
192
- html = me.insertLabDivs(html);
193
-
194
- me.toggleCls('lab', record.name?.startsWith('Lab:'));
195
-
196
- me.html = html;
181
+ // Process custom Neo.mjs component blocks (synchronous)
182
+ content = me.processNeoComponentsBlocks(content, neoComponents);
183
+ // Process custom Live Preview blocks (synchronous)
184
+ content = me.processLivePreviewBlocks(content, neoDivs);
185
+ // Process custom Readonly Code blocks (asynchronous due to HighlightJS)
186
+ // This will replace the markdown fenced block with the highlighted HTML <pre> tag.
187
+ content = await me.processReadonlyCodeBlocks(content, windowId);
188
+ // Parse the (now modified) markdown content into HTML
189
+ // This content string now contains standard markdown PLUS the HTML divs/pres we injected.
190
+ html = marked.parse(content);
191
+ // Insert lab divs (these are markdown comments, so process on the final HTML)
192
+ html = me.insertLabDivs(html); // Keep existing method
193
+
194
+ me.toggleCls('lab', record.name?.startsWith('Lab:')); // Keep existing method
195
+
196
+ me.html = html; // Set the component's HTML
197
197
 
198
198
  await me.timeout(Neo.config.environment === 'development' ? 100 : 150);
199
199
 
200
+ // Create instances for custom components and live previews (keep existing logic)
200
201
  Object.keys(neoComponents).forEach(key => {
201
202
  instance = Neo.create({
202
203
  ...baseConfigs,
203
- className: 'Neo.component.Base',
204
+ className: 'Neo.component.Base', // Adjust if specific component classes are implied by JSON
204
205
  parentId : key,
205
206
  ...neoComponents[key]
206
207
  });
207
-
208
- me.customComponents.push(instance)
208
+ me.customComponents.push(instance);
209
209
  });
210
210
 
211
211
  Object.keys(neoDivs).forEach(key => {
@@ -213,10 +213,9 @@ class ContentComponent extends Component {
213
213
  ...baseConfigs,
214
214
  module : LivePreview,
215
215
  parentId: key,
216
- value : neoDivs[key]
216
+ value : neoDivs[key].code // Pass the extracted code content
217
217
  });
218
-
219
- me.livePreviews.push(instance)
218
+ me.livePreviews.push(instance);
220
219
  });
221
220
 
222
221
  Neo.main.addon.IntersectionObserver.observe({
@@ -224,87 +223,10 @@ class ContentComponent extends Component {
224
223
  id : me.id,
225
224
  observe : ['.neo-h2', '.neo-h3'],
226
225
  windowId : me.windowId
227
- })
226
+ });
228
227
  }
229
228
  }
230
229
 
231
- /**
232
- * @param {String} htmlString
233
- * @param {Object} map
234
- * @returns {String}
235
- */
236
- extractNeoComponents(htmlString, map) {
237
- // 1. Replace <pre data-neo-component> with <div id='neo-learn-content-component-x'/>
238
- // and update map with key/value pairs, where the key is the ID and the value is the <pre> contents.
239
- // Replace the content with tokens, and create a promise to update the corresponding content
240
- return htmlString.replace(preNeoComponentRegex, (match, preContent) => {
241
- const key = Neo.core.IdGenerator.getId('learn-content-component');
242
- map[key] = JSON.parse(preContent);
243
- return `<div id="${key}"></div>`
244
- })
245
- }
246
-
247
- /**
248
- * @param {String} htmlString
249
- * @param {Object} map
250
- * @returns {String}
251
- */
252
- extractLivePreviewContent(htmlString, map) {
253
- // 1. Replace <pre data-neo> with <div id='neo-pre-live-preview-x'/>
254
- // and update map with key/value pairs, where the key is the ID and the value is the <pre> contents.
255
- // Replace the content with tokens, and create a promise to update the corresponding content
256
- return htmlString.replace(preLivePreviewRegex, (match, preContent) => {
257
- const key = Neo.core.IdGenerator.getId('pre-live-preview');
258
- map[key] = preContent;
259
- return `<div id="${key}"></div>`
260
- })
261
- }
262
-
263
- /**
264
- * @param preContent
265
- * @param token
266
- * @param id
267
- * @returns {Object}
268
- */
269
- getHighlightPromise(preContent, token, id) {
270
- // Resolves to an object of the form {after, token}, where after is the updated <pre> tag content
271
- return Neo.main.addon.HighlightJS.highlightAuto({html: preContent, windowId: this.windowId})
272
- .then(value => ({after: `<pre data-javascript id="${id}">${value}</pre>`, token}))
273
- }
274
-
275
- /**
276
- * @param {String} htmlString
277
- * @returns {Promise<*>}
278
- */
279
- async highlightPreContent(htmlString) {
280
- // 1. Replace <pre data-javascript> with unique tokens and create a HighlightJS.highlightAuto promise for each
281
- // 2. When all promises are resolved, use their values to replace the tokens.
282
-
283
- // Note that if we were to import HighlightJS directly, we wouldn't need all this async code.
284
-
285
- // Create an array to store promises for each replacement
286
- const replacementPromises = [];
287
- let count = 0;
288
-
289
- // Replace the content with tokens, and create a promise to update the corresponding content
290
- let updatedHtml = htmlString.replace(preJsRegex, (match, preContent) => {
291
- const token = `__NEO-PRE-TOKEN-${++count}__`;
292
- replacementPromises.push(this.getHighlightPromise(preContent, token, `pre-preview-${Neo.core.IdGenerator.getId()}`));
293
- return token
294
- });
295
-
296
- // Assert: updateHtml is the original, but with <pre data-javascript> replaced with tokens.
297
-
298
- // Wait for all replacement promises to resolve
299
- let replacements = await Promise.all(replacementPromises)
300
-
301
- // Replace each token with the resolved content
302
- replacements.forEach((replacement) => updatedHtml = updatedHtml.replace(replacement.token, replacement.after));
303
-
304
- // Return the final updated HTML string
305
- return updatedHtml
306
- }
307
-
308
230
  /**
309
231
  * @param {String} inputString
310
232
  * @returns {String}
@@ -335,6 +257,75 @@ class ContentComponent extends Component {
335
257
  }
336
258
  }
337
259
 
260
+ /**
261
+ * Extracts live preview code blocks from Markdown content before marked.js parsing.
262
+ * Replaces them with HTML placeholders.
263
+ * @param {String} contentString The raw Markdown content string.
264
+ * @param {Object} map A map to store the extracted code content keyed by placeholder ID.
265
+ * @returns {String} The modified Markdown content string with placeholders.
266
+ */
267
+ processLivePreviewBlocks(contentString, map) {
268
+ return contentString.replace(regexLivePreview, (match, language, code) => {
269
+ const key = Neo.core.IdGenerator.getId('pre-live-preview');
270
+ map[key] = {code, language};
271
+ return `<div id="${key}"></div>`
272
+ })
273
+ }
274
+
275
+ /**
276
+ * Extracts Neo.mjs component config blocks from Markdown content before marked.js parsing.
277
+ * Replaces them with HTML placeholders.
278
+ * @param {String} contentString The raw Markdown content string.
279
+ * @param {Object} map A map to store the extracted JSON config keyed by placeholder ID.
280
+ * @returns {String} The modified Markdown content string with placeholders.
281
+ */
282
+ processNeoComponentsBlocks(contentString, map) {
283
+ return contentString.replace(regexNeoComponent, (match, code) => {
284
+ const key = Neo.core.IdGenerator.getId('learn-content-component');
285
+ map[key] = JSON.parse(code);
286
+ return `<div id="${key}"></div>`
287
+ })
288
+ }
289
+
290
+ /**
291
+ * Highlights readonly code blocks using HighlightJS.
292
+ * Replaces the Markdown fenced block with the highlighted HTML <pre> tag.
293
+ * @param {String} contentString The raw Markdown content string.
294
+ * @param {String} windowId The ID of the current window for HighlightJS.
295
+ * @returns {Promise<String>} A promise that resolves to the modified Markdown string with highlighted HTML.
296
+ */
297
+ async processReadonlyCodeBlocks(contentString, windowId) {
298
+ let replacementPromises = [],
299
+ count = 0,
300
+ replacements;
301
+
302
+ // Replace the content with tokens, and create a promise to update the corresponding content
303
+ let updatedContent = contentString.replace(regexReadonly, (match, language, code) => {
304
+ const token = `__NEO-READONLY-TOKEN-${++count}__`;
305
+ // Call HighlightJS.highlightAuto for each block.
306
+ // The result will be HTML. We'll wrap it in a <pre data-javascript> later.
307
+ replacementPromises.push(
308
+ Neo.main.addon.HighlightJS.highlightAuto({html: code, windowId})
309
+ .then(highlightedHtml => ({
310
+ after: `<pre data-javascript id="pre-readonly-${Neo.core.IdGenerator.getId()}">${highlightedHtml}</pre>`,
311
+ token: token
312
+ }))
313
+ );
314
+
315
+ return token; // Replace the original Markdown block with a temporary token
316
+ });
317
+
318
+ // Wait for all highlighting promises to resolve
319
+ replacements = await Promise.all(replacementPromises);
320
+
321
+ // Replace each token with the resolved highlighted HTML content
322
+ replacements.forEach(replacement => {
323
+ updatedContent = updatedContent.replace(replacement.token, replacement.after)
324
+ });
325
+
326
+ return updatedContent
327
+ }
328
+
338
329
  /**
339
330
  * Updates the contentSections VM store and replaces ## with h2 tags
340
331
  * @param {String} content
@@ -113,7 +113,7 @@ class MainContainer extends ConfigurationViewport {
113
113
  parentId : this.id,
114
114
  store : MainStore,
115
115
 
116
- viewConfig: {
116
+ bodyConfig: {
117
117
  selectionModel: CellModel
118
118
  },
119
119
 
@@ -115,7 +115,7 @@ class MainContainer extends ConfigurationViewport {
115
115
  id : 'myTableStoreContainer',
116
116
  store: MainStore,
117
117
 
118
- viewConfig: {
118
+ bodyConfig: {
119
119
  selectionModel: CellModel
120
120
  },
121
121
 
@@ -139,7 +139,7 @@ class MainContainer extends ConfigurationViewport {
139
139
  text : 'Edit'
140
140
  }));
141
141
 
142
- me.view.updateDepth = -1;
142
+ me.body.updateDepth = -1;
143
143
 
144
144
  return button.createVdomReference()
145
145
  }
@@ -168,7 +168,7 @@ class MainContainer extends ConfigurationViewport {
168
168
  }
169
169
  }));
170
170
 
171
- me.view.updateDepth = -1;
171
+ me.body.updateDepth = -1;
172
172
 
173
173
  return button.createVdomReference()
174
174
  }
@@ -40,17 +40,17 @@ class Viewport extends BaseViewport {
40
40
  module: TableContainer,
41
41
  bind : {store : 'stores.mainStore'},
42
42
 
43
+ bodyConfig: {
44
+ highlightModifiedCells: true
45
+ },
46
+
43
47
  columns: [
44
48
  {dataField: 'user.firstname', text: 'Firstname'},
45
49
  {dataField: 'user.lastname', text: 'Lastname'},
46
50
  {dataField: 'githubId', text: 'Github Id'},
47
51
  {dataField: 'country', text: 'Country', renderer: 'up.countryRenderer'},
48
52
  {dataField: 'edit', text: 'Edit Action', renderer: 'up.editRenderer'}
49
- ],
50
-
51
- viewConfig: {
52
- highlightModifiedCells: true
53
- }
53
+ ]
54
54
  }]
55
55
  }
56
56
 
@@ -118,7 +118,7 @@ class Viewport extends BaseViewport {
118
118
  windowId
119
119
  }));
120
120
 
121
- me.view.updateDepth = -1;
121
+ me.body.updateDepth = -1;
122
122
 
123
123
  return button.createVdomReference()
124
124
  }
@@ -46,10 +46,10 @@ class MainContainer extends Viewport {
46
46
  }]
47
47
  }, {
48
48
  module : TableContainer,
49
+ bodyConfig : {selectionModel: CellModel},
49
50
  id : 'myTableFilterContainer',
50
51
  showHeaderFilters: true,
51
52
  store : MainStore,
52
- viewConfig : {selectionModel: CellModel},
53
53
  width : '100%',
54
54
  wrapperStyle : {height: '300px'},
55
55
 
@@ -72,8 +72,8 @@ class MainContainer extends Container {
72
72
  }]
73
73
  }, {
74
74
  module : TableContainer,
75
+ bodyConfig: {useRowRecordIds: false},
75
76
  reference : 'table',
76
- viewConfig: {useRowRecordIds: false},
77
77
  width : '100%',
78
78
 
79
79
  columnDefaults: {
@@ -72,8 +72,8 @@ class MainContainer2 extends Container {
72
72
  }]
73
73
  }, {
74
74
  module : TableContainer,
75
+ bodyConfig: {useRowRecordIds: false},
75
76
  reference : 'table',
76
- viewConfig: {useRowRecordIds: false},
77
77
  width : '100%',
78
78
 
79
79
  columnDefaults: {
@@ -72,8 +72,8 @@ class MainContainer3 extends Container {
72
72
  }]
73
73
  }, {
74
74
  module : TableContainer,
75
+ bodyConfig : {useRowRecordIds: false},
75
76
  reference : 'table',
76
- viewConfig : {useRowRecordIds: false},
77
77
  useCustomScrollbars: false,
78
78
  width : '100%',
79
79
 
@@ -146,7 +146,7 @@ class MainContainer3 extends Container {
146
146
  rows = me.getReference('amount-rows-field').value,
147
147
  inputData = me.up('viewport').createRandomData(columns, rows);
148
148
 
149
- table.view.createViewData(inputData)
149
+ table.body.createViewData(inputData)
150
150
  }
151
151
 
152
152
  /**
@@ -151,10 +151,10 @@ class MainContainer extends Viewport {
151
151
  }]
152
152
  }, {
153
153
  module : TableContainer,
154
+ bodyConfig : {selectionModel: CellModel},
154
155
  id : 'myTableStoreContainer',
155
156
  reference : 'table',
156
157
  store : MainStore,
157
- viewConfig : {selectionModel: CellModel},
158
158
  width : '100%',
159
159
  wrapperStyle: {height: '300px'},
160
160
 
@@ -179,7 +179,7 @@ class MainContainer extends Viewport {
179
179
  */
180
180
  updateSelectionModel(data) {
181
181
  if (data.value) {
182
- this.getReference('table').view.selectionModel = {
182
+ this.getReference('table').body.selectionModel = {
183
183
  ntype: data.component.value
184
184
  }
185
185
  }
@@ -41,7 +41,7 @@ You can also launch the preview in a window by going to the Preview tab, then cl
41
41
  icon on the right <span class="far fa-xs fa-window-maximize"></span>. This web site is a Neo.mjs application,
42
42
  and the ability to launch browser windows &mdash; all integrated within a single app &mdash; is a unique feature of Neo.mjs!
43
43
 
44
- <pre data-code-livepreview>
44
+ ```javascript live-preview
45
45
  import Button from '../button/Base.mjs';
46
46
  import Container from '../container/Base.mjs';
47
47
 
@@ -57,7 +57,7 @@ class MainView extends Container {
57
57
  }
58
58
 
59
59
  MainView = Neo.setupClass(MainView);
60
- </pre>
60
+ ```
61
61
 
62
62
  ---
63
63
 
@@ -10,7 +10,7 @@ and nested approach to their configuration, a gap that a class config system aim
10
10
  ## A bad example
11
11
  I recently found this Angular code snippet (new public API draft) on LinkedIn:
12
12
 
13
- <pre data-code-readonly>
13
+ ```javascript readonly
14
14
  // MyComponent with an attribute
15
15
  <MyComponent myAttribute="someValue" />
16
16
 
@@ -25,7 +25,7 @@ I recently found this Angular code snippet (new public API draft) on LinkedIn:
25
25
 
26
26
  // Scoped inputs for MyDirective
27
27
  <MyComponent @MyDirective(input1="someString" [input2]="mySignal()") />
28
- </pre>
28
+ ```
29
29
 
30
30
  Now you might wonder why I think that this is not a good way to create apps.
31
31