rotor-framework 0.3.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.
Files changed (39) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +120 -0
  3. package/package.json +59 -0
  4. package/src/source/RotorFramework.bs +654 -0
  5. package/src/source/RotorFrameworkTask.bs +278 -0
  6. package/src/source/base/BaseModel.bs +52 -0
  7. package/src/source/base/BasePlugin.bs +48 -0
  8. package/src/source/base/BaseReducer.bs +184 -0
  9. package/src/source/base/BaseStack.bs +92 -0
  10. package/src/source/base/BaseViewModel.bs +124 -0
  11. package/src/source/base/BaseWidget.bs +104 -0
  12. package/src/source/base/DispatcherCreator.bs +193 -0
  13. package/src/source/base/DispatcherExternal.bs +260 -0
  14. package/src/source/base/ListenerForDispatchers.bs +246 -0
  15. package/src/source/engine/Constants.bs +74 -0
  16. package/src/source/engine/animator/Animator.bs +334 -0
  17. package/src/source/engine/builder/Builder.bs +213 -0
  18. package/src/source/engine/builder/NodePool.bs +236 -0
  19. package/src/source/engine/builder/PluginAdapter.bs +139 -0
  20. package/src/source/engine/builder/PostProcessor.bs +331 -0
  21. package/src/source/engine/builder/Processor.bs +156 -0
  22. package/src/source/engine/builder/Tree.bs +278 -0
  23. package/src/source/engine/builder/TreeBase.bs +313 -0
  24. package/src/source/engine/builder/WidgetCreate.bs +322 -0
  25. package/src/source/engine/builder/WidgetRemove.bs +72 -0
  26. package/src/source/engine/builder/WidgetUpdate.bs +113 -0
  27. package/src/source/engine/providers/Dispatcher.bs +72 -0
  28. package/src/source/engine/providers/DispatcherProvider.bs +95 -0
  29. package/src/source/engine/services/I18n.bs +169 -0
  30. package/src/source/libs/animate/Animate.bs +753 -0
  31. package/src/source/libs/animate/LICENSE.txt +21 -0
  32. package/src/source/plugins/DispatcherProviderPlugin.bs +127 -0
  33. package/src/source/plugins/FieldsPlugin.bs +180 -0
  34. package/src/source/plugins/FocusPlugin.bs +1522 -0
  35. package/src/source/plugins/FontStylePlugin.bs +159 -0
  36. package/src/source/plugins/ObserverPlugin.bs +548 -0
  37. package/src/source/utils/ArrayUtils.bs +495 -0
  38. package/src/source/utils/GeneralUtils.bs +181 -0
  39. package/src/source/utils/NodeUtils.bs +180 -0
