rotor-framework 0.8.0 β 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/source/RotorFramework.bs +2 -2
- package/src/source/RotorFrameworkTask.bs +2 -2
- package/src/source/base/BaseViewModel.bs +0 -2
- package/src/source/base/DispatcherCore.bs +3 -3
- package/src/source/plugins/FieldsPlugin.bs +3 -0
- package/src/source/plugins/FocusPlugin.bs +2 -2
- package/src/source/plugins/ObserverPlugin.bs +19 -9
package/README.md
CHANGED
|
@@ -96,7 +96,7 @@ You can find [π±](./docs/ai/readme.opt.yaml) symbols in all documentation page
|
|
|
96
96
|
|
|
97
97
|
## π Learn More
|
|
98
98
|
|
|
99
|
-

|
|
100
100
|
|
|
101
101
|
### Framework Core
|
|
102
102
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rotor-framework",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Roku toolkit library providing a ViewBuilder, full UI lifecycle with focus handling and many core features, plus MVI-based state management.",
|
|
5
5
|
"author": "BalΓ‘zs MolnΓ‘r",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
' βββββββ ββ β ββ βββββββ βββββββββββββββββ βββββββββ ββββ βββββββββββ
|
|
5
5
|
' ββ βββββββ β βββββββ ββ ββ ββ ββββ ββββ βββββββββββββββββββ ββββ ββ
|
|
6
6
|
' Rotor Frameworkβ’
|
|
7
|
-
' Version 0.8.
|
|
7
|
+
' Version 0.8.1
|
|
8
8
|
' Β© 2025-2026 BalΓ‘zs MolnΓ‘r β Apache License 2.0
|
|
9
9
|
' =========================================================================
|
|
10
10
|
|
|
@@ -86,7 +86,7 @@ namespace Rotor
|
|
|
86
86
|
class Framework
|
|
87
87
|
|
|
88
88
|
name = "Rotor Framework"
|
|
89
|
-
version = "0.8.
|
|
89
|
+
version = "0.8.1"
|
|
90
90
|
|
|
91
91
|
config = {
|
|
92
92
|
tasks: invalid, ' @array (optional)
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
' βββββββ ββ β ββ βββββββ βββββββββββββββββ βββββββββ ββββ βββββββββββ
|
|
5
5
|
' ββ βββββββ β βββββββ ββ ββ ββ ββββ ββββ βββββββββββββββββββ ββββ ββ
|
|
6
6
|
' Rotor Frameworkβ’
|
|
7
|
-
' Version 0.8.
|
|
7
|
+
' Version 0.8.1
|
|
8
8
|
' Β© 2025-2026 BalΓ‘zs MolnΓ‘r β Apache License 2.0
|
|
9
9
|
' =========================================================================
|
|
10
10
|
|
|
@@ -70,7 +70,7 @@ namespace Rotor
|
|
|
70
70
|
class FrameworkTask
|
|
71
71
|
|
|
72
72
|
name = "Rotor Framework"
|
|
73
|
-
version = "0.8.
|
|
73
|
+
version = "0.8.1"
|
|
74
74
|
|
|
75
75
|
config = {
|
|
76
76
|
tasks: invalid, ' optional
|
|
@@ -69,11 +69,12 @@ namespace Rotor
|
|
|
69
69
|
' @param {object} state - New state to notify listeners about
|
|
70
70
|
'
|
|
71
71
|
sub notifyListeners(state as object)
|
|
72
|
-
listenerCount = m.listeners.Count()
|
|
73
72
|
listenerIndex = 0
|
|
74
73
|
|
|
75
74
|
' Iterate through all listeners
|
|
76
|
-
|
|
75
|
+
' Use m.listeners.Count() directly (not cached) to handle external listener
|
|
76
|
+
' removal during iteration (e.g., ViewModel destroyed in a callback)
|
|
77
|
+
while listenerIndex < m.listeners.Count()
|
|
77
78
|
listener = m.listeners[listenerIndex]
|
|
78
79
|
scope = listener.listenerScope
|
|
79
80
|
|
|
@@ -116,7 +117,6 @@ namespace Rotor
|
|
|
116
117
|
listener.listenerScope = invalid
|
|
117
118
|
listener.callback = invalid
|
|
118
119
|
m.listeners.Delete(listenerIndex)
|
|
119
|
-
listenerCount--
|
|
120
120
|
else
|
|
121
121
|
listenerIndex++
|
|
122
122
|
end if
|
|
@@ -149,6 +149,9 @@ namespace Rotor
|
|
|
149
149
|
' Resolve value from key path
|
|
150
150
|
asset = Rotor.Utils.getValueByKeyPath(source, matchKey)
|
|
151
151
|
|
|
152
|
+
' Skip unresolved keys (e.g. @ in email addresses)
|
|
153
|
+
if asset = invalid then goto nextResult
|
|
154
|
+
|
|
152
155
|
' Handle string vs non-string results
|
|
153
156
|
if Rotor.Utils.isString(asset)
|
|
154
157
|
' String interpolation - replace in original string
|
|
@@ -1235,7 +1235,7 @@ namespace Rotor
|
|
|
1235
1235
|
foundHID = ""
|
|
1236
1236
|
for each HID in possibleFocusItems
|
|
1237
1237
|
focusItem = m.get(HID)
|
|
1238
|
-
if focusItem
|
|
1238
|
+
if focusItem?.id = nodeId
|
|
1239
1239
|
foundHID = focusItem.HID
|
|
1240
1240
|
exit for
|
|
1241
1241
|
end if
|
|
@@ -1567,7 +1567,7 @@ namespace Rotor
|
|
|
1567
1567
|
m.isFocused = isFocused
|
|
1568
1568
|
|
|
1569
1569
|
if m.autoSetIsFocusedState
|
|
1570
|
-
m.widget.viewModelState.
|
|
1570
|
+
m.widget.viewModelState.isFocused = isFocused
|
|
1571
1571
|
end if
|
|
1572
1572
|
m.node.setField("isFocused", isFocused)
|
|
1573
1573
|
m.callOnFocusedFnOnWidget(isFocused)
|
|
@@ -385,7 +385,7 @@ namespace Rotor
|
|
|
385
385
|
' Represents a single observer configuration for a node field.
|
|
386
386
|
'
|
|
387
387
|
' Responsibilities:
|
|
388
|
-
' - Stores observer configuration (callback, conditions, etc.)
|
|
388
|
+
' - Stores observer configuration (callback or handler, conditions, etc.)
|
|
389
389
|
' - Sets up initial field value if provided
|
|
390
390
|
' - Provides info fields for observeFieldScoped
|
|
391
391
|
' - Executes callbacks in correct scope
|
|
@@ -407,7 +407,8 @@ namespace Rotor
|
|
|
407
407
|
value as dynamic ' Initial field value (if any)
|
|
408
408
|
once as boolean ' Remove observer after first trigger
|
|
409
409
|
until as function ' Conditional removal function
|
|
410
|
-
callback as
|
|
410
|
+
callback as dynamic ' Callback function called WITH payload (mutually exclusive with handler)
|
|
411
|
+
handler as dynamic ' Handler function called WITHOUT arguments (mutually exclusive with callback)
|
|
411
412
|
parsePayload as function ' Payload transformation function
|
|
412
413
|
alwaysNotify as boolean ' Field alwaysNotify flag
|
|
413
414
|
|
|
@@ -442,9 +443,13 @@ namespace Rotor
|
|
|
442
443
|
m.once = config?.once ?? false
|
|
443
444
|
m.until = config?.until
|
|
444
445
|
|
|
445
|
-
' Set callback (required)
|
|
446
|
-
m.callback = config?.callback
|
|
447
|
-
|
|
446
|
+
' Set callback or handler (one required, mutually exclusive)
|
|
447
|
+
m.callback = config?.callback
|
|
448
|
+
m.handler = config?.handler
|
|
449
|
+
|
|
450
|
+
if m.callback = invalid and m.handler = invalid
|
|
451
|
+
throw "Observer requires either 'callback' or 'handler' configuration"
|
|
452
|
+
end if
|
|
448
453
|
|
|
449
454
|
' Set payload parser (optional)
|
|
450
455
|
m.parsePayload = config?.parsePayload ?? function(payload)
|
|
@@ -498,14 +503,19 @@ namespace Rotor
|
|
|
498
503
|
' =============================================================
|
|
499
504
|
|
|
500
505
|
' ---------------------------------------------------------------------
|
|
501
|
-
' notify - Executes observer callback
|
|
506
|
+
' notify - Executes observer callback or handler
|
|
502
507
|
'
|
|
503
|
-
' Calls the configured callback
|
|
508
|
+
' Calls the configured callback (with payload) or handler (without arguments)
|
|
509
|
+
' in the correct widget scope.
|
|
504
510
|
'
|
|
505
|
-
' @param {dynamic} payload - Data to pass to callback
|
|
511
|
+
' @param {dynamic} payload - Data to pass to callback (ignored for handler)
|
|
506
512
|
'
|
|
507
513
|
sub notify(payload as dynamic)
|
|
508
|
-
|
|
514
|
+
if m.handler <> invalid
|
|
515
|
+
Rotor.Utils.callbackScoped(m.handler, m.listenerScope)
|
|
516
|
+
else
|
|
517
|
+
Rotor.Utils.callbackScoped(m.callback, m.listenerScope, payload)
|
|
518
|
+
end if
|
|
509
519
|
end sub
|
|
510
520
|
|
|
511
521
|
' =============================================================
|