neo.mjs 10.0.0-alpha.5 → 10.0.0-beta.2
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/ServiceWorker.mjs +2 -2
- package/apps/colors/view/GridContainer.mjs +1 -1
- package/apps/covid/view/AttributionComponent.mjs +1 -1
- package/apps/covid/view/HeaderContainer.mjs +6 -6
- package/apps/covid/view/MainContainerController.mjs +5 -5
- package/apps/covid/view/TableContainerController.mjs +1 -1
- package/apps/covid/view/country/Gallery.mjs +13 -13
- package/apps/covid/view/country/Helix.mjs +13 -13
- package/apps/covid/view/country/HistoricalDataTable.mjs +1 -1
- package/apps/email/view/Viewport.mjs +2 -2
- package/apps/form/view/SideNavList.mjs +1 -1
- package/apps/portal/index.html +1 -1
- package/apps/portal/resources/data/examples_devmode.json +26 -27
- package/apps/portal/resources/data/examples_dist_dev.json +26 -27
- package/apps/portal/resources/data/examples_dist_esm.json +25 -26
- package/apps/portal/resources/data/examples_dist_prod.json +26 -27
- package/apps/portal/view/HeaderToolbar.mjs +3 -3
- package/apps/portal/view/about/Container.mjs +2 -2
- package/apps/portal/view/about/MemberContainer.mjs +3 -3
- package/apps/portal/view/blog/List.mjs +7 -7
- package/apps/portal/view/examples/List.mjs +4 -4
- package/apps/portal/view/home/ContentBox.mjs +2 -2
- package/apps/portal/view/home/FeatureSection.mjs +3 -3
- package/apps/portal/view/home/FooterContainer.mjs +7 -7
- package/apps/portal/view/home/parts/AfterMath.mjs +3 -3
- package/apps/portal/view/home/parts/MainNeo.mjs +3 -3
- package/apps/portal/view/home/parts/References.mjs +6 -6
- package/apps/portal/view/learn/ContentComponent.mjs +102 -111
- package/apps/portal/view/learn/PageSectionsContainer.mjs +1 -1
- package/apps/portal/view/learn/PageSectionsList.mjs +2 -2
- package/apps/portal/view/services/Component.mjs +16 -16
- package/apps/realworld/view/FooterComponent.mjs +1 -1
- package/apps/realworld/view/HeaderComponent.mjs +8 -8
- package/apps/realworld/view/HomeComponent.mjs +6 -6
- package/apps/realworld/view/article/CommentComponent.mjs +4 -4
- package/apps/realworld/view/article/Component.mjs +14 -14
- package/apps/realworld/view/article/CreateCommentComponent.mjs +3 -3
- package/apps/realworld/view/article/CreateComponent.mjs +3 -3
- package/apps/realworld/view/article/PreviewComponent.mjs +1 -1
- package/apps/realworld/view/article/TagListComponent.mjs +2 -2
- package/apps/realworld/view/user/ProfileComponent.mjs +8 -8
- package/apps/realworld/view/user/SettingsComponent.mjs +4 -4
- package/apps/realworld/view/user/SignUpComponent.mjs +4 -4
- package/apps/realworld2/view/FooterComponent.mjs +1 -1
- package/apps/realworld2/view/HomeContainer.mjs +3 -3
- package/apps/realworld2/view/article/DetailsContainer.mjs +1 -1
- package/apps/realworld2/view/article/PreviewComponent.mjs +7 -7
- package/apps/realworld2/view/article/TagListComponent.mjs +2 -2
- package/apps/realworld2/view/user/ProfileContainer.mjs +1 -1
- package/apps/route/view/center/CardAdministration.mjs +2 -2
- package/apps/route/view/center/CardAdministrationDenied.mjs +1 -1
- package/apps/route/view/center/CardContact.mjs +2 -2
- package/apps/route/view/center/CardHome.mjs +1 -1
- package/apps/route/view/center/CardSection1.mjs +1 -1
- package/apps/route/view/center/CardSection2.mjs +1 -1
- package/apps/sharedcovid/view/AttributionComponent.mjs +1 -1
- package/apps/sharedcovid/view/HeaderContainer.mjs +6 -6
- package/apps/sharedcovid/view/MainContainerController.mjs +5 -5
- package/apps/sharedcovid/view/TableContainerController.mjs +1 -1
- package/apps/sharedcovid/view/country/Gallery.mjs +13 -13
- package/apps/sharedcovid/view/country/Helix.mjs +13 -13
- package/apps/sharedcovid/view/country/HistoricalDataTable.mjs +1 -1
- package/apps/shareddialog/childapps/shareddialog2/view/MainContainer.mjs +1 -1
- package/apps/shareddialog/view/MainContainer.mjs +1 -1
- package/buildScripts/createApp.mjs +2 -2
- package/examples/table/cellEditing/MainContainer.mjs +1 -1
- package/examples/table/container/MainContainer.mjs +3 -3
- package/examples/table/nestedRecordFields/Viewport.mjs +6 -6
- package/examples/tableFiltering/MainContainer.mjs +1 -1
- package/examples/tablePerformance/MainContainer.mjs +1 -1
- package/examples/tablePerformance/MainContainer2.mjs +1 -1
- package/examples/tablePerformance/MainContainer3.mjs +2 -2
- package/examples/tableStore/MainContainer.mjs +2 -2
- package/learn/Glossary.md +261 -0
- package/learn/UsingTheseTopics.md +2 -2
- package/learn/benefits/ConfigSystem.md +538 -28
- package/learn/benefits/Effort.md +47 -2
- package/learn/benefits/Features.md +50 -32
- package/learn/benefits/FormsEngine.md +68 -38
- package/learn/benefits/MultiWindow.md +33 -7
- package/learn/benefits/OffTheMainThread.md +2 -2
- package/learn/benefits/Quick.md +45 -12
- package/learn/benefits/RPCLayer.md +75 -0
- package/learn/benefits/Speed.md +16 -11
- package/learn/gettingstarted/ComponentModels.md +4 -4
- package/learn/gettingstarted/Config.md +6 -6
- package/learn/gettingstarted/DescribingTheUI.md +4 -4
- package/learn/gettingstarted/Events.md +6 -6
- package/learn/gettingstarted/Extending.md +4 -4
- package/learn/gettingstarted/References.md +6 -6
- package/learn/gettingstarted/Workspaces.md +6 -6
- package/learn/guides/ApplicationBootstrap.md +26 -26
- package/learn/guides/ComponentsAndContainers.md +12 -12
- package/learn/guides/ConfigSystemDeepDive.md +280 -0
- package/learn/guides/CustomComponents.md +2 -2
- package/learn/guides/DeclarativeComponentTreesVsImperativeVdom.md +17 -17
- package/learn/guides/InstanceLifecycle.md +295 -1
- package/learn/guides/MainThreadAddons.md +475 -0
- package/learn/guides/PortalApp.md +2 -2
- package/learn/guides/StateProviders.md +12 -12
- package/learn/guides/WorkingWithVDom.md +14 -14
- package/learn/guides/events/CustomEvents.md +16 -16
- package/learn/guides/events/DomEvents.md +12 -12
- package/learn/javascript/ClassFeatures.md +3 -2
- package/learn/javascript/Classes.md +8 -8
- package/learn/javascript/NewNode.md +4 -4
- package/learn/javascript/Overrides.md +8 -8
- package/learn/javascript/Super.md +10 -8
- package/learn/tree.json +52 -51
- package/learn/tutorials/Earthquakes.md +54 -57
- package/learn/tutorials/TodoList.md +4 -4
- package/package.json +2 -2
- package/resources/scss/src/apps/portal/learn/ContentComponent.scss +12 -0
- package/resources/scss/src/table/{View.scss → Body.scss} +1 -1
- package/resources/scss/src/table/plugin/CellEditing.scss +1 -1
- package/resources/scss/theme-dark/table/{View.scss → Body.scss} +1 -1
- package/resources/scss/theme-light/table/{View.scss → Body.scss} +1 -1
- package/resources/scss/theme-neo-light/Global.scss +1 -2
- package/resources/scss/theme-neo-light/table/{View.scss → Body.scss} +1 -1
- package/src/DefaultConfig.mjs +2 -2
- package/src/Main.mjs +8 -7
- package/src/Neo.mjs +16 -2
- package/src/button/Base.mjs +2 -2
- package/src/calendar/view/SettingsContainer.mjs +2 -2
- package/src/calendar/view/YearComponent.mjs +9 -9
- package/src/calendar/view/calendars/ColorsList.mjs +1 -1
- package/src/calendar/view/calendars/List.mjs +1 -1
- package/src/calendar/view/month/Component.mjs +15 -15
- package/src/calendar/view/week/Component.mjs +12 -12
- package/src/calendar/view/week/EventDragZone.mjs +4 -4
- package/src/calendar/view/week/TimeAxisComponent.mjs +3 -3
- package/src/component/Base.mjs +17 -2
- package/src/component/Carousel.mjs +2 -2
- package/src/component/Chip.mjs +3 -3
- package/src/component/Circle.mjs +2 -2
- package/src/component/DateSelector.mjs +8 -8
- package/src/component/Helix.mjs +1 -1
- package/src/component/Label.mjs +3 -18
- package/src/component/Legend.mjs +3 -3
- package/src/component/MagicMoveText.mjs +6 -14
- package/src/component/Process.mjs +3 -3
- package/src/component/Progress.mjs +1 -1
- package/src/component/StatusBadge.mjs +2 -2
- package/src/component/Timer.mjs +2 -2
- package/src/component/Toast.mjs +5 -3
- package/src/container/AccordionItem.mjs +2 -2
- package/src/container/Base.mjs +1 -1
- package/src/core/Base.mjs +77 -14
- package/src/core/Util.mjs +14 -2
- package/src/date/DayViewComponent.mjs +2 -2
- package/src/date/SelectorContainer.mjs +1 -1
- package/src/draggable/grid/header/toolbar/SortZone.mjs +21 -21
- package/src/draggable/table/header/toolbar/SortZone.mjs +1 -1
- package/src/form/field/CheckBox.mjs +4 -4
- package/src/form/field/FileUpload.mjs +25 -39
- package/src/form/field/Range.mjs +1 -1
- package/src/form/field/Text.mjs +3 -3
- package/src/form/field/TextArea.mjs +2 -3
- package/src/grid/Body.mjs +8 -5
- package/src/grid/_export.mjs +1 -1
- package/src/list/Color.mjs +2 -2
- package/src/main/DeltaUpdates.mjs +157 -98
- package/src/main/addon/AmCharts.mjs +61 -84
- package/src/main/addon/Base.mjs +161 -42
- package/src/main/addon/GoogleMaps.mjs +9 -16
- package/src/main/addon/HighlightJS.mjs +2 -13
- package/src/main/addon/IntersectionObserver.mjs +21 -21
- package/src/main/addon/MonacoEditor.mjs +32 -64
- package/src/manager/ClassHierarchy.mjs +114 -0
- package/src/menu/List.mjs +1 -1
- package/src/plugin/Popover.mjs +2 -2
- package/src/sitemap/Component.mjs +1 -1
- package/src/table/{View.mjs → Body.mjs} +25 -22
- package/src/table/Container.mjs +43 -43
- package/src/table/_export.mjs +2 -2
- package/src/table/plugin/CellEditing.mjs +19 -19
- package/src/tooltip/Base.mjs +1 -6
- package/src/tree/Accordion.mjs +3 -3
- package/src/vdom/Helper.mjs +19 -22
- package/src/worker/App.mjs +1 -2
- package/src/worker/Base.mjs +7 -5
- package/src/worker/Canvas.mjs +2 -3
- package/src/worker/Data.mjs +5 -7
- package/src/worker/Task.mjs +2 -3
- package/src/worker/VDom.mjs +3 -4
- package/src/worker/mixin/RemoteMethodAccess.mjs +5 -2
- package/learn/guides/MainThreadAddonExample.md +0 -15
- package/learn/guides/MainThreadAddonIntro.md +0 -44
@@ -37,6 +37,18 @@ class AmCharts extends Base {
|
|
37
37
|
* @protected
|
38
38
|
*/
|
39
39
|
fallbackPath: 'https://raw.githubusercontent.com/neomjs/pages/main/resources_pub/amCharts/',
|
40
|
+
/**
|
41
|
+
* List methods which must get cached until the addon reaches its `isReady` state
|
42
|
+
* @member {String[]} interceptRemotes
|
43
|
+
*/
|
44
|
+
interceptRemotes: [
|
45
|
+
'callMethod',
|
46
|
+
'create',
|
47
|
+
'destroy',
|
48
|
+
'setProperties',
|
49
|
+
'setProperty',
|
50
|
+
'updateData'
|
51
|
+
],
|
40
52
|
/**
|
41
53
|
* Remote method access for other workers
|
42
54
|
* @member {Object} remote
|
@@ -91,19 +103,15 @@ class AmCharts extends Base {
|
|
91
103
|
callMethod(data) {
|
92
104
|
let me = this;
|
93
105
|
|
94
|
-
if (
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
pathArray = data.path.split('.'),
|
100
|
-
methodName = pathArray.pop(),
|
101
|
-
scope = pathArray.length < 1 ? chart: Neo.ns(pathArray.join('.'), false, chart);
|
106
|
+
if (me.hasChart(data.id)) {
|
107
|
+
let chart = me.charts[data.id],
|
108
|
+
pathArray = data.path.split('.'),
|
109
|
+
methodName = pathArray.pop(),
|
110
|
+
scope = pathArray.length < 1 ? chart: Neo.ns(pathArray.join('.'), false, chart);
|
102
111
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
}
|
112
|
+
scope[methodName].call(scope, ...data.params || [])
|
113
|
+
} else {
|
114
|
+
// todo
|
107
115
|
}
|
108
116
|
}
|
109
117
|
|
@@ -137,29 +145,25 @@ class AmCharts extends Base {
|
|
137
145
|
create(data) {
|
138
146
|
let me = this;
|
139
147
|
|
140
|
-
if
|
141
|
-
|
142
|
-
} else {
|
143
|
-
// todo: check if globalThis[data.package] exists, if not load it and call create afterwards
|
144
|
-
am4core.useTheme(am4themes_dark);
|
148
|
+
// todo: check if globalThis[data.package] exists, if not load it and call create afterwards
|
149
|
+
am4core.useTheme(am4themes_dark);
|
145
150
|
|
146
|
-
|
151
|
+
me.charts[data.id] = am4core.createFromConfig(data.config, data.id, globalThis[data.package][data.type || 'XYChart']);
|
147
152
|
|
148
|
-
|
149
|
-
|
150
|
-
|
153
|
+
if (data.combineSeriesTooltip) {
|
154
|
+
me.combineSeriesTooltip(me.charts[data.id])
|
155
|
+
}
|
151
156
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
}
|
157
|
+
// in case data has arrived before the chart got created, apply it now
|
158
|
+
if (data.data) {
|
159
|
+
me.updateData({
|
160
|
+
data : data.data,
|
161
|
+
dataPath: data.dataPath,
|
162
|
+
id : data.id
|
163
|
+
})
|
164
|
+
} else if (me.dataMap[data.id]) {
|
165
|
+
me.updateData(me.dataMap[data.id]);
|
166
|
+
delete me.dataMap[data.id]
|
163
167
|
}
|
164
168
|
}
|
165
169
|
|
@@ -167,15 +171,9 @@ class AmCharts extends Base {
|
|
167
171
|
* @param {Object} data
|
168
172
|
* @param {String} data.id
|
169
173
|
*/
|
170
|
-
destroy(
|
171
|
-
|
172
|
-
|
173
|
-
if (!me.isReady) {
|
174
|
-
return me.cacheMethodCall({fn: 'destroy', data})
|
175
|
-
} else {
|
176
|
-
me.charts[data.id]?.dispose?.();
|
177
|
-
delete me.charts[data.id]
|
178
|
-
}
|
174
|
+
destroy({id}) {
|
175
|
+
this.charts[id]?.dispose?.();
|
176
|
+
delete this.charts[id]
|
179
177
|
}
|
180
178
|
|
181
179
|
/**
|
@@ -192,7 +190,7 @@ class AmCharts extends Base {
|
|
192
190
|
* => fetching the other files after core.js is loaded
|
193
191
|
* @param {Boolean} useFallback=false
|
194
192
|
*/
|
195
|
-
loadFiles(useFallback=false) {
|
193
|
+
async loadFiles(useFallback=false) {
|
196
194
|
let me = this,
|
197
195
|
useFallbackPath = me.useFallbackPath || useFallback,
|
198
196
|
basePath;
|
@@ -207,24 +205,21 @@ class AmCharts extends Base {
|
|
207
205
|
basePath = useFallbackPath ? me.fallbackPath : me.downloadPath
|
208
206
|
}
|
209
207
|
|
210
|
-
|
208
|
+
try {
|
209
|
+
await DomAccess.loadScript(basePath + 'core.js');
|
211
210
|
|
212
|
-
|
213
|
-
Promise.all([
|
211
|
+
await Promise.all([
|
214
212
|
DomAccess.loadScript(basePath + 'charts.js'),
|
215
213
|
DomAccess.loadScript(basePath + 'maps.js'),
|
216
214
|
DomAccess.loadScript(basePath + 'themes/dark.js'),
|
217
215
|
DomAccess.loadScript(basePath + 'geodata/worldLow.js')
|
218
|
-
])
|
219
|
-
|
220
|
-
me.isReady = true
|
221
|
-
})
|
222
|
-
}).catch(e => {
|
216
|
+
])
|
217
|
+
} catch(e) {
|
223
218
|
if (!useFallback && !me.useFallbackPath) {
|
224
219
|
console.log('Download from amcharts.com failed, switching to fallback', e);
|
225
|
-
me.loadFiles(true)
|
220
|
+
await me.loadFiles(true)
|
226
221
|
}
|
227
|
-
}
|
222
|
+
}
|
228
223
|
}
|
229
224
|
|
230
225
|
/**
|
@@ -232,20 +227,10 @@ class AmCharts extends Base {
|
|
232
227
|
* @param {String} data.id
|
233
228
|
* @param {Object} data.properties
|
234
229
|
*/
|
235
|
-
setProperties(
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
return me.cacheMethodCall({fn: 'setProperties', data})
|
240
|
-
} else {
|
241
|
-
Object.entries(data.properties).forEach(([key, value]) => {
|
242
|
-
me.setProperty({
|
243
|
-
id : data.id,
|
244
|
-
path : key,
|
245
|
-
value
|
246
|
-
})
|
247
|
-
})
|
248
|
-
}
|
230
|
+
setProperties({id, properties}) {
|
231
|
+
Object.entries(properties).forEach(([key, value]) => {
|
232
|
+
this.setProperty({id, path: key, value})
|
233
|
+
})
|
249
234
|
}
|
250
235
|
|
251
236
|
/**
|
@@ -255,22 +240,16 @@ class AmCharts extends Base {
|
|
255
240
|
* @param {String} data.path
|
256
241
|
* @param {*} data.value
|
257
242
|
*/
|
258
|
-
setProperty(
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
243
|
+
setProperty({id, isColor=false, path, value}) {
|
244
|
+
if (this.hasChart(id)) {
|
245
|
+
let chart = this.charts[id],
|
246
|
+
pathArray = path.split('.'),
|
247
|
+
propertyName = pathArray.pop(),
|
248
|
+
scope = Neo.ns(pathArray.join('.'), false, chart);
|
249
|
+
|
250
|
+
scope[propertyName] = isColor ? am4core.color(value) : value
|
263
251
|
} else {
|
264
|
-
|
265
|
-
let chart = this.charts[data.id],
|
266
|
-
pathArray = data.path.split('.'),
|
267
|
-
propertyName = pathArray.pop(),
|
268
|
-
scope = Neo.ns(pathArray.join('.'), false, chart);
|
269
|
-
|
270
|
-
scope[propertyName] = data.isColor ? am4core.color(data.value) : data.value
|
271
|
-
} else {
|
272
|
-
// todo
|
273
|
-
}
|
252
|
+
// todo
|
274
253
|
}
|
275
254
|
}
|
276
255
|
|
@@ -283,9 +262,7 @@ class AmCharts extends Base {
|
|
283
262
|
updateData(data) {
|
284
263
|
let me = this;
|
285
264
|
|
286
|
-
if (!me.
|
287
|
-
return me.cacheMethodCall({fn: 'updateData', data})
|
288
|
-
} else if (!me.hasChart(data.id)) {
|
265
|
+
if (!me.hasChart(data.id)) {
|
289
266
|
me.dataMap[data.id] = data
|
290
267
|
} else {
|
291
268
|
let chart = me.charts[data.id];
|
package/src/main/addon/Base.mjs
CHANGED
@@ -4,6 +4,11 @@ import CoreBase from '../../core/Base.mjs';
|
|
4
4
|
* Base class for main thread addons
|
5
5
|
* @class Neo.main.addon.Base
|
6
6
|
* @extends Neo.core.Base
|
7
|
+
*
|
8
|
+
* This version aligns the file loading and readiness state according to the rule:
|
9
|
+
* `initAsync()` MUST await for `loadFiles()` to be completed before the addon is considered `isReady`.
|
10
|
+
* `preloadFilesDelay` controls when `loadFiles()` is initiated in the background, but can be
|
11
|
+
* overridden by `cacheMethodCall()`.
|
7
12
|
*/
|
8
13
|
class Base extends CoreBase {
|
9
14
|
static config = {
|
@@ -19,34 +24,39 @@ class Base extends CoreBase {
|
|
19
24
|
*/
|
20
25
|
isMainThreadAddon: true,
|
21
26
|
/**
|
22
|
-
*
|
23
|
-
*
|
24
|
-
*
|
25
|
-
*/
|
26
|
-
isReady_: false,
|
27
|
-
/**
|
28
|
-
* Amount in ms to delay the loading of library files, unless remote method access happens
|
29
|
-
* Change the value to false in case you don't want an automated preloading
|
27
|
+
* Amount in ms to delay the background loading of library files.
|
28
|
+
* Set to `false` to disable automated preloading and rely solely on lazy loading
|
29
|
+
* via `cacheMethodCall()`. Set to `0` for immediate background preload.
|
30
30
|
* @member {Boolean|Number} preloadFilesDelay=5000
|
31
31
|
* @protected
|
32
32
|
*/
|
33
|
-
preloadFilesDelay: 5000
|
33
|
+
preloadFilesDelay: 5000
|
34
34
|
}
|
35
35
|
|
36
36
|
/**
|
37
|
+
* Internal cache for remote method calls received when `isReady` is false.
|
37
38
|
* @member {Object[]} cache=[]
|
38
39
|
*/
|
39
40
|
cache = []
|
40
41
|
/**
|
41
|
-
*
|
42
|
-
* @member {Boolean} isLoading
|
42
|
+
* Returns true if `loadFiles()` has been initiated and is currently in progress.
|
43
|
+
* @member {Boolean} isLoading
|
43
44
|
*/
|
44
|
-
isLoading
|
45
|
+
get isLoading() {
|
46
|
+
// isLoading is true if the promise exists and its resolver is still available (meaning it's pending).
|
47
|
+
return !!this.#loadFilesPromise && !!this.#loadFilesPromiseResolver
|
48
|
+
}
|
45
49
|
/**
|
46
|
-
*
|
47
|
-
*
|
50
|
+
* A private promise that tracks the completion of `loadFiles()`.
|
51
|
+
* This ensures `loadFiles()` is called only once and can be awaited by multiple consumers.
|
52
|
+
* @member {Promise<void>|null} #loadFilesPromise=null
|
48
53
|
*/
|
49
|
-
|
54
|
+
#loadFilesPromise = null
|
55
|
+
/**
|
56
|
+
* The `resolve` function for `#loadFilesPromise`, allowing external control over its resolution.
|
57
|
+
* @member {Function|null} #loadFilesPromiseResolver=null
|
58
|
+
*/
|
59
|
+
#loadFilesPromiseResolver = null
|
50
60
|
|
51
61
|
/**
|
52
62
|
* @param {Object} config
|
@@ -56,56 +66,165 @@ class Base extends CoreBase {
|
|
56
66
|
|
57
67
|
let me = this;
|
58
68
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
69
|
+
// Initialize #loadFilesPromise as a controllable promise.
|
70
|
+
// This promise will be awaited by initAsync and resolved by executeLoadFiles.
|
71
|
+
me.#loadFilesPromise = new Promise(resolve => {
|
72
|
+
me.#loadFilesPromiseResolver = resolve
|
73
|
+
});
|
74
|
+
|
75
|
+
if (me.preloadFilesDelay === false) {
|
76
|
+
// No automated preload: resolve #loadFilesPromise immediately as it won't be triggered by delay.
|
77
|
+
// It will only be triggered by cacheMethodCall or initAsync if needed.
|
78
|
+
me.#loadFilesPromiseResolver();
|
79
|
+
me.#loadFilesPromiseResolver = null // Mark as resolved/no longer pending
|
80
|
+
} else {
|
81
|
+
const delay = Neo.isNumber(me.preloadFilesDelay) ? me.preloadFilesDelay : 0;
|
82
|
+
|
83
|
+
if (delay === 0) {
|
84
|
+
// Immediate preload: Directly execute loadFiles and resolve the promise.
|
85
|
+
me.#executeLoadFiles()
|
86
|
+
} else {
|
87
|
+
// Delayed preload: Set up a timer to execute loadFiles later.
|
88
|
+
me.timeout(delay).then(() => {
|
89
|
+
// This callback checks if #loadFilesPromise is still pending (resolver is available).
|
90
|
+
if (me.#loadFilesPromiseResolver) {
|
91
|
+
me.#executeLoadFiles()
|
92
|
+
}
|
93
|
+
})
|
66
94
|
}
|
67
95
|
}
|
68
96
|
}
|
69
97
|
|
70
98
|
/**
|
71
|
-
*
|
99
|
+
* Executes the actual `loadFiles()` method and resolves `#loadFilesPromise`.
|
100
|
+
* This method is called internally to manage the single execution of `loadFiles()`.
|
101
|
+
* It ensures `loadFiles()` is only truly called once.
|
102
|
+
* @private
|
103
|
+
*/
|
104
|
+
async #executeLoadFiles() {
|
105
|
+
let me = this;
|
106
|
+
|
107
|
+
// Only execute if the promise is still pending (resolver is available).
|
108
|
+
if (me.#loadFilesPromiseResolver) {
|
109
|
+
const resolver = me.#loadFilesPromiseResolver;
|
110
|
+
me.#loadFilesPromiseResolver = null; // Mark as no longer pending/resolved
|
111
|
+
|
112
|
+
await me.loadFiles();
|
113
|
+
resolver() // Resolve the main #loadFilesPromise
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
/**
|
118
|
+
* Async initialization hook for instances.
|
119
|
+
* `initAsync` MUST await for `loadFiles()` to be completed. Only then the addon is ready.
|
120
|
+
* @returns {Promise<void>}
|
121
|
+
*/
|
122
|
+
async initAsync() {
|
123
|
+
await super.initAsync();
|
124
|
+
|
125
|
+
let me = this;
|
126
|
+
|
127
|
+
// `initAsync` must always wait for `me.#loadFilesPromise` to complete its resolution,
|
128
|
+
// regardless of how it was triggered (immediate, delayed, or by cacheMethodCall).
|
129
|
+
// `me.#loadFilesPromise` is always initialized in `construct()`.
|
130
|
+
await me.#loadFilesPromise
|
131
|
+
}
|
132
|
+
|
133
|
+
/**
|
134
|
+
* Triggered after the `isReady` config got changed.
|
135
|
+
* When `isReady` becomes true, any cached remote method calls are executed.
|
136
|
+
* At this point, `initAsync` has already ensured that `me.#loadFilesPromise` is resolved.
|
137
|
+
*
|
138
|
+
* This method is kept synchronous, delegating the async cache processing to a private method.
|
139
|
+
*
|
72
140
|
* @param {Boolean} value
|
73
141
|
* @param {Boolean} oldValue
|
74
142
|
* @protected
|
75
143
|
*/
|
76
|
-
afterSetIsReady(value, oldValue) {
|
144
|
+
afterSetIsReady(value, oldValue) { // Keep this synchronous
|
77
145
|
if (value) {
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
me.cache.forEach(item => {
|
82
|
-
returnValue = me[item.fn](item.data);
|
83
|
-
item.resolve(returnValue)
|
84
|
-
});
|
85
|
-
|
86
|
-
me.cache = []
|
146
|
+
// Initiate the asynchronous processing of cached method calls.
|
147
|
+
// This method itself does not need to be awaited here.
|
148
|
+
this.#processCachedMethodCalls();
|
87
149
|
}
|
88
150
|
}
|
89
151
|
|
90
152
|
/**
|
91
|
-
* Internally caches
|
92
|
-
*
|
93
|
-
* @param item
|
94
|
-
* @returns {Promise<unknown>}
|
153
|
+
* Internally caches remote method calls if `isReady` is false.
|
154
|
+
* It also ensures that `loadFiles()` is initiated immediately, bypassing `preloadFilesDelay`.
|
155
|
+
* @param {Object} item - Contains method name (`fn`) and data (`data`).
|
156
|
+
* @returns {Promise<unknown>} A promise that resolves with the method's return value.
|
95
157
|
*/
|
96
158
|
cacheMethodCall(item) {
|
97
159
|
let me = this;
|
98
160
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
me
|
161
|
+
// If loadFiles is defined, and it hasn't started yet (i.e., #loadFilesPromiseResolver is still available),
|
162
|
+
// execute it now, bypassing any pending preloadFilesDelay timer.
|
163
|
+
if (me.#loadFilesPromiseResolver) {
|
164
|
+
me.#executeLoadFiles() // This will resolve #loadFilesPromise immediately
|
103
165
|
}
|
104
166
|
|
105
167
|
return new Promise((resolve, reject) => {
|
106
|
-
me.cache.push({...item, resolve})
|
168
|
+
me.cache.push({...item, reject, resolve})
|
107
169
|
})
|
108
170
|
}
|
171
|
+
|
172
|
+
/**
|
173
|
+
* Placeholder method for loading external files.
|
174
|
+
* Subclasses (e.g., `Neo.main.addon.AmCharts`) must implement this.
|
175
|
+
* It **must** return a Promise that resolves when all necessary files are loaded.
|
176
|
+
* If `loadFiles()` is called multiple times, it should return the same pending promise
|
177
|
+
* or a resolved promise if files are already loaded.
|
178
|
+
* @returns {Promise<void>}
|
179
|
+
*/
|
180
|
+
async loadFiles() {}
|
181
|
+
|
182
|
+
/**
|
183
|
+
* Handles intercepted remote method calls.
|
184
|
+
* If the addon is not ready, the call is cached using `cacheMethodCall()`.
|
185
|
+
* Otherwise, the original method is executed.
|
186
|
+
* @param {Object} msg The remote message object.
|
187
|
+
* @returns {Promise<any>} A promise that resolves with the method's return value.
|
188
|
+
*/
|
189
|
+
onInterceptRemotes(msg) {
|
190
|
+
return this.cacheMethodCall({fn: msg.remoteMethod, data: msg.data})
|
191
|
+
}
|
192
|
+
|
193
|
+
/**
|
194
|
+
* Sequentially processes any method calls that were cached while the addon was not ready.
|
195
|
+
* This method is asynchronous to allow awaiting the execution of individual cached methods.
|
196
|
+
* @returns {Promise<void>} A promise that resolves when all cached methods have been processed.
|
197
|
+
* @private
|
198
|
+
*/
|
199
|
+
async #processCachedMethodCalls() {
|
200
|
+
let me = this;
|
201
|
+
|
202
|
+
// Iterate over the cache items and await each one in sequence
|
203
|
+
for (const item of me.cache) {
|
204
|
+
let returnValue;
|
205
|
+
|
206
|
+
try {
|
207
|
+
returnValue = me[item.fn](item.data);
|
208
|
+
|
209
|
+
if (Neo.isPromise(returnValue)) {
|
210
|
+
returnValue = await returnValue;
|
211
|
+
}
|
212
|
+
|
213
|
+
item.resolve(returnValue)
|
214
|
+
} catch (e) {
|
215
|
+
// If an error occurs (either synchronous or a promise rejection),
|
216
|
+
// reject the promise associated with the current cached item.
|
217
|
+
item.reject(e)
|
218
|
+
|
219
|
+
// *** FAIL-FAST STRATEGY ***
|
220
|
+
// If any cached method call fails, we assume subsequent cached calls
|
221
|
+
// (especially for the same addon instance) are likely to also fail.
|
222
|
+
break
|
223
|
+
}
|
224
|
+
}
|
225
|
+
|
226
|
+
me.cache = []
|
227
|
+
}
|
109
228
|
}
|
110
229
|
|
111
230
|
export default Neo.setupClass(Base);
|
@@ -8,16 +8,19 @@ import Observable from '../../core/Observable.mjs';
|
|
8
8
|
* @extends Neo.main.addon.Base
|
9
9
|
*/
|
10
10
|
class GoogleMaps extends Base {
|
11
|
+
/**
|
12
|
+
* True automatically applies the core.Observable mixin
|
13
|
+
* @member {Boolean} observable=true
|
14
|
+
* @static
|
15
|
+
*/
|
16
|
+
static observable = true
|
17
|
+
|
11
18
|
static config = {
|
12
19
|
/**
|
13
20
|
* @member {String} className='Neo.main.addon.GoogleMaps'
|
14
21
|
* @protected
|
15
22
|
*/
|
16
23
|
className: 'Neo.main.addon.GoogleMaps',
|
17
|
-
/**
|
18
|
-
* @member {Neo.core.Base[]} mixins=[Observable]
|
19
|
-
*/
|
20
|
-
mixins: [Observable],
|
21
24
|
/**
|
22
25
|
* @member {Object} remote
|
23
26
|
* @protected
|
@@ -52,14 +55,6 @@ class GoogleMaps extends Base {
|
|
52
55
|
*/
|
53
56
|
markers = {}
|
54
57
|
|
55
|
-
/**
|
56
|
-
* @param {Object} config
|
57
|
-
*/
|
58
|
-
construct(config) {
|
59
|
-
super.construct(config);
|
60
|
-
this.loadApi()
|
61
|
-
}
|
62
|
-
|
63
58
|
/**
|
64
59
|
* @param {Object} data
|
65
60
|
* @param {Object} [data.anchorPoint] x & y
|
@@ -176,13 +171,11 @@ class GoogleMaps extends Base {
|
|
176
171
|
/**
|
177
172
|
* @protected
|
178
173
|
*/
|
179
|
-
|
174
|
+
async loadFiles() {
|
180
175
|
let key = Neo.config.googleMapsApiKey,
|
181
176
|
url = ' https://maps.googleapis.com/maps/api/js';
|
182
177
|
|
183
|
-
DomAccess.loadScript(`${url}?key=${key}&v=weekly&callback=Neo.emptyFn`)
|
184
|
-
console.log('GoogleMaps API loaded')
|
185
|
-
})
|
178
|
+
await DomAccess.loadScript(`${url}?key=${key}&v=weekly&callback=Neo.emptyFn`)
|
186
179
|
}
|
187
180
|
|
188
181
|
/**
|
@@ -68,17 +68,11 @@ class HighlightJS extends Base {
|
|
68
68
|
|
69
69
|
/**
|
70
70
|
* @param {Object} data
|
71
|
-
* @returns {
|
71
|
+
* @returns {Promise<void>}
|
72
72
|
*/
|
73
73
|
async loadFiles(data) {
|
74
74
|
let me = this;
|
75
75
|
|
76
|
-
if (me.isLoading || me.isReady) {
|
77
|
-
return true
|
78
|
-
}
|
79
|
-
|
80
|
-
me.isLoading = true;
|
81
|
-
|
82
76
|
if (data) {
|
83
77
|
delete data.appName;
|
84
78
|
delete data.windowId;
|
@@ -91,12 +85,7 @@ class HighlightJS extends Base {
|
|
91
85
|
await Promise.all([
|
92
86
|
DomAccess.loadScript(me.highlightJsLineNumbersPath),
|
93
87
|
Neo.main.addon.Stylesheet.createStyleSheet(null, 'hljs-theme', me.themePath)
|
94
|
-
])
|
95
|
-
|
96
|
-
me.isLoading = false;
|
97
|
-
me.isReady = true;
|
98
|
-
|
99
|
-
return true
|
88
|
+
])
|
100
89
|
}
|
101
90
|
|
102
91
|
/**
|
@@ -29,17 +29,17 @@ class NeoIntersectionObserver extends Base {
|
|
29
29
|
}
|
30
30
|
|
31
31
|
/**
|
32
|
-
* Storing
|
32
|
+
* Storing component ids and their IntersectionObservers
|
33
33
|
* @member {Object} map={}
|
34
34
|
* @protected
|
35
35
|
*/
|
36
|
-
|
36
|
+
map = {}
|
37
37
|
/**
|
38
|
-
* Storing
|
39
|
-
* @member {Object}
|
38
|
+
* Storing data from observe() calls which arrived prior to register()
|
39
|
+
* @member {Object} observeCache={}
|
40
40
|
* @protected
|
41
41
|
*/
|
42
|
-
|
42
|
+
observeCache = {}
|
43
43
|
|
44
44
|
/**
|
45
45
|
* @param {Object} data
|
@@ -107,12 +107,12 @@ class NeoIntersectionObserver extends Base {
|
|
107
107
|
* {Number} opts.countTargets: amount of found target nodes inside the DOM
|
108
108
|
*/
|
109
109
|
observe(data) {
|
110
|
-
let me
|
111
|
-
|
112
|
-
|
113
|
-
{
|
114
|
-
observer
|
115
|
-
targets
|
110
|
+
let me = this,
|
111
|
+
cached = false,
|
112
|
+
{id, observe} = data,
|
113
|
+
{observeCache} = me,
|
114
|
+
observer = me.map[data.id],
|
115
|
+
targets = [];
|
116
116
|
|
117
117
|
if (!Neo.isArray(observe)) {
|
118
118
|
observe = [observe]
|
@@ -131,11 +131,11 @@ class NeoIntersectionObserver extends Base {
|
|
131
131
|
} else {
|
132
132
|
cached = true;
|
133
133
|
|
134
|
-
if (!
|
135
|
-
|
134
|
+
if (!observeCache[id]) {
|
135
|
+
observeCache[id] = []
|
136
136
|
}
|
137
137
|
|
138
|
-
|
138
|
+
observeCache[id].push(data);
|
139
139
|
}
|
140
140
|
|
141
141
|
return {
|
@@ -157,10 +157,10 @@ class NeoIntersectionObserver extends Base {
|
|
157
157
|
* if data.observe is not passed: true
|
158
158
|
*/
|
159
159
|
register(data) {
|
160
|
-
let me
|
161
|
-
{
|
162
|
-
{id, observe}
|
163
|
-
returnValue
|
160
|
+
let me = this,
|
161
|
+
{observeCache} = me,
|
162
|
+
{id, observe} = data,
|
163
|
+
returnValue = true,
|
164
164
|
observer;
|
165
165
|
|
166
166
|
me.map[id] = observer = new IntersectionObserver(me[data.callback].bind(me), {
|
@@ -175,9 +175,9 @@ class NeoIntersectionObserver extends Base {
|
|
175
175
|
returnValue = me.observe({id, observe})
|
176
176
|
}
|
177
177
|
|
178
|
-
if (
|
179
|
-
|
180
|
-
delete
|
178
|
+
if (observeCache[id]) {
|
179
|
+
observeCache[id].forEach(item => me.observe(item));
|
180
|
+
delete observeCache[id]
|
181
181
|
}
|
182
182
|
|
183
183
|
return returnValue
|