@@ -0,0 +1,322 @@
1
+ namespace Rotor.ViewBuilder
2
+
3
+ ' =====================================================================
4
+ ' createWidget - Creates and initializes a widget instance. Core widget factory function that handles the complete widget creation lifecycle: 1. Widget tree registration 2. Decorator method injection 3. ViewModel initialization (if applicable) 4. SceneGraph node creation 5. Plugin registration and lifecycle hooks
5
+ '
6
+ ' @param {object} postProcessBuffer - Buffer for deferred plugin/lifecycle operations
7
+ ' @param {object} config - Widget configuration object
8
+ ' @param {string} parentHID - Parent's Hierarchical ID (default: "0" for root)
9
+ ' @returns {object} Widget metadata { HID, children, parentHID, id }
10
+ ' =====================================================================
11
+ function createWidget(postProcessBuffer as object, config as object, parentHID = "0" as string) as object
12
+
13
+ ' Extract and remove ViewModel class from config
14
+ ViewModelClass = config.viewModel
15
+ config.delete("viewModel")
16
+
17
+ ' =============================================================
18
+ ' WIDGET TREE REGISTRATION
19
+ ' =============================================================
20
+
21
+ ' Register widget in tree and get HID
22
+ widget = m.frameworkInstance.builder.widgetTree.add({
23
+ id: config.id,
24
+ parentHID: parentHID
25
+ }, ViewModelClass)
26
+ HID = widget.HID
27
+
28
+ ' =============================================================
29
+ ' WIDGET DECORATOR METHODS
30
+ ' =============================================================
31
+ ' These methods provide widget API for tree navigation,
32
+ ' rendering, state management, and framework integration
33
+
34
+ ' getFrameworkInstance - Returns framework instance *'
35
+ widget.getFrameworkInstance = function() as object
36
+ return GetGlobalAA().rotor_framework_helper.frameworkInstance
37
+ end function
38
+
39
+ ' getInfo - Returns framework info *'
40
+ widget.getInfo = function() as object
41
+ return m.getFrameworkInstance().getInfo()
42
+ end function
43
+
44
+ ' refresh - Refreshes specific widget properties by key paths *'
45
+ widget.refresh = sub(featureKeyPaths as object)
46
+ iterateKeyPaths = Rotor.Utils.ensureArray(featureKeyPaths)
47
+ refreshObject = {}
48
+ for each keyPath in iterateKeyPaths
49
+ refreshObject.append(Rotor.Utils.getCloneByKeyPath(m, keyPath))
50
+ end for
51
+ m.render(refreshObject)
52
+ end sub
53
+
54
+ ' getWidget - Finds widget by search pattern from this widget's context *'
55
+ widget.getWidget = function(searchPattern as string) as object
56
+ return m.getFrameworkInstance().builder.widgetTree.get(searchPattern, m.HID)
57
+ end function
58
+
59
+ ' getSiblingWidget - Gets sibling widget by ID *'
60
+ widget.getSiblingWidget = function(id as string) as object
61
+ return m.parent.children[id]
62
+ end function
63
+
64
+ ' getViewModel - Returns owning ViewModel (self if ViewModel, or parent VM) *'
65
+ widget.getViewModel = function() as object
66
+ if m.isViewModel = true
67
+ return m
68
+ else
69
+ return m.getFrameworkInstance().builder.widgetTree.getByHID(m.vmHID)
70
+ end if
71
+ end function
72
+
73
+ ' getParentViewModel - Returns parent's ViewModel *'
74
+ widget.getParentViewModel = function() as object
75
+ return m.getViewModel().parent.getViewModel()
76
+ end function
77
+
78
+ ' getRootWidget - Returns root widget (HID "0") *'
79
+ widget.getRootWidget = function() as object
80
+ return m.getFrameworkInstance().builder.widgetTree.getByHID("0")
81
+ end function
82
+
83
+ ' findWidgets - Finds multiple widgets by search pattern *'
84
+ widget.findWidgets = function(searchPattern as string) as object
85
+ return m.getFrameworkInstance().builder.widgetTree.find(searchPattern, m.HID)
86
+ end function
87
+
88
+ ' getChildrenWidgets - Gets child widgets with optional pattern matching *'
89
+ widget.getChildrenWidgets = function(matchingPattern = "" as string) as object
90
+ return m.getFrameworkInstance().builder.widgetTree.getChildrenWidgets(m, matchingPattern)
91
+ end function
92
+
93
+ ' getSubtreeClone - Creates a clone of widget subtree *'
94
+ widget.getSubtreeClone = function(searchPattern = "" as string, keyPathList = [] as object) as object
95
+ if searchPattern = "" then searchPattern = m.HID
96
+ return m.getFrameworkInstance().builder.widgetTree.getSubtreeClone(searchPattern, keyPathList, m.parentHID)
97
+ end function
98
+
99
+ ' render - Renders widget updates (self, descendants, or children) *'
100
+ widget.render = sub(payloads as dynamic, params = {} as object)
101
+ for each payload in Rotor.Utils.ensureArray(payloads)
102
+ if payload.DoesExist("id") = false
103
+ ' Self update
104
+ payload.id = m.id
105
+ payload.HID = m.HID
106
+ else if payload.id <> m.id
107
+ ' Update descendants starting from this widget
108
+ payload.parentHID = m.HID
109
+ else
110
+ ' Update descendants starting from parent widget
111
+ payload.parentHID = m.parentHID
112
+ end if
113
+ end for
114
+ if Rotor.Utils.isValid(params.callback) then params.callbackScope = m
115
+ m.getFrameworkInstance().builder.render(payloads, params)
116
+ end sub
117
+
118
+ ' erase - Removes widget(s) from tree *'
119
+ widget.erase = sub(payloads = invalid as dynamic, shouldSkipNodePool = false as boolean)
120
+ if payloads = invalid
121
+ ' Self destroy
122
+ payloads = m.HID
123
+ parentHID = m.parentHID
124
+ else
125
+ ' Children destroy
126
+ parentHID = m.HID
127
+ end if
128
+ m.getFrameworkInstance().builder.erase(payloads, shouldSkipNodePool, parentHID)
129
+ end sub
130
+
131
+ ' getDispatcher - Gets dispatcher facade by ID *'
132
+ widget.getDispatcher = function(dispatcherId as string) as object
133
+ return m.getFrameworkInstance().dispatcherProvider.getFacade(dispatcherId, m)
134
+ end function
135
+
136
+ ' animator - Gets animator factory by ID *'
137
+ widget.animator = function(animatorId) as object
138
+ return m.getFrameworkInstance().animatorProvider.getFactory(animatorId, m)
139
+ end function
140
+
141
+ ' =============================================================
142
+ ' VIEWMODEL INITIALIZATION
143
+ ' =============================================================
144
+
145
+ if widget.isViewModel = true
146
+
147
+ ' Merge props from config
148
+ if Rotor.Utils.isAssociativeArray(config.props)
149
+ Rotor.Utils.deepExtendAA(widget.props, config.props)
150
+ end if
151
+
152
+ ' Merge viewModelState from config
153
+ if Rotor.Utils.isAssociativeArray(config.viewModelState)
154
+ Rotor.Utils.deepExtendAA(widget.viewModelState, config.viewModelState)
155
+ end if
156
+
157
+ ' Setup i18n (l10n)
158
+ i18nService = widget.getFrameworkInstance().i18nService
159
+ keysPaths = Rotor.Utils.isString(config.i18n?.path) ? config.i18n.path : Rotor.Utils.isArray(config.i18n?.paths) ? config.i18n.paths : invalid
160
+ widget.viewModelState.l10n = i18nService.getL10n(keysPaths)
161
+
162
+ ' Include isRTL flag if requested
163
+ if config?.i18n?.includeIsRtl = true
164
+ widget.viewModelState.isRTL = i18nService.getIsRtl()
165
+ end if
166
+
167
+ ' Include locale string if requested
168
+ if config?.i18n?.includeLocale = true
169
+ widget.viewModelState.locale = i18nService.getLocale()
170
+ end if
171
+
172
+ ' Call lifecycle hook before template compilation
173
+ widget.onCreateView()
174
+
175
+ ' Generate template
176
+ template = widget.template()
177
+
178
+ ' Optional template post-processing hook
179
+ if Rotor.Utils.isFunction(widget.onTemplateCreated)
180
+ widget.onTemplateCreated(template)
181
+ end if
182
+
183
+ ' Merge template into config
184
+ if template <> invalid and template.Count() > 0
185
+ templateChildren = template.children
186
+ template.delete("children")
187
+ config = Rotor.Utils.deepExtendAA(config, template)
188
+ config.children = templateChildren
189
+ end if
190
+
191
+ end if
192
+
193
+ ' =============================================================
194
+ ' LIFECYCLE HOOK REGISTRATION
195
+ ' =============================================================
196
+
197
+ ' Register custom lifecycle hooks from config
198
+ if Rotor.Utils.isFunction(config.onMountWidget)
199
+ widget.onMountWidget = config.onMountWidget
200
+ end if
201
+
202
+ if Rotor.Utils.isFunction(config.onUpdateWidget)
203
+ widget.onUpdateWidget = config.onUpdateWidget
204
+ end if
205
+
206
+ if Rotor.Utils.isFunction(config.onDestroyWidget)
207
+ widget.onDestroyWidget = config.onDestroyWidget
208
+ end if
209
+
210
+ ' Queue onRenderSettled callback
211
+ if Rotor.Utils.isFunction(config.onRenderSettled) = true
212
+ m.frameworkInstance.builder.callbackQueue.push({
213
+ callback: config.onRenderSettled,
214
+ callbackScope: widget
215
+ })
216
+ config.delete("onRenderSettled")
217
+ end if
218
+
219
+ ' =============================================================
220
+ ' SCENEGRAPH NODE CREATION
221
+ ' =============================================================
222
+
223
+ ' Get children for processing
224
+ if config.children = invalid then config.children = {}
225
+ children = config.children
226
+
227
+ ' Create SceneGraph node from node pool
228
+ nodeType = Rotor.Utils.isString(config.nodeType) ? config.nodeType : "Group"
229
+ widget.node = m.frameworkInstance.builder.nodePool.acquireNode(nodeType)
230
+ widget.nodeType = nodeType
231
+ widget.markedToAppend = true
232
+
233
+ ' Debug: Set node ID for scene graph inspector
234
+ #if debug
235
+ Rotor.Utils.setCustomFields(widget.node, {
236
+ id: `${widget.id}-${widget.HID}`
237
+ }, true, false)
238
+ #end if
239
+
240
+ ' =============================================================
241
+ ' PLUGIN INTEGRATION
242
+ ' =============================================================
243
+
244
+ ' Process plugins and inject addon methods
245
+ widgetAddon = {}
246
+ pluginKeyList = m.frameworkInstance.builder.pluginAdapter.pluginKeyList
247
+ pluginKeyList.ResetIndex()
248
+ pluginKey = pluginKeyList.GetIndex()
249
+
250
+ while pluginKey <> invalid
251
+
252
+ if config.doesExist(pluginKey)
253
+
254
+ ' Register plugin lifecycle hooks (beforeMount, afterMounted)
255
+ for each LifeCycleHookType in [Rotor.Const.LifeCycleHookType.BEFORE_MOUNT, Rotor.Const.LifeCycleHookType.AFTER_MOUNTED]
256
+ if m.frameworkInstance.builder.pluginAdapter.pluginHooks[LifeCycleHookType].DoesExist(pluginKey)
257
+ widget[pluginKey] = config[pluginKey]
258
+ postProcessBuffer.add({
259
+ isPlugin: true,
260
+ widget: widget,
261
+ hookType: LifeCycleHookType,
262
+ pluginKey: pluginKey
263
+ })
264
+ end if
265
+ end for
266
+
267
+ ' Inject plugin widget methods
268
+ plugin = m.frameworkInstance.plugins[pluginKey]
269
+ if Rotor.Utils.isValid(plugin["widgetMethods"])
270
+ if widgetAddon.plugins = invalid then widgetAddon.plugins = {}
271
+ if widgetAddon.plugins[pluginKey] = invalid then widgetAddon.plugins[pluginKey] = {
272
+ pluginKey: pluginKey,
273
+ id: widget.id,
274
+ HID: widget.HID,
275
+ parentHID: widget.parentHID
276
+ }
277
+ widgetAddon.plugins[pluginKey].append(plugin["widgetMethods"])
278
+ end if
279
+
280
+ end if
281
+
282
+ pluginKey = pluginKeyList.GetIndex()
283
+ end while
284
+
285
+ ' Append all plugin addon methods to widget
286
+ if widgetAddon.Count() > 0
287
+ widget.append(widgetAddon)
288
+ end if
289
+
290
+ ' =============================================================
291
+ ' POST-PROCESS QUEUE
292
+ ' =============================================================
293
+
294
+ ' Queue child append operation
295
+ appendChildProcess = {
296
+ hookType: Rotor.Const.LifeCycleHookType.APPEND_CHILD,
297
+ widget: widget
298
+ }
299
+ if config.zIndex <> invalid
300
+ appendChildProcess.zIndex = config.zIndex
301
+ end if
302
+ postProcessBuffer.add(appendChildProcess)
303
+
304
+ ' Queue mounted lifecycle hook
305
+ if Rotor.Utils.isFunction(widget.onMountWidget) or widget?.isViewModel = true
306
+ postProcessBuffer.add({
307
+ hookType: Rotor.Const.LifeCycleHookType.MOUNTED,
308
+ widget: widget
309
+ })
310
+ end if
311
+
312
+ ' Return widget metadata for tree processing
313
+ return {
314
+ HID: HID,
315
+ children: children,
316
+ parentHID: parentHID,
317
+ id: widget.id
318
+ }
319
+
320
+ end function
321
+
322
+ end namespace
@@ -0,0 +1,72 @@
1
+ namespace Rotor.ViewBuilder
2
+
3
+ ' =====================================================================
4
+ ' removeWidget - Removes a widget and its children from the widget tree. Handles plugin beforeDestroy hooks, widget destruction, and SceneGraph node removal. Recursively marks children for removal.
5
+ '
6
+ ' @param {object} postProcessBuffer - Buffer for deferred plugin/lifecycle operations
7
+ ' @param {object} widget - Widget instance to remove
8
+ ' @param {object} config - Removal configuration with markedToRemove and shouldSkipNodePool flags
9
+ ' @returns {object} Object with children array and HID for recursive processing
10
+ ' =====================================================================
11
+ function removeWidget(postProcessBuffer as object, widget as object, config as object) as object
12
+ HID = widget.HID
13
+
14
+ widget.markedToRemove = config.markedToRemove
15
+
16
+ ' plugin :: beforeDestroy
17
+ pluginKeyList = m.frameworkInstance.builder.pluginAdapter.pluginKeyList
18
+ pluginKeyList.ResetIndex()
19
+ pluginKey = pluginKeyList.GetIndex()
20
+ while pluginKey <> invalid
21
+
22
+ if widget.doesExist(pluginKey)
23
+ LifeCycleHookType = Rotor.Const.LifeCycleHookType.BEFORE_DESTROY
24
+ if m.frameworkInstance.builder.pluginAdapter.pluginHooks[LifeCycleHookType].DoesExist(pluginKey)
25
+ postProcessBuffer.add({
26
+ isPlugin: true,
27
+ ' isView: widget?.isView ?? false,
28
+ widget: widget,
29
+ hookType: LifeCycleHookType,
30
+ pluginKey: pluginKey
31
+ })
32
+ end if
33
+ end if
34
+
35
+ pluginKey = pluginKeyList.GetIndex()
36
+ end while
37
+
38
+ postProcessBuffer.add({
39
+ hookType: Rotor.Const.LifeCycleHookType.DELETE_WIDGET,
40
+ widget: widget
41
+ })
42
+
43
+ isBranchOfRemove = m.frameworkInstance.builder.widgetTree.isBranchOfRemove(widget)
44
+ if isBranchOfRemove
45
+ postProcessBuffer.add({
46
+ hookType: Rotor.Const.LifeCycleHookType.REMOVE_CHILD,
47
+ shouldSkipNodePool: config.shouldSkipNodePool,
48
+ widget: widget
49
+
50
+ })
51
+ end if
52
+
53
+ childrenToRemove = []
54
+ for each id in widget.children
55
+ if not widget.children[id].doesExist("markedToRemove")
56
+ childrenToRemove.push({
57
+ markedToRemove: true,
58
+ id: id,
59
+ HID: widget.children[id].HID,
60
+ parentHID: widget.children[id].parentHID
61
+ })
62
+ end if
63
+ end for
64
+
65
+ return {
66
+ children: childrenToRemove,
67
+ HID: HID
68
+ }
69
+
70
+ end function
71
+
72
+ end namespace
@@ -0,0 +1,113 @@
1
+ namespace Rotor.ViewBuilder
2
+
3
+ ' =====================================================================
4
+ ' updateWidget - Updates an existing widget with new configuration. Handles props updates, viewModelState changes, plugin lifecycle hooks, z-index changes, and custom lifecycle callbacks.
5
+ '
6
+ ' @param {object} postProcessBuffer - Buffer for deferred plugin/lifecycle operations
7
+ ' @param {object} widget - Existing widget instance to update
8
+ ' @param {object} newConfig - New configuration to apply
9
+ ' @returns {object} Object with children and HID for further processing
10
+ ' =====================================================================
11
+ function updateWidget(postProcessBuffer as object, widget as object, newConfig as object) as object
12
+ HID = widget.HID
13
+
14
+ isWidgetChanged = false
15
+
16
+ if widget?.isViewModel = true and newConfig.DoesExist("props")
17
+ newConfig.delete("viewModel") ' In case app update with the same template.
18
+ postProcessBuffer.add({
19
+ hookType: Rotor.Const.LifeCycleHookType.VIEWMODEL_STATE_UPDATE,
20
+ widget: widget,
21
+ props: newConfig.props
22
+ })
23
+ end if
24
+
25
+ if newConfig.DoesExist("viewModelState")
26
+ Rotor.Utils.deepExtendAA(widget.viewModelState, newConfig.viewModelState)
27
+ isWidgetChanged = true
28
+ end if
29
+
30
+ ' Method decorators during update
31
+
32
+ pluginKeyList = m.frameworkInstance.builder.pluginAdapter.pluginKeyList
33
+ pluginKeyList.ResetIndex()
34
+ pluginKey = pluginKeyList.GetIndex()
35
+ while pluginKey <> invalid
36
+
37
+ if newConfig.doesExist(pluginKey)
38
+ for each LifeCycleHookType in [Rotor.Const.LifeCycleHookType.BEFORE_UPDATE, Rotor.Const.LifeCycleHookType.AFTER_UPDATED]
39
+ if m.frameworkInstance.builder.pluginAdapter.pluginHooks[LifeCycleHookType].DoesExist(pluginKey)
40
+
41
+ isWidgetChanged = true
42
+
43
+ process = {
44
+ isPlugin: true,
45
+ widget: widget,
46
+ hookType: LifeCycleHookType,
47
+ ' isView: widget?.isView ?? false,
48
+ pluginKey: pluginKey
49
+ }
50
+
51
+ if LifeCycleHookType = Rotor.Const.LifeCycleHookType.BEFORE_UPDATE or LifeCycleHookType = Rotor.Const.LifeCycleHookType.AFTER_UPDATED
52
+ newValue = newConfig[pluginKey]
53
+ oldValue = widget[pluginKey]
54
+ ' Change is depend on plugin ;)
55
+ process.append({
56
+ oldValue: oldValue,
57
+ newValue: newValue
58
+ })
59
+ end if
60
+
61
+ postProcessBuffer.add(process)
62
+
63
+ end if
64
+ end for
65
+ end if
66
+
67
+ pluginKey = pluginKeyList.GetIndex()
68
+ end while
69
+
70
+
71
+ if Rotor.Utils.isInteger(newConfig.zIndex)
72
+ postProcessBuffer.add({
73
+ hookType: Rotor.Const.LifeCycleHookType.REINDEX_CHILD,
74
+ widget: widget,
75
+ zIndex: newConfig.zIndex
76
+ })
77
+ end if
78
+
79
+
80
+ if Rotor.Utils.isFunction(newConfig.onUpdateWidget)
81
+ isWidgetChanged = true
82
+ widget.onUpdateWidget = newConfig.onUpdateWidget
83
+ end if
84
+
85
+ if Rotor.Utils.isFunction(newConfig.onDestroyWidget)
86
+ widget.onDestroyWidget = newConfig.onDestroyWidget
87
+ end if
88
+
89
+ if Rotor.Utils.isFunction(newConfig.onRenderSettled) = true
90
+ m.frameworkInstance.builder.callbackQueue.push({
91
+ callback: newConfig.onRenderSettled,
92
+ callbackScope: widget
93
+ })
94
+ newConfig.delete("onRenderSettled")
95
+ end if
96
+
97
+
98
+ if isWidgetChanged = true and Rotor.Utils.isFunction(widget.onUpdateWidget)
99
+ postProcessBuffer.add({
100
+ hookType: Rotor.Const.LifeCycleHookType.UPDATED,
101
+ widget: widget
102
+ })
103
+ end if
104
+
105
+ return {
106
+ children: newConfig.children,
107
+ HID: HID
108
+ }
109
+
110
+ end function
111
+
112
+
113
+ end namespace
@@ -0,0 +1,72 @@
1
+
2
+ namespace Rotor
3
+
4
+ ' =====================================================================
5
+ ' Dispatcher - Dispatcher Facade that provides a simplified interface for state management
6
+ '
7
+ ' Acts as a proxy to the underlying dispatcher instance, managing intents,
8
+ ' listeners, and state access. The facade pattern allows for clean separation
9
+ ' between the dispatcher implementation and its consumers, enabling multiple
10
+ ' listeners to subscribe to the same dispatcher with different scopes.
11
+ ' =====================================================================
12
+ class Dispatcher
13
+
14
+ dispatcherId as string
15
+ listenerId as string
16
+ listenerScope as object
17
+ dispatcherInstance as object
18
+
19
+ sub new(dispatcherInstance as dynamic, dispatcherId as string, listenerId = "" as string, listenerScope = invalid as object)
20
+ m.dispatcherId = dispatcherId
21
+ m.listenerScope = listenerScope
22
+ m.listenerId = listenerId
23
+ m.dispatcherInstance = dispatcherInstance
24
+ end sub
25
+
26
+ ' ---------------------------------------------------------------------
27
+ ' dispatch - Dispatches an intent to trigger state changes through the reducer
28
+ '
29
+ ' @param {Intent} intent - The intent object containing action type and payload
30
+ '
31
+ sub dispatch(intent)
32
+ m.dispatcherInstance.dispatch(intent)
33
+ end sub
34
+
35
+ ' ---------------------------------------------------------------------
36
+ ' addListener - Adds a listener to subscribe to state changes from this dispatcher
37
+ '
38
+ ' @param {Dynamic} listenerConfig - Configuration object for the listener (can be a function or object with mapStateToProps)
39
+ '
40
+ sub addListener(listenerConfig as dynamic)
41
+ m.dispatcherInstance.addListener(listenerConfig, m.listenerId, m.listenerScope)
42
+ end sub
43
+
44
+ ' ---------------------------------------------------------------------
45
+ ' removeAllListenersByListenerId - Removes all listeners associated with this dispatcher's listener ID
46
+ '
47
+ sub removeAllListenersByListenerId()
48
+ m.dispatcherInstance.removeAllListenersByListenerId(m.listenerId)
49
+ end sub
50
+
51
+ ' ---------------------------------------------------------------------
52
+ ' getState - Retrieves the current state from the dispatcher, optionally mapped through a selector function
53
+ '
54
+ ' @param {Dynamic} mapStateToProps - Optional function to map/select specific parts of the state
55
+ ' @returns {Object} The current state object (mapped if selector provided)
56
+ '
57
+ function getState(mapStateToProps = invalid as Dynamic) as object
58
+ return m.dispatcherInstance.getState(mapStateToProps, m.listenerScope)
59
+ end function
60
+
61
+ ' ---------------------------------------------------------------------
62
+ ' destroy - Cleans up the dispatcher facade by removing all listeners and clearing references
63
+ '
64
+ sub destroy()
65
+ m.removeAllListenersByListenerId()
66
+ m.listenerScope = invalid
67
+ m.dispatcherInstance = invalid
68
+ end sub
69
+
70
+ end class
71
+
72
+ end namespace
@@ -0,0 +1,95 @@
1
+
2
+ namespace Rotor
3
+
4
+ ' =====================================================================
5
+ ' DispatcherProvider - Manages a collection of dispatcher instances and provides facade access
6
+ '
7
+ ' Manages a collection of dispatcher instances and provides facade access
8
+ ' to dispatchers. Extends BaseStack to store and organize dispatchers by ID.
9
+ ' Handles both internal and external dispatcher registration/deregistration.
10
+ ' =====================================================================
11
+ class DispatcherProvider extends Rotor.BaseStack
12
+
13
+ threadType as string
14
+
15
+ ' ---------------------------------------------------------------------
16
+ ' new - Initializes the dispatcher provider with thread type
17
+ '
18
+ ' @param {string} threadType - Type of thread this provider operates on (render/task)
19
+ '
20
+ sub new(threadType as string)
21
+ super()
22
+ m.threadType = threadType
23
+ end sub
24
+
25
+ ' ---------------------------------------------------------------------
26
+ ' getFacade - Returns a dispatcher facade for the requested dispatcher ID
27
+ '
28
+ ' @param {string} dispatcherId - The unique identifier for the dispatcher
29
+ ' @param {object} listenerScope - Optional scope for the listener (default: invalid)
30
+ ' @returns {object} Dispatcher facade instance
31
+ ' @throws {object} Error if dispatcher ID is not registered (debug mode only)
32
+ '
33
+ function getFacade(dispatcherId as string, listenerScope = invalid as object) as object
34
+ listenerId = Rotor.Utils.getUUIDHex(32)
35
+
36
+ dispatcherTaskNode = m.stack.LookupCI(dispatcherId)
37
+
38
+ #if debug
39
+ if dispatcherTaskNode = invalid
40
+ throw { message: `[DISPATCHER_PROVIDER][ERROR] Dispatcher id:${dispatcherId} is not registered.` }
41
+ end if
42
+ #end if
43
+
44
+ dispatcherFacadeInstance = new Rotor.Dispatcher(dispatcherTaskNode, dispatcherId, listenerId, listenerScope)
45
+
46
+ return dispatcherFacadeInstance
47
+ end function
48
+
49
+ ' ---------------------------------------------------------------------
50
+ ' registerDispatcher - Registers a dispatcher instance with the provider
51
+ '
52
+ ' @param {object} dispatcherInstance - The dispatcher instance to register
53
+ ' @param {string} id - The unique identifier for the dispatcher
54
+ '
55
+ override sub registerDispatcher(dispatcherInstance, id as string)
56
+
57
+ m.set(id, dispatcherInstance)
58
+
59
+ end sub
60
+
61
+ ' ---------------------------------------------------------------------
62
+ ' registerExternalDispatchers - Registers external dispatchers from another thread
63
+ '
64
+ ' @param {dynamic} dispatcherIds - Single ID string or array of dispatcher IDs
65
+ ' @param {object} taskNode - The task node for cross-thread communication
66
+ '
67
+ sub registerExternalDispatchers(dispatcherIds as dynamic, taskNode as object)
68
+ dispatcherIds = Rotor.Utils.ensureArray(dispatcherIds)
69
+ for each dispatcherId in dispatcherIds
70
+ dispatcherInstance = new Rotor.DispatcherExternal(dispatcherId, taskNode, m.threadType)
71
+ m.set(dispatcherId, dispatcherInstance)
72
+ end for
73
+ end sub
74
+
75
+ ' ---------------------------------------------------------------------
76
+ ' deregisterDispatcher - Removes a dispatcher from the provider
77
+ '
78
+ ' @param {dynamic} dispatcherId - The ID of the dispatcher to remove
79
+ '
80
+ sub deregisterDispatcher(dispatcherId as dynamic)
81
+ m.remove(dispatcherId)
82
+ end sub
83
+
84
+ ' ---------------------------------------------------------------------
85
+ ' destroy - Destroys all registered dispatchers and cleans up resources
86
+ '
87
+ sub destroy()
88
+ dispatchers = m.getAll()
89
+ for each dispatcherId in dispatchers
90
+ dispatchers[dispatcherId].destroy() ' Note that destroy will deregister dispatcher automatically
91
+ end for
92
+ end sub
93
+
94
+ end class
95
+ end namespace