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 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
- ![Version](https://img.shields.io/badge/version-v0.8.0-blue?label=Documents%20TAG)
99
+ ![Version](https://img.shields.io/badge/version-v0.8.1-blue?label=Documents%20TAG)
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.0",
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.0
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.0"
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.0
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.0"
73
+ version = "0.8.1"
74
74
 
75
75
  config = {
76
76
  tasks: invalid, ' optional
@@ -82,8 +82,6 @@ namespace Rotor
82
82
  ' Called automatically when setProps() is invoked.
83
83
  '
84
84
  sub onUpdateView()
85
- ' Re-render template
86
- m.render()
87
85
  end sub
88
86
 
89
87
  ' ---------------------------------------------------------------------
@@ -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
- while listenerIndex < listenerCount
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.id = nodeId
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.isInFocusChain = isFocused
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 function ' Callback function to execute
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 ?? sub() throw "Callback has not configured for observer"
447
- end Sub
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 function in the correct scope with the payload.
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
- Rotor.Utils.callbackScoped(m.callback, m.listenerScope, payload)
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
  ' =============================================================