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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Haystack TV Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,127 @@
1
+ import "../base/BasePlugin.bs"
2
+
3
+ namespace Rotor
4
+
5
+ ' =====================================================================
6
+ ' DispatcherProviderPlugin - Manages MVI dispatcher integration with widgets
7
+ '
8
+ ' Rotor Framework plugin for managing MVI dispatcher integration with widgets.
9
+ '
10
+ ' Key Responsibilities:
11
+ ' - Provides dispatcher facades to widgets for state management
12
+ ' - Manages dispatcher lifecycle (creation, updates, cleanup)
13
+ ' - Integrates cross-thread MVI pattern with widget context
14
+ ' - Stores dispatcher references in viewModelState for easy access
15
+ '
16
+ ' Usage:
17
+ ' Widget config: dispatcher: "dispatcherId" or dispatcher: ["id1", "id2"]
18
+ ' Access: widget.viewModelState.dispatcher[dispatcherId]
19
+ '
20
+ ' =====================================================================
21
+ class DispatcherProviderPlugin extends Rotor.BasePlugin
22
+
23
+ ' =============================================================
24
+ ' CONSTRUCTOR
25
+ ' =============================================================
26
+
27
+ ' ---------------------------------------------------------------------
28
+ ' new - Initializes the DispatcherProviderPlugin instance
29
+ '
30
+ ' @param {string} key - Plugin identifier (default: "dispatcher")
31
+ '
32
+ sub new(key = "dispatcher" as string)
33
+ super(key)
34
+ end sub
35
+
36
+ ' =============================================================
37
+ ' LIFECYCLE HOOKS
38
+ ' =============================================================
39
+
40
+ hooks = {
41
+ ' ---------------------------------------------------------------------
42
+ ' beforeMount - Attaches dispatchers when widget is mounted
43
+ '
44
+ ' Creates dispatcher facades and stores them in viewModelState.
45
+ '
46
+ ' @param {object} scope - Plugin instance (m)
47
+ ' @param {object} widget - Widget being mounted
48
+ '
49
+ beforeMount: sub(scope as object, widget as object)
50
+ scope.setDispatcherOnContext(widget, scope.key)
51
+ end sub,
52
+
53
+ ' ---------------------------------------------------------------------
54
+ ' beforeUpdate - Updates dispatcher configuration
55
+ '
56
+ ' Extends existing dispatcher list with new IDs (prevents duplicates).
57
+ ' Creates facades for any newly added dispatchers.
58
+ '
59
+ ' @param {object} scope - Plugin instance (m)
60
+ ' @param {object} widget - Widget being updated
61
+ ' @param {dynamic} newValue - New dispatcher ID(s)
62
+ ' @param {dynamic} oldValue - Previous dispatcher ID(s)
63
+ '
64
+ beforeUpdate: sub(scope as object, widget as object, newValue, oldValue = [])
65
+ ' Merge old and new dispatcher IDs (no duplicates)
66
+ oldArrayOfDispatcherIds = Rotor.Utils.ensureArray(oldValue ?? [])
67
+ newArrayOfDispatcherIds = Rotor.Utils.ensureArray(newValue)
68
+ widget[scope.key] = Rotor.Utils.extendArrayOfStrings(oldArrayOfDispatcherIds, newArrayOfDispatcherIds)
69
+ scope.setDispatcherOnContext(widget, scope.key)
70
+ end sub,
71
+
72
+ ' ---------------------------------------------------------------------
73
+ ' beforeDestroy - Cleans up dispatchers before widget destruction
74
+ '
75
+ ' Destroys all dispatcher facades and clears references.
76
+ '
77
+ ' @param {object} scope - Plugin instance (m)
78
+ ' @param {object} widget - Widget being destroyed
79
+ '
80
+ beforeDestroy: sub(scope as object, widget as object)
81
+ dispatcherFacades = widget.viewModelState[scope.key]
82
+ if dispatcherFacades.Count() > 0
83
+ for each dispatcherFacadeKey in dispatcherFacades
84
+ dispatcherFacadeInstance = dispatcherFacades[dispatcherFacadeKey]
85
+ dispatcherFacadeInstance.destroy()
86
+ widget.viewModelState[scope.key][dispatcherFacadeKey] = invalid
87
+ end for
88
+ end if
89
+ end sub
90
+ }
91
+
92
+ ' =============================================================
93
+ ' DISPATCHER MANAGEMENT
94
+ ' =============================================================
95
+
96
+ ' ---------------------------------------------------------------------
97
+ ' setDispatcherOnContext - Creates and stores dispatcher facades
98
+ '
99
+ ' For each dispatcher ID in the widget config:
100
+ ' 1. Initializes viewModelState.dispatcher object if needed
101
+ ' 2. Checks if facade already exists for this ID
102
+ ' 3. Creates new facade via DispatcherProvider if not present
103
+ ' 4. Stores facade in viewModelState.dispatcher[dispatcherId]
104
+ '
105
+ ' @param {object} widget - Widget instance
106
+ ' @param {string} scopeKey - Plugin key (typically "dispatcher")
107
+ '
108
+ sub setDispatcherOnContext(widget, scopeKey)
109
+ viewModelState = widget.viewModelState
110
+ config = widget[scopeKey]
111
+
112
+ for each dispatcherId in Rotor.Utils.ensureArray(config)
113
+ ' Initialize dispatcher storage in viewModelState
114
+ if viewModelState[m.key] = invalid then viewModelState[m.key] = {}
115
+
116
+ ' Create facade if it doesn't exist
117
+ if not viewModelState[m.key].DoesExist(dispatcherId)
118
+ globalScope = GetGlobalAA()
119
+ frameworkInstance = globalScope.rotor_framework_helper.frameworkInstance
120
+ viewModelState[m.key][dispatcherId] = frameworkInstance.dispatcherProvider.getFacade(dispatcherId, widget)
121
+ end if
122
+ end for
123
+ end sub
124
+
125
+ end class
126
+
127
+ end namespace
@@ -0,0 +1,180 @@
1
+ import "../base/BasePlugin.bs"
2
+
3
+ namespace Rotor
4
+
5
+ ' =====================================================================
6
+ ' FieldsPlugin - Handles dynamic field expressions and interpolation
7
+ '
8
+ ' Rotor Framework plugin for handling dynamic field expressions and interpolation.
9
+ '
10
+ ' Key Features:
11
+ ' - Evaluates function-based field values
12
+ ' - Interpolates @-prefixed expressions (e.g., @viewModelState.value)
13
+ ' - Supports dynamic field resolution from widget context
14
+ ' - Automatically updates fields on widget lifecycle changes
15
+ '
16
+ ' Expression Syntax:
17
+ ' @key.path - Resolves from widget.viewModelState
18
+ '
19
+ ' =====================================================================
20
+ class FieldsPlugin extends Rotor.BasePlugin
21
+
22
+ ' =============================================================
23
+ ' MEMBER VARIABLES
24
+ ' =============================================================
25
+
26
+ ' Regex pattern for matching @-prefixed expressions
27
+ ' Matches: @ followed by any characters except space, @, or comma
28
+ configRegex = /(\@)([^\s\@\,]*)/i
29
+
30
+ ' =============================================================
31
+ ' CONSTRUCTOR
32
+ ' =============================================================
33
+
34
+ ' ---------------------------------------------------------------------
35
+ ' new - Initializes the FieldsPlugin instance
36
+ '
37
+ ' @param {string} key - Plugin identifier (default: "fields")
38
+ ' @param {object} params - Additional parameters (unused)
39
+ '
40
+ sub new(key = "fields" as string, params = invalid as object)
41
+ super(key)
42
+ end sub
43
+
44
+ ' =============================================================
45
+ ' LIFECYCLE HOOKS
46
+ ' =============================================================
47
+
48
+ hooks = {
49
+ ' ---------------------------------------------------------------------
50
+ ' beforeMount - Sets custom fields when widget is mounted
51
+ '
52
+ ' Evaluates and applies all field expressions after widget creation.
53
+ '
54
+ ' @param {object} scope - Plugin instance (m)
55
+ ' @param {object} widget - Widget being mounted
56
+ '
57
+ beforeMount: sub(scope as object, widget as object)
58
+ value = widget[scope.key]
59
+ scope.setCustomFields(widget, value)
60
+ end sub,
61
+
62
+ ' ---------------------------------------------------------------------
63
+ ' beforeUpdate - Updates custom fields when widget config changes
64
+ '
65
+ ' Merges new field config with existing config and re-evaluates all fields.
66
+ '
67
+ ' @param {object} scope - Plugin instance (m)
68
+ ' @param {object} widget - Widget being updated
69
+ ' @param {object} newValue - New field configuration
70
+ ' @param {object} oldValue - Previous field configuration
71
+ '
72
+ beforeUpdate: sub(scope as object, widget as object, newValue = {}, oldValue = {})
73
+ ' Extend old config with new values
74
+ Rotor.Utils.deepExtendAA(widget[scope.key], newValue)
75
+ scope.setCustomFields(widget, newValue)
76
+ end sub,
77
+
78
+ ' ---------------------------------------------------------------------
79
+ ' beforeDestroy - Clears field configuration on destruction
80
+ '
81
+ ' @param {object} scope - Plugin instance (m)
82
+ ' @param {object} widget - Widget being destroyed
83
+ '
84
+ beforeDestroy: sub(scope as object, widget as object)
85
+ widget[scope.key].Clear()
86
+ end sub
87
+ }
88
+
89
+ ' =============================================================
90
+ ' FIELD PROCESSING
91
+ ' =============================================================
92
+
93
+ ' ---------------------------------------------------------------------
94
+ ' setCustomFields - Evaluates and applies fields to widget node
95
+ '
96
+ ' Parses field expressions and sets the resolved values on the widget's SceneGraph node.
97
+ '
98
+ ' @param {object} widget - Widget instance
99
+ ' @param {object} fields - Field configuration object
100
+ '
101
+ sub setCustomFields(widget as object, fields)
102
+ parsedFields = m.parseFields(widget, fields)
103
+ node = widget.node
104
+ Rotor.Utils.setCustomFields(node, parsedFields, true)
105
+ end sub
106
+
107
+ ' ---------------------------------------------------------------------
108
+ ' parseFields - Parses and resolves field expressions
109
+ '
110
+ ' Processes field values through multiple resolution strategies:
111
+ ' 1. Function values - Executes function in widget scope
112
+ ' 2. String interpolation - Resolves @-prefixed expressions
113
+ ' 3. Direct values - Passes through unchanged
114
+ '
115
+ ' Expression Resolution:
116
+ ' - @key.path resolves from widget.viewModelState
117
+ ' - If result is string, performs string interpolation
118
+ ' - If result is non-string, replaces entire value
119
+ '
120
+ ' @param {object} widget - Widget instance providing context
121
+ ' @param {object} fields - Field configuration to parse
122
+ ' @returns {object} Parsed fields with resolved values
123
+ '
124
+ function parseFields(widget as object, fields as object) as object
125
+ parsedFields = {}
126
+
127
+ for each fieldId in fields
128
+ value = fields[fieldId]
129
+
130
+ ' Step 1: Resolve function-based values
131
+ if Rotor.Utils.isFunction(value)
132
+ parsedFields[fieldId] = Rotor.Utils.callbackScoped(value, widget)
133
+ value = parsedFields[fieldId]
134
+ end if
135
+
136
+ ' Step 2: Process string interpolation
137
+ if Rotor.Utils.isString(value)
138
+ results = m.configRegex.MatchAll(value)
139
+
140
+ if results.Count() > 0
141
+ for each result in results
142
+ matchKey = result[2] ' The key path after @
143
+ sourceTypeOperator = result[1] ' The @ symbol
144
+
145
+ ' Determine source based on operator
146
+ if sourceTypeOperator = "@"
147
+ source = widget.viewModelState
148
+ else
149
+ source = widget
150
+ end if
151
+
152
+ ' Resolve value from key path
153
+ asset = Rotor.Utils.getValueByKeyPath(source, matchKey)
154
+
155
+ ' Handle string vs non-string results
156
+ if Rotor.Utils.isString(asset)
157
+ ' String interpolation - replace in original string
158
+ replaceRegex = CreateObject("roRegex", sourceTypeOperator + matchKey, "ig")
159
+ value = replaceRegex.ReplaceAll(value, asset)
160
+ else
161
+ ' Non-string value - replace entire field value
162
+ value = asset
163
+ exit for
164
+ end if
165
+ end for
166
+ end if
167
+
168
+ parsedFields[fieldId] = value
169
+ else
170
+ ' Step 3: Direct value assignment
171
+ parsedFields[fieldId] = value
172
+ end if
173
+ end for
174
+
175
+ return parsedFields
176
+ end function
177
+
178
+ end class
179
+
180
+ end namespace