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.
- package/LICENSE.md +21 -0
- package/README.md +120 -0
- package/package.json +59 -0
- package/src/source/RotorFramework.bs +654 -0
- package/src/source/RotorFrameworkTask.bs +278 -0
- package/src/source/base/BaseModel.bs +52 -0
- package/src/source/base/BasePlugin.bs +48 -0
- package/src/source/base/BaseReducer.bs +184 -0
- package/src/source/base/BaseStack.bs +92 -0
- package/src/source/base/BaseViewModel.bs +124 -0
- package/src/source/base/BaseWidget.bs +104 -0
- package/src/source/base/DispatcherCreator.bs +193 -0
- package/src/source/base/DispatcherExternal.bs +260 -0
- package/src/source/base/ListenerForDispatchers.bs +246 -0
- package/src/source/engine/Constants.bs +74 -0
- package/src/source/engine/animator/Animator.bs +334 -0
- package/src/source/engine/builder/Builder.bs +213 -0
- package/src/source/engine/builder/NodePool.bs +236 -0
- package/src/source/engine/builder/PluginAdapter.bs +139 -0
- package/src/source/engine/builder/PostProcessor.bs +331 -0
- package/src/source/engine/builder/Processor.bs +156 -0
- package/src/source/engine/builder/Tree.bs +278 -0
- package/src/source/engine/builder/TreeBase.bs +313 -0
- package/src/source/engine/builder/WidgetCreate.bs +322 -0
- package/src/source/engine/builder/WidgetRemove.bs +72 -0
- package/src/source/engine/builder/WidgetUpdate.bs +113 -0
- package/src/source/engine/providers/Dispatcher.bs +72 -0
- package/src/source/engine/providers/DispatcherProvider.bs +95 -0
- package/src/source/engine/services/I18n.bs +169 -0
- package/src/source/libs/animate/Animate.bs +753 -0
- package/src/source/libs/animate/LICENSE.txt +21 -0
- package/src/source/plugins/DispatcherProviderPlugin.bs +127 -0
- package/src/source/plugins/FieldsPlugin.bs +180 -0
- package/src/source/plugins/FocusPlugin.bs +1522 -0
- package/src/source/plugins/FontStylePlugin.bs +159 -0
- package/src/source/plugins/ObserverPlugin.bs +548 -0
- package/src/source/utils/ArrayUtils.bs +495 -0
- package/src/source/utils/GeneralUtils.bs +181 -0
- 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
|