From d9e050e4fd9e464b5c0f4e908348290cb805cd0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 2 Aug 2024 01:42:58 -0600 Subject: [PATCH 001/141] refactor(core,framework,signal): Recollect state in `State` constructor to ensure proper binding. --- packages/reactter/lib/src/core/binding_zone.dart | 2 +- packages/reactter/lib/src/core/hook.dart | 2 +- packages/reactter/lib/src/core/state.dart | 4 ++++ .../reactter/lib/src/framework/rt_state.dart | 16 ---------------- .../reactter/lib/src/signal/signal_impl.dart | 2 +- 5 files changed, 7 insertions(+), 19 deletions(-) diff --git a/packages/reactter/lib/src/core/binding_zone.dart b/packages/reactter/lib/src/core/binding_zone.dart index 618343be..c8358074 100644 --- a/packages/reactter/lib/src/core/binding_zone.dart +++ b/packages/reactter/lib/src/core/binding_zone.dart @@ -45,7 +45,7 @@ class BindingZone { for (final state in states) { if (state is State && state.instanceBinded != null) { state._validateInstanceBinded(); - } else { + } else if (state != instance) { state.bind(instance); } } diff --git a/packages/reactter/lib/src/core/hook.dart b/packages/reactter/lib/src/core/hook.dart index b24a22da..cd3e34d1 100644 --- a/packages/reactter/lib/src/core/hook.dart +++ b/packages/reactter/lib/src/core/hook.dart @@ -2,7 +2,7 @@ part of 'core.dart'; /// An abstract class that provides the base functionality for creating /// custom hooks in the Reactter library. -abstract class Hook with State implements StateBase { +abstract class Hook extends State implements StateBase { /// This variable is used to register [Hook] /// and attach the [StateBase] that are defined here. @protected diff --git a/packages/reactter/lib/src/core/state.dart b/packages/reactter/lib/src/core/state.dart index be0699ba..da2cdf07 100644 --- a/packages/reactter/lib/src/core/state.dart +++ b/packages/reactter/lib/src/core/state.dart @@ -4,6 +4,10 @@ part of 'core.dart'; /// state in Reactter. @internal abstract class State implements StateBase { + State() { + BindingZone.recollectState(this); + } + bool _isUpdating = false; /// The reference instance to the current state. diff --git a/packages/reactter/lib/src/framework/rt_state.dart b/packages/reactter/lib/src/framework/rt_state.dart index a33e0ea8..dbbd51a1 100644 --- a/packages/reactter/lib/src/framework/rt_state.dart +++ b/packages/reactter/lib/src/framework/rt_state.dart @@ -22,25 +22,9 @@ abstract class RtState extends State { Logger get logger => Rt; } -/// {@template reactter.rt_state_impl} -/// An implementation of the [RtState]. -/// {@endtemplate} -abstract class RtStateImpl extends RtState { - RtStateImpl() { - BindingZone.recollectState(this); - } -} - /// {@macro reactter.rt_state} @Deprecated( 'Use `RtState` instead. ' 'This feature was deprecated after v7.3.0.', ) typedef ReactterState = RtState; - -/// {@macro reactter.rt_state_impl} -@Deprecated( - 'Use `RtStateImpl` instead. ' - 'This feature was deprecated after v7.3.0.', -) -typedef ReactterStateImpl = RtStateImpl; diff --git a/packages/reactter/lib/src/signal/signal_impl.dart b/packages/reactter/lib/src/signal/signal_impl.dart index a19c316e..4df7dbfd 100644 --- a/packages/reactter/lib/src/signal/signal_impl.dart +++ b/packages/reactter/lib/src/signal/signal_impl.dart @@ -76,7 +76,7 @@ enum SignalEvent { onGetValue, onSetValue } /// /// * [Obj], a base-class that can be used to store a value of [T]. /// {@endtemplate} -class Signal extends RtStateImpl with ObjBase implements Obj { +class Signal extends RtState with ObjBase implements Obj { T _value; /// {@macro signal} From cf8d033365512068ff0262ce19215741ba22af38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Tue, 13 Aug 2024 01:23:03 -0600 Subject: [PATCH 002/141] reword 34f9565 feat(core,framework,test): Implement state observer and add observer manager. \### Enhancements - Add `Rt.addObserver` and `Rt.removeObserver` methods to manage the observers. - Add `RtStateObserver` class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). - Add `debugLabel` and `debugProperties` to state to get info for debugging. \### Internal - Add `StateObserver` test. - Rename some variables to use consistent naming convention. --- .../reactter/lib/src/core/binding_zone.dart | 2 +- packages/reactter/lib/src/core/core.dart | 2 + packages/reactter/lib/src/core/observer.dart | 27 +++ packages/reactter/lib/src/core/state.dart | 93 +++++++--- .../reactter/lib/src/core/state_observer.dart | 37 ++++ .../lib/src/framework/rt_interface.dart | 7 +- .../reactter/lib/src/framework/rt_state.dart | 3 + .../reactter/lib/src/hooks/use_effect.dart | 16 +- .../test/core/state_observer_test.dart | 159 ++++++++++++++++++ .../reactter/test/hooks/use_effect_test.dart | 6 +- 10 files changed, 316 insertions(+), 36 deletions(-) create mode 100644 packages/reactter/lib/src/core/observer.dart create mode 100644 packages/reactter/lib/src/core/state_observer.dart create mode 100644 packages/reactter/test/core/state_observer_test.dart diff --git a/packages/reactter/lib/src/core/binding_zone.dart b/packages/reactter/lib/src/core/binding_zone.dart index c8358074..c12cc130 100644 --- a/packages/reactter/lib/src/core/binding_zone.dart +++ b/packages/reactter/lib/src/core/binding_zone.dart @@ -43,7 +43,7 @@ class BindingZone { } for (final state in states) { - if (state is State && state.instanceBinded != null) { + if (state is State && state.boundInstance != null) { state._validateInstanceBinded(); } else if (state != instance) { state.bind(instance); diff --git a/packages/reactter/lib/src/core/core.dart b/packages/reactter/lib/src/core/core.dart index fc8a4076..619e35c7 100644 --- a/packages/reactter/lib/src/core/core.dart +++ b/packages/reactter/lib/src/core/core.dart @@ -14,7 +14,9 @@ part 'dependency_ref.dart'; part 'lifecycle_observer.dart'; part 'lifecycle.dart'; part 'logger.dart'; +part 'observer.dart'; part 'state_base.dart'; part 'state_management.dart'; +part 'state_observer.dart'; part 'state.dart'; part 'binding_zone.dart'; diff --git a/packages/reactter/lib/src/core/observer.dart b/packages/reactter/lib/src/core/observer.dart new file mode 100644 index 00000000..72ba47a0 --- /dev/null +++ b/packages/reactter/lib/src/core/observer.dart @@ -0,0 +1,27 @@ +part of 'core.dart'; + +/// An abstract class representing an observer. +abstract class Observer {} + +/// An abstract class representing an observer manager. +abstract class ObserverManager { + /// Adds an observer to the manager. + /// + /// The [observer] parameter is the observer to be added. + /// Only [StateObserver] instances can be added. + void addObserver(covariant Observer observer) { + if (observer is StateObserver) { + StateObserver._observers.add(observer); + } + } + + /// Removes an observer from the manager. + /// + /// The [observer] parameter is the observer to be removed. + /// Only [StateObserver] instances can be removed. + void removeObserver(covariant Observer observer) { + if (observer is StateObserver) { + StateObserver._observers.remove(observer); + } + } +} diff --git a/packages/reactter/lib/src/core/state.dart b/packages/reactter/lib/src/core/state.dart index da2cdf07..7db19258 100644 --- a/packages/reactter/lib/src/core/state.dart +++ b/packages/reactter/lib/src/core/state.dart @@ -4,15 +4,17 @@ part of 'core.dart'; /// state in Reactter. @internal abstract class State implements StateBase { - State() { - BindingZone.recollectState(this); - } - bool _isUpdating = false; + /// A label used for debugging purposes. + String get debugLabel => "$runtimeType[$hashCode]"; + + /// A map containing properties used for debugging purposes. + Map get debugProperties => {}; + /// The reference instance to the current state. - Object? get instanceBinded => _instanceBinded; - Object? _instanceBinded; + Object? get boundInstance => _boundInstance; + Object? _boundInstance; /// Returns `true` if the state has been disposed. bool get isDisposed => _isDisposed; @@ -20,22 +22,29 @@ abstract class State implements StateBase { bool get _hasListeners => eventHandler._hasListeners(this) || - (_instanceBinded != null && eventHandler._hasListeners(_instanceBinded)); + (_boundInstance != null && eventHandler._hasListeners(_boundInstance)); + + State() { + BindingZone.recollectState(this); + _notifyCreated(); + } @mustCallSuper @override void bind(Object instance) { assert(!_isDisposed, "Can't bind when it's been disposed"); assert( - _instanceBinded == null, + _boundInstance == null, "Can't bind a new instance because an instance is already.\n" - "Use `detachInstance` method, if you want to bind a new instance.", + "You must unbind the current instance before binding a new one.", ); eventHandler.one(instance, Lifecycle.deleted, _onInstanceDeleted); - _instanceBinded = instance; + _boundInstance = instance; if (BindingZone.currentZone == null) _validateInstanceBinded(); + + _notifyBound(instance); } @override @@ -43,10 +52,12 @@ abstract class State implements StateBase { void unbind() { assert(!_isDisposed, "Can't unbind when it's been disposed"); - if (_instanceBinded == null) return; + if (_boundInstance == null) return; + + _notifyUnbound(); - eventHandler.off(_instanceBinded!, Lifecycle.deleted, _onInstanceDeleted); - _instanceBinded = null; + eventHandler.off(_boundInstance!, Lifecycle.deleted, _onInstanceDeleted); + _boundInstance = null; } @override @@ -56,6 +67,7 @@ abstract class State implements StateBase { if (!_hasListeners || _isUpdating) { fnUpdate(); + _notifyUpdated(); return; } @@ -63,6 +75,7 @@ abstract class State implements StateBase { _notify(Lifecycle.willUpdate); fnUpdate(); _notify(Lifecycle.didUpdate); + _notifyUpdated(); _isUpdating = false; } @@ -72,11 +85,13 @@ abstract class State implements StateBase { assert(!_isDisposed, "Can't refresh when it's been disposed"); if (!_hasListeners || _isUpdating) { - return _notify(Lifecycle.didUpdate); + _notifyUpdated(); + return; } _isUpdating = true; _notify(Lifecycle.didUpdate); + _notifyUpdated(); _isUpdating = false; } @@ -85,22 +100,24 @@ abstract class State implements StateBase { void dispose() { _isDisposed = true; - if (_instanceBinded != null) { - eventHandler.off(_instanceBinded!, Lifecycle.deleted, _onInstanceDeleted); - _instanceBinded = null; + if (_boundInstance != null) { + eventHandler.off(_boundInstance!, Lifecycle.deleted, _onInstanceDeleted); + _boundInstance = null; } eventHandler.offAll(this); + + _notifyDisponsed(); } void _validateInstanceBinded() { - if (dependencyInjection.isActive(instanceBinded)) return; + if (dependencyInjection.isActive(boundInstance)) return; logger.log( - "The instance binded($instanceBinded) to $this is not in Reactter's context and cannot be disposed automatically.\n" + "The instance binded($boundInstance) to $this is not in Reactter's context and cannot be disposed automatically.\n" "You can solve this problem in two ways:\n" "1. Call the 'dispose' method manually when $this is no longer needed.\n" - "2. Create $instanceBinded using the dependency injection methods.\n" + "2. Create $boundInstance using the dependency injection methods.\n" "**Ignore this message if you are sure that it will be disposed.**", level: LogLevel.warning, ); @@ -112,7 +129,7 @@ abstract class State implements StateBase { /// Notifies the listeners about the specified [event]. /// If [Rt._isUntrackedRunning] is true, the notification is skipped. /// If [Rt._isBatchRunning] is true, the notification is deferred until the batch is completed. - /// The [event] is emitted using [Rt.emit] for the current instance and [_instanceBinded]. + /// The [event] is emitted using [Rt.emit] for the current instance and [_boundInstance]. void _notify(Enum event) { if (stateManagment._isUntrackedRunning) return; @@ -122,8 +139,38 @@ abstract class State implements StateBase { emit(this, event, this); - if (_instanceBinded == null) return; + if (_boundInstance != null) { + emit(_boundInstance!, event, this); + } + } + + void _notifyCreated() { + for (final observer in StateObserver._observers) { + observer.onStateCreated(this); + } + } + + void _notifyBound(Object instance) { + for (final observer in StateObserver._observers) { + observer.onStateBound(this, instance); + } + } - emit(_instanceBinded!, event, this); + void _notifyUnbound() { + for (final observer in StateObserver._observers) { + observer.onStateUnbound(this, _boundInstance!); + } + } + + void _notifyUpdated() { + for (final observer in StateObserver._observers) { + observer.onStateUpdated(this); + } + } + + void _notifyDisponsed() { + for (final observer in StateObserver._observers) { + observer.onStateDisposed(this); + } } } diff --git a/packages/reactter/lib/src/core/state_observer.dart b/packages/reactter/lib/src/core/state_observer.dart new file mode 100644 index 00000000..dbb527b9 --- /dev/null +++ b/packages/reactter/lib/src/core/state_observer.dart @@ -0,0 +1,37 @@ +part of 'core.dart'; + +/// {@template reactter.state_observer} +/// An abstract class that defines the interface for observing state changes. +/// Implementations of this class can be used to monitor the lifecycle of states. +/// {@endtemplate} +abstract class StateObserver implements Observer { + /// A set of all registered state observers. + static final _observers = {}; + + /// Called when a state is created. + /// + /// [state] - The state that was created. + void onStateCreated(covariant State state); + + /// Called when a state is bound to an instance. + /// + /// [state] - The state that was bound. + /// [instance] - The instance to which the state was bound. + void onStateBound(covariant State state, Object instance); + + /// Called when a state is unbound from an instance. + /// + /// [state] - The state that was unbound. + /// [instance] - The instance from which the state was unbound. + void onStateUnbound(covariant State state, Object instance); + + /// Called when a state is updated. + /// + /// [state] - The state that was updated. + void onStateUpdated(covariant State state); + + /// Called when a state is disposed. + /// + /// [state] - The state that was disposed. + void onStateDisposed(covariant State state); +} diff --git a/packages/reactter/lib/src/framework/rt_interface.dart b/packages/reactter/lib/src/framework/rt_interface.dart index a1e2df2f..0525cbd9 100644 --- a/packages/reactter/lib/src/framework/rt_interface.dart +++ b/packages/reactter/lib/src/framework/rt_interface.dart @@ -18,7 +18,12 @@ void defaultLogWriterCallback( /// It is intended to be used as a mixin with other classes. /// {@endtemplate} class RtInterface - with StateManagement, DependencyInjection, EventHandler, Logger { + with + StateManagement, + DependencyInjection, + EventHandler, + Logger, + ObserverManager { static final _reactterInterface = RtInterface._(); factory RtInterface() => _reactterInterface; RtInterface._(); diff --git a/packages/reactter/lib/src/framework/rt_state.dart b/packages/reactter/lib/src/framework/rt_state.dart index dbbd51a1..d32249bc 100644 --- a/packages/reactter/lib/src/framework/rt_state.dart +++ b/packages/reactter/lib/src/framework/rt_state.dart @@ -28,3 +28,6 @@ abstract class RtState extends State { 'This feature was deprecated after v7.3.0.', ) typedef ReactterState = RtState; + +/// {@macro reactter.state_observer} +typedef RtStateObserver = StateObserver; diff --git a/packages/reactter/lib/src/hooks/use_effect.dart b/packages/reactter/lib/src/hooks/use_effect.dart index 1d65c41b..77997ce7 100644 --- a/packages/reactter/lib/src/hooks/use_effect.dart +++ b/packages/reactter/lib/src/hooks/use_effect.dart @@ -162,7 +162,7 @@ class UseEffect extends RtHook { @override void bind(Object instance) { - final shouldListen = instanceBinded == null; + final shouldListen = boundInstance == null; super.bind(instance); @@ -170,9 +170,9 @@ class UseEffect extends RtHook { _watchInstanceAttached(); - if (!_isDispatched && instanceBinded is DispatchEffect) { - _runCleanupAndUnwatchDependencies(instanceBinded); - _runCallbackAndWatchDependencies(instanceBinded); + if (!_isDispatched && boundInstance is DispatchEffect) { + _runCleanupAndUnwatchDependencies(boundInstance); + _runCallbackAndWatchDependencies(boundInstance); return; } @@ -200,12 +200,12 @@ class UseEffect extends RtHook { void _watchInstanceAttached() { Rt.on( - instanceBinded!, + boundInstance!, Lifecycle.didMount, _runCallbackAndWatchDependencies, ); Rt.on( - instanceBinded!, + boundInstance!, Lifecycle.willUnmount, _runCleanupAndUnwatchDependencies, ); @@ -213,12 +213,12 @@ class UseEffect extends RtHook { void _unwatchInstanceAttached() { Rt.off( - instanceBinded!, + boundInstance!, Lifecycle.didMount, _runCallbackAndWatchDependencies, ); Rt.off( - instanceBinded!, + boundInstance!, Lifecycle.willUnmount, _runCleanupAndUnwatchDependencies, ); diff --git a/packages/reactter/test/core/state_observer_test.dart b/packages/reactter/test/core/state_observer_test.dart new file mode 100644 index 00000000..4838ad93 --- /dev/null +++ b/packages/reactter/test/core/state_observer_test.dart @@ -0,0 +1,159 @@ +import 'package:reactter/reactter.dart'; +import 'package:reactter/src/core/core.dart'; +import 'package:test/test.dart'; + +class StateObserverTest extends StateObserver { + int onStateCreatedCalledCount = 0; + String? lastStateCreated; + + int onStateBoundCalledCount = 0; + String? lastStateBound; + Object? lastInstanceBound; + + int onStateUnboundCalledCount = 0; + String? lastStateUnbound; + + int onStateUpdatedCalledCount = 0; + String? lastStateUpdated; + + int onStateDisposedCalledCount = 0; + String? lastStateDisposed; + + @override + void onStateCreated(covariant State state) { + lastStateCreated = state.debugLabel; + onStateCreatedCalledCount++; + } + + @override + void onStateBound(covariant State state, Object instance) { + lastStateBound = state.debugLabel; + lastInstanceBound = instance; + onStateBoundCalledCount++; + } + + @override + void onStateUnbound(covariant State state, Object instance) { + lastStateUnbound = state.debugLabel; + onStateUnboundCalledCount++; + } + + @override + void onStateUpdated(covariant State state) { + lastStateUpdated = state.debugLabel; + onStateUpdatedCalledCount++; + } + + @override + void onStateDisposed(covariant State state) { + lastStateDisposed = state.debugLabel; + onStateDisposedCalledCount++; + } +} + +void main() { + group("StateObserverTest", () { + test("should be observed when a state is created", () { + final observer = StateObserverTest(); + Rt.addObserver(observer); + + UseState(0, debugLabel: "stateA"); + + expect(observer.onStateCreatedCalledCount, 1); + expect(observer.lastStateCreated, "stateA"); + + UseState(1, debugLabel: "stateB"); + + expect(observer.onStateCreatedCalledCount, 2); + expect(observer.lastStateCreated, "stateB"); + + Rt.removeObserver(observer); + }); + + test("should be observed when a state is bound", () { + final observer = StateObserverTest(); + Rt.addObserver(observer); + + final stateA = UseState(0, debugLabel: "stateA"); + final instanceA = Object(); + stateA.bind(instanceA); + + expect(observer.onStateBoundCalledCount, 1); + expect(observer.lastStateBound, "stateA"); + expect(observer.lastInstanceBound, instanceA); + + final stateB = UseState(1, debugLabel: "stateB"); + final instanceB = Object(); + stateB.bind(instanceB); + + expect(observer.onStateBoundCalledCount, 2); + expect(observer.lastStateBound, "stateB"); + expect(observer.lastInstanceBound, instanceB); + + Rt.removeObserver(observer); + }); + + test("should be observed when a state is unbound", () { + final observer = StateObserverTest(); + Rt.addObserver(observer); + + final stateA = UseState(0, debugLabel: "stateA"); + final instanceA = Object(); + stateA.bind(instanceA); + stateA.unbind(); + + expect(observer.onStateUnboundCalledCount, 1); + expect(observer.lastStateUnbound, "stateA"); + expect(observer.lastInstanceBound, instanceA); + + final stateB = UseState(1, debugLabel: "stateB"); + final instanceB = Object(); + stateB.bind(instanceB); + stateB.unbind(); + + expect(observer.onStateUnboundCalledCount, 2); + expect(observer.lastStateUnbound, "stateB"); + expect(observer.lastInstanceBound, instanceB); + + Rt.removeObserver(observer); + }); + + test("should be observed when a state is updated", () { + final observer = StateObserverTest(); + Rt.addObserver(observer); + + final stateA = UseState(0, debugLabel: "stateA"); + stateA.value = 1; + + expect(observer.onStateUpdatedCalledCount, 1); + expect(observer.lastStateUpdated, "stateA"); + + final stateB = UseState(1, debugLabel: "stateB"); + stateB.value = 2; + + expect(observer.onStateUpdatedCalledCount, 2); + expect(observer.lastStateUpdated, "stateB"); + + Rt.removeObserver(observer); + }); + + test("should be observed when a state is disposed", () { + final observer = StateObserverTest(); + Rt.addObserver(observer); + + final stateA = UseState(0, debugLabel: "stateA"); + stateA.dispose(); + + expect(observer.onStateDisposedCalledCount, 1); + expect(observer.lastStateDisposed, "stateA"); + + final stateB = UseState(1, debugLabel: "stateB"); + stateB.dispose(); + + expect(observer.onStateDisposedCalledCount, 2); + expect(observer.lastStateDisposed, "stateB"); + + Rt.removeObserver(observer); + }); + }); +} diff --git a/packages/reactter/test/hooks/use_effect_test.dart b/packages/reactter/test/hooks/use_effect_test.dart index 66f09ce0..420fe724 100644 --- a/packages/reactter/test/hooks/use_effect_test.dart +++ b/packages/reactter/test/hooks/use_effect_test.dart @@ -8,11 +8,11 @@ void main() { final testController = Rt.create(() => TestController()); final uEffect = UseEffect(() {}, [])..bind(testController!); - expect(uEffect.instanceBinded, isA()); + expect(uEffect.boundInstance, isA()); uEffect.unbind(); - expect(uEffect.instanceBinded, isNull); + expect(uEffect.boundInstance, isNull); Rt.delete(); }); @@ -146,7 +146,7 @@ void main() { expect(nCalls, 0); expect(uEffect, isA()); - expect(uEffect.instanceBinded, isA()); + expect(uEffect.boundInstance, isA()); expect(nCalls, 1); }); From 1573490bc9d7c50161d10af6525e98338c194886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Thu, 15 Aug 2024 10:51:18 -0600 Subject: [PATCH 003/141] refactor(hooks): Add support `debugLabel` and `debugProperties` in `useAsyncSstate`. ## Enhancements - Add `debugLabel` argument to `UseAsyncState` and `UseAsyncState.withArg`. ## Breakings - Move `asyncFunction` to the first argument of `UseAsyncState`and `UseAsyncState.withArg`. --- .../lib/src/hooks/use_async_state.dart | 55 ++++++++++++++----- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/packages/reactter/lib/src/hooks/use_async_state.dart b/packages/reactter/lib/src/hooks/use_async_state.dart index 230b1c35..f8e44b0c 100644 --- a/packages/reactter/lib/src/hooks/use_async_state.dart +++ b/packages/reactter/lib/src/hooks/use_async_state.dart @@ -28,11 +28,23 @@ abstract class UseAsyncStateBase extends RtHook { Object? get error => _error.value; UseAsyncStateStatus get status => _status.value; + final String? _debugLabel; + @override + String get debugLabel => _debugLabel ?? super.debugLabel; + @override + Map get debugProperties => { + 'value': value, + 'error': error, + 'status': status, + }; + UseAsyncStateBase( - T initialValue, Function asyncFunction, - ) : _initialValue = initialValue, + T initialValue, { + String? debugLabel, + }) : _initialValue = initialValue, _asyncFunction = asyncFunction, + _debugLabel = debugLabel, _value = UseState(initialValue); /// Execute [asyncFunction] to resolve [value]. @@ -104,7 +116,7 @@ abstract class UseAsyncStateBase extends RtHook { } } -/// {@template use_async_state} +/// {@template reactter.use_async_state} /// A [ReactteHook] that manages the state as async way. /// /// [T] is use to define the type of [value]. @@ -156,18 +168,28 @@ abstract class UseAsyncStateBase extends RtHook { /// * [UseAsyncStateArg], the same as it, but with arguments. /// {@endtemplate} class UseAsyncState extends UseAsyncStateBase { - /// {@macro use_async_state} + /// {@macro reactter.use_async_state} UseAsyncState( - T initialValue, AsyncFunction asyncFunction, - ) : super(initialValue, asyncFunction); + T initialValue, { + String? debugLabel, + }) : super( + asyncFunction, + initialValue, + debugLabel: debugLabel, + ); - /// {@macro use_async_state_arg} + /// {@macro reactter.use_async_state_arg} static UseAsyncStateArg withArg( - T initialValue, AsyncFunctionArg asyncFunction, - ) { - return UseAsyncStateArg(initialValue, asyncFunction); + T initialValue, { + String? debugLabel, + }) { + return UseAsyncStateArg( + asyncFunction, + initialValue, + debugLabel: debugLabel, + ); } /// Execute [asyncFunction] to resolve [value]. @@ -176,7 +198,7 @@ class UseAsyncState extends UseAsyncStateBase { } } -/// {@template use_async_state_arg} +/// {@template reactter.use_async_state_arg} /// A [ReactteHook] that manages the state as async way. /// /// [T] is use to define the type of [value] @@ -229,11 +251,16 @@ class UseAsyncState extends UseAsyncStateBase { /// * [Args], a generic arguments. /// {@endtemplate} class UseAsyncStateArg extends UseAsyncStateBase { - /// {@macro use_async_state_arg} + /// {@macro reactter.use_async_state_arg} UseAsyncStateArg( - T initialValue, AsyncFunctionArg asyncFunction, - ) : super(initialValue, asyncFunction); + T initialValue, { + String? debugLabel, + }) : super( + asyncFunction, + initialValue, + debugLabel: debugLabel, + ); /// Execute [asyncFunction] to resolve [value]. FutureOr resolve(A arg) async { From dcdb5aa0cfb696a50f2a6981b43c79cd5ae00892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Thu, 15 Aug 2024 15:45:35 -0600 Subject: [PATCH 004/141] refactor(hooks): Add support `debugLabel` and `debugProperties` in the states. ## Enhancements - Add `debugLabel` argument to `UseCompute`, `UseDependency`, `UseEffect`, `UseReduce`, `UseState` and `Signal`. --- .../reactter/lib/src/hooks/use_compute.dart | 18 +- .../lib/src/hooks/use_dependency.dart | 192 +++++++++++------- .../reactter/lib/src/hooks/use_effect.dart | 26 ++- .../reactter/lib/src/hooks/use_reducer.dart | 17 +- .../reactter/lib/src/hooks/use_state.dart | 16 +- .../reactter/lib/src/signal/signal_impl.dart | 21 +- 6 files changed, 198 insertions(+), 92 deletions(-) diff --git a/packages/reactter/lib/src/hooks/use_compute.dart b/packages/reactter/lib/src/hooks/use_compute.dart index 983d3650..8249872c 100644 --- a/packages/reactter/lib/src/hooks/use_compute.dart +++ b/packages/reactter/lib/src/hooks/use_compute.dart @@ -1,6 +1,6 @@ part of 'hooks.dart'; -/// {@template use_compute} +/// {@template reactter.use_compute} /// A [RtHook] that allows to compute a value /// using a predetermined [compute] function and a list of state [dependencies], /// and which automatically updates the computed [value] if a dependency changes. @@ -44,11 +44,21 @@ class UseCompute extends RtHook { T get value => _valueComputed; - /// {@macro use_compute} + final String? _debugLabel; + @override + String get debugLabel => _debugLabel ?? super.debugLabel; + @override + Map get debugProperties => { + 'value': value, + 'dependencies': dependencies, + }; + + /// {@macro reactter.use_compute} UseCompute( this.compute, - this.dependencies, - ) { + this.dependencies, { + String? debugLabel, + }) : _debugLabel = debugLabel { _valueComputed = compute(); for (var dependency in dependencies) { diff --git a/packages/reactter/lib/src/hooks/use_dependency.dart b/packages/reactter/lib/src/hooks/use_dependency.dart index 427f0323..e97e4bd4 100644 --- a/packages/reactter/lib/src/hooks/use_dependency.dart +++ b/packages/reactter/lib/src/hooks/use_dependency.dart @@ -6,7 +6,7 @@ part of 'hooks.dart'; ) typedef UseInstance = UseDependency; -/// {@template use_dependency} +/// {@template reactter.use_dependency} /// A [RtHook] that allows to manages a dependency of [T] with/without [id]. /// /// ```dart @@ -37,36 +37,36 @@ typedef UseInstance = UseDependency; /// The instance must be created by [DependencyInjection] using the following methods: /// /// - **Rt.get**: -/// {@macro get} +/// {@macro reactter.get} /// - **Rt.create**: -/// {@macro create} +/// {@macro reactter.create} /// - **Rt.builder**: -/// {@macro builder} +/// {@macro reactter.builder} /// - **Rt.singleton**: -/// {@macro builder} +/// {@macro reactter.builder} /// /// or created by [RtProvider] of [`flutter_reactter`](https://pub.dev/packages/flutter_reactter) /// /// [UseDependency] providers the following constructors: /// /// - **[UseDependency.register]**: -/// {@macro register} +/// {@macro reactter.register} /// - **[UseDependency.lazyBuilder]**: -/// {@macro lazy_builder} +/// {@macro reactter.lazy_builder} /// - **[UseDependency.lazyFactory]**: /// {@macro lazy_factory} /// - **[UseDependency.lazySingleton]**: -/// {@macro lazy_singleton} +/// {@macro reactter.lazy_singleton} /// - **[UseDependency.create]**: -/// {@macro create} +/// {@macro reactter.create} /// - **[UseDependency.builder]**: -/// {@macro builder} +/// {@macro reactter.builder} /// - **[UseDependency.factory]**: -/// {@macro factory} +/// {@macro reactter.factory} /// - **[UseDependency.singleton]**: -/// {@macro singleton} +/// {@macro reactter.singleton} /// - **[UseDependency.get]**: -/// {@macro get} +/// {@macro reactter.get} /// /// > **IMPORTANT** /// > You should call [dispose] when it's no longer needed. @@ -108,18 +108,31 @@ class UseDependency extends RtHook { /// It's used to identify the instance of [T] dependency. final String? id; - /// {@macro use_dependency} - UseDependency([this.id]) { + final String? _debugLabel; + @override + String get debugLabel => _debugLabel ?? super.debugLabel; + @override + Map get debugProperties => { + 'instance': instance, + 'id': id, + }; + + /// {@macro reactter.use_dependency} + UseDependency({ + this.id, + String? debugLabel, + }) : _debugLabel = debugLabel { _instance = Rt.find(id); _listen(); } - /// {@macro register} + /// {@macro reactter.register} UseDependency.register( InstanceBuilder builder, { DependencyMode mode = DependencyMode.builder, this.id, - }) { + String? debugLabel, + }) : _debugLabel = debugLabel { Rt.register( builder, id: id, @@ -128,37 +141,55 @@ class UseDependency extends RtHook { _listen(); } - /// {@macro lazy_builder} - factory UseDependency.lazyBuilder(InstanceBuilder builder, [String? id]) => - UseDependency.register( - builder, - mode: DependencyMode.builder, - id: id, - ); - - /// {@macro lazy_factory} - factory UseDependency.lazyFactory(InstanceBuilder builder, [String? id]) => - UseDependency.register( - builder, - mode: DependencyMode.factory, - id: id, - ); - - /// {@macro lazy_singleton} - factory UseDependency.lazySingleton(InstanceBuilder builder, - [String? id]) => - UseDependency.register( - builder, - mode: DependencyMode.singleton, - id: id, - ); - - /// {@macro create} + /// {@macro reactter.lazy_builder} + factory UseDependency.lazyBuilder( + InstanceBuilder builder, { + String? id, + String? debugLabel, + }) { + return UseDependency.register( + builder, + mode: DependencyMode.builder, + id: id, + debugLabel: debugLabel, + ); + } + + /// {@macro reactter.lazy_factory} + factory UseDependency.lazyFactory( + InstanceBuilder builder, { + String? id, + String? debugLabel, + }) { + return UseDependency.register( + builder, + mode: DependencyMode.factory, + id: id, + debugLabel: debugLabel, + ); + } + + /// {@macro reactter.lazy_singleton} + factory UseDependency.lazySingleton( + InstanceBuilder builder, { + String? id, + String? debugLabel, + }) { + return UseDependency.register( + builder, + mode: DependencyMode.singleton, + id: id, + debugLabel: debugLabel, + ); + } + + /// {@macro reactter.create} UseDependency.create( InstanceBuilder builder, { this.id, DependencyMode mode = DependencyMode.builder, - }) { + String? debugLabel, + }) : _debugLabel = debugLabel { _instance = Rt.create( builder, id: id, @@ -168,32 +199,53 @@ class UseDependency extends RtHook { _listen(); } - /// {@macro builder} - factory UseDependency.builder(InstanceBuilder builder, [String? id]) => - UseDependency.create( - builder, - id: id, - mode: DependencyMode.builder, - ); - - /// {@macro factory} - factory UseDependency.factory(InstanceBuilder builder, [String? id]) => - UseDependency.create( - builder, - id: id, - mode: DependencyMode.factory, - ); - - /// {@macro singleton} - factory UseDependency.singleton(InstanceBuilder builder, [String? id]) => - UseDependency.create( - builder, - id: id, - mode: DependencyMode.singleton, - ); - - /// {@macro get} - UseDependency.get([this.id]) { + /// {@macro reactter.builder} + factory UseDependency.builder( + InstanceBuilder builder, { + String? id, + String? debugLabel, + }) { + return UseDependency.create( + builder, + id: id, + mode: DependencyMode.builder, + debugLabel: debugLabel, + ); + } + + /// {@macro reactter.factory} + factory UseDependency.factory( + InstanceBuilder builder, { + String? id, + String? debugLabel, + }) { + return UseDependency.create( + builder, + id: id, + mode: DependencyMode.factory, + debugLabel: debugLabel, + ); + } + + /// {@macro reactter.singleton} + factory UseDependency.singleton( + InstanceBuilder builder, { + String? id, + String? debugLabel, + }) { + return UseDependency.create( + builder, + id: id, + mode: DependencyMode.singleton, + debugLabel: debugLabel, + ); + } + + /// {@macro reactter.get} + UseDependency.get({ + this.id, + String? debugLabel, + }) : _debugLabel = debugLabel { _instance = Rt.get(id, this); _listen(); } diff --git a/packages/reactter/lib/src/hooks/use_effect.dart b/packages/reactter/lib/src/hooks/use_effect.dart index 77997ce7..72e81557 100644 --- a/packages/reactter/lib/src/hooks/use_effect.dart +++ b/packages/reactter/lib/src/hooks/use_effect.dart @@ -1,6 +1,6 @@ part of 'hooks.dart'; -/// {@template use_effect} +/// {@template reactter.use_effect} /// A [RtHook] that manages `side-effect`. /// /// @@ -142,15 +142,31 @@ class UseEffect extends RtHook { /// It's used to store the states as dependencies of [UseEffect]. final List dependencies; - /// {@macro use_effect} - UseEffect(this.callback, this.dependencies) { + final String? _debugLabel; + @override + String get debugLabel => _debugLabel ?? super.debugLabel; + @override + Map get debugProperties => { + 'dependencies': dependencies, + }; + + /// {@macro reactter.use_effect} + UseEffect( + this.callback, + this.dependencies, { + String? debugLabel, + }) : _debugLabel = debugLabel { if (BindingZone.currentZone != null) return; _watchDependencies(); } - /// {@macro use_effect} - UseEffect.runOnInit(this.callback, this.dependencies) : super() { + /// {@macro reactter.use_effect} + UseEffect.runOnInit( + this.callback, + this.dependencies, { + String? debugLabel, + }) : _debugLabel = debugLabel { _runCallback(this, this); _isUpdating = false; _isDispatched = true; diff --git a/packages/reactter/lib/src/hooks/use_reducer.dart b/packages/reactter/lib/src/hooks/use_reducer.dart index 3572f0bb..d6d09fb4 100644 --- a/packages/reactter/lib/src/hooks/use_reducer.dart +++ b/packages/reactter/lib/src/hooks/use_reducer.dart @@ -58,11 +58,18 @@ class UseReducer extends RtHook { T get value => _state.value; - /// {@macro use_reducer} - UseReducer( - this.reducer, - T initialState, - ) : _state = UseState(initialState) { + final String? _debugLabel; + @override + String get debugLabel => _debugLabel ?? super.debugLabel; + @override + Map get debugProperties => { + 'value': value, + }; + + /// {@macro reactter.use_reducer} + UseReducer(this.reducer, T initialState, {String? debugLabel}) + : _state = UseState(initialState), + _debugLabel = debugLabel { UseEffect(update, [_state]); } diff --git a/packages/reactter/lib/src/hooks/use_state.dart b/packages/reactter/lib/src/hooks/use_state.dart index 1bb7b6b0..d435d107 100644 --- a/packages/reactter/lib/src/hooks/use_state.dart +++ b/packages/reactter/lib/src/hooks/use_state.dart @@ -1,6 +1,6 @@ part of 'hooks.dart'; -/// {@template use_state} +/// {@template reactter.use_state} /// A [RtHook] that manages a state. /// /// Contains a [value] of type [T] which represents the current state. @@ -44,8 +44,11 @@ class UseState extends RtHook { @override final $ = RtHook.$register; - /// {@macro use_state} - UseState(T initialValue) : _value = initialValue; + final String? _debugLabel; + @override + String get debugLabel => _debugLabel ?? super.debugLabel; + @override + Map get debugProperties => {'value': value}; T _value; @@ -58,4 +61,11 @@ class UseState extends RtHook { update(() => _value = value); } } + + /// {@macro reactter.use_state} + UseState( + T initialValue, { + String? debugLabel, + }) : _value = initialValue, + _debugLabel = debugLabel; } diff --git a/packages/reactter/lib/src/signal/signal_impl.dart b/packages/reactter/lib/src/signal/signal_impl.dart index 4df7dbfd..02bbb50a 100644 --- a/packages/reactter/lib/src/signal/signal_impl.dart +++ b/packages/reactter/lib/src/signal/signal_impl.dart @@ -4,7 +4,7 @@ part of 'signal.dart'; /// getting or setting the value of a `Signal` object. enum SignalEvent { onGetValue, onSetValue } -/// {@template signal} +/// {@template reactter.signal} /// A base-class that store a value of [T] and notify the listeners /// when the value is updated. /// @@ -77,14 +77,25 @@ enum SignalEvent { onGetValue, onSetValue } /// * [Obj], a base-class that can be used to store a value of [T]. /// {@endtemplate} class Signal extends RtState with ObjBase implements Obj { - T _value; - - /// {@macro signal} - Signal(T value) : _value = value; + /// {@macro reactter.signal} + Signal( + T value, { + String? debugLabel, + }) : _value = value, + _debugLabel = debugLabel; bool _shouldGetValueNotify = true; bool _shouldSetValueNotify = true; + T _value; + final String? _debugLabel; + + @override + String get debugLabel => _debugLabel ?? super.debugLabel; + + @override + Map get debugProperties => {'value': value}; + /// Returns the [value] of the signal. @override T get value { From 044ae65adc2ceaae6fc9e68fca65ed3f8e508f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Thu, 15 Aug 2024 15:52:08 -0600 Subject: [PATCH 005/141] refactor(test): Update `UseDependency` to support `id` parameter in tests. --- .../test/hooks/use_dependency_test.dart | 39 +++++++++++++------ .../test/shareds/test_controllers.dart | 13 ++++--- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/packages/reactter/test/hooks/use_dependency_test.dart b/packages/reactter/test/hooks/use_dependency_test.dart index e8da2cb0..5a4b9e2e 100644 --- a/packages/reactter/test/hooks/use_dependency_test.dart +++ b/packages/reactter/test/hooks/use_dependency_test.dart @@ -61,8 +61,10 @@ void main() { }); test("should register a dependency with id in builder mode", () { - final useDependency = - UseDependency.lazyBuilder(() => TestController(), ID); + final useDependency = UseDependency.lazyBuilder( + () => TestController(), + id: ID, + ); expect(useDependency.instance, null); final instance = Rt.get(ID); @@ -94,8 +96,10 @@ void main() { }); test("should register a dependency with id in factory mode", () { - final useDependency = - UseDependency.lazyFactory(() => TestController(), ID); + final useDependency = UseDependency.lazyFactory( + () => TestController(), + id: ID, + ); expect(useDependency.instance, null); final instance = Rt.get(ID); @@ -127,8 +131,10 @@ void main() { }); test("should register a dependency with id in singleton mode", () { - final useDependency = - UseDependency.lazySingleton(() => TestController(), ID); + final useDependency = UseDependency.lazySingleton( + () => TestController(), + id: ID, + ); expect(useDependency.instance, null); final instance = Rt.get(ID); @@ -167,7 +173,10 @@ void main() { }); test("should create a dependency with id in builder mode", () { - final useDependency = UseDependency.builder(() => TestController(), ID); + final useDependency = UseDependency.builder( + () => TestController(), + id: ID, + ); expect(useDependency.instance, isA()); final isActive = Rt.isActive(useDependency.instance); @@ -193,7 +202,10 @@ void main() { }); test("should create a dependency with id in factory mode", () { - final useDependency = UseDependency.factory(() => TestController(), ID); + final useDependency = UseDependency.factory( + () => TestController(), + id: ID, + ); expect(useDependency.instance, isA()); final isActive = Rt.isActive(useDependency.instance); @@ -219,7 +231,10 @@ void main() { }); test("should create a dependency with id in singleton mode", () { - final useDependency = UseDependency.singleton(() => TestController(), ID); + final useDependency = UseDependency.singleton( + () => TestController(), + id: ID, + ); expect(useDependency.instance, isA()); final isActive = Rt.isActive(useDependency.instance); @@ -246,7 +261,7 @@ void main() { test("should get a dependency with id", () { Rt.register(() => TestController(), id: ID); - final useDependency = UseDependency.get(ID); + final useDependency = UseDependency.get(id: ID); expect(useDependency.instance, isA()); final isActive = Rt.isActive(useDependency.instance); @@ -260,7 +275,7 @@ void main() { void _testController([String? id]) { Rt.create(() => TestController(), id: id); - final useDependency = UseDependency(id); + final useDependency = UseDependency(id: id); expect(useDependency.instance, isA()); @@ -269,7 +284,7 @@ void _testController([String? id]) { void _testControllerLate([String? id]) { late final TestController instance; - final useDependency = UseDependency(id); + final useDependency = UseDependency(id: id); UseEffect(() { if (useDependency.instance != null) { diff --git a/packages/reactter/test/shareds/test_controllers.dart b/packages/reactter/test/shareds/test_controllers.dart index bb70ac29..4a2ee957 100644 --- a/packages/reactter/test/shareds/test_controllers.dart +++ b/packages/reactter/test/shareds/test_controllers.dart @@ -84,14 +84,17 @@ class TestController extends RtState { final stateList = UseState([]); final stateMap = UseState({}); final stateClass = UseState(null); - final stateAsync = UseAsyncState("initial", _resolveStateAsync); + final stateAsync = UseAsyncState(_resolveStateAsync, "initial"); final stateAsyncWithArg = UseAsyncState.withArg( - "initial", _resolveStateAsync, + "initial", + ); + final stateAsyncWithError = UseAsyncState( + () { + throw Exception("has a error"); + }, + "initial", ); - final stateAsyncWithError = UseAsyncState("initial", () { - throw Exception("has a error"); - }); final stateReduce = UseReducer(_reducer, TestStore(count: 0)); late final stateCompute = Rt.lazyState( From ae53da7afd7f862ca4a47451b31b1e9b0addbbe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Thu, 15 Aug 2024 15:54:54 -0600 Subject: [PATCH 006/141] refactor: Update annotated markdown references for clarity. --- .../lib/src/framework/provider_base.dart | 16 ++++++------- packages/reactter/lib/src/args.dart | 16 ++++++------- .../lib/src/core/dependency_injection.dart | 24 +++++++++---------- .../lib/src/core/dependency_mode.dart | 8 ++++--- .../lib/src/core/lifecycle_observer.dart | 2 +- .../lib/src/core/state_management.dart | 6 ++--- packages/reactter/lib/src/memo/memo_impl.dart | 4 ++-- packages/reactter/lib/src/obj/obj_impl.dart | 4 ++-- 8 files changed, 41 insertions(+), 39 deletions(-) diff --git a/packages/flutter_reactter/lib/src/framework/provider_base.dart b/packages/flutter_reactter/lib/src/framework/provider_base.dart index e7dfc719..22947171 100644 --- a/packages/flutter_reactter/lib/src/framework/provider_base.dart +++ b/packages/flutter_reactter/lib/src/framework/provider_base.dart @@ -4,7 +4,7 @@ part of '../framework.dart'; @internal abstract class ProviderBase extends Widget { - /// {@template provider_base.id} + /// {@template flutter_reactter.provider_base.id} /// It's used to identify the dependency of [T] type /// that is provided by the provider. /// @@ -16,7 +16,7 @@ abstract class ProviderBase extends Widget { /// {@endtemplate} final String? id; - /// {@template provider_base.mode} + /// {@template flutter_reactter.provider_base.mode} /// It's used to specify the type of dependency creation for the provided object. /// /// It is of mode [DependencyMode], which is an enum with three possible values: @@ -24,27 +24,27 @@ abstract class ProviderBase extends Widget { /// {@endtemplate} final DependencyMode mode; - /// {@template provider_base.instanceBuilder} + /// {@template flutter_reactter.provider_base.instanceBuilder} /// Create a [T] instance. /// {@endtemplate} @protected final InstanceBuilder instanceBuilder; - /// {@template provider_base.init} + /// {@template flutter_reactter.provider_base.init} /// Immediately create the dependency defined /// on firts parameter([instanceBuilder]). /// {@endtemplate} @protected final bool init; - /// {@template provider_base.isLazy} + /// {@template flutter_reactter.provider_base.isLazy} /// Lazily create the dependency defined /// on firts parameter([instanceBuilder]). /// {@endtemplate} @protected final bool isLazy; - /// {@template provider_base.builder} + /// {@template flutter_reactter.provider_base.builder} /// Method which has the render logic /// /// Exposes [BuilderContext], instance of the dependency and [child] widget as arguments. @@ -53,7 +53,7 @@ abstract class ProviderBase extends Widget { @protected final InstanceChildBuilder? builder; - /// {@template provider_base.lazyBuilder} + /// {@template flutter_reactter.provider_base.lazyBuilder} /// Method which has the render logic /// /// Exposes [BuilderContext] and [child] widget as arguments. @@ -62,7 +62,7 @@ abstract class ProviderBase extends Widget { @protected final ChildBuilder? lazyBuilder; - /// {@template provider_base.init} + /// {@template flutter_reactter.provider_base.init} /// The child widget that will be wrapped by the provider. /// The child widget can be accessed within the `builder` method of the provider. /// {@endtemplate} diff --git a/packages/reactter/lib/src/args.dart b/packages/reactter/lib/src/args.dart index 1618ae6b..28aa1c4d 100644 --- a/packages/reactter/lib/src/args.dart +++ b/packages/reactter/lib/src/args.dart @@ -1,12 +1,12 @@ import 'dart:async'; -/// {@template args} +/// {@template reactter.args} /// A class that represents an argument /// {@endtemplate} class Args { final List arguments; - /// {@macro args} + /// {@macro reactter.args} const Args([this.arguments = const []]); /// Returns the first element of the `arguments` list. @@ -25,14 +25,14 @@ class Args { other is Args && this.hashCode == other.hashCode; } -/// {@template args1} +/// {@template reactter.args1} /// A class that represents an argument /// {@endtemplate} class Args1 extends Args { @override final A arg1; - /// {@macro args1} + /// {@macro reactter.args1} const Args1(this.arg1) : super(); /// Returns a list containing all arguments. @@ -43,13 +43,13 @@ class Args1 extends Args { List get arguments => [arg1]; } -/// {@template args2} +/// {@template reactter.args2} /// A class that represents a set of two arguments. /// {@endtemplate} class Args2 extends Args1 { final A2 arg2; - /// {@macro args2} + /// {@macro reactter.args2} const Args2(A arg1, this.arg2) : super(arg1); /// Returns a list containing all arguments. @@ -60,13 +60,13 @@ class Args2 extends Args1 { List get arguments => [...super.arguments, arg2]; } -/// {@template args3} +/// {@template reactter.args3} /// A class that represents a set of three arguments. /// {@endtemplate} class Args3 extends Args2 { final A3 arg3; - /// {@macro args3} + /// {@macro reactter.args3} const Args3(A arg1, A2 arg2, this.arg3) : super(arg1, arg2); /// Returns a list containing all arguments. diff --git a/packages/reactter/lib/src/core/dependency_injection.dart b/packages/reactter/lib/src/core/dependency_injection.dart index a38d47be..175a0ad2 100644 --- a/packages/reactter/lib/src/core/dependency_injection.dart +++ b/packages/reactter/lib/src/core/dependency_injection.dart @@ -23,7 +23,7 @@ abstract class DependencyInjection { /// It stores the dependencies and its registers for quick access. final _instances = HashMap(); - /// {@template register} + /// {@template reactter.register} /// Register a [builder] function of the [T] dependency with/without [id]. /// {@endtemplate} /// @@ -59,7 +59,7 @@ abstract class DependencyInjection { return true; } - /// {@template lazy_builder} + /// {@template reactter.lazy_builder} /// Register a [builder] function of the [T] dependency with/without [id] /// as [DependencyMode.builder]. /// {@endtemplate} @@ -89,7 +89,7 @@ abstract class DependencyInjection { ); } - /// {@template lazy_factory} + /// {@template reactter.lazy_factory} /// Register a [builder] function of the [T] dependency with/without [id] /// as [DependencyMode.factory]. /// {@endtemplate} @@ -119,7 +119,7 @@ abstract class DependencyInjection { ); } - /// {@template lazy_singleton} + /// {@template reactter.lazy_singleton} /// Register a [builder] function of the [T] dependency with/without [id] /// as [DependencyMode.singleton]. /// {@endtemplate} @@ -149,7 +149,7 @@ abstract class DependencyInjection { ); } - /// {@template create} + /// {@template reactter.create} /// Register a [builder] function of the [T] dependency with/without [id] /// and creates the instance if it isn't already registered, /// else gets its instance only. @@ -157,7 +157,7 @@ abstract class DependencyInjection { /// /// Use [mode] parameter for defining how to manage the dependency. /// - /// {@template create_conditions} + /// {@template reactter.create_conditions} /// Under the following conditions: /// /// - if not found and hasn't registered it, registers, creates and returns it. @@ -176,7 +176,7 @@ abstract class DependencyInjection { return _getOrCreateIfNotExtist(id, ref)?.instance; } - /// {@template builder} + /// {@template reactter.builder} /// Register a [builder] function of the [T] dependency with/without [id] /// as [DependencyMode.builder] /// and creates the instance if it isn't already registered, @@ -212,7 +212,7 @@ abstract class DependencyInjection { ); } - /// {@template factory} + /// {@template reactter.factory} /// Register a [builder] function of the [T] dependency with/without [id] /// as [DependencyMode.factory] /// and creates the instance if it isn't already registered, @@ -247,7 +247,7 @@ abstract class DependencyInjection { ); } - /// {@template singleton} + /// {@template reactter.singleton} /// Register a [builder] function of the [T] dependency with/without [id] /// as [DependencyMode.singleton] /// and creates the instance if it isn't already registered, @@ -282,11 +282,11 @@ abstract class DependencyInjection { ); } - /// {@template get} + /// {@template reactter.get} /// Creates and/or gets the instance of the [T] dependency with/without [id]. /// {@endtemplate} /// - /// {@template get_conditions} + /// {@template reactter.get_conditions} /// Under the following conditions: /// /// - if found it, returns it. @@ -418,7 +418,7 @@ abstract class DependencyInjection { return unregister(id); } - /// {@template find} + /// {@template reactter.find} /// Gets the instance of the [T] dependency with/without [id]. /// {@endtemplate} /// diff --git a/packages/reactter/lib/src/core/dependency_mode.dart b/packages/reactter/lib/src/core/dependency_mode.dart index 557dd025..512bfd32 100644 --- a/packages/reactter/lib/src/core/dependency_mode.dart +++ b/packages/reactter/lib/src/core/dependency_mode.dart @@ -6,9 +6,11 @@ part of 'core.dart'; ) typedef InstanceManageMode = DependencyMode; +/// {@template reactter.dependency_mode} /// Represents different ways for managing instances. +/// {@endtemplate} enum DependencyMode { - /// {@template dependency_mode.builder} + /// {@template reactter.dependency_mode.builder} /// It's a ways to manage a dependency, which registers a builder function /// and creates the instance, unless it has already done so. /// @@ -20,7 +22,7 @@ enum DependencyMode { /// {@endtemplate} builder, - /// {@template dependency_mode.factory} + /// {@template reactter.dependency_mode.factory} /// It's a ways to manage a dependency, which registers /// a builder function only once and creates the instance if not already done. /// @@ -32,7 +34,7 @@ enum DependencyMode { /// {@endtemplate} factory, - /// {@template dependency_mode.singleton} + /// {@template reactter.dependency_mode.singleton} /// It's a ways to manage a dependency, which registers a builder function /// and creates the instance only once. /// diff --git a/packages/reactter/lib/src/core/lifecycle_observer.dart b/packages/reactter/lib/src/core/lifecycle_observer.dart index 8c45cebb..1e824d95 100644 --- a/packages/reactter/lib/src/core/lifecycle_observer.dart +++ b/packages/reactter/lib/src/core/lifecycle_observer.dart @@ -1,6 +1,6 @@ part of 'core.dart'; -/// {@template lifecycle_observer} +/// {@template reactter.lifecycle_observer} /// It's a mixin that provides a set of methods that can be used to observe /// the lifecycle of a dependency. /// diff --git a/packages/reactter/lib/src/core/state_management.dart b/packages/reactter/lib/src/core/state_management.dart index da1a86a0..136a6aea 100644 --- a/packages/reactter/lib/src/core/state_management.dart +++ b/packages/reactter/lib/src/core/state_management.dart @@ -9,7 +9,7 @@ abstract class StateManagement { bool _isBatchRunning = false; final HashMap _deferredEvents = HashMap(); - /// {@template lazy_state} + /// {@template reactter.lazy_state} /// Lazily initializes a state of type [StateBase] and attaches it to the given [instance]. /// /// This method is recommended to use when initializing state inside a class @@ -45,7 +45,7 @@ abstract class StateManagement { } } - /// {@template untracked} + /// {@template reactter.untracked} /// Executes the given [callback] function without tracking any state changes. /// This means that any state changes that occur inside the [callback] function /// will not trigger any side effects. @@ -81,7 +81,7 @@ abstract class StateManagement { } } - /// {@template batch} + /// {@template reactter.batch} /// Executes the given [callback] function within a batch operation. /// /// A batch operation allows multiple state changes to be grouped together, diff --git a/packages/reactter/lib/src/memo/memo_impl.dart b/packages/reactter/lib/src/memo/memo_impl.dart index 8cbbb8fd..12714213 100644 --- a/packages/reactter/lib/src/memo/memo_impl.dart +++ b/packages/reactter/lib/src/memo/memo_impl.dart @@ -1,6 +1,6 @@ part of 'memo.dart'; -/// {@template memo} +/// {@template reactter.memo} /// A class callable that is used for memoizing values([T]) /// returned by a calcutate function([calculateValue]). /// @@ -46,7 +46,7 @@ class Memo { /// and handle various events during the memoization process. final MemoInterceptor? _interceptor; - /// {@macro memo} + /// {@macro reactter.memo} Memo( FunctionArg computeValue, [ MemoInterceptor? interceptor, diff --git a/packages/reactter/lib/src/obj/obj_impl.dart b/packages/reactter/lib/src/obj/obj_impl.dart index 556af6ff..2570a2ac 100644 --- a/packages/reactter/lib/src/obj/obj_impl.dart +++ b/packages/reactter/lib/src/obj/obj_impl.dart @@ -1,6 +1,6 @@ part of 'obj.dart'; -/// {@template obj} +/// {@template reactter.obj} /// A base-class that store a value of [T]. /// /// You can create a new [Obj]: @@ -39,7 +39,7 @@ class Obj with ObjBase { @override T value; - /// {@macro obj} + /// {@macro reactter.obj} Obj(this.value); } From 20a1d02cf049fdba8d22e78f2d5e72837abce6f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Thu, 15 Aug 2024 16:02:18 -0600 Subject: [PATCH 007/141] build: Add Reactter devtools extension and configuration files. --- .../example/devtools_options.yaml | 3 + .../flutter_reactter/example/pubspec.lock | 4 +- .../extension/devtools/config.yaml | 5 + .../reactter_devtools_extension/.gitignore | 43 ++ .../reactter_devtools_extension/.metadata | 30 ++ .../reactter_devtools_extension/README.md | 7 + .../analysis_options.yaml | 28 ++ .../reactter_devtools_extension/lib/main.dart | 7 + .../lib/src/reactter_devtools_extension.dart | 18 + .../reactter_devtools_extension/pubspec.lock | 381 ++++++++++++++++++ .../reactter_devtools_extension/pubspec.yaml | 27 ++ .../test/widget_test.dart | 30 ++ .../web/favicon.png | Bin 0 -> 917 bytes .../web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes .../web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes .../web/index.html | 59 +++ .../web/manifest.json | 35 ++ 19 files changed, 675 insertions(+), 2 deletions(-) create mode 100644 packages/flutter_reactter/example/devtools_options.yaml create mode 100644 packages/flutter_reactter/extension/devtools/config.yaml create mode 100644 packages/reactter_devtools_extension/.gitignore create mode 100644 packages/reactter_devtools_extension/.metadata create mode 100644 packages/reactter_devtools_extension/README.md create mode 100644 packages/reactter_devtools_extension/analysis_options.yaml create mode 100644 packages/reactter_devtools_extension/lib/main.dart create mode 100644 packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart create mode 100644 packages/reactter_devtools_extension/pubspec.lock create mode 100644 packages/reactter_devtools_extension/pubspec.yaml create mode 100644 packages/reactter_devtools_extension/test/widget_test.dart create mode 100644 packages/reactter_devtools_extension/web/favicon.png create mode 100644 packages/reactter_devtools_extension/web/icons/Icon-192.png create mode 100644 packages/reactter_devtools_extension/web/icons/Icon-512.png create mode 100644 packages/reactter_devtools_extension/web/icons/Icon-maskable-192.png create mode 100644 packages/reactter_devtools_extension/web/icons/Icon-maskable-512.png create mode 100644 packages/reactter_devtools_extension/web/index.html create mode 100644 packages/reactter_devtools_extension/web/manifest.json diff --git a/packages/flutter_reactter/example/devtools_options.yaml b/packages/flutter_reactter/example/devtools_options.yaml new file mode 100644 index 00000000..428f2ac0 --- /dev/null +++ b/packages/flutter_reactter/example/devtools_options.yaml @@ -0,0 +1,3 @@ +extensions: + - provider: false + - reactter: true \ No newline at end of file diff --git a/packages/flutter_reactter/example/pubspec.lock b/packages/flutter_reactter/example/pubspec.lock index a944f7ba..9dc1440c 100644 --- a/packages/flutter_reactter/example/pubspec.lock +++ b/packages/flutter_reactter/example/pubspec.lock @@ -170,10 +170,10 @@ packages: dependency: "direct main" description: name: flutter_reactter - sha256: "82eaa9fd97d3520656bcdc07ff701d57a382b8e8620e1d585fb9fa40103c9183" + sha256: ade4971b87071de16f81b18f2dd021eec76daa1d6734d50b59266b381fb647d0 url: "https://pub.dev" source: hosted - version: "7.3.0" + version: "7.3.1" flutter_web_plugins: dependency: transitive description: flutter diff --git a/packages/flutter_reactter/extension/devtools/config.yaml b/packages/flutter_reactter/extension/devtools/config.yaml new file mode 100644 index 00000000..64da7229 --- /dev/null +++ b/packages/flutter_reactter/extension/devtools/config.yaml @@ -0,0 +1,5 @@ +name: reactter +version: 0.0.1 +issueTracker: https://github.com/2devs-team/reactter/issues +materialIconCodePoint: "0xf0537" +requiresConnection: false diff --git a/packages/reactter_devtools_extension/.gitignore b/packages/reactter_devtools_extension/.gitignore new file mode 100644 index 00000000..29a3a501 --- /dev/null +++ b/packages/reactter_devtools_extension/.gitignore @@ -0,0 +1,43 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/packages/reactter_devtools_extension/.metadata b/packages/reactter_devtools_extension/.metadata new file mode 100644 index 00000000..b5ef4875 --- /dev/null +++ b/packages/reactter_devtools_extension/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "ba393198430278b6595976de84fe170f553cc728" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: ba393198430278b6595976de84fe170f553cc728 + base_revision: ba393198430278b6595976de84fe170f553cc728 + - platform: web + create_revision: ba393198430278b6595976de84fe170f553cc728 + base_revision: ba393198430278b6595976de84fe170f553cc728 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/packages/reactter_devtools_extension/README.md b/packages/reactter_devtools_extension/README.md new file mode 100644 index 00000000..61428ca0 --- /dev/null +++ b/packages/reactter_devtools_extension/README.md @@ -0,0 +1,7 @@ +This is the source of the Reactter's devtool. + +You can locally run it with: + +``` +flutter run -d chrome --dart-define=use_simulated_environment=true +``` diff --git a/packages/reactter_devtools_extension/analysis_options.yaml b/packages/reactter_devtools_extension/analysis_options.yaml new file mode 100644 index 00000000..0d290213 --- /dev/null +++ b/packages/reactter_devtools_extension/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/packages/reactter_devtools_extension/lib/main.dart b/packages/reactter_devtools_extension/lib/main.dart new file mode 100644 index 00000000..809cd645 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/main.dart @@ -0,0 +1,7 @@ +import 'package:devtools_extensions/devtools_extensions.dart'; +import 'package:flutter/material.dart'; +import 'package:reactter_devtools_extension/src/reactter_devtools_extension.dart'; + +void main() { + runApp(const DevToolsExtension(child: ReactterDevtoolsExtension())); +} diff --git a/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart b/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart new file mode 100644 index 00000000..3507d708 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart @@ -0,0 +1,18 @@ +import 'package:flutter/widgets.dart'; + +class ReactterDevtoolsExtension extends StatefulWidget { + const ReactterDevtoolsExtension({super.key}); + + @override + State createState() => + _ReactterDevtoolsExtensionState(); +} + +class _ReactterDevtoolsExtensionState extends State { + @override + Widget build(BuildContext context) { + return Container( + child: const Text('Reactter Devtools Extension'), + ); + } +} diff --git a/packages/reactter_devtools_extension/pubspec.lock b/packages/reactter_devtools_extension/pubspec.lock new file mode 100644 index 00000000..76bcf49f --- /dev/null +++ b/packages/reactter_devtools_extension/pubspec.lock @@ -0,0 +1,381 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + args: + dependency: transitive + description: + name: args + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + url: "https://pub.dev" + source: hosted + version: "2.5.0" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" + source: hosted + version: "1.0.8" + devtools_app_shared: + dependency: transitive + description: + name: devtools_app_shared + sha256: b8dbeb8352ffaff88fc1e00e0a5f1a659003fb75e0ff1250ec2add6ad6cd54c3 + url: "https://pub.dev" + source: hosted + version: "0.0.9" + devtools_extensions: + dependency: "direct main" + description: + name: devtools_extensions + sha256: "1d1ece240f182f42374694ca743eb0bb10838c801d680d8523151a10f5464901" + url: "https://pub.dev" + source: hosted + version: "0.0.13" + devtools_shared: + dependency: transitive + description: + name: devtools_shared + sha256: "1f46a18cf389dd47b10e1b963cff463c0fede747b8c0fc787d2455eb034135e8" + url: "https://pub.dev" + source: hosted + version: "6.0.4" + extension_discovery: + dependency: transitive + description: + name: extension_discovery + sha256: "20735622d0763865f9d94c3ecdce4441174530870760253e9d364fb4f3da8688" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + url: "https://pub.dev" + source: hosted + version: "0.8.0" + meta: + dependency: transitive + description: + name: meta + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + path: + dependency: transitive + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + pointer_interceptor: + dependency: transitive + description: + name: pointer_interceptor + sha256: adf7a637f97c077041d36801b43be08559fd4322d2127b3f20bb7be1b9eebc22 + url: "https://pub.dev" + source: hosted + version: "0.9.3+7" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + sse: + dependency: transitive + description: + name: sse + sha256: "8168874cdbd42c36ea118ba9f88a656ad97f604f28c976c61cb6d5b281c5319c" + url: "https://pub.dev" + source: hosted + version: "4.1.4" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + url: "https://pub.dev" + source: hosted + version: "0.6.1" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + usage: + dependency: transitive + description: + name: usage + sha256: "0bdbde65a6e710343d02a56552eeaefd20b735e04bfb6b3ee025b6b22e8d0e15" + url: "https://pub.dev" + source: hosted + version: "4.1.1" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + url: "https://pub.dev" + source: hosted + version: "13.0.0" + web: + dependency: transitive + description: + name: web + sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05" + url: "https://pub.dev" + source: hosted + version: "0.4.2" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: "939ab60734a4f8fa95feacb55804fa278de28bdeef38e616dc08e44a84adea23" + url: "https://pub.dev" + source: hosted + version: "2.4.3" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" + yaml_edit: + dependency: transitive + description: + name: yaml_edit + sha256: e9c1a3543d2da0db3e90270dbb1e4eebc985ee5e3ffe468d83224472b2194a5f + url: "https://pub.dev" + source: hosted + version: "2.2.1" +sdks: + dart: ">=3.3.1 <4.0.0" + flutter: ">=3.17.0-0.0.pre" diff --git a/packages/reactter_devtools_extension/pubspec.yaml b/packages/reactter_devtools_extension/pubspec.yaml new file mode 100644 index 00000000..beae6748 --- /dev/null +++ b/packages/reactter_devtools_extension/pubspec.yaml @@ -0,0 +1,27 @@ +name: reactter_devtools_extension +descripqion: DevTools extension for Reactter +publish_to: "none" +version: 1.0.0+1 + +environment: + sdk: ">=3.3.1 <4.0.0" + +dependencies: + flutter: + sdk: flutter + cupertino_icons: ^1.0.6 + devtools_extensions: ^0.0.13 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^3.0.0 + +flutter: + uses-material-design: true + +scripts: + dev: flutter run -d chrome --dart-define=use_simulated_environment=true + build: + $before: flutter pub get + $script: dart run devtools_extensions build_and_copy --source=. --dest=../flutter_reactter/extension/devtools diff --git a/packages/reactter_devtools_extension/test/widget_test.dart b/packages/reactter_devtools_extension/test/widget_test.dart new file mode 100644 index 00000000..2e966f48 --- /dev/null +++ b/packages/reactter_devtools_extension/test/widget_test.dart @@ -0,0 +1,30 @@ +// // This is a basic Flutter widget test. +// // +// // To perform an interaction with a widget in your test, use the WidgetTester +// // utility in the flutter_test package. For example, you can send tap and scroll +// // gestures. You can also use WidgetTester to find child widgets in the widget +// // tree, read text, and verify that the values of widget properties are correct. + +// import 'package:flutter/material.dart'; +// import 'package:flutter_test/flutter_test.dart'; + +// import 'package:reactter_devtools_extension/main.dart'; + +// void main() { +// testWidgets('Counter increments smoke test', (WidgetTester tester) async { +// // Build our app and trigger a frame. +// await tester.pumpWidget(const MyApp()); + +// // Verify that our counter starts at 0. +// expect(find.text('0'), findsOneWidget); +// expect(find.text('1'), findsNothing); + +// // Tap the '+' icon and trigger a frame. +// await tester.tap(find.byIcon(Icons.add)); +// await tester.pump(); + +// // Verify that our counter has incremented. +// expect(find.text('0'), findsNothing); +// expect(find.text('1'), findsOneWidget); +// }); +// } diff --git a/packages/reactter_devtools_extension/web/favicon.png b/packages/reactter_devtools_extension/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/packages/reactter_devtools_extension/web/icons/Icon-192.png b/packages/reactter_devtools_extension/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/packages/reactter_devtools_extension/web/icons/Icon-512.png b/packages/reactter_devtools_extension/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/packages/reactter_devtools_extension/web/icons/Icon-maskable-192.png b/packages/reactter_devtools_extension/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/packages/reactter_devtools_extension/web/icons/Icon-maskable-512.png b/packages/reactter_devtools_extension/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 zcmeFZ_gj-)&^4Nb2tlbLMU<{!p(#yjqEe+=0IA_oih%ScH9@5#MNp&}Y#;;(h=A0@ zh7{>lT2MkSQ344eAvrhici!td|HJuyvJm#Y_w1Q9Yu3!26dNlO-oxUDK_C#XnW^Co z5C{VN6#{~B0)K2j7}*1Xq(Nqemv23A-6&=ZpEijkVnSwVGqLv40?n0=p;k3-U5e5+ z+z3>aS`u9DS=!wg8ROu?X4TFoW6CFLL&{GzoVT)ldhLekLM|+j3tIxRd|*5=c{=s&*vfPdBr(Fyj(v@%eQj1Soy7m4^@VRl1~@-PV7y+c!xz$8436WBn$t{=}mEdK#k`aystimGgI{(IBx$!pAwFoE9Y`^t^;> zKAD)C(Dl^s%`?q5$P|fZf8Xymrtu^Pv(7D`rn>Z-w$Ahs!z9!94WNVxrJuXfHAaxg zC6s@|Z1$7R$(!#t%Jb{{s6(Y?NoQXDYq)!}X@jKPhe`{9KQ@sAU8y-5`xt?S9$jKH zoi}6m5PcG*^{kjvt+kwPpyQzVg4o)a>;LK`aaN2x4@itBD3Aq?yWTM20VRn1rrd+2 zKO=P0rMjEGq_UqpMa`~7B|p?xAN1SCoCp}QxAv8O`jLJ5CVh@umR%c%i^)6!o+~`F zaalSTQcl5iwOLC&H)efzd{8(88mo`GI(56T<(&p7>Qd^;R1hn1Y~jN~tApaL8>##U zd65bo8)79CplWxr#z4!6HvLz&N7_5AN#x;kLG?zQ(#p|lj<8VUlKY=Aw!ATqeL-VG z42gA!^cMNPj>(`ZMEbCrnkg*QTsn*u(nQPWI9pA{MQ=IsPTzd7q5E#7+z>Ch=fx$~ z;J|?(5jTo5UWGvsJa(Sx0?S#56+8SD!I^tftyeh_{5_31l6&Hywtn`bbqYDqGZXI( zCG7hBgvksX2ak8+)hB4jnxlO@A32C_RM&g&qDSb~3kM&)@A_j1*oTO@nicGUyv+%^ z=vB)4(q!ykzT==Z)3*3{atJ5}2PV*?Uw+HhN&+RvKvZL3p9E?gHjv{6zM!A|z|UHK z-r6jeLxbGn0D@q5aBzlco|nG2tr}N@m;CJX(4#Cn&p&sLKwzLFx1A5izu?X_X4x8r@K*d~7>t1~ zDW1Mv5O&WOxbzFC`DQ6yNJ(^u9vJdj$fl2dq`!Yba_0^vQHXV)vqv1gssZYzBct!j zHr9>ydtM8wIs}HI4=E}qAkv|BPWzh3^_yLH(|kdb?x56^BlDC)diWyPd*|f!`^12_U>TD^^94OCN0lVv~Sgvs94ecpE^}VY$w`qr_>Ue zTfH~;C<3H<0dS5Rkf_f@1x$Gms}gK#&k()IC0zb^QbR!YLoll)c$Agfi6MKI0dP_L z=Uou&u~~^2onea2%XZ@>`0x^L8CK6=I{ge;|HXMj)-@o~h&O{CuuwBX8pVqjJ*o}5 z#8&oF_p=uSo~8vn?R0!AMWvcbZmsrj{ZswRt(aEdbi~;HeVqIe)-6*1L%5u$Gbs}| zjFh?KL&U(rC2izSGtwP5FnsR@6$-1toz?RvLD^k~h9NfZgzHE7m!!7s6(;)RKo2z} zB$Ci@h({l?arO+vF;s35h=|WpefaOtKVx>l399}EsX@Oe3>>4MPy%h&^3N_`UTAHJ zI$u(|TYC~E4)|JwkWW3F!Tib=NzjHs5ii2uj0^m|Qlh-2VnB#+X~RZ|`SA*}}&8j9IDv?F;(Y^1=Z0?wWz;ikB zewU>MAXDi~O7a~?jx1x=&8GcR-fTp>{2Q`7#BE#N6D@FCp`?ht-<1|y(NArxE_WIu zP+GuG=Qq>SHWtS2M>34xwEw^uvo4|9)4s|Ac=ud?nHQ>ax@LvBqusFcjH0}{T3ZPQ zLO1l<@B_d-(IS682}5KA&qT1+{3jxKolW+1zL4inqBS-D>BohA!K5++41tM@ z@xe<-qz27}LnV#5lk&iC40M||JRmZ*A##K3+!j93eouU8@q-`W0r%7N`V$cR&JV;iX(@cS{#*5Q>~4BEDA)EikLSP@>Oo&Bt1Z~&0d5)COI%3$cLB_M?dK# z{yv2OqW!al-#AEs&QFd;WL5zCcp)JmCKJEdNsJlL9K@MnPegK23?G|O%v`@N{rIRa zi^7a}WBCD77@VQ-z_v{ZdRsWYrYgC$<^gRQwMCi6);%R~uIi31OMS}=gUTE(GKmCI z$zM>mytL{uNN+a&S38^ez(UT=iSw=l2f+a4)DyCA1Cs_N-r?Q@$3KTYosY!;pzQ0k zzh1G|kWCJjc(oZVBji@kN%)UBw(s{KaYGy=i{g3{)Z+&H8t2`^IuLLKWT6lL<-C(! zSF9K4xd-|VO;4}$s?Z7J_dYqD#Mt)WCDnsR{Kpjq275uUq6`v0y*!PHyS(}Zmv)_{>Vose9-$h8P0|y;YG)Bo}$(3Z%+Gs0RBmFiW!^5tBmDK-g zfe5%B*27ib+7|A*Fx5e)2%kIxh7xWoc3pZcXS2zik!63lAG1;sC1ja>BqH7D zODdi5lKW$$AFvxgC-l-)!c+9@YMC7a`w?G(P#MeEQ5xID#<}W$3bSmJ`8V*x2^3qz zVe<^^_8GHqYGF$nIQm0Xq2kAgYtm#UC1A(=&85w;rmg#v906 zT;RyMgbMpYOmS&S9c38^40oUp?!}#_84`aEVw;T;r%gTZkWeU;;FwM@0y0adt{-OK z(vGnPSlR=Nv2OUN!2=xazlnHPM9EWxXg2EKf0kI{iQb#FoP>xCB<)QY>OAM$Dcdbm zU6dU|%Mo(~avBYSjRc13@|s>axhrPl@Sr81{RSZUdz4(=|82XEbV*JAX6Lfbgqgz584lYgi0 z2-E{0XCVON$wHfvaLs;=dqhQJ&6aLn$D#0i(FkAVrXG9LGm3pSTf&f~RQb6|1_;W> z?n-;&hrq*~L=(;u#jS`*Yvh@3hU-33y_Kv1nxqrsf>pHVF&|OKkoC)4DWK%I!yq?P z=vXo8*_1iEWo8xCa{HJ4tzxOmqS0&$q+>LroMKI*V-rxhOc%3Y!)Y|N6p4PLE>Yek>Y(^KRECg8<|%g*nQib_Yc#A5q8Io z6Ig&V>k|~>B6KE%h4reAo*DfOH)_01tE0nWOxX0*YTJgyw7moaI^7gW*WBAeiLbD?FV9GSB zPv3`SX*^GRBM;zledO`!EbdBO_J@fEy)B{-XUTVQv}Qf~PSDpK9+@I`7G7|>Dgbbu z_7sX9%spVo$%qwRwgzq7!_N;#Td08m5HV#?^dF-EV1o)Q=Oa+rs2xH#g;ykLbwtCh znUnA^dW!XjspJ;otq$yV@I^s9Up(5k7rqhQd@OLMyyxVLj_+$#Vc*}Usevp^I(^vH zmDgHc0VMme|K&X?9&lkN{yq_(If)O`oUPW8X}1R5pSVBpfJe0t{sPA(F#`eONTh_) zxeLqHMfJX#?P(@6w4CqRE@Eiza; z;^5)Kk=^5)KDvd9Q<`=sJU8rjjxPmtWMTmzcH={o$U)j=QBuHarp?=}c??!`3d=H$nrJMyr3L-& zA#m?t(NqLM?I3mGgWA_C+0}BWy3-Gj7bR+d+U?n*mN$%5P`ugrB{PeV>jDUn;eVc- zzeMB1mI4?fVJatrNyq|+zn=!AiN~<}eoM#4uSx^K?Iw>P2*r=k`$<3kT00BE_1c(02MRz4(Hq`L^M&xt!pV2 zn+#U3@j~PUR>xIy+P>51iPayk-mqIK_5rlQMSe5&tDkKJk_$i(X&;K(11YGpEc-K= zq4Ln%^j>Zi_+Ae9eYEq_<`D+ddb8_aY!N;)(&EHFAk@Ekg&41ABmOXfWTo)Z&KotA zh*jgDGFYQ^y=m)<_LCWB+v48DTJw*5dwMm_YP0*_{@HANValf?kV-Ic3xsC}#x2h8 z`q5}d8IRmqWk%gR)s~M}(Qas5+`np^jW^oEd-pzERRPMXj$kS17g?H#4^trtKtq;C?;c ztd|%|WP2w2Nzg@)^V}!Gv++QF2!@FP9~DFVISRW6S?eP{H;;8EH;{>X_}NGj^0cg@ z!2@A>-CTcoN02^r6@c~^QUa={0xwK0v4i-tQ9wQq^=q*-{;zJ{Qe%7Qd!&X2>rV@4 z&wznCz*63_vw4>ZF8~%QCM?=vfzW0r_4O^>UA@otm_!N%mH)!ERy&b!n3*E*@?9d^ zu}s^By@FAhG(%?xgJMuMzuJw2&@$-oK>n z=UF}rt%vuaP9fzIFCYN-1&b#r^Cl6RDFIWsEsM|ROf`E?O(cy{BPO2Ie~kT+^kI^i zp>Kbc@C?}3vy-$ZFVX#-cx)Xj&G^ibX{pWggtr(%^?HeQL@Z( zM-430g<{>vT*)jK4aY9(a{lSy{8vxLbP~n1MXwM527ne#SHCC^F_2@o`>c>>KCq9c(4c$VSyMl*y3Nq1s+!DF| z^?d9PipQN(mw^j~{wJ^VOXDCaL$UtwwTpyv8IAwGOg<|NSghkAR1GSNLZ1JwdGJYm zP}t<=5=sNNUEjc=g(y)1n5)ynX(_$1-uGuDR*6Y^Wgg(LT)Jp><5X|}bt z_qMa&QP?l_n+iVS>v%s2Li_;AIeC=Ca^v1jX4*gvB$?H?2%ndnqOaK5-J%7a} zIF{qYa&NfVY}(fmS0OmXA70{znljBOiv5Yod!vFU{D~*3B3Ka{P8?^ zfhlF6o7aNT$qi8(w<}OPw5fqA7HUje*r*Oa(YV%*l0|9FP9KW@U&{VSW{&b0?@y)M zs%4k1Ax;TGYuZ9l;vP5@?3oQsp3)rjBeBvQQ>^B;z5pc=(yHhHtq6|0m(h4envn_j787fizY@V`o(!SSyE7vlMT zbo=Z1c=atz*G!kwzGB;*uPL$Ei|EbZLh8o+1BUMOpnU(uX&OG1MV@|!&HOOeU#t^x zr9=w2ow!SsTuJWT7%Wmt14U_M*3XiWBWHxqCVZI0_g0`}*^&yEG9RK9fHK8e+S^m? zfCNn$JTswUVbiC#>|=wS{t>-MI1aYPLtzO5y|LJ9nm>L6*wpr_m!)A2Fb1RceX&*|5|MwrvOk4+!0p99B9AgP*9D{Yt|x=X}O% zgIG$MrTB=n-!q%ROT|SzH#A$Xm;|ym)0>1KR}Yl0hr-KO&qMrV+0Ej3d@?FcgZ+B3 ztEk16g#2)@x=(ko8k7^Tq$*5pfZHC@O@}`SmzT1(V@x&NkZNM2F#Q-Go7-uf_zKC( zB(lHZ=3@dHaCOf6C!6i8rDL%~XM@rVTJbZL09?ht@r^Z_6x}}atLjvH^4Vk#Ibf(^LiBJFqorm?A=lE zzFmwvp4bT@Nv2V>YQT92X;t9<2s|Ru5#w?wCvlhcHLcsq0TaFLKy(?nzezJ>CECqj zggrI~Hd4LudM(m{L@ezfnpELsRFVFw>fx;CqZtie`$BXRn#Ns%AdoE$-Pf~{9A8rV zf7FbgpKmVzmvn-z(g+&+-ID=v`;6=)itq8oM*+Uz**SMm_{%eP_c0{<%1JGiZS19o z@Gj7$Se~0lsu}w!%;L%~mIAO;AY-2i`9A*ZfFs=X!LTd6nWOZ7BZH2M{l2*I>Xu)0 z`<=;ObglnXcVk!T>e$H?El}ra0WmPZ$YAN0#$?|1v26^(quQre8;k20*dpd4N{i=b zuN=y}_ew9SlE~R{2+Rh^7%PA1H5X(p8%0TpJ=cqa$65XL)$#ign-y!qij3;2>j}I; ziO@O|aYfn&up5F`YtjGw68rD3{OSGNYmBnl?zdwY$=RFsegTZ=kkzRQ`r7ZjQP!H( zp4>)&zf<*N!tI00xzm-ME_a{_I!TbDCr;8E;kCH4LlL-tqLxDuBn-+xgPk37S&S2^ z2QZumkIimwz!c@!r0)j3*(jPIs*V!iLTRl0Cpt_UVNUgGZzdvs0(-yUghJfKr7;=h zD~y?OJ-bWJg;VdZ^r@vlDoeGV&8^--!t1AsIMZ5S440HCVr%uk- z2wV>!W1WCvFB~p$P$$_}|H5>uBeAe>`N1FI8AxM|pq%oNs;ED8x+tb44E) zTj{^fbh@eLi%5AqT?;d>Es5D*Fi{Bpk)q$^iF!!U`r2hHAO_?#!aYmf>G+jHsES4W zgpTKY59d?hsb~F0WE&dUp6lPt;Pm zcbTUqRryw^%{ViNW%Z(o8}dd00H(H-MmQmOiTq{}_rnwOr*Ybo7*}3W-qBT!#s0Ie z-s<1rvvJx_W;ViUD`04%1pra*Yw0BcGe)fDKUK8aF#BwBwMPU;9`!6E(~!043?SZx z13K%z@$$#2%2ovVlgFIPp7Q6(vO)ud)=*%ZSucL2Dh~K4B|%q4KnSpj#n@(0B})!9 z8p*hY@5)NDn^&Pmo;|!>erSYg`LkO?0FB@PLqRvc>4IsUM5O&>rRv|IBRxi(RX(gJ ztQ2;??L~&Mv;aVr5Q@(?y^DGo%pO^~zijld41aA0KKsy_6FeHIn?fNHP-z>$OoWer zjZ5hFQTy*-f7KENRiCE$ZOp4|+Wah|2=n@|W=o}bFM}Y@0e62+_|#fND5cwa3;P{^pEzlJbF1Yq^}>=wy8^^^$I2M_MH(4Dw{F6hm+vrWV5!q;oX z;tTNhz5`-V={ew|bD$?qcF^WPR{L(E%~XG8eJx(DoGzt2G{l8r!QPJ>kpHeOvCv#w zr=SSwMDaUX^*~v%6K%O~i)<^6`{go>a3IdfZ8hFmz&;Y@P%ZygShQZ2DSHd`m5AR= zx$wWU06;GYwXOf(%MFyj{8rPFXD};JCe85Bdp4$YJ2$TzZ7Gr#+SwCvBI1o$QP0(c zy`P51FEBV2HTisM3bHqpmECT@H!Y2-bv2*SoSPoO?wLe{M#zDTy@ujAZ!Izzky~3k zRA1RQIIoC*Mej1PH!sUgtkR0VCNMX(_!b65mo66iM*KQ7xT8t2eev$v#&YdUXKwGm z7okYAqYF&bveHeu6M5p9xheRCTiU8PFeb1_Rht0VVSbm%|1cOVobc8mvqcw!RjrMRM#~=7xibH&Fa5Imc|lZ{eC|R__)OrFg4@X_ ze+kk*_sDNG5^ELmHnZ7Ue?)#6!O)#Nv*Dl2mr#2)w{#i-;}0*_h4A%HidnmclH#;Q zmQbq+P4DS%3}PpPm7K_K3d2s#k~x+PlTul7+kIKol0@`YN1NG=+&PYTS->AdzPv!> zQvzT=)9se*Jr1Yq+C{wbK82gAX`NkbXFZ)4==j4t51{|-v!!$H8@WKA={d>CWRW+g z*`L>9rRucS`vbXu0rzA1#AQ(W?6)}1+oJSF=80Kf_2r~Qm-EJ6bbB3k`80rCv(0d` zvCf3;L2ovYG_TES%6vSuoKfIHC6w;V31!oqHM8-I8AFzcd^+_86!EcCOX|Ta9k1!s z_Vh(EGIIsI3fb&dF$9V8v(sTBC%!#<&KIGF;R+;MyC0~}$gC}}= zR`DbUVc&Bx`lYykFZ4{R{xRaUQkWCGCQlEc;!mf=+nOk$RUg*7 z;kP7CVLEc$CA7@6VFpsp3_t~m)W0aPxjsA3e5U%SfY{tp5BV5jH-5n?YX7*+U+Zs%LGR>U- z!x4Y_|4{gx?ZPJobISy991O znrmrC3otC;#4^&Rg_iK}XH(XX+eUHN0@Oe06hJk}F?`$)KmH^eWz@@N%wEc)%>?Ft z#9QAroDeyfztQ5Qe{m*#R#T%-h*&XvSEn@N$hYRTCMXS|EPwzF3IIysD2waj`vQD{ zv_#^Pgr?s~I*NE=acf@dWVRNWTr(GN0wrL)Z2=`Dr>}&ZDNX|+^Anl{Di%v1Id$_p zK5_H5`RDjJx`BW7hc85|> zHMMsWJ4KTMRHGu+vy*kBEMjz*^K8VtU=bXJYdhdZ-?jTXa$&n)C?QQIZ7ln$qbGlr zS*TYE+ppOrI@AoPP=VI-OXm}FzgXRL)OPvR$a_=SsC<3Jb+>5makX|U!}3lx4tX&L z^C<{9TggZNoeX!P1jX_K5HkEVnQ#s2&c#umzV6s2U-Q;({l+j^?hi7JnQ7&&*oOy9 z(|0asVTWUCiCnjcOnB2pN0DpuTglKq;&SFOQ3pUdye*eT<2()7WKbXp1qq9=bhMWlF-7BHT|i3TEIT77AcjD(v=I207wi-=vyiw5mxgPdTVUC z&h^FEUrXwWs9en2C{ywZp;nvS(Mb$8sBEh-*_d-OEm%~p1b2EpcwUdf<~zmJmaSTO zSX&&GGCEz-M^)G$fBvLC2q@wM$;n4jp+mt0MJFLuJ%c`tSp8$xuP|G81GEd2ci$|M z4XmH{5$j?rqDWoL4vs!}W&!?!rtj=6WKJcE>)?NVske(p;|#>vL|M_$as=mi-n-()a*OU3Okmk0wC<9y7t^D(er-&jEEak2!NnDiOQ99Wx8{S8}=Ng!e0tzj*#T)+%7;aM$ z&H}|o|J1p{IK0Q7JggAwipvHvko6>Epmh4RFRUr}$*2K4dz85o7|3#Bec9SQ4Y*;> zXWjT~f+d)dp_J`sV*!w>B%)#GI_;USp7?0810&3S=WntGZ)+tzhZ+!|=XlQ&@G@~3 z-dw@I1>9n1{+!x^Hz|xC+P#Ab`E@=vY?3%Bc!Po~e&&&)Qp85!I|U<-fCXy*wMa&t zgDk!l;gk;$taOCV$&60z+}_$ykz=Ea*)wJQ3-M|p*EK(cvtIre0Pta~(95J7zoxBN zS(yE^3?>88AL0Wfuou$BM{lR1hkrRibz=+I9ccwd`ZC*{NNqL)3pCcw^ygMmrG^Yp zn5f}Xf>%gncC=Yq96;rnfp4FQL#{!Y*->e82rHgY4Zwy{`JH}b9*qr^VA{%~Z}jtp z_t$PlS6}5{NtTqXHN?uI8ut8rOaD#F1C^ls73S=b_yI#iZDOGz3#^L@YheGd>L;<( z)U=iYj;`{>VDNzIxcjbTk-X3keXR8Xbc`A$o5# zKGSk-7YcoBYuAFFSCjGi;7b<;n-*`USs)IX z=0q6WZ=L!)PkYtZE-6)azhXV|+?IVGTOmMCHjhkBjfy@k1>?yFO3u!)@cl{fFAXnRYsWk)kpT?X{_$J=|?g@Q}+kFw|%n!;Zo}|HE@j=SFMvT8v`6Y zNO;tXN^036nOB2%=KzxB?n~NQ1K8IO*UE{;Xy;N^ZNI#P+hRZOaHATz9(=)w=QwV# z`z3+P>9b?l-@$@P3<;w@O1BdKh+H;jo#_%rr!ute{|YX4g5}n?O7Mq^01S5;+lABE+7`&_?mR_z7k|Ja#8h{!~j)| zbBX;*fsbUak_!kXU%HfJ2J+G7;inu#uRjMb|8a){=^))y236LDZ$$q3LRlat1D)%7K0!q5hT5V1j3qHc7MG9 z_)Q=yQ>rs>3%l=vu$#VVd$&IgO}Za#?aN!xY>-<3PhzS&q!N<=1Q7VJBfHjug^4|) z*fW^;%3}P7X#W3d;tUs3;`O&>;NKZBMR8au6>7?QriJ@gBaorz-+`pUWOP73DJL=M z(33uT6Gz@Sv40F6bN|H=lpcO z^AJl}&=TIjdevuDQ!w0K*6oZ2JBOhb31q!XDArFyKpz!I$p4|;c}@^bX{>AXdt7Bm zaLTk?c%h@%xq02reu~;t@$bv`b3i(P=g}~ywgSFpM;}b$zAD+=I!7`V~}ARB(Wx0C(EAq@?GuxOL9X+ffbkn3+Op0*80TqmpAq~EXmv%cq36celXmRz z%0(!oMp&2?`W)ALA&#|fu)MFp{V~~zIIixOxY^YtO5^FSox8v$#d0*{qk0Z)pNTt0QVZ^$`4vImEB>;Lo2!7K05TpY-sl#sWBz_W-aDIV`Ksabi zvpa#93Svo!70W*Ydh)Qzm{0?CU`y;T^ITg-J9nfWeZ-sbw)G@W?$Eomf%Bg2frfh5 zRm1{|E0+(4zXy){$}uC3%Y-mSA2-^I>Tw|gQx|7TDli_hB>``)Q^aZ`LJC2V3U$SABP}T)%}9g2pF9dT}aC~!rFFgkl1J$ z`^z{Arn3On-m%}r}TGF8KQe*OjSJ=T|caa_E;v89A{t@$yT^(G9=N9F?^kT*#s3qhJq!IH5|AhnqFd z0B&^gm3w;YbMNUKU>naBAO@fbz zqw=n!@--}o5;k6DvTW9pw)IJVz;X}ncbPVrmH>4x);8cx;q3UyiML1PWp%bxSiS|^ zC5!kc4qw%NSOGQ*Kcd#&$30=lDvs#*4W4q0u8E02U)7d=!W7+NouEyuF1dyH$D@G& zaFaxo9Ex|ZXA5y{eZT*i*dP~INSMAi@mvEX@q5i<&o&#sM}Df?Og8n8Ku4vOux=T% zeuw~z1hR}ZNwTn8KsQHKLwe2>p^K`YWUJEdVEl|mO21Bov!D0D$qPoOv=vJJ`)|%_ z>l%`eexY7t{BlVKP!`a^U@nM?#9OC*t76My_E_<16vCz1x_#82qj2PkWiMWgF8bM9 z(1t4VdHcJ;B~;Q%x01k_gQ0>u2*OjuEWNOGX#4}+N?Gb5;+NQMqp}Puqw2HnkYuKA zzKFWGHc&K>gwVgI1Sc9OT1s6fq=>$gZU!!xsilA$fF`kLdGoX*^t}ao@+^WBpk>`8 z4v_~gK|c2rCq#DZ+H)$3v~Hoi=)=1D==e3P zpKrRQ+>O^cyTuWJ%2}__0Z9SM_z9rptd*;-9uC1tDw4+A!=+K%8~M&+Zk#13hY$Y$ zo-8$*8dD5@}XDi19RjK6T^J~DIXbF5w&l?JLHMrf0 zLv0{7*G!==o|B%$V!a=EtVHdMwXLtmO~vl}P6;S(R2Q>*kTJK~!}gloxj)m|_LYK{ zl(f1cB=EON&wVFwK?MGn^nWuh@f95SHatPs(jcwSY#Dnl1@_gkOJ5=f`%s$ZHljRH0 z+c%lrb=Gi&N&1>^L_}#m>=U=(oT^vTA&3!xXNyqi$pdW1BDJ#^{h|2tZc{t^vag3& zAD7*8C`chNF|27itjBUo^CCDyEpJLX3&u+(L;YeeMwnXEoyN(ytoEabcl$lSgx~Ltatn}b$@j_yyMrBb03)shJE*$;Mw=;mZd&8e>IzE+4WIoH zCSZE7WthNUL$|Y#m!Hn?x7V1CK}V`KwW2D$-7&ODy5Cj;!_tTOOo1Mm%(RUt)#$@3 zhurA)t<7qik%%1Et+N1?R#hdBB#LdQ7{%-C zn$(`5e0eFh(#c*hvF>WT*07fk$N_631?W>kfjySN8^XC9diiOd#s?4tybICF;wBjp zIPzilX3{j%4u7blhq)tnaOBZ_`h_JqHXuI7SuIlNTgBk9{HIS&3|SEPfrvcE<@}E` zKk$y*nzsqZ{J{uWW9;#n=de&&h>m#A#q)#zRonr(?mDOYU&h&aQWD;?Z(22wY?t$U3qo`?{+amA$^TkxL+Ex2dh`q7iR&TPd0Ymwzo#b? zP$#t=elB5?k$#uE$K>C$YZbYUX_JgnXA`oF_Ifz4H7LEOW~{Gww&3s=wH4+j8*TU| zSX%LtJWqhr-xGNSe{;(16kxnak6RnZ{0qZ^kJI5X*It_YuynSpi(^-}Lolr{)#z_~ zw!(J-8%7Ybo^c3(mED`Xz8xecP35a6M8HarxRn%+NJBE;dw>>Y2T&;jzRd4FSDO3T zt*y+zXCtZQ0bP0yf6HRpD|WmzP;DR^-g^}{z~0x~z4j8m zucTe%k&S9Nt-?Jb^gYW1w6!Y3AUZ0Jcq;pJ)Exz%7k+mUOm6%ApjjSmflfKwBo6`B zhNb@$NHTJ>guaj9S{@DX)!6)b-Shav=DNKWy(V00k(D!v?PAR0f0vDNq*#mYmUp6> z76KxbFDw5U{{qx{BRj(>?|C`82ICKbfLxoldov-M?4Xl+3;I4GzLHyPOzYw7{WQST zPNYcx5onA%MAO9??41Po*1zW(Y%Zzn06-lUp{s<3!_9vv9HBjT02On0Hf$}NP;wF) zP<`2p3}A^~1YbvOh{ePMx$!JGUPX-tbBzp3mDZMY;}h;sQ->!p97GA)9a|tF(Gh{1$xk7 zUw?ELkT({Xw!KIr);kTRb1b|UL`r2_`a+&UFVCdJ)1T#fdh;71EQl9790Br0m_`$x z9|ZANuchFci8GNZ{XbP=+uXSJRe(;V5laQz$u18#?X*9}x7cIEbnr%<=1cX3EIu7$ zhHW6pe5M(&qEtsqRa>?)*{O;OJT+YUhG5{km|YI7I@JL_3Hwao9aXneiSA~a* z|Lp@c-oMNyeAEuUz{F?kuou3x#C*gU?lon!RC1s37gW^0Frc`lqQWH&(J4NoZg3m8 z;Lin#8Q+cFPD7MCzj}#|ws7b@?D9Q4dVjS4dpco=4yX5SSH=A@U@yqPdp@?g?qeia zH=Tt_9)G=6C2QIPsi-QipnK(mc0xXIN;j$WLf@n8eYvMk;*H-Q4tK%(3$CN}NGgO8n}fD~+>?<3UzvsrMf*J~%i;VKQHbF%TPalFi=#sgj)(P#SM^0Q=Tr>4kJVw8X3iWsP|e8tj}NjlMdWp z@2+M4HQu~3!=bZpjh;;DIDk&X}=c8~kn)FWWH z2KL1w^rA5&1@@^X%MjZ7;u(kH=YhH2pJPFQe=hn>tZd5RC5cfGYis8s9PKaxi*}-s6*W zRA^PwR=y^5Z){!(4D9-KC;0~;b*ploznFOaU`bJ_7U?qAi#mTo!&rIECRL$_y@yI27x2?W+zqDBD5~KCVYKFZLK+>ABC(Kj zeAll)KMgIlAG`r^rS{loBrGLtzhHY8$)<_S<(Dpkr(Ym@@vnQ&rS@FC*>2@XCH}M+an74WcRDcoQ+a3@A z9tYhl5$z7bMdTvD2r&jztBuo37?*k~wcU9GK2-)MTFS-lux-mIRYUuGUCI~V$?s#< z?1qAWb(?ZLm(N>%S%y10COdaq_Tm5c^%ooIxpR=`3e4C|@O5wY+eLik&XVi5oT7oe zmxH)Jd*5eo@!7t`x8!K=-+zJ-Sz)B_V$)s1pW~CDU$=q^&ABvf6S|?TOMB-RIm@CoFg>mjIQE)?+A1_3s6zmFU_oW&BqyMz1mY*IcP_2knjq5 zqw~JK(cVsmzc7*EvTT2rvpeqhg)W=%TOZ^>f`rD4|7Z5fq*2D^lpCttIg#ictgqZ$P@ru6P#f$x#KfnfTZj~LG6U_d-kE~`;kU_X)`H5so@?C zWmb!7x|xk@0L~0JFall*@ltyiL^)@3m4MqC7(7H0sH!WidId1#f#6R{Q&A!XzO1IAcIx;$k66dumt6lpUw@nL2MvqJ5^kbOVZ<^2jt5-njy|2@`07}0w z;M%I1$FCoLy`8xp8Tk)bFr;7aJeQ9KK6p=O$U0-&JYYy8woV*>b+FB?xLX`=pirYM z5K$BA(u)+jR{?O2r$c_Qvl?M{=Ar{yQ!UVsVn4k@0!b?_lA;dVz9uaQUgBH8Oz(Sb zrEs;&Ey>_ex8&!N{PmQjp+-Hlh|OA&wvDai#GpU=^-B70V0*LF=^bi+Nhe_o|azZ%~ZZ1$}LTmWt4aoB1 zPgccm$EwYU+jrdBaQFxQfn5gd(gM`Y*Ro1n&Zi?j=(>T3kmf94vdhf?AuS8>$Va#P zGL5F+VHpxdsCUa}+RqavXCobI-@B;WJbMphpK2%6t=XvKWWE|ruvREgM+|V=i6;;O zx$g=7^`$XWn0fu!gF=Xe9cMB8Z_SelD>&o&{1XFS`|nInK3BXlaeD*rc;R-#osyIS zWv&>~^TLIyBB6oDX+#>3<_0+2C4u2zK^wmHXXDD9_)kmLYJ!0SzM|%G9{pi)`X$uf zW}|%%#LgyK7m(4{V&?x_0KEDq56tk|0YNY~B(Sr|>WVz-pO3A##}$JCT}5P7DY+@W z#gJv>pA5>$|E3WO2tV7G^SuymB?tY`ooKcN3!vaQMnBNk-WATF{-$#}FyzgtJ8M^; zUK6KWSG)}6**+rZ&?o@PK3??uN{Q)#+bDP9i1W&j)oaU5d0bIWJ_9T5ac!qc?x66Q z$KUSZ`nYY94qfN_dpTFr8OW~A?}LD;Yty-BA)-be5Z3S#t2Io%q+cAbnGj1t$|qFR z9o?8B7OA^KjCYL=-!p}w(dkC^G6Nd%_I=1))PC0w5}ZZGJxfK)jP4Fwa@b-SYBw?% zdz9B-<`*B2dOn(N;mcTm%Do)rIvfXRNFX&1h`?>Rzuj~Wx)$p13nrDlS8-jwq@e@n zNIj_|8or==8~1h*Ih?w*8K7rYkGlwlTWAwLKc5}~dfz3y`kM&^Q|@C%1VAp_$wnw6zG~W4O+^ z>i?NY?oXf^Puc~+fDM$VgRNBpOZj{2cMP~gCqWAX4 z7>%$ux8@a&_B(pt``KSt;r+sR-$N;jdpY>|pyvPiN)9ohd*>mVST3wMo)){`B(&eX z1?zZJ-4u9NZ|~j1rdZYq4R$?swf}<6(#ex%7r{kh%U@kT)&kWuAszS%oJts=*OcL9 zaZwK<5DZw%1IFHXgFplP6JiL^dk8+SgM$D?8X+gE4172hXh!WeqIO>}$I9?Nry$*S zQ#f)RuH{P7RwA3v9f<-w>{PSzom;>(i&^l{E0(&Xp4A-*q-@{W1oE3K;1zb{&n28dSC2$N+6auXe0}e4b z)KLJ?5c*>@9K#I^)W;uU_Z`enquTUxr>mNq z1{0_puF-M7j${rs!dxxo3EelGodF1TvjV;Zpo;s{5f1pyCuRp=HDZ?s#IA4f?h|-p zGd|Mq^4hDa@Bh!c4ZE?O&x&XZ_ptZGYK4$9F4~{%R!}G1leCBx`dtNUS|K zL-7J5s4W@%mhXg1!}a4PD%!t&Qn%f_oquRajn3@C*)`o&K9o7V6DwzVMEhjVdDJ1fjhr#@=lp#@4EBqi=CCQ>73>R(>QKPNM&_Jpe5G`n4wegeC`FYEPJ{|vwS>$-`fuRSp3927qOv|NC3T3G-0 zA{K`|+tQy1yqE$ShWt8ny&5~)%ITb@^+x$w0)f&om;P8B)@}=Wzy59BwUfZ1vqw87 za2lB8J(&*l#(V}Id8SyQ0C(2amzkz3EqG&Ed0Jq1)$|&>4_|NIe=5|n=3?siFV0fI z{As5DLW^gs|B-b4C;Hd(SM-S~GQhzb>HgF2|2Usww0nL^;x@1eaB)=+Clj+$fF@H( z-fqP??~QMT$KI-#m;QC*&6vkp&8699G3)Bq0*kFZXINw=b9OVaed(3(3kS|IZ)CM? zJdnW&%t8MveBuK21uiYj)_a{Fnw0OErMzMN?d$QoPwkhOwcP&p+t>P)4tHlYw-pPN z^oJ=uc$Sl>pv@fZH~ZqxSvdhF@F1s=oZawpr^-#l{IIOGG=T%QXjtwPhIg-F@k@uIlr?J->Ia zpEUQ*=4g|XYn4Gez&aHr*;t$u3oODPmc2Ku)2Og|xjc%w;q!Zz+zY)*3{7V8bK4;& zYV82FZ+8?v)`J|G1w4I0fWdKg|2b#iaazCv;|?(W-q}$o&Y}Q5d@BRk^jL7#{kbCK zSgkyu;=DV+or2)AxCBgq-nj5=@n^`%T#V+xBGEkW4lCqrE)LMv#f;AvD__cQ@Eg3`~x| zW+h9mofSXCq5|M)9|ez(#X?-sxB%Go8};sJ?2abp(Y!lyi>k)|{M*Z$c{e1-K4ky` MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/packages/reactter_devtools_extension/web/index.html b/packages/reactter_devtools_extension/web/index.html new file mode 100644 index 00000000..07c43f45 --- /dev/null +++ b/packages/reactter_devtools_extension/web/index.html @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + reactter_devtools_extension + + + + + + + + + + diff --git a/packages/reactter_devtools_extension/web/manifest.json b/packages/reactter_devtools_extension/web/manifest.json new file mode 100644 index 00000000..6d8d8428 --- /dev/null +++ b/packages/reactter_devtools_extension/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "reactter_devtools_extension", + "short_name": "reactter_devtools_extension", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} From a7410cb139e784e8f424b75216c53d4eeee7c63b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 16 Aug 2024 00:46:14 -0600 Subject: [PATCH 008/141] fix(hooks): Fix bug in `UseEffect` causing dependencies to not be unwatched. --- packages/reactter/lib/src/hooks/use_effect.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/reactter/lib/src/hooks/use_effect.dart b/packages/reactter/lib/src/hooks/use_effect.dart index 72e81557..0569e02a 100644 --- a/packages/reactter/lib/src/hooks/use_effect.dart +++ b/packages/reactter/lib/src/hooks/use_effect.dart @@ -192,7 +192,6 @@ class UseEffect extends RtHook { return; } - _unwatchDependencies(); _watchDependencies(); } @@ -251,6 +250,8 @@ class UseEffect extends RtHook { } void _watchDependencies() { + _unwatchDependencies(); + for (final dependency in dependencies) { Rt.on(dependency, Lifecycle.willUpdate, _runCleanup); Rt.on(dependency, Lifecycle.didUpdate, _runCallback); From 9a12b3fce875de54ec7e7afafa5610ca68017c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 16 Aug 2024 00:47:34 -0600 Subject: [PATCH 009/141] refactor(test): Rearrange arguments in `UseAsyncState` constructor. --- packages/flutter_reactter/test/shareds/test_controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_reactter/test/shareds/test_controller.dart b/packages/flutter_reactter/test/shareds/test_controller.dart index 33510454..11678305 100644 --- a/packages/flutter_reactter/test/shareds/test_controller.dart +++ b/packages/flutter_reactter/test/shareds/test_controller.dart @@ -26,5 +26,5 @@ class TestController extends LifecycleObserver { final stateList = UseState([]); final stateMap = UseState({}); final stateClass = UseState(null); - final stateAsync = UseAsyncState("initial", _resolveStateAsync); + final stateAsync = UseAsyncState(_resolveStateAsync, "initial"); } From 9d1e2b0567d3dfdc17d7cb2402bbbc51bd680fd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 16 Aug 2024 00:53:15 -0600 Subject: [PATCH 010/141] refactor(hooks): Simplify the `UseReducer` implementation. --- packages/reactter/lib/src/hooks/use_reducer.dart | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/reactter/lib/src/hooks/use_reducer.dart b/packages/reactter/lib/src/hooks/use_reducer.dart index d6d09fb4..321c9b3b 100644 --- a/packages/reactter/lib/src/hooks/use_reducer.dart +++ b/packages/reactter/lib/src/hooks/use_reducer.dart @@ -51,7 +51,7 @@ class UseReducer extends RtHook { @override final $ = RtHook.$register; - late final UseState _state; + final UseState _state; /// Calculates a new state with state([T]) and action([RtAction]) given. final Reducer reducer; @@ -67,11 +67,12 @@ class UseReducer extends RtHook { }; /// {@macro reactter.use_reducer} - UseReducer(this.reducer, T initialState, {String? debugLabel}) - : _state = UseState(initialState), - _debugLabel = debugLabel { - UseEffect(update, [_state]); - } + UseReducer( + this.reducer, + T initialState, { + String? debugLabel, + }) : _state = UseState(initialState), + _debugLabel = debugLabel; /// Receives a [RtAction] and sends it to [reducer] method for resolved void dispatch(A action) { From 89b22a4ed83ff979edab78c0ae2dcd81723e5a3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 16 Aug 2024 01:17:30 -0600 Subject: [PATCH 011/141] feat(core,test): Add observer support for nested state updates. --- packages/reactter/lib/src/core/state.dart | 6 ++++++ .../test/core/state_observer_test.dart | 21 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/packages/reactter/lib/src/core/state.dart b/packages/reactter/lib/src/core/state.dart index 7db19258..20867d0f 100644 --- a/packages/reactter/lib/src/core/state.dart +++ b/packages/reactter/lib/src/core/state.dart @@ -166,6 +166,12 @@ abstract class State implements StateBase { for (final observer in StateObserver._observers) { observer.onStateUpdated(this); } + + if (boundInstance is State) { + for (final observer in StateObserver._observers) { + observer.onStateUpdated(boundInstance as State); + } + } } void _notifyDisponsed() { diff --git a/packages/reactter/test/core/state_observer_test.dart b/packages/reactter/test/core/state_observer_test.dart index 4838ad93..5115b201 100644 --- a/packages/reactter/test/core/state_observer_test.dart +++ b/packages/reactter/test/core/state_observer_test.dart @@ -155,5 +155,26 @@ void main() { Rt.removeObserver(observer); }); + + test("should be observed when a nested state is updated", () { + final observer = StateObserverTest(); + Rt.addObserver(observer); + + final stateA = UseState(0, debugLabel: "stateA"); + final stateB = UseState(1, debugLabel: "stateB"); + stateB.bind(stateA); + + stateB.value = 2; + + expect(observer.onStateUpdatedCalledCount, 2); + expect(observer.lastStateUpdated, "stateA"); + + stateA.value = 3; + + expect(observer.onStateUpdatedCalledCount, 3); + expect(observer.lastStateUpdated, "stateA"); + + Rt.removeObserver(observer); + }); }); } From bb63cbb919aa767f5663b9fc0330d09acccf4c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 16 Aug 2024 11:14:18 -0600 Subject: [PATCH 012/141] build: Allow communication between application and devtools extension. --- .../lib/flutter_reactter.dart | 1 + .../flutter_reactter/lib/src/devtools.dart | 100 ++++++++++++++++++ .../flutter_01.png | Bin 0 -> 64973 bytes .../lib/src/reactter_devtools_extension.dart | 46 ++++++++ .../reactter_devtools_extension/pubspec.lock | 4 +- .../reactter_devtools_extension/pubspec.yaml | 2 + 6 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 packages/flutter_reactter/lib/src/devtools.dart create mode 100644 packages/reactter_devtools_extension/flutter_01.png diff --git a/packages/flutter_reactter/lib/flutter_reactter.dart b/packages/flutter_reactter/lib/flutter_reactter.dart index a8921473..d9f5c690 100644 --- a/packages/flutter_reactter/lib/flutter_reactter.dart +++ b/packages/flutter_reactter/lib/flutter_reactter.dart @@ -5,4 +5,5 @@ export 'src/framework.dart' export 'src/extensions.dart'; export 'src/types.dart'; export 'src/widgets.dart' hide ProviderWrapper; +export 'src/devtools.dart' hide ReactterDevTools; export 'reactter.dart'; diff --git a/packages/flutter_reactter/lib/src/devtools.dart b/packages/flutter_reactter/lib/src/devtools.dart new file mode 100644 index 00000000..5a273309 --- /dev/null +++ b/packages/flutter_reactter/lib/src/devtools.dart @@ -0,0 +1,100 @@ +import 'dart:convert'; +import 'dart:developer' as dev; + +import 'package:flutter/foundation.dart'; +import 'package:meta/meta.dart'; +import 'package:reactter/reactter.dart'; + +@internal +class ReactterDevTools extends RtStateObserver { + ReactterDevTools._() { + print('ReactterDevTools initialized $hashCode'); + Rt.addObserver(this); + _initDevTools(); + } + + static final debugInstance = kDebugMode + ? ReactterDevTools._() + : throw UnsupportedError( + 'Cannot use ReactterDevTools in release mode', + ); + + static final Set states = {}; + + @override + void onStateCreated(covariant RtState state) { + states.add(state); + dev.postEvent('ext.reactter.onStateCreated', { + 'state': _getState(state), + }); + } + + @override + void onStateBound(covariant RtState state, Object instance) { + dev.postEvent('ext.reactter.onStateBound', { + 'state': _getState(state), + 'instance': instance.hashCode, + }); + } + + @override + void onStateUnbound(covariant RtState state, Object instance) { + dev.postEvent('ext.reactter.onStateUnbound', { + 'state': _getState(state), + 'instance': instance.hashCode, + }); + } + + @override + void onStateUpdated(covariant RtState state) { + dev.postEvent('ext.reactter.onStateUpdated', { + 'state': _getState(state), + }); + } + + @override + void onStateDisposed(covariant RtState state) { + states.remove(state); + dev.postEvent('ext.reactter.onStateDisposed', { + 'state': _getState(state), + }); + } + + void _initDevTools() { + dev.registerExtension( + 'ext.reactter.getAllStates', + (method, parameters) async { + return dev.ServiceExtensionResponse.result( + json.encode(_getStates()), + ); + }, + ); + } + + Map _getStates() { + return { + 'states': states.map(_getState).toList(), + }; + } + + Map _getState(RtState state) { + return { + 'id': state.hashCode, + 'type': state.runtimeType.toString(), + 'label': state.debugLabel, + 'properties': state.debugProperties.map( + (key, value) { + var valueEncode = value; + + try { + valueEncode = jsonEncode(value); + } catch (e) { + valueEncode = value.toString(); + } + + return MapEntry(key, valueEncode); + }, + ), + }; + } +} diff --git a/packages/reactter_devtools_extension/flutter_01.png b/packages/reactter_devtools_extension/flutter_01.png new file mode 100644 index 0000000000000000000000000000000000000000..c2929ce9a05e8ba63c82bc96d0d6e4d42e898f18 GIT binary patch literal 64973 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 literal 0 HcmV?d00001 diff --git a/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart b/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart index 3507d708..8850047c 100644 --- a/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart +++ b/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart @@ -1,3 +1,7 @@ +import 'dart:async'; + +import 'package:devtools_app_shared/service.dart'; +import 'package:devtools_extensions/devtools_extensions.dart'; import 'package:flutter/widgets.dart'; class ReactterDevtoolsExtension extends StatefulWidget { @@ -9,6 +13,48 @@ class ReactterDevtoolsExtension extends StatefulWidget { } class _ReactterDevtoolsExtensionState extends State { + late final StreamSubscription extensionEventSubscription; + + @override + void initState() { + super.initState(); + init(); + } + + Future init() async { + final vmService = await serviceManager.onServiceAvailable; + + final evalOnDartLibrary = EvalOnDartLibrary( + 'package:flutter_reactter/src/devtools.dart', + vmService, + serviceManager: serviceManager, + ); + + try { + final instanceRef = await evalOnDartLibrary.safeEval( + 'ReactterDevTools.debugInstance', + isAlive: null, + ); + + print( + 'ReactterDevTools.debugInstance: ${instanceRef.identityHashCode}', + ); + } catch (e) { + print('Error: $e'); + } + + extensionEventSubscription = vmService.onExtensionEvent.listen((event) { + if (!(event.extensionKind?.startsWith('ext.reactter.') ?? false)) { + return; + } + + print( + "Received event: ${event.extensionKind}" + " with data: ${event.extensionData}", + ); + }); + } + @override Widget build(BuildContext context) { return Container( diff --git a/packages/reactter_devtools_extension/pubspec.lock b/packages/reactter_devtools_extension/pubspec.lock index 76bcf49f..f0bb5e22 100644 --- a/packages/reactter_devtools_extension/pubspec.lock +++ b/packages/reactter_devtools_extension/pubspec.lock @@ -66,7 +66,7 @@ packages: source: hosted version: "1.0.8" devtools_app_shared: - dependency: transitive + dependency: "direct main" description: name: devtools_app_shared sha256: b8dbeb8352ffaff88fc1e00e0a5f1a659003fb75e0ff1250ec2add6ad6cd54c3 @@ -329,7 +329,7 @@ packages: source: hosted version: "2.1.4" vm_service: - dependency: transitive + dependency: "direct main" description: name: vm_service sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 diff --git a/packages/reactter_devtools_extension/pubspec.yaml b/packages/reactter_devtools_extension/pubspec.yaml index beae6748..2fa825c5 100644 --- a/packages/reactter_devtools_extension/pubspec.yaml +++ b/packages/reactter_devtools_extension/pubspec.yaml @@ -11,6 +11,8 @@ dependencies: sdk: flutter cupertino_icons: ^1.0.6 devtools_extensions: ^0.0.13 + devtools_app_shared: ^0.0.9 + vm_service: ^13.0.0 dev_dependencies: flutter_test: From f91bc026eca41c00098b9ae010fb132c6b232056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Thu, 22 Aug 2024 23:46:45 -0600 Subject: [PATCH 013/141] refactor: Add new features, solve state bugs and improve code. ### Enhancements - Add `RtContext` abstract class to provide access to the `Rt` instance. - Add `RtStateBase` abstract class to implement the base logic of the state. - Add `Rt.createState` method to create a new state. ### Breakings - Convert `RtState` to interface class, use `RtStateBase` abstract class instead, to implement the state logic. ### Fixes - Fix to show the correct dependency mode in the logger of the `Rt.register` method. - Fix nested `Rt.batch` method to work correctly. ### Internal - Add `IContext` interface class to represent the current context what the application is running in. - Replace `StateBase` to `State` interface. - Separate logic of `EventNotifier` to `Notifier` class. - Restructure the framework and core classes. --- packages/reactter/lib/reactter.dart | 2 +- .../reactter/lib/src/core/binding_zone.dart | 50 ++- packages/reactter/lib/src/core/core.dart | 22 -- .../lib/src/core/dependency_injection.dart | 11 +- .../lib/src/core/dependency_mode.dart | 2 +- .../reactter/lib/src/core/dependency_ref.dart | 2 +- .../lib/src/core/dependency_register.dart | 2 +- .../reactter/lib/src/core/event_handler.dart | 16 +- .../reactter/lib/src/core/event_notifier.dart | 344 ++---------------- packages/reactter/lib/src/core/hook.dart | 28 -- .../reactter/lib/src/core/hook_register.dart | 10 - packages/reactter/lib/src/core/lifecycle.dart | 6 +- .../lib/src/core/lifecycle_observer.dart | 12 +- packages/reactter/lib/src/core/logger.dart | 2 +- packages/reactter/lib/src/core/notifier.dart | 336 +++++++++++++++++ .../{observer.dart => observer_manager.dart} | 9 +- .../reactter/lib/src/core/state_base.dart | 41 --- .../lib/src/core/state_management.dart | 86 +++-- .../reactter/lib/src/core/state_observer.dart | 14 +- packages/reactter/lib/src/framework.dart | 19 + .../reactter/lib/src/framework/framework.dart | 12 - .../lib/src/framework/rt_context.dart | 34 ++ .../lib/src/framework/rt_dependency.dart | 2 +- .../reactter/lib/src/framework/rt_hook.dart | 47 ++- .../lib/src/framework/rt_interface.dart | 24 +- .../reactter/lib/src/framework/rt_state.dart | 31 +- .../rt_state_base.dart} | 103 ++++-- packages/reactter/lib/src/hooks/hooks.dart | 4 +- .../lib/src/hooks/use_async_state.dart | 12 +- .../reactter/lib/src/hooks/use_compute.dart | 11 +- .../lib/src/hooks/use_dependency.dart | 7 +- .../reactter/lib/src/hooks/use_effect.dart | 12 +- .../reactter/lib/src/interfaces/context.dart | 23 ++ .../reactter/lib/src/interfaces/hook.dart | 21 ++ .../reactter/lib/src/interfaces/observer.dart | 4 + .../reactter/lib/src/interfaces/state.dart | 60 +++ packages/reactter/lib/src/internals.dart | 26 ++ packages/reactter/lib/src/signal/signal.dart | 4 +- .../reactter/lib/src/signal/signal_impl.dart | 10 +- packages/reactter/lib/src/types.dart | 2 +- packages/reactter/pubspec.yaml | 6 + .../test/core/dependency_injection_test.dart | 8 +- .../test/core/state_management_test.dart | 38 +- .../test/core/state_observer_test.dart | 11 +- .../test/hooks/use_async_state_test.dart | 10 +- .../reactter/test/hooks/use_effect_test.dart | 6 +- .../reactter/test/hooks/use_reducer_test.dart | 8 +- packages/reactter/test/memo_test.dart | 18 +- .../test/shareds/test_controllers.dart | 2 +- packages/reactter/test/signal_test.dart | 16 +- 50 files changed, 899 insertions(+), 687 deletions(-) delete mode 100644 packages/reactter/lib/src/core/core.dart delete mode 100644 packages/reactter/lib/src/core/hook.dart delete mode 100644 packages/reactter/lib/src/core/hook_register.dart create mode 100644 packages/reactter/lib/src/core/notifier.dart rename packages/reactter/lib/src/core/{observer.dart => observer_manager.dart} (75%) delete mode 100644 packages/reactter/lib/src/core/state_base.dart create mode 100644 packages/reactter/lib/src/framework.dart delete mode 100644 packages/reactter/lib/src/framework/framework.dart create mode 100644 packages/reactter/lib/src/framework/rt_context.dart rename packages/reactter/lib/src/{core/state.dart => framework/rt_state_base.dart} (62%) create mode 100644 packages/reactter/lib/src/interfaces/context.dart create mode 100644 packages/reactter/lib/src/interfaces/hook.dart create mode 100644 packages/reactter/lib/src/interfaces/observer.dart create mode 100644 packages/reactter/lib/src/interfaces/state.dart create mode 100644 packages/reactter/lib/src/internals.dart diff --git a/packages/reactter/lib/reactter.dart b/packages/reactter/lib/reactter.dart index 9e0b46f1..e754dd59 100644 --- a/packages/reactter/lib/reactter.dart +++ b/packages/reactter/lib/reactter.dart @@ -1,6 +1,6 @@ library reactter; -export 'src/framework/framework.dart'; +export 'src/framework.dart'; export 'src/hooks/hooks.dart' hide UseAsyncStateBase; export 'src/memo/memo.dart'; export 'src/obj/obj.dart'; diff --git a/packages/reactter/lib/src/core/binding_zone.dart b/packages/reactter/lib/src/core/binding_zone.dart index c12cc130..61e0ac1b 100644 --- a/packages/reactter/lib/src/core/binding_zone.dart +++ b/packages/reactter/lib/src/core/binding_zone.dart @@ -1,9 +1,9 @@ -part of 'core.dart'; +part of '../internals.dart'; /// Represents an environment that Reactter uses for managing and /// attaching instances to a collection of states. @internal -class BindingZone { +class BindingZone { /// It's used to keep track of the current [BindingZone]. static BindingZone? _currentZone; @@ -11,46 +11,68 @@ class BindingZone { /// creating a new instance. final _parentZone = _currentZone; - /// It's used to store a collection of [StateBase]. - final states = {}; + /// It's used to store a collection of [IState]. + final states = {}; /// Returns the current [BindingZone]. static BindingZone? get currentZone => _currentZone; + /// The verification status indicates whether the object is verified or not. + /// + /// Note: The [_isVerified] variable is set to `true` within a finally block, + /// ensuring it is always updated. + bool _isVerified = false; + get isVerified { + try { + return _isVerified; + } finally { + _isVerified = true; + } + } + BindingZone() { /// This is done to keep track of the current [BindingZone] instance. _currentZone = this; } - /// It's used to bind the instance to the stored states([StateBase]). - static void autoBinding(Object? Function() getInstance) { - final zone = BindingZone(); - zone.bindInstanceToStates(getInstance()); + /// {@template reactter.binding_zone.auto_binding} + /// It's used to bind the instance to the stored states([IState]). + /// If the instance is null, it adds the stored states to the [BindingZone] parent. + /// {@endtemplate} + static T autoBinding(T Function() getInstance) { + final zone = BindingZone(); + final instance = getInstance(); + zone.bindInstanceToStates(instance); + + return instance; } /// Stores the state given from parameter. - static void recollectState(StateBase state) { + static void recollectState(IState state) { _currentZone?.states.add(state); } - /// Attaches the instance to the stored states([StateBase]), and if the instance is null, + /// Attaches the instance to the stored states([IState]), and if the instance is null, /// it adds the stored states to the [BindingZone] parent. - void bindInstanceToStates(T instance) { + E bindInstanceToStates(E instance) { try { if (instance == null) { _parentZone?.states.addAll(states); - return; + return instance; } - for (final state in states) { - if (state is State && state.boundInstance != null) { + for (final state in states.toList(growable: false)) { + if (state.boundInstance != null) { state._validateInstanceBinded(); } else if (state != instance) { state.bind(instance); } } + + return instance; } finally { _dispose(); + if (instance is IState) instance._register(); } } diff --git a/packages/reactter/lib/src/core/core.dart b/packages/reactter/lib/src/core/core.dart deleted file mode 100644 index 619e35c7..00000000 --- a/packages/reactter/lib/src/core/core.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'dart:async'; -import 'dart:collection'; -import 'package:meta/meta.dart'; -import 'package:reactter/reactter.dart'; - -part 'event_handler.dart'; -part 'event_notifier.dart'; -part 'hook_register.dart'; -part 'hook.dart'; -part 'dependency_mode.dart'; -part 'dependency_injection.dart'; -part 'dependency_register.dart'; -part 'dependency_ref.dart'; -part 'lifecycle_observer.dart'; -part 'lifecycle.dart'; -part 'logger.dart'; -part 'observer.dart'; -part 'state_base.dart'; -part 'state_management.dart'; -part 'state_observer.dart'; -part 'state.dart'; -part 'binding_zone.dart'; diff --git a/packages/reactter/lib/src/core/dependency_injection.dart b/packages/reactter/lib/src/core/dependency_injection.dart index 175a0ad2..169a71c9 100644 --- a/packages/reactter/lib/src/core/dependency_injection.dart +++ b/packages/reactter/lib/src/core/dependency_injection.dart @@ -1,4 +1,4 @@ -part of 'core.dart'; +part of '../internals.dart'; /// A mixin-class that adds dependency injection features /// to classes that use it. @@ -13,10 +13,7 @@ part of 'core.dart'; /// emits lifecycle events when dependencies are registered, unregistered, /// initialized, and destroyed. @internal -abstract class DependencyInjection { - Logger get logger; - EventHandler get eventHandler; - +abstract class DependencyInjection implements IContext { /// It stores the dependencies registered. final _dependencyRegisters = HashSet(); @@ -39,7 +36,7 @@ abstract class DependencyInjection { if (dependencyRegister != null) { logger.log( - 'The "$dependencyRegister" builder already registered as `$mode`.', + 'The "$dependencyRegister" builder already registered as `${dependencyRegister.mode}`.', ); return false; } @@ -548,7 +545,7 @@ abstract class DependencyInjection { eventHandler.emit(dependencyRegister, Lifecycle.deleted); logger.log(log); - if (instance is StateBase) instance.dispose(); + if (instance is IState) instance.dispose(); } /// Returns the [DependencyRef] associated with the given instance. diff --git a/packages/reactter/lib/src/core/dependency_mode.dart b/packages/reactter/lib/src/core/dependency_mode.dart index 512bfd32..daff0efd 100644 --- a/packages/reactter/lib/src/core/dependency_mode.dart +++ b/packages/reactter/lib/src/core/dependency_mode.dart @@ -1,4 +1,4 @@ -part of 'core.dart'; +part of '../internals.dart'; @Deprecated( 'Use `DependencyMode` instead. ' diff --git a/packages/reactter/lib/src/core/dependency_ref.dart b/packages/reactter/lib/src/core/dependency_ref.dart index ca42273b..8d635e2a 100644 --- a/packages/reactter/lib/src/core/dependency_ref.dart +++ b/packages/reactter/lib/src/core/dependency_ref.dart @@ -1,4 +1,4 @@ -part of 'core.dart'; +part of '../internals.dart'; /// A generic class that represents the reference to a dependency in Reactter's context. /// Provides an optional [id] parameter for identifying the dependency. diff --git a/packages/reactter/lib/src/core/dependency_register.dart b/packages/reactter/lib/src/core/dependency_register.dart index 128a4c14..e674c085 100644 --- a/packages/reactter/lib/src/core/dependency_register.dart +++ b/packages/reactter/lib/src/core/dependency_register.dart @@ -1,4 +1,4 @@ -part of 'core.dart'; +part of '../internals.dart'; /// A class that represents a dependency register in Reactter. /// diff --git a/packages/reactter/lib/src/core/event_handler.dart b/packages/reactter/lib/src/core/event_handler.dart index fd288fb0..e9d8f9d9 100644 --- a/packages/reactter/lib/src/core/event_handler.dart +++ b/packages/reactter/lib/src/core/event_handler.dart @@ -1,17 +1,11 @@ -part of 'core.dart'; +part of '../internals.dart'; /// A abstract-class that adds event handler features to classes that use it. /// /// It contains methods for adding, removing, and triggering events, /// as well as storing event callbacks. @internal -abstract class EventHandler { - @internal - DependencyInjection get dependencyInjection; - - @internal - Logger get logger; - +abstract class EventHandler implements IContext { final _notifiers = HashSet(); /// Puts on to listen [eventName] event. @@ -86,9 +80,10 @@ abstract class EventHandler { return notifier == instance; }); - for (final notifier in {...notifiers}) { + for (final notifier in notifiers.toList(growable: false)) { notifier.dispose(); _notifiers.remove(notifier); + stateManagement._deferredEvents.remove(notifier); } } @@ -105,7 +100,6 @@ abstract class EventHandler { instance, eventName, dependencyInjection, - logger, _offEventNotifier, ); } @@ -140,7 +134,7 @@ abstract class EventHandler { void _resolveLifecycleEvent( Object? instance, Lifecycle lifecycle, [ - StateBase? state, + IState? state, ]) { if (instance is LifecycleObserver) { return _executeLifecycleObserver(instance, lifecycle, state); diff --git a/packages/reactter/lib/src/core/event_notifier.dart b/packages/reactter/lib/src/core/event_notifier.dart index 67de77fc..e3d637f5 100644 --- a/packages/reactter/lib/src/core/event_notifier.dart +++ b/packages/reactter/lib/src/core/event_notifier.dart @@ -1,4 +1,4 @@ -part of 'core.dart'; +part of '../internals.dart'; /// A class that represents an event notifier reference for the [EventHandler]. class EventNotifierRef { @@ -53,37 +53,12 @@ class EventNotifierRef { /// behavior when notifying listeners. /// @internal -class EventNotifier extends EventNotifierRef { - int _count = 0; - // The _listeners is intentionally set to a fixed-length _GrowableList instead - // of const []. - // - // The const [] creates an instance of _ImmutableList which would be - // different from fixed-length _GrowableList used elsewhere in this class. - // keeping runtime type the same during the lifetime of this class lets the - // compiler to infer concrete type for this property, and thus improves - // performance. - static final List _emptyListeners = - List.filled(0, null); - List _listeners = _emptyListeners; - final _listenersSingleUse = {}; - - int _notificationCallStackDepth = 0; - int _reentrantlyRemovedListeners = 0; - bool _debugDisposed = false; - +class EventNotifier extends EventNotifierRef with Notifier { final DependencyInjection dependencyInjection; - final Logger logger; + @override + String get target => "$instanceObj about $event"; final void Function(EventNotifier notifier) onNotifyComplete; - EventNotifier( - Object? instanceOrObj, - Enum event, - this.dependencyInjection, - this.logger, - this.onNotifyComplete, - ) : super(instanceOrObj, event); - DependencyRef? get instanceRef => _dependencyRef ?? dependencyInjection._getDependencyRef(_instanceObj); @@ -91,6 +66,13 @@ class EventNotifier extends EventNotifierRef { _instanceObj ?? dependencyInjection._getDependencyRegisterByRef(_dependencyRef)?.instance; + EventNotifier( + Object? instanceOrObj, + Enum event, + this.dependencyInjection, + this.onNotifyComplete, + ) : super(instanceOrObj, event); + @override int get hashCode => Object.hash( _dependencyRef.hashCode, @@ -117,218 +99,6 @@ class EventNotifier extends EventNotifierRef { return instanceObj == other; } - /// Copied from Flutter - /// Used by subclasses to assert that the [EventNotifier] has not yet been - /// disposed. - /// - /// {@tool snippet} - /// The [debugAssertNotDisposed] function should only be called inside of an - /// assert, as in this example. - /// - /// ```dart - /// class MyNotifier with EventNotifier { - /// void doUpdate() { - /// assert(ReacterNotifier.debugAssertNotDisposed(this)); - /// // ... - /// } - /// } - /// ``` - /// {@end-tool} - // This is static and not an instance method because too many people try to - // implement EventNotifier instead of extending it (and so it is too breaking - // to add a method, especially for debug). - // coverage:ignore-start - static bool debugAssertNotDisposed(EventNotifier notifier) { - assert(() { - if (notifier._debugDisposed) { - throw AssertionError( - 'A ${notifier.runtimeType} was used after being disposed.\n' - 'Once you have called dispose() on a ${notifier.runtimeType}, it ' - 'can no longer be used.', - ); - } - return true; - }()); - return true; - } - // coverage:ignore-end - - /// Copied from Flutter - /// Whether any listeners are currently registered. - /// - /// Clients should not depend on this value for their behavior, because having - /// one listener's logic change when another listener happens to start or stop - /// listening will lead to extremely hard-to-track bugs. Subclasses might use - /// this information to determine whether to do any work when there are no - /// listeners, however; for example, resuming a [Stream] when a listener is - /// added and pausing it when a listener is removed. - /// - /// Typically this is used by overriding [addListener], checking if - /// [hasListeners] is false before calling `super.addListener()`, and if so, - /// starting whatever work is needed to determine when to call - /// [notifyListeners]; and similarly, by overriding [removeListener], checking - /// if [hasListeners] is false after calling `super.removeListener()`, and if - /// so, stopping that same work. - /// - /// This method returns false if [dispose] has been called. - @protected - bool get hasListeners => _count > 0; - - /// Copied from Flutter - /// Register a closure to be called when the object changes. - /// - /// If the given closure is already registered, an additional instance is - /// added, and must be removed the same number of times it is added before it - /// will stop being called. - /// - /// This method must not be called after [dispose] has been called. - /// - /// {@template reactter.EventNotifier.addListener} - /// If a listener is added twice, and is removed once during an iteration - /// (e.g. in response to a notification), it will still be called again. If, - /// on the other hand, it is removed as many times as it was registered, then - /// it will no longer be called. This odd behavior is the result of the - /// [EventNotifier] not being able to determine which listener is being - /// removed, since they are identical, therefore it will conservatively still - /// call all the listeners when it knows that any are still registered. - /// - /// This surprising behavior can be unexpectedly observed when registering a - /// listener on two separate objects which are both forwarding all - /// registrations to a common upstream object. - /// {@endtemplate} - /// - /// See also: - /// - /// * [removeListener], which removes a previously registered closure from - /// the list of closures that are notified when the object changes. - void addListener(Function listener, [bool singleUse = false]) { - assert(EventNotifier.debugAssertNotDisposed(this)); - - if (_count == _listeners.length) { - if (_count == 0) { - _listeners = List.filled(1, null); - } else { - final newListeners = List.filled( - _listeners.length * 2, - null, - ); - - for (int i = 0; i < _count; i++) { - newListeners[i] = _listeners[i]; - } - - _listeners = newListeners; - } - } - - _listeners[_count++] = listener; - - if (singleUse) { - _listenersSingleUse.add(listener); - } - } - - /// Copied from Flutter - // coverage:ignore-start - void _removeAt(int index) { - // The list holding the listeners is not growable for performances reasons. - // We still want to shrink this list if a lot of listeners have been added - // and then removed outside a notifyListeners iteration. - // We do this only when the real number of listeners is half the length - // of our list. - _count -= 1; - if (_count * 2 <= _listeners.length) { - final newListeners = List.filled(_count, null); - - // Listeners before the index are at the same place. - for (int i = 0; i < index; i++) { - newListeners[i] = _listeners[i]; - } - - // Listeners after the index move towards the start of the list. - for (int i = index; i < _count; i++) { - newListeners[i] = _listeners[i + 1]; - } - - _listeners = newListeners; - } else { - // When there are more listeners than half the length of the list, we only - // shift our listeners, so that we avoid to reallocate memory for the - // whole list. - for (int i = index; i < _count; i++) { - _listeners[i] = _listeners[i + 1]; - } - _listeners[_count] = null; - } - } - // coverage:ignore-end - - /// Copied from Flutter - /// Remove a previously registered closure from the list of closures that are - /// notified when the object changes. - /// - /// If the given listener is not registered, the call is ignored. - /// - /// This method returns immediately if [dispose] has been called. - /// - /// See also: - /// - /// * [addListener], which registers a closure to be called when the object - /// changes. - void removeListener(Function listener) { - // This method is allowed to be called on disposed instances for usability - // reasons. Due to how our frame scheduling logic between render objects and - // overlays, it is common that the owner of this instance would be disposed a - // frame earlier than the listeners. Allowing calls to this method after it - // is disposed makes it easier for listeners to properly clean up. - for (int i = 0; i < _count; i++) { - final Function? listenerAtIndex = _listeners[i]; - if (listenerAtIndex == listener) { - if (_notificationCallStackDepth > 0) { - // We don't resize the list during notifyListeners iterations - // but we set to null, the listeners we want to remove. We will - // effectively resize the list at the end of all notifyListeners - // iterations. - _listeners[i] = null; - _reentrantlyRemovedListeners++; - } else { - // When we are outside the notifyListeners iterations we can - // effectively shrink the list. - _removeAt(i); - } - break; - } - } - } - - /// Copied from Flutter - /// Discards any resources used by the object. After this is called, the - /// object is not in a usable state and should be discarded (calls to - /// [addListener] will throw after the object is disposed). - /// - /// This method should only be called by the object's owner. - /// - /// This method does not notify listeners, and clears the listener list once - /// it is called. Consumers of this class must decide on whether to notify - /// listeners or not immediately before disposal. - @mustCallSuper - void dispose() { - assert(EventNotifier.debugAssertNotDisposed(this)); - assert( - _notificationCallStackDepth == 0, - 'The "dispose()" method on $this was called during the call to ' - '"notifyListeners()". This is likely to cause errors since it modifies ' - 'the list of listeners while the list is being used.', - ); - assert(() { - _debugDisposed = true; - return true; - }()); - - _listeners = _emptyListeners; - _count = 0; - } - /// Copied from Flutter /// Call all the registered listeners. /// @@ -342,91 +112,21 @@ class EventNotifier extends EventNotifierRef { /// Surprising behavior can result when reentrantly removing a listener (e.g. /// in response to a notification) that has been registered multiple times. /// See the discussion at [removeListener]. + @override @protected @visibleForTesting void notifyListeners(Object? param) { - assert(EventNotifier.debugAssertNotDisposed(this)); - - if (_count == 0) { - return; - } - - // To make sure that listeners removed during this iteration are not called, - // we set them to null, but we don't shrink the list right away. - // By doing this, we can continue to iterate on our list until it reaches - // the last listener added before the call to this method. - - // To allow potential listeners to recursively call notifyListener, we track - // the number of times this method is called in _notificationCallStackDepth. - // Once every recursive iteration is finished (i.e. when _notificationCallStackDepth == 0), - // we can safely shrink our list so that it will only contain not null - // listeners. - - _notificationCallStackDepth++; - - final int end = _count; - for (int i = 0; i < end; i++) { - try { - final listener = _listeners[i]; - - if (_listenersSingleUse.contains(listener)) { - removeListener(listener!); - _listenersSingleUse.remove(listener); - } - - listener?.call(instanceObj, param); - } catch (error, _) { - logger.log( - 'An error was caught during notifyListeners on $instanceObj about $event event', - error: error, - level: LogLevel.error, - ); - } - } - - _notificationCallStackDepth--; - - // coverage:ignore-start - // No coverage for the following block because it is only used for - // performance optimization. - if (_notificationCallStackDepth == 0 && _reentrantlyRemovedListeners > 0) { - // We really remove the listeners when all notifications are done. - final int newLength = _count - _reentrantlyRemovedListeners; - if (newLength * 2 <= _listeners.length) { - // As in _removeAt, we only shrink the list when the real number of - // listeners is half the length of our list. - final List newListeners = - List.filled(newLength, null); - - int newIndex = 0; - for (int i = 0; i < _count; i++) { - final Function? listener = _listeners[i]; - if (listener != null) { - newListeners[newIndex++] = listener; - } - } - - _listeners = newListeners; - } else { - // Otherwise we put all the null references at the end. - for (int i = 0; i < newLength; i += 1) { - if (_listeners[i] == null) { - // We swap this item with the next not null item. - int swapIndex = i + 1; - while (_listeners[swapIndex] == null) { - swapIndex += 1; - } - _listeners[i] = _listeners[swapIndex]; - _listeners[swapIndex] = null; - } - } - } - // coverage:ignore-end - - _reentrantlyRemovedListeners = 0; - _count = newLength; - + try { + super.notifyListeners(param); + } catch (e) { + if (e is! AssertionError) rethrow; + } finally { onNotifyComplete(this); } } + + @override + void listenerCall(Function? listener, Object? param) { + listener?.call(instanceObj, param); + } } diff --git a/packages/reactter/lib/src/core/hook.dart b/packages/reactter/lib/src/core/hook.dart deleted file mode 100644 index cd3e34d1..00000000 --- a/packages/reactter/lib/src/core/hook.dart +++ /dev/null @@ -1,28 +0,0 @@ -part of 'core.dart'; - -/// An abstract class that provides the base functionality for creating -/// custom hooks in the Reactter library. -abstract class Hook extends State implements StateBase { - /// This variable is used to register [Hook] - /// and attach the [StateBase] that are defined here. - @protected - HookRegister get $; - - /// Initializes a new instance of the [Hook] class. - /// - /// This constructor calls the `end` method of the [HookRegister] instance - /// to register the hook and attach the collected states. - Hook() { - $.end(this); - } - - /// Executes [callback], and notifies the listeners about the update. - /// - /// If [callback] is provided, it will be executed before notifying the listeners. - /// If [callback] is not provided, an empty function will be executed. - @override - @mustCallSuper - void update([Function? callback]) { - return super.update(callback ?? () {}); - } -} diff --git a/packages/reactter/lib/src/core/hook_register.dart b/packages/reactter/lib/src/core/hook_register.dart deleted file mode 100644 index c58ef0c6..00000000 --- a/packages/reactter/lib/src/core/hook_register.dart +++ /dev/null @@ -1,10 +0,0 @@ -part of 'core.dart'; - -@internal -class HookRegister extends BindingZone { - /// Stores the instance of the [Hook] and attaches the previously collected states to it. - void end(Hook hook) { - bindInstanceToStates(hook); - BindingZone.recollectState(hook); - } -} diff --git a/packages/reactter/lib/src/core/lifecycle.dart b/packages/reactter/lib/src/core/lifecycle.dart index b147ece9..4dad14d1 100644 --- a/packages/reactter/lib/src/core/lifecycle.dart +++ b/packages/reactter/lib/src/core/lifecycle.dart @@ -1,4 +1,4 @@ -part of 'core.dart'; +part of '../internals.dart'; enum Lifecycle { /// This event is triggered when the [DependencyInjection] registers the dependency. @@ -20,10 +20,10 @@ enum Lifecycle { /// This event(exclusive to `flutter_reactter`) happens after the dependency has been successfully mounted in the widget tree. didMount, - /// This event is triggered anytime the dependency's state is about to be updated. The event parameter is a [StateBase]. + /// This event is triggered anytime the dependency's state is about to be updated. The event parameter is a [IState]. willUpdate, - /// This event is triggered anytime the dependency's state has been updated. The event parameter is a [StateBase]. + /// This event is triggered anytime the dependency's state has been updated. The event parameter is a [IState]. didUpdate, /// This event(exclusive to `flutter_reactter`) happens when the dependency is going to be unmounted from the widget tree. diff --git a/packages/reactter/lib/src/core/lifecycle_observer.dart b/packages/reactter/lib/src/core/lifecycle_observer.dart index 1e824d95..dd2d9bb4 100644 --- a/packages/reactter/lib/src/core/lifecycle_observer.dart +++ b/packages/reactter/lib/src/core/lifecycle_observer.dart @@ -1,4 +1,4 @@ -part of 'core.dart'; +part of '../internals.dart'; /// {@template reactter.lifecycle_observer} /// It's a mixin that provides a set of methods that can be used to observe @@ -47,12 +47,12 @@ abstract class LifecycleObserver { void onDidMount() {} /// This method is called when the dependency's state is about to be updated. - /// The parameter is a [StateBase]. - void onWillUpdate(covariant StateBase? state) {} + /// The parameter is a [IState]. + void onWillUpdate(covariant IState? state) {} /// This method is called when the dependency's state has been updated. - /// The parameter is a [StateBase]. - void onDidUpdate(covariant StateBase? state) {} + /// The parameter is a [IState]. + void onDidUpdate(covariant IState? state) {} /// This method is called when the dependency is going to be unmounted /// from the widget tree(exclusive to `flutter_reactter`). @@ -66,7 +66,7 @@ abstract class LifecycleObserver { void _executeLifecycleObserver( LifecycleObserver observer, Lifecycle lifecycle, [ - StateBase? state, + IState? state, ]) { switch (lifecycle) { // ignore: deprecated_member_use_from_same_package diff --git a/packages/reactter/lib/src/core/logger.dart b/packages/reactter/lib/src/core/logger.dart index bf85b286..35c4988c 100644 --- a/packages/reactter/lib/src/core/logger.dart +++ b/packages/reactter/lib/src/core/logger.dart @@ -1,4 +1,4 @@ -part of 'core.dart'; +part of '../internals.dart'; enum LogLevel { info, warning, error } diff --git a/packages/reactter/lib/src/core/notifier.dart b/packages/reactter/lib/src/core/notifier.dart new file mode 100644 index 00000000..3a210b0a --- /dev/null +++ b/packages/reactter/lib/src/core/notifier.dart @@ -0,0 +1,336 @@ +part of '../internals.dart'; + +@internal +abstract class Notifier { + int _count = 0; + // The _listeners is intentionally set to a fixed-length _GrowableList instead + // of const []. + // + // The const [] creates an instance of _ImmutableList which would be + // different from fixed-length _GrowableList used elsewhere in this class. + // keeping runtime type the same during the lifetime of this class lets the + // compiler to infer concrete type for this property, and thus improves + // performance. + static final List _emptyListeners = + List.filled(0, null); + List _listeners = _emptyListeners as List; + final _listenersSingleUse = {}; + + int _notificationCallStackDepth = 0; + int _reentrantlyRemovedListeners = 0; + bool _debugDisposed = false; + + String get target; + + /// Copied from Flutter + /// Used by subclasses to assert that the [Notifier] has not yet been + /// disposed. + /// + /// {@tool snippet} + /// The [debugAssertNotDisposed] function should only be called inside of an + /// assert, as in this example. + /// + /// ```dart + /// class MyNotifier with Notifier { + /// void doUpdate() { + /// assert(ReacterNotifier.debugAssertNotDisposed(this)); + /// // ... + /// } + /// } + /// ``` + /// {@end-tool} + // This is static and not an instance method because too many people try to + // implement Notifier instead of extending it (and so it is too breaking + // to add a method, especially for debug). + // coverage:ignore-start + static bool debugAssertNotDisposed(Notifier notifier) { + assert(() { + if (notifier._debugDisposed) { + throw AssertionError( + 'A ${notifier.runtimeType} was used after being disposed.\n' + 'Once you have called dispose() on a ${notifier.runtimeType}, it ' + 'can no longer be used.', + ); + } + return true; + }()); + return true; + } + // coverage:ignore-end + + /// Copied from Flutter + /// Whether any listeners are currently registered. + /// + /// Clients should not depend on this value for their behavior, because having + /// one listener's logic change when another listener happens to start or stop + /// listening will lead to extremely hard-to-track bugs. Subclasses might use + /// this information to determine whether to do any work when there are no + /// listeners, however; for example, resuming a [Stream] when a listener is + /// added and pausing it when a listener is removed. + /// + /// Typically this is used by overriding [addListener], checking if + /// [hasListeners] is false before calling `super.addListener()`, and if so, + /// starting whatever work is needed to determine when to call + /// [notifyListeners]; and similarly, by overriding [removeListener], checking + /// if [hasListeners] is false after calling `super.removeListener()`, and if + /// so, stopping that same work. + /// + /// This method returns false if [dispose] has been called. + @protected + bool get hasListeners => _count > 0; + + /// Copied from Flutter + /// Register a closure to be called when the object changes. + /// + /// If the given closure is already registered, an additional instance is + /// added, and must be removed the same number of times it is added before it + /// will stop being called. + /// + /// This method must not be called after [dispose] has been called. + /// + /// {@template reactter.Notifier.addListener} + /// If a listener is added twice, and is removed once during an iteration + /// (e.g. in response to a notification), it will still be called again. If, + /// on the other hand, it is removed as many times as it was registered, then + /// it will no longer be called. This odd behavior is the result of the + /// [Notifier] not being able to determine which listener is being + /// removed, since they are identical, therefore it will conservatively still + /// call all the listeners when it knows that any are still registered. + /// + /// This surprising behavior can be unexpectedly observed when registering a + /// listener on two separate objects which are both forwarding all + /// registrations to a common upstream object. + /// {@endtemplate} + /// + /// See also: + /// + /// * [removeListener], which removes a previously registered closure from + /// the list of closures that are notified when the object changes. + void addListener(T listener, [bool singleUse = false]) { + assert(Notifier.debugAssertNotDisposed(this)); + + if (_count == _listeners.length) { + if (_count == 0) { + _listeners = List.filled(1, null); + } else { + final newListeners = List.filled( + _listeners.length * 2, + null, + ); + + for (int i = 0; i < _count; i++) { + newListeners[i] = _listeners[i]; + } + + _listeners = newListeners; + } + } + + _listeners[_count++] = listener; + + if (singleUse) { + _listenersSingleUse.add(listener); + } + } + + /// Copied from Flutter + // coverage:ignore-start + void _removeAt(int index) { + // The list holding the listeners is not growable for performances reasons. + // We still want to shrink this list if a lot of listeners have been added + // and then removed outside a notifyListeners iteration. + // We do this only when the real number of listeners is half the length + // of our list. + _count -= 1; + if (_count * 2 <= _listeners.length) { + final newListeners = List.filled(_count, null); + + // Listeners before the index are at the same place. + for (int i = 0; i < index; i++) { + newListeners[i] = _listeners[i]; + } + + // Listeners after the index move towards the start of the list. + for (int i = index; i < _count; i++) { + newListeners[i] = _listeners[i + 1]; + } + + _listeners = newListeners; + } else { + // When there are more listeners than half the length of the list, we only + // shift our listeners, so that we avoid to reallocate memory for the + // whole list. + for (int i = index; i < _count; i++) { + _listeners[i] = _listeners[i + 1]; + } + _listeners[_count] = null; + } + } + // coverage:ignore-end + + /// Copied from Flutter + /// Remove a previously registered closure from the list of closures that are + /// notified when the object changes. + /// + /// If the given listener is not registered, the call is ignored. + /// + /// This method returns immediately if [dispose] has been called. + /// + /// See also: + /// + /// * [addListener], which registers a closure to be called when the object + /// changes. + void removeListener(Function listener) { + // This method is allowed to be called on disposed instances for usability + // reasons. Due to how our frame scheduling logic between render objects and + // overlays, it is common that the owner of this instance would be disposed a + // frame earlier than the listeners. Allowing calls to this method after it + // is disposed makes it easier for listeners to properly clean up. + for (int i = 0; i < _count; i++) { + final Function? listenerAtIndex = _listeners[i]; + if (listenerAtIndex == listener) { + if (_notificationCallStackDepth > 0) { + // We don't resize the list during notifyListeners iterations + // but we set to null, the listeners we want to remove. We will + // effectively resize the list at the end of all notifyListeners + // iterations. + _listeners[i] = null; + _reentrantlyRemovedListeners++; + } else { + // When we are outside the notifyListeners iterations we can + // effectively shrink the list. + _removeAt(i); + } + break; + } + } + } + + /// Copied from Flutter + /// Discards any resources used by the object. After this is called, the + /// object is not in a usable state and should be discarded (calls to + /// [addListener] will throw after the object is disposed). + /// + /// This method should only be called by the object's owner. + /// + /// This method does not notify listeners, and clears the listener list once + /// it is called. Consumers of this class must decide on whether to notify + /// listeners or not immediately before disposal. + @mustCallSuper + void dispose() { + assert(Notifier.debugAssertNotDisposed(this)); + assert( + _notificationCallStackDepth == 0, + 'The "dispose()" method on $this was called during the call to ' + '"notifyListeners()". This is likely to cause errors since it modifies ' + 'the list of listeners while the list is being used.', + ); + assert(() { + _debugDisposed = true; + return true; + }()); + + _listeners = _emptyListeners as List; + _count = 0; + } + + /// Copied from Flutter + /// Call all the registered listeners. + /// + /// Call this method whenever the object changes, to notify any clients the + /// object may have changed. Listeners that are added during this iteration + /// will not be visited. Listeners that are removed during this iteration will + /// not be visited after they are removed. + /// + /// This method must not be called after [dispose] has been called. + /// + /// Surprising behavior can result when reentrantly removing a listener (e.g. + /// in response to a notification) that has been registered multiple times. + /// See the discussion at [removeListener]. + @protected + @visibleForTesting + void notifyListeners(Object? param) { + assert(Notifier.debugAssertNotDisposed(this)); + + if (_count == 0) { + return; + } + + // To make sure that listeners removed during this iteration are not called, + // we set them to null, but we don't shrink the list right away. + // By doing this, we can continue to iterate on our list until it reaches + // the last listener added before the call to this method. + + // To allow potential listeners to recursively call notifyListener, we track + // the number of times this method is called in _notificationCallStackDepth. + // Once every recursive iteration is finished (i.e. when _notificationCallStackDepth == 0), + // we can safely shrink our list so that it will only contain not null + // listeners. + + _notificationCallStackDepth++; + + final int end = _count; + for (int i = 0; i < end; i++) { + try { + final listener = _listeners[i]; + + if (_listenersSingleUse.contains(listener)) { + removeListener(listener!); + _listenersSingleUse.remove(listener); + } + + listenerCall(listener, param); + } catch (error, _) { + throw AssertionError( + 'An error was caught during Notifier on $target', + ); + } + } + + _notificationCallStackDepth--; + + // coverage:ignore-start + // No coverage for the following block because it is only used for + // performance optimization. + if (_notificationCallStackDepth == 0 && _reentrantlyRemovedListeners > 0) { + // We really remove the listeners when all notifications are done. + final int newLength = _count - _reentrantlyRemovedListeners; + if (newLength * 2 <= _listeners.length) { + // As in _removeAt, we only shrink the list when the real number of + // listeners is half the length of our list. + final List newListeners = List.filled(newLength, null); + + int newIndex = 0; + for (int i = 0; i < _count; i++) { + final T? listener = _listeners[i]; + if (listener != null) { + newListeners[newIndex++] = listener; + } + } + + _listeners = newListeners; + } else { + // Otherwise we put all the null references at the end. + for (int i = 0; i < newLength; i += 1) { + if (_listeners[i] == null) { + // We swap this item with the next not null item. + int swapIndex = i + 1; + while (_listeners[swapIndex] == null) { + swapIndex += 1; + } + _listeners[i] = _listeners[swapIndex]; + _listeners[swapIndex] = null; + } + } + } + // coverage:ignore-end + + _reentrantlyRemovedListeners = 0; + _count = newLength; + } + } + + /// Calls the [listener] with the given [param]. + /// Override this method to provide custom behavior when notifying listeners. + void listenerCall(T? listener, Object? param); +} diff --git a/packages/reactter/lib/src/core/observer.dart b/packages/reactter/lib/src/core/observer_manager.dart similarity index 75% rename from packages/reactter/lib/src/core/observer.dart rename to packages/reactter/lib/src/core/observer_manager.dart index 72ba47a0..29a6d3e5 100644 --- a/packages/reactter/lib/src/core/observer.dart +++ b/packages/reactter/lib/src/core/observer_manager.dart @@ -1,7 +1,4 @@ -part of 'core.dart'; - -/// An abstract class representing an observer. -abstract class Observer {} +part of '../internals.dart'; /// An abstract class representing an observer manager. abstract class ObserverManager { @@ -9,7 +6,7 @@ abstract class ObserverManager { /// /// The [observer] parameter is the observer to be added. /// Only [StateObserver] instances can be added. - void addObserver(covariant Observer observer) { + void addObserver(covariant IObserver observer) { if (observer is StateObserver) { StateObserver._observers.add(observer); } @@ -19,7 +16,7 @@ abstract class ObserverManager { /// /// The [observer] parameter is the observer to be removed. /// Only [StateObserver] instances can be removed. - void removeObserver(covariant Observer observer) { + void removeObserver(covariant IObserver observer) { if (observer is StateObserver) { StateObserver._observers.remove(observer); } diff --git a/packages/reactter/lib/src/core/state_base.dart b/packages/reactter/lib/src/core/state_base.dart deleted file mode 100644 index 38cad85b..00000000 --- a/packages/reactter/lib/src/core/state_base.dart +++ /dev/null @@ -1,41 +0,0 @@ -part of 'core.dart'; - -/// A abstract class that represents a stare in Reactter. -@internal -abstract class StateBase { - @internal - DependencyInjection get dependencyInjection; - @internal - StateManagement get stateManagment; - @internal - EventHandler get eventHandler; - @internal - Logger get logger; - - /// Stores a reference to an object instance - @mustCallSuper - void bind(Object instance); - - /// Removes the reference to the object instance - @mustCallSuper - void unbind(); - - /// Executes [fnUpdate], and notify the listeners about to update. - /// - /// This method triggers the `Lifecycle.didUpdate` event, - /// which allows listeners to react to the updated state. - @mustCallSuper - void update(covariant Function fnUpdate); - - /// It's used to notify listeners that the state has been updated. - /// It is typically called after making changes to the state object. - /// - /// This method triggers the `Lifecycle.didUpdate` event, - /// which allows listeners to react to the updated state. - @mustCallSuper - void refresh(); - - /// Called when this object is removed - @mustCallSuper - void dispose(); -} diff --git a/packages/reactter/lib/src/core/state_management.dart b/packages/reactter/lib/src/core/state_management.dart index 136a6aea..3190be92 100644 --- a/packages/reactter/lib/src/core/state_management.dart +++ b/packages/reactter/lib/src/core/state_management.dart @@ -1,16 +1,41 @@ -part of 'core.dart'; +part of '../internals.dart'; @internal -abstract class StateManagement { - @internal - EventHandler get eventHandler; +abstract class StateManagement implements IContext { + int _batchRunningCount = 0; + bool get _isBatchRunning => _batchRunningCount > 0; - bool _isUntrackedRunning = false; - bool _isBatchRunning = false; - final HashMap _deferredEvents = HashMap(); + int _untrackedRunningCount = 0; + bool get _isUntrackedRunning => _untrackedRunningCount > 0; + + final LinkedHashMap _deferredEvents = LinkedHashMap(); + + /// Creates a new state by invoking the provided `buildState` function. + /// + /// The `buildState` function should return an instance of a class that extends [IState]. + /// The created state is automatically bound to the current binding zone using `BindingZone.autoBinding`. + /// + /// Example usage: + /// ```dart + /// class MyState with RtContext, RtStateBase { + /// int _value = 0; + /// int get value => value; + /// set value(int n) { + /// if (n == _value) return; + /// update(() => _value = n); + /// } + /// } + /// + /// final state = Rt.createState(() => MyState()); + /// ``` + /// + /// Returns the created state. + T createState(T Function() buildState) { + return BindingZone.autoBinding(buildState); + } /// {@template reactter.lazy_state} - /// Lazily initializes a state of type [StateBase] and attaches it to the given [instance]. + /// Lazily initializes a state of type [IState] and attaches it to the given [instance]. /// /// This method is recommended to use when initializing state inside a class /// using the `late` keyword. @@ -37,9 +62,8 @@ abstract class StateManagement { /// {@endtemplate} T lazyState(T Function() buildState, Object instance) { final zone = BindingZone(); - try { - return buildState(); + return createState(buildState); } finally { zone.bindInstanceToStates(instance); } @@ -69,15 +93,11 @@ abstract class StateManagement { /// ``` /// {@endtemplate} T untracked(T Function() callback) { - if (_isUntrackedRunning) { - return callback(); - } - try { - _isUntrackedRunning = true; + _untrackedRunningCount++; return callback(); } finally { - _isUntrackedRunning = false; + _untrackedRunningCount--; } } @@ -111,28 +131,34 @@ abstract class StateManagement { /// ``` /// {@endtemplate} T batch(T Function() callback) { - if (_isBatchRunning) { - return callback(); - } - try { - _isBatchRunning = true; + _batchRunningCount++; return callback(); } finally { - _isBatchRunning = false; - _endBatch(); + _batchRunningCount--; + + if (_batchRunningCount == 0) { + _endBatch(); + } } } void _endBatch() { - try { - for (final event in _deferredEvents.entries) { - final notifier = event.key; - final param = event.value; - notifier.notifyListeners(param); + for (final event in _deferredEvents.entries.toList(growable: false)) { + final notifier = event.key; + final param = event.value; + + if (_deferredEvents.isEmpty) { + break; + } + + if (_deferredEvents.containsKey(notifier)) { + try { + notifier.notifyListeners(param); + } finally { + _deferredEvents.remove(notifier); + } } - } finally { - _deferredEvents.clear(); } } diff --git a/packages/reactter/lib/src/core/state_observer.dart b/packages/reactter/lib/src/core/state_observer.dart index dbb527b9..5522e800 100644 --- a/packages/reactter/lib/src/core/state_observer.dart +++ b/packages/reactter/lib/src/core/state_observer.dart @@ -1,37 +1,37 @@ -part of 'core.dart'; +part of '../internals.dart'; /// {@template reactter.state_observer} /// An abstract class that defines the interface for observing state changes. /// Implementations of this class can be used to monitor the lifecycle of states. /// {@endtemplate} -abstract class StateObserver implements Observer { +abstract class StateObserver implements IObserver { /// A set of all registered state observers. static final _observers = {}; /// Called when a state is created. /// /// [state] - The state that was created. - void onStateCreated(covariant State state); + void onStateCreated(covariant IState state); /// Called when a state is bound to an instance. /// /// [state] - The state that was bound. /// [instance] - The instance to which the state was bound. - void onStateBound(covariant State state, Object instance); + void onStateBound(covariant IState state, Object instance); /// Called when a state is unbound from an instance. /// /// [state] - The state that was unbound. /// [instance] - The instance from which the state was unbound. - void onStateUnbound(covariant State state, Object instance); + void onStateUnbound(covariant IState state, Object instance); /// Called when a state is updated. /// /// [state] - The state that was updated. - void onStateUpdated(covariant State state); + void onStateUpdated(covariant IState state); /// Called when a state is disposed. /// /// [state] - The state that was disposed. - void onStateDisposed(covariant State state); + void onStateDisposed(covariant IState state); } diff --git a/packages/reactter/lib/src/framework.dart b/packages/reactter/lib/src/framework.dart new file mode 100644 index 00000000..1620373a --- /dev/null +++ b/packages/reactter/lib/src/framework.dart @@ -0,0 +1,19 @@ +import 'dart:developer' as dev; +import 'package:meta/meta.dart'; +import 'types.dart'; +import 'internals.dart'; + +export 'internals.dart' + show + Lifecycle, + LifecycleObserver, + DependencyMode, + LogLevel, + StateObserver, + RtHook, + RtState, + RtStateBase; + +part 'framework/rt_context.dart'; +part 'framework/rt_dependency.dart'; +part 'framework/rt_interface.dart'; diff --git a/packages/reactter/lib/src/framework/framework.dart b/packages/reactter/lib/src/framework/framework.dart deleted file mode 100644 index 31862945..00000000 --- a/packages/reactter/lib/src/framework/framework.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'dart:developer' as dev; -import 'package:meta/meta.dart'; -import 'package:reactter/src/types.dart'; - -import '../core/core.dart'; -export '../core/core.dart' - show Lifecycle, LifecycleObserver, DependencyMode, LogLevel; - -part 'rt_dependency.dart'; -part 'rt_hook.dart'; -part 'rt_interface.dart'; -part 'rt_state.dart'; diff --git a/packages/reactter/lib/src/framework/rt_context.dart b/packages/reactter/lib/src/framework/rt_context.dart new file mode 100644 index 00000000..04b44f4f --- /dev/null +++ b/packages/reactter/lib/src/framework/rt_context.dart @@ -0,0 +1,34 @@ +// ignore_for_file: non_constant_identifier_names + +part of '../framework.dart'; + +/// {@template reactter.rt} +/// This class represents the interface for the Reactter framework. +/// It provides methods and properties for interacting with the Reactter framework. +/// {@endtemplate} +final Rt = RtInterface(); + +mixin RtContext implements IContext { + @override + @internal + DependencyInjection get dependencyInjection => Rt; + + @override + @internal + StateManagement get stateManagement => Rt; + + @override + @internal + EventHandler get eventHandler => Rt; + + @override + @internal + Logger get logger => Rt; +} + +/// {@macro reactter.rt} +@Deprecated( + 'Use `Rt` instead. ' + 'This feature was deprecated after v7.3.0.', +) +final Reactter = Rt; diff --git a/packages/reactter/lib/src/framework/rt_dependency.dart b/packages/reactter/lib/src/framework/rt_dependency.dart index 1f98c401..68b69a62 100644 --- a/packages/reactter/lib/src/framework/rt_dependency.dart +++ b/packages/reactter/lib/src/framework/rt_dependency.dart @@ -1,4 +1,4 @@ -part of 'framework.dart'; +part of '../framework.dart'; /// {@template reactter.rt_dependency} /// Represents dependency managed by Reactter's dependency injection. diff --git a/packages/reactter/lib/src/framework/rt_hook.dart b/packages/reactter/lib/src/framework/rt_hook.dart index 81c813a9..fc1fe1e1 100644 --- a/packages/reactter/lib/src/framework/rt_hook.dart +++ b/packages/reactter/lib/src/framework/rt_hook.dart @@ -1,4 +1,4 @@ -part of 'framework.dart'; +part of '../internals.dart'; /// {@template reactter.rt_hook} /// An abstract-class that provides the functionality of [RtState]. @@ -40,24 +40,37 @@ part of 'framework.dart'; /// See also: /// * [RtState], adds state management features to [RtHook]. /// {@endtemplate} -abstract class RtHook extends Hook implements RtState { - @override - @internal - DependencyInjection get dependencyInjection => Rt; - @override - @internal - StateManagement get stateManagment => Rt; - @override - @internal - EventHandler get eventHandler => Rt; - @override - @internal - Logger get logger => Rt; - +abstract class RtHook with RtContext, RtStateBase implements IHook { + /// {@template reactter.rt_hook.register} /// This getter allows access to the [HookRegister] instance - /// which is responsible for registering a [Hook] + /// which is responsible for registering a [RtHook] /// and attaching previously collected states to it. - static HookRegister get $register => HookRegister(); + /// {@endtemplate} + static get $register => HookRegister(); + + /// This variable is used to register [RtHook] + /// and attach the [RtState] that are defined here. + @override + @protected + HookRegister get $; + + /// Initializes a new instance of the [RtHook] class. + /// + /// This constructor calls the `end` method of the [BindingHookZone] instance + /// to register the hook and attach the collected states. + RtHook() { + $.bindInstanceToStates(this); + } + + /// Executes [callback], and notifies the listeners about the update. + /// + /// If [callback] is provided, it will be executed before notifying the listeners. + /// If [callback] is not provided, an empty function will be executed. + @override + @mustCallSuper + void update([Function? callback]) { + return super.update(callback ?? () {}); + } } /// {@macro reactter.rt_hook} diff --git a/packages/reactter/lib/src/framework/rt_interface.dart b/packages/reactter/lib/src/framework/rt_interface.dart index 0525cbd9..23b2f365 100644 --- a/packages/reactter/lib/src/framework/rt_interface.dart +++ b/packages/reactter/lib/src/framework/rt_interface.dart @@ -1,6 +1,4 @@ -// ignore_for_file: non_constant_identifier_names - -part of 'framework.dart'; +part of '../framework.dart'; void defaultLogWriterCallback( String value, { @@ -24,10 +22,9 @@ class RtInterface EventHandler, Logger, ObserverManager { - static final _reactterInterface = RtInterface._(); - factory RtInterface() => _reactterInterface; - RtInterface._(); - + @override + @internal + StateManagement get stateManagement => this; @override @internal DependencyInjection get dependencyInjection => this; @@ -42,22 +39,9 @@ class RtInterface LogWriterCallback get log => defaultLogWriterCallback; } -/// {@template reactter.rt} -/// This class represents the interface for the Reactter framework. -/// It provides methods and properties for interacting with the Reactter framework. -/// {@endtemplate} -final Rt = RtInterface(); - /// {@macro reactter.rt_interface} @Deprecated( 'Use `RtInterface` instead. ' 'This feature was deprecated after v7.3.0.', ) typedef ReactterInterface = RtInterface; - -/// {@macro reactter.rt} -@Deprecated( - 'Use `Rt` instead. ' - 'This feature was deprecated after v7.3.0.', -) -final Reactter = Rt; diff --git a/packages/reactter/lib/src/framework/rt_state.dart b/packages/reactter/lib/src/framework/rt_state.dart index d32249bc..8b41d636 100644 --- a/packages/reactter/lib/src/framework/rt_state.dart +++ b/packages/reactter/lib/src/framework/rt_state.dart @@ -1,33 +1,6 @@ -part of 'framework.dart'; +part of '../internals.dart'; /// {@template reactter.rt_state} /// A abstract class that represents a stare in Reactter. -/// -/// It provides methods for attaching and detaching an object instance to -/// the state, notifying listeners of state changes, and disposing of the state -/// object when it is no longer needed. /// {@endtemplate} -abstract class RtState extends State { - @override - @internal - DependencyInjection get dependencyInjection => Rt; - @override - @internal - StateManagement get stateManagment => Rt; - @override - @internal - EventHandler get eventHandler => Rt; - @override - @internal - Logger get logger => Rt; -} - -/// {@macro reactter.rt_state} -@Deprecated( - 'Use `RtState` instead. ' - 'This feature was deprecated after v7.3.0.', -) -typedef ReactterState = RtState; - -/// {@macro reactter.state_observer} -typedef RtStateObserver = StateObserver; +abstract class RtState implements IState {} diff --git a/packages/reactter/lib/src/core/state.dart b/packages/reactter/lib/src/framework/rt_state_base.dart similarity index 62% rename from packages/reactter/lib/src/core/state.dart rename to packages/reactter/lib/src/framework/rt_state_base.dart index 20867d0f..214f8543 100644 --- a/packages/reactter/lib/src/core/state.dart +++ b/packages/reactter/lib/src/framework/rt_state_base.dart @@ -1,22 +1,63 @@ -part of 'core.dart'; +part of '../internals.dart'; + +/// {@template reactter.rt_state_base} +/// A base class that implements the [IState] interface. +/// +/// This class provides the basic functionality for managing the state. +/// {@endtemplate} +abstract class RtStateBase> implements RtState { + /// Debug assertion for registering a state object. + /// + /// This method is used to assert that a state object is being created within + /// a binding zone. If the assertion fails, an [AssertionError] is thrown. + static bool debugAssertRegistering() { + assert(() { + final currentZone = BindingZone.currentZone; + final isRegistering = currentZone != null && + (currentZone is BindingZone || currentZone is BindingZone) && + !currentZone.isVerified; + + if (!isRegistering) { + throw AssertionError( + "The state($E) must be create within the BindingZone.\n" + "You can use the 'Rt.createState' method or register as dependency " + "using the dependency injection to ensure that the state is registered.", + ); + } + return true; + }()); + + return true; + } + + // ignore: unused_field + final _debugAssertRegistering = debugAssertRegistering(); -/// An abstract class that provides common functionality for managing -/// state in Reactter. -@internal -abstract class State implements StateBase { bool _isUpdating = false; + @override + @protected + @mustCallSuper + void _register() { + BindingZone.recollectState(this); + _notifyCreated(); + } + /// A label used for debugging purposes. + @override String get debugLabel => "$runtimeType[$hashCode]"; /// A map containing properties used for debugging purposes. + @override Map get debugProperties => {}; /// The reference instance to the current state. + @override Object? get boundInstance => _boundInstance; Object? _boundInstance; /// Returns `true` if the state has been disposed. + @override bool get isDisposed => _isDisposed; bool _isDisposed = false; @@ -24,11 +65,6 @@ abstract class State implements StateBase { eventHandler._hasListeners(this) || (_boundInstance != null && eventHandler._hasListeners(_boundInstance)); - State() { - BindingZone.recollectState(this); - _notifyCreated(); - } - @mustCallSuper @override void bind(Object instance) { @@ -62,18 +98,18 @@ abstract class State implements StateBase { @override @mustCallSuper - void update(covariant Function fnUpdate) { + void update(covariant Function? fnUpdate) { assert(!_isDisposed, "Can't update when it's been disposed"); if (!_hasListeners || _isUpdating) { - fnUpdate(); + fnUpdate?.call(); _notifyUpdated(); return; } _isUpdating = true; _notify(Lifecycle.willUpdate); - fnUpdate(); + fnUpdate?.call(); _notify(Lifecycle.didUpdate); _notifyUpdated(); _isUpdating = false; @@ -81,7 +117,12 @@ abstract class State implements StateBase { @override @mustCallSuper - void refresh() { + @Deprecated("Use 'notify' instead.") + void refresh() => notify(); + + @override + @mustCallSuper + void notify() { assert(!_isDisposed, "Can't refresh when it's been disposed"); if (!_hasListeners || _isUpdating) { @@ -98,6 +139,8 @@ abstract class State implements StateBase { @override @mustCallSuper void dispose() { + if (_isDisposed) return; + _isDisposed = true; if (_boundInstance != null) { @@ -105,11 +148,21 @@ abstract class State implements StateBase { _boundInstance = null; } + eventHandler.emit(this, Lifecycle.deleted); eventHandler.offAll(this); _notifyDisponsed(); } + void revive() { + _isDisposed = false; + + if (_boundInstance != null) { + eventHandler.one(_boundInstance!, Lifecycle.deleted, _onInstanceDeleted); + } + } + + @override void _validateInstanceBinded() { if (dependencyInjection.isActive(boundInstance)) return; @@ -131,10 +184,10 @@ abstract class State implements StateBase { /// If [Rt._isBatchRunning] is true, the notification is deferred until the batch is completed. /// The [event] is emitted using [Rt.emit] for the current instance and [_boundInstance]. void _notify(Enum event) { - if (stateManagment._isUntrackedRunning) return; + if (stateManagement._isUntrackedRunning) return; - final emit = stateManagment._isBatchRunning - ? stateManagment._emitDefferred + final emit = stateManagement._isBatchRunning + ? stateManagement._emitDefferred : eventHandler.emit; emit(this, event, this); @@ -145,37 +198,35 @@ abstract class State implements StateBase { } void _notifyCreated() { - for (final observer in StateObserver._observers) { + for (final observer in StateObserver._observers.toList(growable: false)) { observer.onStateCreated(this); } } void _notifyBound(Object instance) { - for (final observer in StateObserver._observers) { + for (final observer in StateObserver._observers.toList(growable: false)) { observer.onStateBound(this, instance); } } void _notifyUnbound() { - for (final observer in StateObserver._observers) { + for (final observer in StateObserver._observers.toList(growable: false)) { observer.onStateUnbound(this, _boundInstance!); } } void _notifyUpdated() { - for (final observer in StateObserver._observers) { + for (final observer in StateObserver._observers.toList(growable: false)) { observer.onStateUpdated(this); - } - if (boundInstance is State) { - for (final observer in StateObserver._observers) { - observer.onStateUpdated(boundInstance as State); + if (boundInstance is RtState) { + observer.onStateUpdated(boundInstance as RtState); } } } void _notifyDisponsed() { - for (final observer in StateObserver._observers) { + for (final observer in StateObserver._observers.toList(growable: false)) { observer.onStateDisposed(this); } } diff --git a/packages/reactter/lib/src/hooks/hooks.dart b/packages/reactter/lib/src/hooks/hooks.dart index 632e5db9..8636f900 100644 --- a/packages/reactter/lib/src/hooks/hooks.dart +++ b/packages/reactter/lib/src/hooks/hooks.dart @@ -2,8 +2,8 @@ import 'dart:async'; import 'package:meta/meta.dart'; import '../args.dart'; -import '../core/core.dart'; -import '../framework/framework.dart'; +import '../internals.dart'; +import '../framework.dart'; import '../types.dart'; part 'use_async_state.dart'; diff --git a/packages/reactter/lib/src/hooks/use_async_state.dart b/packages/reactter/lib/src/hooks/use_async_state.dart index f8e44b0c..43a4065e 100644 --- a/packages/reactter/lib/src/hooks/use_async_state.dart +++ b/packages/reactter/lib/src/hooks/use_async_state.dart @@ -28,6 +28,9 @@ abstract class UseAsyncStateBase extends RtHook { Object? get error => _error.value; UseAsyncStateStatus get status => _status.value; + Future _future = Completer().future; + Future get future => _future; + final String? _debugLabel; @override String get debugLabel => _debugLabel ?? super.debugLabel; @@ -55,9 +58,9 @@ abstract class UseAsyncStateBase extends RtHook { final asyncFunctionExecuting = arg == null ? _asyncFunction() : _asyncFunction(arg); - _value.value = asyncFunctionExecuting is Future - ? await asyncFunctionExecuting - : asyncFunctionExecuting; + _future = Future.value(asyncFunctionExecuting); + + _value.value = await _future; _status.value = UseAsyncStateStatus.done; @@ -65,9 +68,8 @@ abstract class UseAsyncStateBase extends RtHook { } catch (e) { _error.value = e; _status.value = UseAsyncStateStatus.error; - return null; - } + } finally {} } /// Returns a new value of [R] depending on the state of the hook: diff --git a/packages/reactter/lib/src/hooks/use_compute.dart b/packages/reactter/lib/src/hooks/use_compute.dart index 8249872c..a496e662 100644 --- a/packages/reactter/lib/src/hooks/use_compute.dart +++ b/packages/reactter/lib/src/hooks/use_compute.dart @@ -38,10 +38,10 @@ class UseCompute extends RtHook { @override final $ = RtHook.$register; - late T _valueComputed; final T Function() compute; final List dependencies; + late T _valueComputed; T get value => _valueComputed; final String? _debugLabel; @@ -58,17 +58,16 @@ class UseCompute extends RtHook { this.compute, this.dependencies, { String? debugLabel, - }) : _debugLabel = debugLabel { - _valueComputed = compute(); - - for (var dependency in dependencies) { + }) : _debugLabel = debugLabel, + _valueComputed = compute() { + for (var dependency in dependencies.toList(growable: false)) { Rt.on(dependency, Lifecycle.didUpdate, _onDependencyChanged); } } @override void dispose() { - for (var dependency in dependencies) { + for (var dependency in dependencies.toList(growable: false)) { Rt.off(dependency, Lifecycle.didUpdate, _onDependencyChanged); } diff --git a/packages/reactter/lib/src/hooks/use_dependency.dart b/packages/reactter/lib/src/hooks/use_dependency.dart index e97e4bd4..cd17a661 100644 --- a/packages/reactter/lib/src/hooks/use_dependency.dart +++ b/packages/reactter/lib/src/hooks/use_dependency.dart @@ -260,9 +260,10 @@ class UseDependency extends RtHook { _unlisten(); Rt.delete(id, this); - update(() => _instance = null); - - super.dispose(); + update(() { + _instance = null; + super.dispose(); + }); } void _listen() { diff --git a/packages/reactter/lib/src/hooks/use_effect.dart b/packages/reactter/lib/src/hooks/use_effect.dart index 0569e02a..038bff13 100644 --- a/packages/reactter/lib/src/hooks/use_effect.dart +++ b/packages/reactter/lib/src/hooks/use_effect.dart @@ -213,6 +213,14 @@ class UseEffect extends RtHook { super.dispose(); } + @override + void revive() { + _watchInstanceAttached(); + _watchDependencies(); + + super.revive(); + } + void _watchInstanceAttached() { Rt.on( boundInstance!, @@ -252,14 +260,14 @@ class UseEffect extends RtHook { void _watchDependencies() { _unwatchDependencies(); - for (final dependency in dependencies) { + for (final dependency in dependencies.toList(growable: false)) { Rt.on(dependency, Lifecycle.willUpdate, _runCleanup); Rt.on(dependency, Lifecycle.didUpdate, _runCallback); } } void _unwatchDependencies() { - for (final dependency in dependencies) { + for (final dependency in dependencies.toList(growable: false)) { Rt.off(dependency, Lifecycle.willUpdate, _runCleanup); Rt.off(dependency, Lifecycle.didUpdate, _runCallback); } diff --git a/packages/reactter/lib/src/interfaces/context.dart b/packages/reactter/lib/src/interfaces/context.dart new file mode 100644 index 00000000..8d10d986 --- /dev/null +++ b/packages/reactter/lib/src/interfaces/context.dart @@ -0,0 +1,23 @@ +part of '../internals.dart'; + +/// Represents an interface for the context of the application. +/// This interface provides access to various components and services used within the application. +/// +/// The [IContext] interface includes the following properties: +/// - [dependencyInjection]: An instance of the [DependencyInjection] class that handles dependency injection. +/// - [stateManagement]: An instance of the [StateManagement] class that manages the state of the application. +/// - [eventHandler]: An instance of the [EventHandler] class that handles events within the application. +/// - [logger]: An instance of the [Logger] class that provides logging functionality. +abstract class IContext { + @internal + DependencyInjection get dependencyInjection; + + @internal + StateManagement get stateManagement; + + @internal + EventHandler get eventHandler; + + @internal + Logger get logger; +} diff --git a/packages/reactter/lib/src/interfaces/hook.dart b/packages/reactter/lib/src/interfaces/hook.dart new file mode 100644 index 00000000..cdd9a336 --- /dev/null +++ b/packages/reactter/lib/src/interfaces/hook.dart @@ -0,0 +1,21 @@ +part of '../internals.dart'; + +/// An abstract class that provides the base functionality for creating +/// custom hooks in the Reactter library. +abstract class IHook implements IState { + /// This variable is used to register [IHook] + /// and attach the [IState] that are defined here. + @protected + HookRegister get $; + + /// Executes [callback], and notifies the listeners about the update. + /// + /// If [callback] is provided, it will be executed before notifying the listeners. + /// If [callback] is not provided, an empty function will be executed. + @override + @mustCallSuper + void update([Function? callback]); +} + +@internal +class HookRegister extends BindingZone {} diff --git a/packages/reactter/lib/src/interfaces/observer.dart b/packages/reactter/lib/src/interfaces/observer.dart new file mode 100644 index 00000000..32789485 --- /dev/null +++ b/packages/reactter/lib/src/interfaces/observer.dart @@ -0,0 +1,4 @@ +part of '../internals.dart'; + +/// An abstract class representing an observer. +abstract class IObserver {} diff --git a/packages/reactter/lib/src/interfaces/state.dart b/packages/reactter/lib/src/interfaces/state.dart new file mode 100644 index 00000000..73dfe114 --- /dev/null +++ b/packages/reactter/lib/src/interfaces/state.dart @@ -0,0 +1,60 @@ +part of '../internals.dart'; + +/// {@template reactter.rt_state} +/// A abstract class that represents a stare in Reactter. +/// {@endtemplate} +abstract class IState implements IContext { + bool get isDisposed; + + /// The reference instance to the current state. + Object? get boundInstance; + + /// A label used for debugging purposes. + String get debugLabel => "$runtimeType[$hashCode]"; + + /// A map containing properties used for debugging purposes. + Map get debugProperties; + + /// This method is typically used for internal + /// registration purposes within the state management system. + @protected + @mustCallSuper + void _register(); + + /// Stores a reference to an object instance + @mustCallSuper + void bind(Object instance); + + /// Removes the reference to the object instance + @mustCallSuper + void unbind(); + + /// Executes [fnUpdate], and notify the listeners about to update. + /// + /// This method triggers the `Lifecycle.didUpdate` event, + /// which allows listeners to react to the updated state. + @mustCallSuper + void update(covariant Function fnUpdate); + + /// Executes [fnUpdate], and notify the listeners about to update. + /// + /// This method triggers the `Lifecycle.didUpdate` event, + /// which allows listeners to react to the updated state. + @mustCallSuper + @Deprecated("Use 'notify' instead.") + void refresh(); + + /// It's used to notify listeners that the state has been updated. + /// It is typically called after making changes to the state object. + /// + /// This method triggers the `Lifecycle.didUpdate` event, + /// which allows listeners to react to the updated state. + @mustCallSuper + void notify(); + + /// Called when this object is removed + @mustCallSuper + void dispose(); + + void _validateInstanceBinded(); +} diff --git a/packages/reactter/lib/src/internals.dart b/packages/reactter/lib/src/internals.dart new file mode 100644 index 00000000..969071fa --- /dev/null +++ b/packages/reactter/lib/src/internals.dart @@ -0,0 +1,26 @@ +import 'dart:async'; +import 'dart:collection'; +import 'package:meta/meta.dart'; +import 'package:reactter/reactter.dart'; + +part 'core/binding_zone.dart'; +part 'core/dependency_injection.dart'; +part 'core/dependency_mode.dart'; +part 'core/dependency_ref.dart'; +part 'core/dependency_register.dart'; +part 'core/event_handler.dart'; +part 'core/event_notifier.dart'; +part 'core/lifecycle_observer.dart'; +part 'core/lifecycle.dart'; +part 'core/logger.dart'; +part 'core/notifier.dart'; +part 'core/observer_manager.dart'; +part 'core/state_management.dart'; +part 'core/state_observer.dart'; +part 'framework/rt_hook.dart'; +part 'framework/rt_state.dart'; +part 'framework/rt_state_base.dart'; +part 'interfaces/context.dart'; +part 'interfaces/hook.dart'; +part 'interfaces/observer.dart'; +part 'interfaces/state.dart'; diff --git a/packages/reactter/lib/src/signal/signal.dart b/packages/reactter/lib/src/signal/signal.dart index 857a1592..56e05da0 100644 --- a/packages/reactter/lib/src/signal/signal.dart +++ b/packages/reactter/lib/src/signal/signal.dart @@ -1,6 +1,8 @@ import 'dart:math'; -import '../framework/framework.dart'; +import 'package:reactter/src/internals.dart'; + +import '../framework.dart'; import '../obj/obj.dart'; part 'signal_impl.dart'; diff --git a/packages/reactter/lib/src/signal/signal_impl.dart b/packages/reactter/lib/src/signal/signal_impl.dart index 02bbb50a..f98b7cb6 100644 --- a/packages/reactter/lib/src/signal/signal_impl.dart +++ b/packages/reactter/lib/src/signal/signal_impl.dart @@ -76,13 +76,19 @@ enum SignalEvent { onGetValue, onSetValue } /// /// * [Obj], a base-class that can be used to store a value of [T]. /// {@endtemplate} -class Signal extends RtState with ObjBase implements Obj { +class Signal + with RtContext, RtStateBase>, ObjBase + implements Obj { + final _$ = BindingZone>(); + /// {@macro reactter.signal} Signal( T value, { String? debugLabel, }) : _value = value, - _debugLabel = debugLabel; + _debugLabel = debugLabel { + _$.bindInstanceToStates(this); + } bool _shouldGetValueNotify = true; bool _shouldSetValueNotify = true; diff --git a/packages/reactter/lib/src/types.dart b/packages/reactter/lib/src/types.dart index 66998650..c80bc506 100644 --- a/packages/reactter/lib/src/types.dart +++ b/packages/reactter/lib/src/types.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'args.dart'; -import 'core/core.dart' show LogLevel; +import 'internals.dart' show LogLevel; import 'hooks/hooks.dart'; import 'memo/memo.dart' show Memo; diff --git a/packages/reactter/pubspec.yaml b/packages/reactter/pubspec.yaml index 0eb4847f..692efea3 100644 --- a/packages/reactter/pubspec.yaml +++ b/packages/reactter/pubspec.yaml @@ -16,3 +16,9 @@ dev_dependencies: fake_async: ^1.3.1 lints: ^2.0.1 test: ^1.25.2 + +scripts: + test: "dart run test --coverage=./coverage " + analyze: "dart analyze ." + public-dry-run: "dart pub publish --dry-run" + public: "dart pub publish" \ No newline at end of file diff --git a/packages/reactter/test/core/dependency_injection_test.dart b/packages/reactter/test/core/dependency_injection_test.dart index b1fdc29c..58108227 100644 --- a/packages/reactter/test/core/dependency_injection_test.dart +++ b/packages/reactter/test/core/dependency_injection_test.dart @@ -3,6 +3,10 @@ import 'package:test/test.dart'; import '../shareds/test_controllers.dart'; +class MyTest { + final uState = UseState(0); +} + void main() { group("DependencyInjection", () { test("should register a dependency", () { @@ -242,7 +246,7 @@ void main() { }); test("should check if an instance is registered", () { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); bool isActive = Rt.isActive(testController); expect(isActive, false); @@ -256,7 +260,7 @@ void main() { }); test("should check if a dependency is registered", () { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); bool hasRegister = Rt.hasRegister(); expect(hasRegister, false); diff --git a/packages/reactter/test/core/state_management_test.dart b/packages/reactter/test/core/state_management_test.dart index e87a1c4d..ac88ceb4 100644 --- a/packages/reactter/test/core/state_management_test.dart +++ b/packages/reactter/test/core/state_management_test.dart @@ -80,22 +80,44 @@ void main() { }); test("should run the callback as nested batch", () { - final state = UseState(0); - final computed = UseCompute(() => state.value + 1, [state]); + final stateA = UseState(0); + final stateB = UseState(0); + final stateC = UseState(0); + final computed = UseCompute( + () => stateA.value + stateB.value + stateC.value, + [stateA, stateB, stateC], + ); + + UseEffect(() { + Rt.batch(() { + stateB.value += 1; // 3 + stateC.value += 1; // 1 + + // stateA(2) + stateB(2) + stateC(0) + expect(computed.value, 4); + }); + + // stateA(2) + stateB(3) + stateC(1) + expect(computed.value, 6); + }, [stateA]); Rt.batch(() { + stateA.value += 1; // 1 + Rt.batch(() { - state.value = 2; + stateB.value += 1; // 1 - expect(computed.value, 1); + expect(computed.value, 0); }); - state.value += 1; + stateB.value += 1; // 2 + stateA.value += 1; // 2 - expect(computed.value, 1); - }); + expect(computed.value, 0); + }); // -> go to UseEffect - expect(computed.value, 4); + // stateA(2) + stateB(3) + stateC(1) + expect(computed.value, 6); }); }); } diff --git a/packages/reactter/test/core/state_observer_test.dart b/packages/reactter/test/core/state_observer_test.dart index 5115b201..3151423a 100644 --- a/packages/reactter/test/core/state_observer_test.dart +++ b/packages/reactter/test/core/state_observer_test.dart @@ -1,5 +1,4 @@ import 'package:reactter/reactter.dart'; -import 'package:reactter/src/core/core.dart'; import 'package:test/test.dart'; class StateObserverTest extends StateObserver { @@ -20,32 +19,32 @@ class StateObserverTest extends StateObserver { String? lastStateDisposed; @override - void onStateCreated(covariant State state) { + void onStateCreated(covariant RtState state) { lastStateCreated = state.debugLabel; onStateCreatedCalledCount++; } @override - void onStateBound(covariant State state, Object instance) { + void onStateBound(covariant RtState state, Object instance) { lastStateBound = state.debugLabel; lastInstanceBound = instance; onStateBoundCalledCount++; } @override - void onStateUnbound(covariant State state, Object instance) { + void onStateUnbound(covariant RtState state, Object instance) { lastStateUnbound = state.debugLabel; onStateUnboundCalledCount++; } @override - void onStateUpdated(covariant State state) { + void onStateUpdated(covariant RtState state) { lastStateUpdated = state.debugLabel; onStateUpdatedCalledCount++; } @override - void onStateDisposed(covariant State state) { + void onStateDisposed(covariant RtState state) { lastStateDisposed = state.debugLabel; onStateDisposedCalledCount++; } diff --git a/packages/reactter/test/hooks/use_async_state_test.dart b/packages/reactter/test/hooks/use_async_state_test.dart index 813dd4a3..0bd57905 100644 --- a/packages/reactter/test/hooks/use_async_state_test.dart +++ b/packages/reactter/test/hooks/use_async_state_test.dart @@ -6,7 +6,7 @@ import '../shareds/test_controllers.dart'; void main() { group("UseAsyncState", () { test("should resolve state", () async { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); final stateAsync = testController.stateAsync; expect(stateAsync.value, "initial"); @@ -17,7 +17,7 @@ void main() { }); test("should catch error", () async { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); final stateAsync = testController.stateAsyncWithError; await stateAsync.resolve(); @@ -28,7 +28,7 @@ void main() { }); test("should reset state", () async { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); final stateAsync = testController.stateAsync; await stateAsync.resolve(); @@ -41,7 +41,7 @@ void main() { }); test("should resolve state with arguments", () async { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); final stateAsync = testController.stateAsyncWithArg; await stateAsync.resolve(Args1('arg1')); @@ -58,7 +58,7 @@ void main() { }); test("should get value when", () async { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); final stateAsync = testController.stateAsyncWithArg; final s1 = stateAsync.when(standby: (value) => value); diff --git a/packages/reactter/test/hooks/use_effect_test.dart b/packages/reactter/test/hooks/use_effect_test.dart index 420fe724..c45fb227 100644 --- a/packages/reactter/test/hooks/use_effect_test.dart +++ b/packages/reactter/test/hooks/use_effect_test.dart @@ -151,7 +151,7 @@ void main() { }); test("shouldn't be called by instance that was not registered", () { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); int nCalls = 0; UseEffect( @@ -244,7 +244,7 @@ void main() { }); test("should be called with dispatchEffect", () { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); final stateA = testController.stateBool; final stateB = testController.stateInt; final stateC = testController.signalString; @@ -330,7 +330,7 @@ void main() { class UseEffectDispatchController extends DispatchEffect {} class UseEffectTestController extends TestController { - final testControllerInner = TestController(); + final testControllerInner = Rt.createState(() => TestController()); int nCalls1 = 0; int nCalls2 = 0; diff --git a/packages/reactter/test/hooks/use_reducer_test.dart b/packages/reactter/test/hooks/use_reducer_test.dart index 97818b0f..c1c1596f 100644 --- a/packages/reactter/test/hooks/use_reducer_test.dart +++ b/packages/reactter/test/hooks/use_reducer_test.dart @@ -6,13 +6,13 @@ import '../shareds/test_controllers.dart'; void main() { group("UseReducer", () { test("should have a initial value", () { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); expect(testController.stateReduce.value.count, 0); }); test("should dispatch a action", () { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); testController.stateReduce.dispatch(IncrementAction()); expect(testController.stateReduce.value.count, 1); @@ -32,7 +32,7 @@ void main() { }); test("should dispatch a action callable", () { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); testController.stateReduce.dispatch(IncrementActionCallable(quantity: 2)); expect(testController.stateReduce.value.count, 2); @@ -42,7 +42,7 @@ void main() { }); test("should update state when dispatch action", () { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); late bool? isStateChanged; UseEffect(() { diff --git a/packages/reactter/test/memo_test.dart b/packages/reactter/test/memo_test.dart index fefa781a..89799a46 100644 --- a/packages/reactter/test/memo_test.dart +++ b/packages/reactter/test/memo_test.dart @@ -8,13 +8,13 @@ import 'shareds/test_controllers.dart'; void main() { group("Memo", () { test("should be a class callable with arguments", () { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); expect(testController.memo.call, isA()); }); test("should memoize the returned value by the calculate function", () { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); final value0 = testController.memo(null); final value1 = testController.memo(Args1(1)); @@ -32,7 +32,7 @@ void main() { }); test("should override the cached value", () { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); final value0 = testController.memo(null); final value1 = testController.memo(Args1(1)); @@ -50,7 +50,7 @@ void main() { }); test("should remove all cached data", () { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); final value0 = testController.memo(null); final value1 = testController.memo(Args1(1)); @@ -71,7 +71,7 @@ void main() { }); test("should removed the cached data", () { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); final value0 = testController.memo(null); final value1 = testController.memo(Args1(1)); @@ -93,7 +93,7 @@ void main() { }); test("shouldn't memoize when an error occurs", () { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); expect( () => testController.memo(Args1(ArgumentError())), @@ -257,13 +257,13 @@ void main() { group("Memo.inline", () { test("should be a class callable with arguments", () { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); expect(testController.inlineMemo.call, isA()); }); test("should memoize the returned value by the calculate function", () { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); final value0 = testController.inlineMemo(null); final value1 = testController.inlineMemo(Args1(1)); @@ -281,7 +281,7 @@ void main() { }); test("should override the cached value", () { - final testController = TestController(); + final testController = Rt.createState(() => TestController()); final value0 = testController.inlineMemo(null); final value1 = testController.inlineMemo(Args1(1)); diff --git a/packages/reactter/test/shareds/test_controllers.dart b/packages/reactter/test/shareds/test_controllers.dart index 4a2ee957..d522fd0a 100644 --- a/packages/reactter/test/shareds/test_controllers.dart +++ b/packages/reactter/test/shareds/test_controllers.dart @@ -75,7 +75,7 @@ TestStore _reducer(TestStore state, RtAction action) { } } -class TestController extends RtState { +class TestController with RtContext, RtStateBase { final signalString = Signal("initial"); final stateBool = UseState(false); final stateString = UseState("initial"); diff --git a/packages/reactter/test/signal_test.dart b/packages/reactter/test/signal_test.dart index 9932e047..42373659 100644 --- a/packages/reactter/test/signal_test.dart +++ b/packages/reactter/test/signal_test.dart @@ -139,7 +139,7 @@ void main() { }); test("should be able to bind to another instance", () { - final testController = Rt.create(() => SignalTestController())!; + final testController = Rt.create(() => TestController())!; final signalString = testController.signalString; expect(signalString(), "initial"); @@ -148,18 +148,14 @@ void main() { expect(signalString(), "change signal"); - Rt.destroy(); + Rt.destroy(); expect( - signalString("no throw a assertion error"), - "no throw a assertion error", + () { + signalString("throw a assertion error because it's been destroyed"); + }, + throwsA(isA()), ); }); }); } - -class SignalTestController extends TestController { - SignalTestController() { - signalString.bind(TestController()); - } -} From 9e82d71cb3234f19dfbcc1c9a5c5ba0cde5516ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Thu, 22 Aug 2024 23:48:06 -0600 Subject: [PATCH 014/141] refactor(widgets): Add dependency `mode` to `RtComponent`. --- packages/flutter_reactter/lib/src/widgets/rt_component.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/flutter_reactter/lib/src/widgets/rt_component.dart b/packages/flutter_reactter/lib/src/widgets/rt_component.dart index d0687c55..d3104edd 100644 --- a/packages/flutter_reactter/lib/src/widgets/rt_component.dart +++ b/packages/flutter_reactter/lib/src/widgets/rt_component.dart @@ -79,6 +79,9 @@ abstract class RtComponent extends StatelessWidget { /// An indentify of the [T] dependency. String? get id => null; + /// How to handle the [T] dependency. + DependencyMode get mode => DependencyMode.builder; + /// How to builder the [T] dependency. InstanceBuilder? get builder => null; @@ -112,6 +115,7 @@ abstract class RtComponent extends StatelessWidget { return RtProvider( builder!, id: id, + mode: mode, builder: (context, instance, _) => render(context, _getInstance(context)), ); } From 52bcd72dd47c8cce5af1ae6180a1a96226604889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Thu, 22 Aug 2024 23:49:24 -0600 Subject: [PATCH 015/141] reactor(widgets): Update `RtWatcher` to extend `RtScope` and refactor code. --- .../lib/src/widgets/rt_watcher.dart | 87 +++++-------------- 1 file changed, 20 insertions(+), 67 deletions(-) diff --git a/packages/flutter_reactter/lib/src/widgets/rt_watcher.dart b/packages/flutter_reactter/lib/src/widgets/rt_watcher.dart index b2de8e25..45c1f627 100644 --- a/packages/flutter_reactter/lib/src/widgets/rt_watcher.dart +++ b/packages/flutter_reactter/lib/src/widgets/rt_watcher.dart @@ -60,7 +60,15 @@ part of '../widgets.dart'; /// ); /// ``` /// {@endtemplate} -class RtWatcher extends StatefulWidget { +class RtWatcher extends StatelessWidget { + /// Provides a widget , which render one time. + /// + /// It's expose on [builder] method as second parameter. + final Widget? child; + + /// {@macro flutter_reactter.watch_child_builder} + final WatchChildBuilder builder; + /// {@macro flutter_reactter.rt_watcher} RtWatcher( WatchBuilder builder, { @@ -75,76 +83,21 @@ class RtWatcher extends StatefulWidget { required this.builder, }) : super(key: key); - /// Provides a widget , which render one time. - /// - /// It's expose on [builder] method as second parameter. - final Widget? child; - - /// {@macro flutter_reactter.watch_child_builder} - final WatchChildBuilder builder; - - @override - State createState() => _RtWatcherState(); -} - -class _RtWatcherState extends State { - final Set _states = {}; - - static _RtWatcherState? _currentState; - @override Widget build(BuildContext context) { - final prevState = _currentState; - - _currentState = this; - - final widgetBuit = widget.builder(context, _watchState, widget.child); - - _currentState = prevState; - - return widgetBuit; + return RtScope( + child: Builder( + builder: (context) => builder( + context, + (T state) => _watchState(context, state), + child, + ), + ), + ); } - @override - void dispose() { - _clearStates(); - super.dispose(); - } - - T _watchState(T state) { - if (_currentState != this || _states.contains(state)) { - return state; - } - - _states.add(state); - Rt.on(state, Lifecycle.didUpdate, _onStateDidUpdate); - + T _watchState(BuildContext context, T state) { + context.watch((_) => [state]); return state; } - - void _onStateDidUpdate(_, __) { - _clearStates(); - _rebuild(); - } - - void _rebuild() async { - if (!mounted) return; - - // coverage:ignore-start - if (SchedulerBinding.instance.schedulerPhase != SchedulerPhase.idle) { - await SchedulerBinding.instance.endOfFrame; - } - // coverage:ignore-end - - if (!mounted) return; - - (context as Element).markNeedsBuild(); - } - - void _clearStates() { - for (final state in _states) { - Rt.off(state, Lifecycle.didUpdate, _onStateDidUpdate); - } - _states.clear(); - } } From 2138ad31135ffd7bd1e299fe663d49231d499c91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Thu, 22 Aug 2024 23:53:25 -0600 Subject: [PATCH 016/141] build(example): Update examples to use new features. --- .../lib/examples/1_counter/counter_page.dart | 1 + .../3_tree/controllers/tree_controller.dart | 13 + .../lib/examples/3_tree/states/tree_list.dart | 26 ++ .../lib/examples/3_tree/states/tree_node.dart | 173 ++++++++++ .../lib/examples/3_tree/tree_node.dart | 60 ---- .../lib/examples/3_tree/tree_page.dart | 36 +- .../3_tree/widgets/custom_icon_button.dart | 5 +- .../examples/3_tree/widgets/tree_item.dart | 310 ++++++++++-------- .../examples/3_tree/widgets/tree_items.dart | 131 -------- .../5_api/controllers/api_controller.dart | 2 +- .../lib/examples/6_todo/models/todo.dart | 7 + .../examples/6_todo/stores/todo_store.dart | 8 + .../example/lib/examples_page.dart | 10 +- .../flutter_reactter/example/lib/main.dart | 2 + .../example/test/widget_test.dart | 30 ++ 15 files changed, 476 insertions(+), 338 deletions(-) create mode 100644 packages/flutter_reactter/example/lib/examples/3_tree/controllers/tree_controller.dart create mode 100644 packages/flutter_reactter/example/lib/examples/3_tree/states/tree_list.dart create mode 100644 packages/flutter_reactter/example/lib/examples/3_tree/states/tree_node.dart delete mode 100644 packages/flutter_reactter/example/lib/examples/3_tree/tree_node.dart delete mode 100644 packages/flutter_reactter/example/lib/examples/3_tree/widgets/tree_items.dart create mode 100644 packages/flutter_reactter/example/test/widget_test.dart diff --git a/packages/flutter_reactter/example/lib/examples/1_counter/counter_page.dart b/packages/flutter_reactter/example/lib/examples/1_counter/counter_page.dart index 7b0693c9..d7babd84 100644 --- a/packages/flutter_reactter/example/lib/examples/1_counter/counter_page.dart +++ b/packages/flutter_reactter/example/lib/examples/1_counter/counter_page.dart @@ -8,6 +8,7 @@ class CounterPage extends StatelessWidget { @override Widget build(BuildContext context) { + return Scaffold( appBar: AppBar( title: const Text("Counter"), diff --git a/packages/flutter_reactter/example/lib/examples/3_tree/controllers/tree_controller.dart b/packages/flutter_reactter/example/lib/examples/3_tree/controllers/tree_controller.dart new file mode 100644 index 00000000..dff26bf6 --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/3_tree/controllers/tree_controller.dart @@ -0,0 +1,13 @@ +import 'package:examples/examples/3_tree/states/tree_list.dart'; +import 'package:examples/examples/3_tree/states/tree_node.dart'; +import 'package:flutter_reactter/reactter.dart'; + +class TreeController { + final root = TreeNode(null); + final treeList = Rt.createState(() => TreeList()); + + TreeController() { + treeList.add(root); + root.bind(treeList); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/3_tree/states/tree_list.dart b/packages/flutter_reactter/example/lib/examples/3_tree/states/tree_list.dart new file mode 100644 index 00000000..82eb37e4 --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/3_tree/states/tree_list.dart @@ -0,0 +1,26 @@ +import 'dart:collection'; + +import 'package:flutter_reactter/reactter.dart'; + +class TreeList> extends LinkedList + with RtContext, RtStateBase> { + @override + void add(E entry) => update(() => super.add(entry)); + + @override + void addFirst(E entry) => update(() => super.addFirst(entry)); + + @override + void addAll(Iterable entries) => update(() => super.addAll(entries)); + + @override + bool remove(E entry) { + final res = super.remove(entry); + if (res) notify(); + + return res; + } + + @override + void clear() => update(() => super.clear()); +} diff --git a/packages/flutter_reactter/example/lib/examples/3_tree/states/tree_node.dart b/packages/flutter_reactter/example/lib/examples/3_tree/states/tree_node.dart new file mode 100644 index 00000000..a5df694c --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/3_tree/states/tree_node.dart @@ -0,0 +1,173 @@ +/// I challange you to do the same thing with another solution. +/// You will see that Reactter is faster, simple and easy to use. + +import 'dart:collection'; +import 'package:flutter_reactter/flutter_reactter.dart'; + +class TreeNode extends LinkedListEntry + with RtContext, RtStateBase { + /// A unique identifier for each instance of [TreeNode]. + static int _lastId = 0; + static String _getId() => (_lastId++).toString(); + + final TreeNode? parent; + final String id = _getId(); + + late final String path = parent != null ? "${parent?.path} > $id" : id; + late final int depth = parent != null ? parent!.depth + 1 : 0; + + final uChildren = UseState({}); + final uIsExpanded = UseState(false); + final uHasNext = UseState(false); + final uCount = UseState(0); + final uDescendantsTotal = UseState(0); + late final uTotal = Rt.lazyState( + () => UseCompute( + () => uCount.value + uDescendantsTotal.value, + [uCount, uDescendantsTotal], + ), + this, + ); + + TreeNode._(this.parent) { + UseEffect(_onIsExpandedChanged, [uIsExpanded]); + UseEffect(_onChildrenChanged, [uChildren]); + + if (parent != null) { + UseEffect(parent!._calculateDescendantsTotal, [uTotal]); + } + + /// Bind the instance to the parent or list + /// This way, the instance is automatically disposed including it's hooks + /// when the parent or list is destroyed. + if (parent != null) { + bind(parent!); + } else if (list != null) { + bind(list!); + } + } + + /// Create a new instance of [TreeNode] and register as a singleton. + /// This way, the instance can be accessed from anywhere in the application + /// and keep it in memory until it is destroyed. + factory TreeNode(TreeNode? parent) { + return Rt.singleton( + () => TreeNode._(parent), + id: _lastId.toString(), + )!; + } + + @override + unlink() { + final rList = list; + super.unlink(); + if (rList is RtState) (rList as RtState).notify(); + } + + @override + void insertAfter(TreeNode entry) { + super.insertAfter(entry); + if (list is RtState) (list as RtState).notify(); + } + + @override + void insertBefore(TreeNode entry) { + super.insertBefore(entry); + if (list is RtState) (list as RtState).notify(); + } + + void addChild() { + Rt.batch(() { + final node = TreeNode(this); + + uChildren.value.add(node); + uChildren.notify(); + + if (uChildren.value.length > 1) { + node.uHasNext.value = true; + } + + uIsExpanded.value = true; + _showChildren(); + }); + } + + void remove() { + Rt.batch(() { + final children = uChildren.value.toList(); + + for (final node in children) { + node.remove(); + } + + if (list != null) { + unlink(); + } + + parent?.uChildren.value.remove(this); + parent?.uChildren.notify(); + + /// It's need to destroy the instance because it's a singleton + /// and it's not automatically destroyed when it's removed from the parent. + Rt.destroy(id: id); + }); + } + + void toggleExpansion() => uIsExpanded.value = !uIsExpanded.value; + + void increase() => uCount.value++; + + void decrease() => uCount.value--; + + void _onIsExpandedChanged() { + if (uIsExpanded.value) { + return _showChildren(); + } + + _hideChildren(); + } + + void _onChildrenChanged() { + if (uChildren.value.isNotEmpty) { + uChildren.value.first.uHasNext.value = false; + } + + _calculateDescendantsTotal(); + } + + void _showChildren() { + Rt.batch(() { + for (final node in uChildren.value) { + if (node.list != null) { + continue; + } + + insertAfter(node); + + if (node.uIsExpanded.value) { + node._showChildren(); + } + } + }); + } + + void _hideChildren() { + Rt.batch(() { + for (final node in uChildren.value) { + if (node.list == null) { + continue; + } + + node._hideChildren(); + node.unlink(); + } + }); + } + + void _calculateDescendantsTotal() { + uDescendantsTotal.value = uChildren.value.fold( + 0, + (acc, child) => acc + child.uTotal.value, + ); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/3_tree/tree_node.dart b/packages/flutter_reactter/example/lib/examples/3_tree/tree_node.dart deleted file mode 100644 index 9a96658d..00000000 --- a/packages/flutter_reactter/example/lib/examples/3_tree/tree_node.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:flutter_reactter/flutter_reactter.dart'; - -class TreeNode { - final TreeNode? parent; - - final uIsExpanded = UseState(false); - final uChildren = UseState([]); - final uCount = UseState(0); - final uChildrenTotal = UseState(0); - late final uTotal = Rt.lazyState( - () => UseCompute( - () => uCount.value + uChildrenTotal.value, - [uCount, uChildrenTotal], - ), - this, - ); - - String get path => "${parent?.path ?? ''} > $hashCode"; - - TreeNode([this.parent]) { - if (parent != null) { - UseEffect( - parent!._calculateChildrenTotal, - [uTotal], - ); - } - - UseEffect(() { - uIsExpanded.value = true; - _calculateChildrenTotal(); - }, [uChildren]); - } - - void increase() => uCount.value++; - - void decrease() => uCount.value--; - - void toggleExpansion() => uIsExpanded.value = !uIsExpanded.value; - - void addChild() { - uChildren.update( - () => uChildren.value.add(TreeNode(this)), - ); - } - - void removeChild(TreeNode child) { - uChildren.update( - () => uChildren.value.remove(child), - ); - } - - void removeFromParent() => parent?.removeChild(this); - - void _calculateChildrenTotal() { - uChildrenTotal.value = uChildren.value.fold( - 0, - (acc, child) => acc + child.uTotal.value, - ); - } -} diff --git a/packages/flutter_reactter/example/lib/examples/3_tree/tree_page.dart b/packages/flutter_reactter/example/lib/examples/3_tree/tree_page.dart index 384b932c..0d51f77f 100644 --- a/packages/flutter_reactter/example/lib/examples/3_tree/tree_page.dart +++ b/packages/flutter_reactter/example/lib/examples/3_tree/tree_page.dart @@ -1,23 +1,49 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:examples/examples/3_tree/tree_node.dart'; +import 'package:examples/examples/3_tree/controllers/tree_controller.dart'; import 'package:examples/examples/3_tree/widgets/tree_item.dart'; +class Test extends ChangeNotifier { + final int value; + + Test(this.value); +} + class TreePage extends StatelessWidget { const TreePage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return RtProvider( - TreeNode.new, - builder: (context, treeContext, _) { + TreeController.new, + builder: (context, treeController, _) { return Scaffold( appBar: AppBar( title: const Text("Tree widget"), ), - body: SingleChildScrollView( - child: TreeItem(treeNode: treeContext), + body: CustomScrollView( + slivers: [ + RtConsumer( + listenAll: true, + builder: (context, treeController, _) { + return SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + final treeNode = + treeController.treeList.elementAt(index); + + return TreeItem( + key: ObjectKey(treeNode), + treeNode: treeNode, + ); + }, + childCount: treeController.treeList.length, + ), + ); + }, + ), + ], ), ); }, diff --git a/packages/flutter_reactter/example/lib/examples/3_tree/widgets/custom_icon_button.dart b/packages/flutter_reactter/example/lib/examples/3_tree/widgets/custom_icon_button.dart index 637acd7a..6e44f386 100644 --- a/packages/flutter_reactter/example/lib/examples/3_tree/widgets/custom_icon_button.dart +++ b/packages/flutter_reactter/example/lib/examples/3_tree/widgets/custom_icon_button.dart @@ -4,20 +4,23 @@ class CustomIconButton extends StatelessWidget { const CustomIconButton({ Key? key, required this.icon, + this.tooltip, required this.onPressed, }) : super(key: key); final Widget icon; + final String? tooltip; final void Function()? onPressed; @override Widget build(BuildContext context) { return IconButton( - padding: const EdgeInsets.all(6), + padding: const EdgeInsets.all(2), splashRadius: 18, iconSize: 24, constraints: const BoxConstraints.tightForFinite(), icon: icon, + tooltip: tooltip, onPressed: onPressed, ); } diff --git a/packages/flutter_reactter/example/lib/examples/3_tree/widgets/tree_item.dart b/packages/flutter_reactter/example/lib/examples/3_tree/widgets/tree_item.dart index 01ad4ffa..d4b29fd0 100644 --- a/packages/flutter_reactter/example/lib/examples/3_tree/widgets/tree_item.dart +++ b/packages/flutter_reactter/example/lib/examples/3_tree/widgets/tree_item.dart @@ -2,101 +2,77 @@ import 'dart:async'; import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:examples/examples/3_tree/tree_node.dart'; +import 'package:examples/examples/3_tree/states/tree_node.dart'; import 'package:examples/examples/3_tree/widgets/custom_icon_button.dart'; -import 'package:examples/examples/3_tree/widgets/tree_items.dart'; class TreeItem extends RtComponent { - const TreeItem({ - Key? key, - required this.treeNode, - this.isTreeNodeLast = false, - }) : super(key: key); - final TreeNode treeNode; - final bool isTreeNodeLast; @override - String get id => "${treeNode.hashCode}"; + get builder => () => treeNode; @override - get builder => () => treeNode; + String? get id => treeNode.id; + + String get idStr => id != null ? 'ID: $id' : "root"; + + const TreeItem({ + Key? key, + required this.treeNode, + }) : super(key: key); @override - Widget render(context, treeContext) { + Widget render(context, treeNode) { return InkWell( onTap: () => _openDialog(context), child: Stack( + clipBehavior: Clip.none, children: [ - Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(width: 8), - _buildExpandButton(), - CustomIconButton( - icon: const Icon(Icons.add_circle), - onPressed: treeContext.addChild, - ), - if (treeContext.parent != null) - CustomIconButton( - icon: Transform.rotate( - angle: -pi / 4, - child: const Icon(Icons.add_circle), - ), - onPressed: treeContext.removeFromParent, - ), - Expanded( - child: Align( - alignment: Alignment.centerRight, - child: Text( - id, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.bodySmall, + Padding( + padding: const EdgeInsets.symmetric(vertical: 2), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(width: 24.0 * treeNode.depth + 8), + _buildNode(), + const Expanded( + child: Divider( + height: 2, + thickness: 2, ), ), - ), - const SizedBox(width: 8), - _buildCountLabel(), - CustomIconButton( - icon: const Icon(Icons.indeterminate_check_box_rounded), - onPressed: treeContext.decrease, - ), - CustomIconButton( - icon: const Icon(Icons.add_box), - onPressed: treeContext.increase, - ), - ], - ), - _buildTreeItems(), - ], + _buildCount(), + ], + ), + ], + ), ), - _buildTreeLine(treeContext), + _buildTreeLine(), ], ), ); } Future _openDialog(BuildContext context) { - final treeNode = context.use(id); - return showDialog( context: context, builder: (context) { return AlertDialog( - title: Text("About item[id='$id']"), + title: Text("About item[$idStr]"), content: Column( mainAxisSize: MainAxisSize.min, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text("path:"), + const Text("Path:"), const SizedBox(width: 8), Expanded( child: Text( @@ -109,7 +85,7 @@ class TreeItem extends RtComponent { ), Row( children: [ - const Text("children:"), + const Text("Children:"), const SizedBox(width: 8), Text( "${treeNode.uChildren.value.length}", @@ -119,7 +95,7 @@ class TreeItem extends RtComponent { ), Row( children: [ - const Text("count:"), + const Text("Count:"), const SizedBox(width: 8), Text( "${treeNode.uCount.value}", @@ -129,17 +105,17 @@ class TreeItem extends RtComponent { ), Row( children: [ - const Text("childrenTotal:"), + const Text("Descendants Total:"), const SizedBox(width: 8), Text( - "${treeNode.uChildrenTotal.value}", + "${treeNode.uDescendantsTotal.value}", style: Theme.of(context).textTheme.bodySmall, ), ], ), Row( children: [ - const Text("total(count + childrenTotal):"), + const Text("Total (Count + Descendants Total):"), const SizedBox(width: 8), Text( "${treeNode.uTotal.value}", @@ -154,99 +130,159 @@ class TreeItem extends RtComponent { ); } - Widget _buildExpandButton() { - return Builder( - builder: (context) { - final treeNode = context.watchId( - id, - (inst) => [inst.uIsExpanded, inst.uChildren], - ); + Widget _buildNode() { + return RtWatcher((context, watch) { + final children = watch(treeNode.uChildren).value; + final isExpanded = watch(treeNode.uIsExpanded).value; - if (treeNode.uChildren.value.isEmpty) { - return const SizedBox(width: 36); - } - - return CustomIconButton( - icon: Transform.rotate( - angle: treeNode.uIsExpanded.value ? 0 : -pi / 2, - child: const Icon(Icons.expand_circle_down_rounded), + return Container( + margin: EdgeInsets.only( + left: children.isEmpty ? 28 : 0, + ), + decoration: ShapeDecoration( + shape: RoundedRectangleBorder( + side: BorderSide( + color: Theme.of(context).colorScheme.outlineVariant, + width: 2, + ), + borderRadius: const BorderRadius.all(Radius.circular(20)), ), - onPressed: treeNode.toggleExpansion, - ); - }, - ); - } - - Widget _buildCountLabel() { - return Builder( - builder: (context) { - final treeNode = context.watchId( - id, - (inst) => [inst.uCount, inst.uTotal], - ); - - return Text( - "${treeNode.uCount.value} (${treeNode.uTotal.value})", - ); - }, - ); + ), + child: Row( + children: [ + if (children.isNotEmpty) + CustomIconButton( + tooltip: isExpanded ? "Collapse" : "Expand", + icon: Transform.rotate( + angle: isExpanded ? 0 : -pi / 2, + child: const Icon(Icons.expand_circle_down_rounded), + ), + onPressed: treeNode.toggleExpansion, + ), + CustomIconButton( + tooltip: "Add child", + icon: const Icon(Icons.add_circle), + onPressed: treeNode.addChild, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 2).copyWith( + right: treeNode.parent == null ? 8 : 0, + ), + child: Text( + idStr, + style: Theme.of(context).textTheme.bodySmall, + ), + ), + if (treeNode.parent != null) + CustomIconButton( + tooltip: "Remove", + icon: const Icon(Icons.cancel), + onPressed: treeNode.remove, + ), + ], + ), + ); + }); } - Widget _buildTreeItems() { + Widget _buildCount() { return Builder( builder: (context) { - final treeNode = context.watchId( - id, - (inst) => [ - inst.uIsExpanded, - inst.uChildren, - ], - ); - - return TreeItems( - children: treeNode.uChildren.value, - expanded: treeNode.uIsExpanded.value, + return Container( + decoration: ShapeDecoration( + shape: RoundedRectangleBorder( + side: BorderSide( + color: Theme.of(context).colorScheme.outlineVariant, + width: 2, + ), + ), + ), + child: Row( + children: [ + CustomIconButton( + tooltip: "Decrease", + icon: const Icon(Icons.indeterminate_check_box_rounded), + onPressed: treeNode.decrease, + ), + RtWatcher((context, watch) { + return Text( + "${watch(treeNode.uCount).value} (${watch(treeNode.uTotal).value})", + style: Theme.of(context).textTheme.bodySmall, + ); + }), + CustomIconButton( + tooltip: "Increase", + icon: const Icon(Icons.add_box), + onPressed: treeNode.increase, + ), + ], + ), ); }, ); } - Widget _buildTreeLine(TreeNode treeContext) { + Widget _buildTreeLine() { return Positioned( - top: 0, - bottom: 0, + top: -4, + bottom: -4, child: Align( alignment: Alignment.topLeft, child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( - height: treeContext.parent == null || isTreeNodeLast ? 19 : null, - child: const VerticalDivider( - width: 2, - thickness: 2, - ), - ), - Builder( - builder: (context) { - final treeNode = context.watchId( - id, - (inst) => [inst.uChildren], - ); - - return SizedBox( - width: treeNode.uChildren.value.isEmpty ? 42 : 6, - height: 36, - child: const Divider( - height: 2, - thickness: 2, - ), - ); - }, - ), + ..._buildParentLines(treeNode, []), + RtWatcher((context, watch) { + return SizedBox( + height: + treeNode.parent == null || !watch(treeNode.uHasNext).value + ? 23 + : null, + child: const VerticalDivider( + width: 2, + thickness: 2, + ), + ); + }), + RtWatcher((context, watch) { + return SizedBox( + width: watch(treeNode.uChildren).value.isEmpty ? 36 : 6, + height: 48, + child: const Divider( + height: 2, + thickness: 2, + ), + ); + }), ], ), ), ); } + + List _buildParentLines(TreeNode node, List parentLines) { + if (node.parent == null) { + return parentLines; + } + + parentLines.insert( + 0, + RtWatcher((context, watch) { + if (!watch(node.parent!.uHasNext).value) { + return const SizedBox(width: 24); + } + + return Container( + width: 24, + alignment: Alignment.centerLeft, + child: const VerticalDivider( + width: 2, + thickness: 2, + ), + ); + }), + ); + + return _buildParentLines(node.parent!, parentLines); + } } diff --git a/packages/flutter_reactter/example/lib/examples/3_tree/widgets/tree_items.dart b/packages/flutter_reactter/example/lib/examples/3_tree/widgets/tree_items.dart deleted file mode 100644 index c5087751..00000000 --- a/packages/flutter_reactter/example/lib/examples/3_tree/widgets/tree_items.dart +++ /dev/null @@ -1,131 +0,0 @@ -// ignore_for_file: avoid_renaming_method_parameters - -import 'package:flutter/material.dart'; - -import 'package:examples/examples/3_tree/tree_node.dart'; -import 'package:examples/examples/3_tree/widgets/tree_item.dart'; - -class TreeItems extends StatefulWidget { - const TreeItems({ - super.key, - required this.children, - required this.expanded, - }); - - final List children; - final bool expanded; - - @override - State createState() => _TreeItemsState(); -} - -class _TreeItemsState extends State with TickerProviderStateMixin { - late AnimationController _rotationController; - - bool _hide = false; - - @override - void initState() { - super.initState(); - - _rotationController = AnimationController( - duration: const Duration(milliseconds: 300), - vsync: this, - ) - ..drive(CurveTween(curve: Curves.easeOut)) - ..addStatusListener((status) { - if (status == AnimationStatus.dismissed) { - return setState(() { - _hide = true; - }); - } - - if (_hide) { - setState(() { - _hide = false; - }); - } - }); - - if (widget.expanded) { - _rotationController.forward(); - } - } - - @override - void didUpdateWidget(TreeItems oldWidget) { - super.didUpdateWidget(oldWidget); - - if (widget.expanded) { - _rotationController.forward(); - } else { - _rotationController.reverse(); - } - } - - @override - Widget build(BuildContext context) { - // Remove instance build - if (_hide) return const SizedBox(); - - final treeItems = _generateTreeItems(); - - return SizeTransition( - sizeFactor: _rotationController, - axisAlignment: -1, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(width: 24), - Expanded( - child: Column( - children: treeItems, - ), - ), - ], - ), - ); - } - - void toggleExpansion() { - if (widget.expanded) { - _rotationController.forward(); - } else { - _rotationController.reverse(); - } - - setState(() {}); - } - - @override - void dispose() { - _rotationController.dispose(); - super.dispose(); - } - - List _generateTreeItems() { - final treeNodes = widget.children; - final treeNodeLast = treeNodes.isNotEmpty ? treeNodes.last : null; - - return [ - for (final treeNode in treeNodes) - // Fix StackOverflowError when building too many TreeItem - if (treeNodes.indexOf(treeNode) % 2 == 0) - LayoutBuilder( - builder: (_, __) { - return TreeItem( - key: ObjectKey(treeNode), - treeNode: treeNode, - isTreeNodeLast: treeNode == treeNodeLast, - ); - }, - ) - else - TreeItem( - key: ObjectKey(treeNode), - treeNode: treeNode, - isTreeNodeLast: treeNode == treeNodeLast, - ), - ]; - } -} diff --git a/packages/flutter_reactter/example/lib/examples/5_api/controllers/api_controller.dart b/packages/flutter_reactter/example/lib/examples/5_api/controllers/api_controller.dart index ec97f212..0c1929ec 100644 --- a/packages/flutter_reactter/example/lib/examples/5_api/controllers/api_controller.dart +++ b/packages/flutter_reactter/example/lib/examples/5_api/controllers/api_controller.dart @@ -10,7 +10,6 @@ class ApiController { late final uEntity = Rt.lazyState( () => UseAsyncState.withArg( - null, Memo.inline, String>( getEntity, const MemoMultiInterceptor([ @@ -18,6 +17,7 @@ class ApiController { MemoTemporaryCacheInterceptor(Duration(seconds: 30)), ]), ), + null, ), this, ); diff --git a/packages/flutter_reactter/example/lib/examples/6_todo/models/todo.dart b/packages/flutter_reactter/example/lib/examples/6_todo/models/todo.dart index 066db5d7..91f95270 100644 --- a/packages/flutter_reactter/example/lib/examples/6_todo/models/todo.dart +++ b/packages/flutter_reactter/example/lib/examples/6_todo/models/todo.dart @@ -15,4 +15,11 @@ class Todo { title: title ?? this.title, isDone: isDone ?? this.isDone, ); + + Map toJson() { + return { + 'title': title, + 'isDone': isDone, + }; + } } diff --git a/packages/flutter_reactter/example/lib/examples/6_todo/stores/todo_store.dart b/packages/flutter_reactter/example/lib/examples/6_todo/stores/todo_store.dart index 52e4d276..033e4bb2 100644 --- a/packages/flutter_reactter/example/lib/examples/6_todo/stores/todo_store.dart +++ b/packages/flutter_reactter/example/lib/examples/6_todo/stores/todo_store.dart @@ -23,4 +23,12 @@ class TodoStore { filteredBy: filteredBy ?? this.filteredBy, doneCount: doneCount ?? this.doneCount, ); + + Map toJson() { + return { + 'todoList': todoList.map((e) => e.toJson()).toList(), + 'filteredBy': filteredBy.toString(), + 'doneCount': doneCount, + }; + } } diff --git a/packages/flutter_reactter/example/lib/examples_page.dart b/packages/flutter_reactter/example/lib/examples_page.dart index fa58dc64..6ee1546b 100644 --- a/packages/flutter_reactter/example/lib/examples_page.dart +++ b/packages/flutter_reactter/example/lib/examples_page.dart @@ -40,11 +40,15 @@ final examples = [ "3. Tree widget", "Add, remove and hide child widget with counter.", [ - "BuilContext.use", - "BuilContext.watchId", + "Rt.batch", + "Rt.createSate", "Rt.lazyState", + "RtContext", "RtComponent", + "RtConsumer", "RtProvider", + "RtState", + "RtWatcher", "UseCompute", "UseEffect", "UseState", @@ -118,7 +122,7 @@ class ExamplesPage extends StatelessWidget { @override Widget build(BuildContext context) { return Title( - title: 'Reactter | Exmaples', + title: 'Reactter | Examples', color: Theme.of(context).primaryColor, child: Scaffold( appBar: AppBar( diff --git a/packages/flutter_reactter/example/lib/main.dart b/packages/flutter_reactter/example/lib/main.dart index 96194c5f..86a5ca06 100644 --- a/packages/flutter_reactter/example/lib/main.dart +++ b/packages/flutter_reactter/example/lib/main.dart @@ -1,6 +1,8 @@ import 'package:examples/app.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; Future main() async { + Rt.initializeDebugging(); runApp(const MyApp()); } diff --git a/packages/flutter_reactter/example/test/widget_test.dart b/packages/flutter_reactter/example/test/widget_test.dart new file mode 100644 index 00000000..d1ac2333 --- /dev/null +++ b/packages/flutter_reactter/example/test/widget_test.dart @@ -0,0 +1,30 @@ +// // This is a basic Flutter widget test. +// // +// // To perform an interaction with a widget in your test, use the WidgetTester +// // utility in the flutter_test package. For example, you can send tap and scroll +// // gestures. You can also use WidgetTester to find child widgets in the widget +// // tree, read text, and verify that the values of widget properties are correct. + +// import 'package:flutter/material.dart'; +// import 'package:flutter_test/flutter_test.dart'; + +// import 'package:example/main.dart'; + +// void main() { +// testWidgets('Counter increments smoke test', (WidgetTester tester) async { +// // Build our app and trigger a frame. +// await tester.pumpWidget(const MyApp()); + +// // Verify that our counter starts at 0. +// expect(find.text('0'), findsOneWidget); +// expect(find.text('1'), findsNothing); + +// // Tap the '+' icon and trigger a frame. +// await tester.tap(find.byIcon(Icons.add)); +// await tester.pump(); + +// // Verify that our counter has incremented. +// expect(find.text('0'), findsNothing); +// expect(find.text('1'), findsOneWidget); +// }); +// } From 31c44247eae96d53234fcd4b6ed8b10352ad8fb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 23 Aug 2024 00:29:51 -0600 Subject: [PATCH 017/141] feat(core, framework): Add `RtStateObserver` to monitor the lifecycle of states. --- .../flutter_reactter/lib/src/devtools.dart | 52 +++++++++++-------- .../lib/src/core/observer_manager.dart | 14 +---- packages/reactter/lib/src/framework.dart | 6 +-- .../lib/src/framework/rt_interface.dart | 16 +++++- .../lib/src/framework/rt_state_base.dart | 10 ++-- .../rt_state_observer.dart} | 16 +++--- packages/reactter/lib/src/internals.dart | 4 +- .../rt_state_observer_test.dart} | 16 +++--- 8 files changed, 72 insertions(+), 62 deletions(-) rename packages/reactter/lib/src/{core/state_observer.dart => framework/rt_state_observer.dart} (67%) rename packages/reactter/test/{core/state_observer_test.dart => framework/rt_state_observer_test.dart} (92%) diff --git a/packages/flutter_reactter/lib/src/devtools.dart b/packages/flutter_reactter/lib/src/devtools.dart index 5a273309..15f8e2e6 100644 --- a/packages/flutter_reactter/lib/src/devtools.dart +++ b/packages/flutter_reactter/lib/src/devtools.dart @@ -5,28 +5,42 @@ import 'package:flutter/foundation.dart'; import 'package:meta/meta.dart'; import 'package:reactter/reactter.dart'; +extension RtExt on RtInterface { + void initializeDebugging() { + if (kDebugMode) { + ReactterDevTools._initializeDebugging(); + } + } +} + @internal class ReactterDevTools extends RtStateObserver { + static ReactterDevTools? _instance; + + static void _initializeDebugging() { + if (kDebugMode) { + _instance ??= ReactterDevTools._(); + } + } + + final Map states = {}; + + List get stateIdRefs => states.keys.toList(); + ReactterDevTools._() { print('ReactterDevTools initialized $hashCode'); Rt.addObserver(this); _initDevTools(); } - static final debugInstance = kDebugMode - ? ReactterDevTools._() - : throw UnsupportedError( - 'Cannot use ReactterDevTools in release mode', - ); - - static final Set states = {}; - @override void onStateCreated(covariant RtState state) { - states.add(state); + states[state.hashCode.toString()] = state; dev.postEvent('ext.reactter.onStateCreated', { 'state': _getState(state), }); + print("states len: ${states.length}"); + print("state created: ${state.debugLabel}"); } @override @@ -54,29 +68,25 @@ class ReactterDevTools extends RtStateObserver { @override void onStateDisposed(covariant RtState state) { - states.remove(state); + states.remove(state.hashCode.toString()); dev.postEvent('ext.reactter.onStateDisposed', { 'state': _getState(state), }); + print("states len: ${states.length}"); + print("state disposed: ${state.debugLabel}"); } void _initDevTools() { dev.registerExtension( - 'ext.reactter.getAllStates', + 'ext.reactter.getStateIdRefs', (method, parameters) async { return dev.ServiceExtensionResponse.result( - json.encode(_getStates()), + json.encode(stateIdRefs), ); }, ); } - Map _getStates() { - return { - 'states': states.map(_getState).toList(), - }; - } - Map _getState(RtState state) { return { 'id': state.hashCode, @@ -86,11 +96,7 @@ class ReactterDevTools extends RtStateObserver { (key, value) { var valueEncode = value; - try { - valueEncode = jsonEncode(value); - } catch (e) { - valueEncode = value.toString(); - } + valueEncode = value.toString(); return MapEntry(key, valueEncode); }, diff --git a/packages/reactter/lib/src/core/observer_manager.dart b/packages/reactter/lib/src/core/observer_manager.dart index 29a6d3e5..2edaf605 100644 --- a/packages/reactter/lib/src/core/observer_manager.dart +++ b/packages/reactter/lib/src/core/observer_manager.dart @@ -5,20 +5,10 @@ abstract class ObserverManager { /// Adds an observer to the manager. /// /// The [observer] parameter is the observer to be added. - /// Only [StateObserver] instances can be added. - void addObserver(covariant IObserver observer) { - if (observer is StateObserver) { - StateObserver._observers.add(observer); - } - } + void addObserver(covariant IObserver observer); /// Removes an observer from the manager. /// /// The [observer] parameter is the observer to be removed. - /// Only [StateObserver] instances can be removed. - void removeObserver(covariant IObserver observer) { - if (observer is StateObserver) { - StateObserver._observers.remove(observer); - } - } + void removeObserver(covariant IObserver observer); } diff --git a/packages/reactter/lib/src/framework.dart b/packages/reactter/lib/src/framework.dart index 1620373a..c1245466 100644 --- a/packages/reactter/lib/src/framework.dart +++ b/packages/reactter/lib/src/framework.dart @@ -1,6 +1,4 @@ -import 'dart:developer' as dev; import 'package:meta/meta.dart'; -import 'types.dart'; import 'internals.dart'; export 'internals.dart' @@ -9,11 +7,11 @@ export 'internals.dart' LifecycleObserver, DependencyMode, LogLevel, - StateObserver, + RtInterface, + RtStateObserver, RtHook, RtState, RtStateBase; part 'framework/rt_context.dart'; part 'framework/rt_dependency.dart'; -part 'framework/rt_interface.dart'; diff --git a/packages/reactter/lib/src/framework/rt_interface.dart b/packages/reactter/lib/src/framework/rt_interface.dart index 23b2f365..9f941adb 100644 --- a/packages/reactter/lib/src/framework/rt_interface.dart +++ b/packages/reactter/lib/src/framework/rt_interface.dart @@ -1,4 +1,4 @@ -part of '../framework.dart'; +part of '../internals.dart'; void defaultLogWriterCallback( String value, { @@ -37,6 +37,20 @@ class RtInterface @override LogWriterCallback get log => defaultLogWriterCallback; + + @override + void addObserver(covariant IObserver observer) { + if (observer is RtStateObserver) { + RtStateObserver._observers.add(observer); + } + } + + @override + void removeObserver(covariant IObserver observer) { + if (observer is RtStateObserver) { + RtStateObserver._observers.remove(observer); + } + } } /// {@macro reactter.rt_interface} diff --git a/packages/reactter/lib/src/framework/rt_state_base.dart b/packages/reactter/lib/src/framework/rt_state_base.dart index 214f8543..6c0eb56d 100644 --- a/packages/reactter/lib/src/framework/rt_state_base.dart +++ b/packages/reactter/lib/src/framework/rt_state_base.dart @@ -198,25 +198,25 @@ abstract class RtStateBase> implements RtState { } void _notifyCreated() { - for (final observer in StateObserver._observers.toList(growable: false)) { + for (final observer in RtStateObserver._observers.toList(growable: false)) { observer.onStateCreated(this); } } void _notifyBound(Object instance) { - for (final observer in StateObserver._observers.toList(growable: false)) { + for (final observer in RtStateObserver._observers.toList(growable: false)) { observer.onStateBound(this, instance); } } void _notifyUnbound() { - for (final observer in StateObserver._observers.toList(growable: false)) { + for (final observer in RtStateObserver._observers.toList(growable: false)) { observer.onStateUnbound(this, _boundInstance!); } } void _notifyUpdated() { - for (final observer in StateObserver._observers.toList(growable: false)) { + for (final observer in RtStateObserver._observers.toList(growable: false)) { observer.onStateUpdated(this); if (boundInstance is RtState) { @@ -226,7 +226,7 @@ abstract class RtStateBase> implements RtState { } void _notifyDisponsed() { - for (final observer in StateObserver._observers.toList(growable: false)) { + for (final observer in RtStateObserver._observers.toList(growable: false)) { observer.onStateDisposed(this); } } diff --git a/packages/reactter/lib/src/core/state_observer.dart b/packages/reactter/lib/src/framework/rt_state_observer.dart similarity index 67% rename from packages/reactter/lib/src/core/state_observer.dart rename to packages/reactter/lib/src/framework/rt_state_observer.dart index 5522e800..93e8ca90 100644 --- a/packages/reactter/lib/src/core/state_observer.dart +++ b/packages/reactter/lib/src/framework/rt_state_observer.dart @@ -1,37 +1,37 @@ part of '../internals.dart'; -/// {@template reactter.state_observer} +/// {@template reactter.rt_state_observer} /// An abstract class that defines the interface for observing state changes. /// Implementations of this class can be used to monitor the lifecycle of states. /// {@endtemplate} -abstract class StateObserver implements IObserver { +abstract class RtStateObserver implements IObserver { /// A set of all registered state observers. - static final _observers = {}; + static final _observers = {}; /// Called when a state is created. /// /// [state] - The state that was created. - void onStateCreated(covariant IState state); + void onStateCreated(covariant RtState state); /// Called when a state is bound to an instance. /// /// [state] - The state that was bound. /// [instance] - The instance to which the state was bound. - void onStateBound(covariant IState state, Object instance); + void onStateBound(covariant RtState state, Object instance); /// Called when a state is unbound from an instance. /// /// [state] - The state that was unbound. /// [instance] - The instance from which the state was unbound. - void onStateUnbound(covariant IState state, Object instance); + void onStateUnbound(covariant RtState state, Object instance); /// Called when a state is updated. /// /// [state] - The state that was updated. - void onStateUpdated(covariant IState state); + void onStateUpdated(covariant RtState state); /// Called when a state is disposed. /// /// [state] - The state that was disposed. - void onStateDisposed(covariant IState state); + void onStateDisposed(covariant RtState state); } diff --git a/packages/reactter/lib/src/internals.dart b/packages/reactter/lib/src/internals.dart index 969071fa..ed49f04c 100644 --- a/packages/reactter/lib/src/internals.dart +++ b/packages/reactter/lib/src/internals.dart @@ -1,3 +1,4 @@ +import 'dart:developer' as dev; import 'dart:async'; import 'dart:collection'; import 'package:meta/meta.dart'; @@ -16,10 +17,11 @@ part 'core/logger.dart'; part 'core/notifier.dart'; part 'core/observer_manager.dart'; part 'core/state_management.dart'; -part 'core/state_observer.dart'; part 'framework/rt_hook.dart'; +part 'framework/rt_interface.dart'; part 'framework/rt_state.dart'; part 'framework/rt_state_base.dart'; +part 'framework/rt_state_observer.dart'; part 'interfaces/context.dart'; part 'interfaces/hook.dart'; part 'interfaces/observer.dart'; diff --git a/packages/reactter/test/core/state_observer_test.dart b/packages/reactter/test/framework/rt_state_observer_test.dart similarity index 92% rename from packages/reactter/test/core/state_observer_test.dart rename to packages/reactter/test/framework/rt_state_observer_test.dart index 3151423a..a49a6be6 100644 --- a/packages/reactter/test/core/state_observer_test.dart +++ b/packages/reactter/test/framework/rt_state_observer_test.dart @@ -1,7 +1,7 @@ import 'package:reactter/reactter.dart'; import 'package:test/test.dart'; -class StateObserverTest extends StateObserver { +class RtStateObserverTest extends RtStateObserver { int onStateCreatedCalledCount = 0; String? lastStateCreated; @@ -51,9 +51,9 @@ class StateObserverTest extends StateObserver { } void main() { - group("StateObserverTest", () { + group("RtStateObserver", () { test("should be observed when a state is created", () { - final observer = StateObserverTest(); + final observer = RtStateObserverTest(); Rt.addObserver(observer); UseState(0, debugLabel: "stateA"); @@ -70,7 +70,7 @@ void main() { }); test("should be observed when a state is bound", () { - final observer = StateObserverTest(); + final observer = RtStateObserverTest(); Rt.addObserver(observer); final stateA = UseState(0, debugLabel: "stateA"); @@ -93,7 +93,7 @@ void main() { }); test("should be observed when a state is unbound", () { - final observer = StateObserverTest(); + final observer = RtStateObserverTest(); Rt.addObserver(observer); final stateA = UseState(0, debugLabel: "stateA"); @@ -118,7 +118,7 @@ void main() { }); test("should be observed when a state is updated", () { - final observer = StateObserverTest(); + final observer = RtStateObserverTest(); Rt.addObserver(observer); final stateA = UseState(0, debugLabel: "stateA"); @@ -137,7 +137,7 @@ void main() { }); test("should be observed when a state is disposed", () { - final observer = StateObserverTest(); + final observer = RtStateObserverTest(); Rt.addObserver(observer); final stateA = UseState(0, debugLabel: "stateA"); @@ -156,7 +156,7 @@ void main() { }); test("should be observed when a nested state is updated", () { - final observer = StateObserverTest(); + final observer = RtStateObserverTest(); Rt.addObserver(observer); final stateA = UseState(0, debugLabel: "stateA"); From 42ee0a016a174a080adff8ecaa1773de82b61c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Tue, 3 Sep 2024 00:49:19 -0600 Subject: [PATCH 018/141] fix(core): Add error handling listeners and continue to next listeners in `Notifier` class. --- packages/reactter/lib/src/core/notifier.dart | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/reactter/lib/src/core/notifier.dart b/packages/reactter/lib/src/core/notifier.dart index 3a210b0a..ebbdae43 100644 --- a/packages/reactter/lib/src/core/notifier.dart +++ b/packages/reactter/lib/src/core/notifier.dart @@ -274,16 +274,27 @@ abstract class Notifier { try { final listener = _listeners[i]; + assert(listener != null, 'Unexpected null listener in $target.'); + if (_listenersSingleUse.contains(listener)) { removeListener(listener!); _listenersSingleUse.remove(listener); } listenerCall(listener, param); - } catch (error, _) { - throw AssertionError( - 'An error was caught during Notifier on $target', - ); + } catch (error) { + assert(() { + throw AssertionError( + 'An error was thrown by a listener of $target.\n' + 'The error thrown was:\n' + ' $error\n', + ); + }()); + + rethrow; + } finally { + // ignore: control_flow_in_finally + continue; } } From 7c19717115e5e5350162769ad51cf3c03a1b34b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Tue, 3 Sep 2024 00:51:26 -0600 Subject: [PATCH 019/141] refactor(widgets, framework, core, test): Update dependency references to use objects instead of hashcodes. --- .../lib/src/framework/provider_impl.dart | 10 +++++----- .../lib/src/widgets/rt_provider.dart | 2 +- .../lib/src/core/dependency_injection.dart | 14 +++++++------- .../reactter/lib/src/core/dependency_register.dart | 2 +- .../test/core/dependency_injection_test.dart | 7 +++---- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/packages/flutter_reactter/lib/src/framework/provider_impl.dart b/packages/flutter_reactter/lib/src/framework/provider_impl.dart index ccde0b31..5b7ea04e 100644 --- a/packages/flutter_reactter/lib/src/framework/provider_impl.dart +++ b/packages/flutter_reactter/lib/src/framework/provider_impl.dart @@ -147,7 +147,7 @@ class ProviderElement extends InheritedElement bool _isLazyInstanceObtained = false; bool get isRoot { - return Rt.getHashCodeRefAt(0, widget.id) == widget.ref.hashCode; + return Rt.getRefAt(0, widget.id) == widget; } @override @@ -157,7 +157,7 @@ class ProviderElement extends InheritedElement T? get instance { if (!_isLazyInstanceObtained && widget.isLazy) { - final instance = Rt.get(widget.id, widget.ref); + final instance = Rt.get(widget.id, widget); _isLazyInstanceObtained = instance != null; @@ -178,7 +178,7 @@ class ProviderElement extends InheritedElement widget.instanceBuilder, id: widget.id, mode: widget.mode, - ref: widget.ref, + ref: widget, ); return; @@ -194,7 +194,7 @@ class ProviderElement extends InheritedElement @override void mount(Element? parent, Object? newSlot) { if (!widget.init && !widget.isLazy) { - Rt.get(widget.id, widget.ref); + Rt.get(widget.id, widget); } _updateInheritedElementWithId(parent); @@ -224,7 +224,7 @@ class ProviderElement extends InheritedElement @override void unmount() { final id = widget.id; - final ref = widget.ref; + final ref = widget; final isRoot = this.isRoot; final instance = this.instance; diff --git a/packages/flutter_reactter/lib/src/widgets/rt_provider.dart b/packages/flutter_reactter/lib/src/widgets/rt_provider.dart index 2cf68b7a..dab11782 100644 --- a/packages/flutter_reactter/lib/src/widgets/rt_provider.dart +++ b/packages/flutter_reactter/lib/src/widgets/rt_provider.dart @@ -244,7 +244,7 @@ class RtProvider extends ProviderBase class RtProviderElement extends ComponentElement with WrapperElementMixin> { bool get isRoot { - return Rt.getHashCodeRefAt(0, widget.id) == _widget.hashCode; + return Rt.getRefAt(0, widget.id) == _widget; } final RtProvider _widget; diff --git a/packages/reactter/lib/src/core/dependency_injection.dart b/packages/reactter/lib/src/core/dependency_injection.dart index 169a71c9..d0869b5e 100644 --- a/packages/reactter/lib/src/core/dependency_injection.dart +++ b/packages/reactter/lib/src/core/dependency_injection.dart @@ -317,7 +317,7 @@ abstract class DependencyInjection implements IContext { } if (ref != null) { - dependencyRegister!.refs.remove(ref.hashCode); + dependencyRegister!.refs.remove(ref); } if (dependencyRegister!.refs.isNotEmpty) { @@ -463,9 +463,9 @@ abstract class DependencyInjection implements IContext { return _instances[instance]?.mode; } - /// Returns the hashCode reference at a specified index for a given type and + /// Returns the reference at a specified index for a given type and /// optional ID. - int? getHashCodeRefAt(int index, [String? id]) { + Object? getRefAt(int index, [String? id]) { final refs = _getDependencyRegister(id)?.refs; if (refs == null || refs.length < index + 1) return null; @@ -496,6 +496,10 @@ abstract class DependencyInjection implements IContext { return _getDependencyRegisterByRef(dependencyRef); } + if (ref != null) { + instanceRegister.refs.add(ref); + } + if (instanceRegister.instance != null) { logger.log('The "$instanceRegister" instance already created.'); @@ -504,10 +508,6 @@ abstract class DependencyInjection implements IContext { BindingZone.autoBinding(() => _createInstance(instanceRegister)); - if (ref != null) { - instanceRegister.refs.add(ref.hashCode); - } - // ignore: deprecated_member_use_from_same_package eventHandler.emit(instanceRegister, Lifecycle.initialized); eventHandler.emit(instanceRegister, Lifecycle.created); diff --git a/packages/reactter/lib/src/core/dependency_register.dart b/packages/reactter/lib/src/core/dependency_register.dart index e674c085..38f3c21c 100644 --- a/packages/reactter/lib/src/core/dependency_register.dart +++ b/packages/reactter/lib/src/core/dependency_register.dart @@ -17,7 +17,7 @@ class DependencyRegister extends DependencyRef { final DependencyMode mode; /// Stores the refs where the instance was created. - final refs = HashSet(); + final refs = HashSet(); T? _instance; T? get instance => _instance; diff --git a/packages/reactter/test/core/dependency_injection_test.dart b/packages/reactter/test/core/dependency_injection_test.dart index 58108227..639e80e6 100644 --- a/packages/reactter/test/core/dependency_injection_test.dart +++ b/packages/reactter/test/core/dependency_injection_test.dart @@ -419,14 +419,13 @@ void main() { Rt.destroy(id: id); }); - test("should get hashcode ref by index", () { + test("should get ref by index", () { final ref = 'myRef'; Rt.create(() => TestController(), ref: ref); - final hashCodeRef = Rt.getHashCodeRefAt(0); - expect(hashCodeRef, isA()); - expect(hashCodeRef, ref.hashCode); + final hashCodeRef = Rt.getRefAt(0); + expect(hashCodeRef, ref); Rt.destroy(); }); From 17c9fe4b1fddd6c85a725e697ea66e067857577a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Tue, 3 Sep 2024 00:53:25 -0600 Subject: [PATCH 020/141] refactor(framework, hooks): Improve dispose logic in `RtStateBase` and `UseDependency` --- .../reactter/lib/src/framework/rt_state_base.dart | 13 ++----------- packages/reactter/lib/src/hooks/use_dependency.dart | 6 +++--- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/packages/reactter/lib/src/framework/rt_state_base.dart b/packages/reactter/lib/src/framework/rt_state_base.dart index 6c0eb56d..ebc7fa96 100644 --- a/packages/reactter/lib/src/framework/rt_state_base.dart +++ b/packages/reactter/lib/src/framework/rt_state_base.dart @@ -139,9 +139,7 @@ abstract class RtStateBase> implements RtState { @override @mustCallSuper void dispose() { - if (_isDisposed) return; - - _isDisposed = true; + assert(!_isDisposed, "Can't dispose when it's been disposed"); if (_boundInstance != null) { eventHandler.off(_boundInstance!, Lifecycle.deleted, _onInstanceDeleted); @@ -152,14 +150,7 @@ abstract class RtStateBase> implements RtState { eventHandler.offAll(this); _notifyDisponsed(); - } - - void revive() { - _isDisposed = false; - - if (_boundInstance != null) { - eventHandler.one(_boundInstance!, Lifecycle.deleted, _onInstanceDeleted); - } + _isDisposed = true; } @override diff --git a/packages/reactter/lib/src/hooks/use_dependency.dart b/packages/reactter/lib/src/hooks/use_dependency.dart index cd17a661..97a1a867 100644 --- a/packages/reactter/lib/src/hooks/use_dependency.dart +++ b/packages/reactter/lib/src/hooks/use_dependency.dart @@ -87,7 +87,7 @@ class UseDependency extends RtHook { /// or `null` if the dependency is not found it /// or has not been created yet. T? get instance { - assert(!_isDisposed); + assert(!_isDisposed, 'Cannot use a disposed hook.'); if (_instance == null) { final instanceFound = Rt.find(id); @@ -255,8 +255,6 @@ class UseDependency extends RtHook { void dispose() { if (_isDisposed) return; - _isDisposed = true; - _unlisten(); Rt.delete(id, this); @@ -264,6 +262,8 @@ class UseDependency extends RtHook { _instance = null; super.dispose(); }); + + _isDisposed = true; } void _listen() { From 4d37a8650ebaff3919e626d5d8444b6fd1be4341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Tue, 3 Sep 2024 01:05:48 -0600 Subject: [PATCH 021/141] refactor(hooks): Update hook registration and state attachment logic. --- packages/reactter/lib/src/framework/rt_hook.dart | 6 +++--- packages/reactter/lib/src/hooks/use_effect.dart | 8 -------- packages/reactter/lib/src/interfaces/hook.dart | 10 ++++------ packages/reactter/lib/src/signal/signal_impl.dart | 12 +++++++----- 4 files changed, 14 insertions(+), 22 deletions(-) diff --git a/packages/reactter/lib/src/framework/rt_hook.dart b/packages/reactter/lib/src/framework/rt_hook.dart index fc1fe1e1..76a46513 100644 --- a/packages/reactter/lib/src/framework/rt_hook.dart +++ b/packages/reactter/lib/src/framework/rt_hook.dart @@ -42,17 +42,17 @@ part of '../internals.dart'; /// {@endtemplate} abstract class RtHook with RtContext, RtStateBase implements IHook { /// {@template reactter.rt_hook.register} - /// This getter allows access to the [HookRegister] instance + /// This getter allows access to the [HookBindingZone] instance /// which is responsible for registering a [RtHook] /// and attaching previously collected states to it. /// {@endtemplate} - static get $register => HookRegister(); + static get $register => HookBindingZone(); /// This variable is used to register [RtHook] /// and attach the [RtState] that are defined here. @override @protected - HookRegister get $; + HookBindingZone get $; /// Initializes a new instance of the [RtHook] class. /// diff --git a/packages/reactter/lib/src/hooks/use_effect.dart b/packages/reactter/lib/src/hooks/use_effect.dart index 038bff13..197310cd 100644 --- a/packages/reactter/lib/src/hooks/use_effect.dart +++ b/packages/reactter/lib/src/hooks/use_effect.dart @@ -213,14 +213,6 @@ class UseEffect extends RtHook { super.dispose(); } - @override - void revive() { - _watchInstanceAttached(); - _watchDependencies(); - - super.revive(); - } - void _watchInstanceAttached() { Rt.on( boundInstance!, diff --git a/packages/reactter/lib/src/interfaces/hook.dart b/packages/reactter/lib/src/interfaces/hook.dart index cdd9a336..afd1903a 100644 --- a/packages/reactter/lib/src/interfaces/hook.dart +++ b/packages/reactter/lib/src/interfaces/hook.dart @@ -1,21 +1,19 @@ part of '../internals.dart'; -/// An abstract class that provides the base functionality for creating -/// custom hooks in the Reactter library. -abstract class IHook implements IState { +/// An abstract class that represents a hook in Reactter. +abstract class IHook { /// This variable is used to register [IHook] /// and attach the [IState] that are defined here. @protected - HookRegister get $; + HookBindingZone get $; /// Executes [callback], and notifies the listeners about the update. /// /// If [callback] is provided, it will be executed before notifying the listeners. /// If [callback] is not provided, an empty function will be executed. - @override @mustCallSuper void update([Function? callback]); } @internal -class HookRegister extends BindingZone {} +class HookBindingZone extends BindingZone {} diff --git a/packages/reactter/lib/src/signal/signal_impl.dart b/packages/reactter/lib/src/signal/signal_impl.dart index f98b7cb6..ed9475cb 100644 --- a/packages/reactter/lib/src/signal/signal_impl.dart +++ b/packages/reactter/lib/src/signal/signal_impl.dart @@ -79,15 +79,17 @@ enum SignalEvent { onGetValue, onSetValue } class Signal with RtContext, RtStateBase>, ObjBase implements Obj { - final _$ = BindingZone>(); - /// {@macro reactter.signal} - Signal( + Signal._( T value, { String? debugLabel, }) : _value = value, - _debugLabel = debugLabel { - _$.bindInstanceToStates(this); + _debugLabel = debugLabel; + + factory Signal(T value, {String? debugLabel}) { + return Rt.createState( + () => Signal._(value, debugLabel: debugLabel), + ); } bool _shouldGetValueNotify = true; From 146529e17c6c9c4b8d1fe8afc42576ebf2259ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Tue, 3 Sep 2024 01:12:55 -0600 Subject: [PATCH 022/141] refactor(core): Improve null listener handling in `Notifier` class. --- packages/reactter/lib/src/core/notifier.dart | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/reactter/lib/src/core/notifier.dart b/packages/reactter/lib/src/core/notifier.dart index ebbdae43..23f60211 100644 --- a/packages/reactter/lib/src/core/notifier.dart +++ b/packages/reactter/lib/src/core/notifier.dart @@ -271,11 +271,10 @@ abstract class Notifier { final int end = _count; for (int i = 0; i < end; i++) { - try { - final listener = _listeners[i]; - - assert(listener != null, 'Unexpected null listener in $target.'); + final listener = _listeners[i]; + assert(listener != null, 'Unexpected null listener in $target.'); + try { if (_listenersSingleUse.contains(listener)) { removeListener(listener!); _listenersSingleUse.remove(listener); From d1afe461d3d5727f34d6852720bb1ad4bc630471 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 20 Sep 2024 01:15:02 -0600 Subject: [PATCH 023/141] reactor(core, framework): Improve dispose, register and notify logic of state. --- .../lib/src/core/dependency_injection.dart | 4 ++- .../reactter/lib/src/core/event_notifier.dart | 18 +++++------ .../lib/src/core/state_management.dart | 7 ++-- .../lib/src/framework/rt_state_base.dart | 32 +++++++++++++------ 4 files changed, 36 insertions(+), 25 deletions(-) diff --git a/packages/reactter/lib/src/core/dependency_injection.dart b/packages/reactter/lib/src/core/dependency_injection.dart index d0869b5e..c9cd5fdf 100644 --- a/packages/reactter/lib/src/core/dependency_injection.dart +++ b/packages/reactter/lib/src/core/dependency_injection.dart @@ -545,7 +545,9 @@ abstract class DependencyInjection implements IContext { eventHandler.emit(dependencyRegister, Lifecycle.deleted); logger.log(log); - if (instance is IState) instance.dispose(); + if (instance is IState && !(instance as IState).isDisposed) { + instance.dispose(); + } } /// Returns the [DependencyRef] associated with the given instance. diff --git a/packages/reactter/lib/src/core/event_notifier.dart b/packages/reactter/lib/src/core/event_notifier.dart index e3d637f5..c335f73b 100644 --- a/packages/reactter/lib/src/core/event_notifier.dart +++ b/packages/reactter/lib/src/core/event_notifier.dart @@ -23,19 +23,19 @@ class EventNotifierRef { @override bool operator ==(Object other) { - if (other is EventNotifierRef) { - if (other.event != event) { - return false; - } + if (other is! EventNotifierRef) { + return false; + } - if (_dependencyRef != null && other._dependencyRef != null) { - return _dependencyRef == other._dependencyRef; - } + if (other.event != event) { + return false; + } - return _instanceObj == other._instanceObj; + if (_dependencyRef != null && other._dependencyRef != null) { + return _dependencyRef == other._dependencyRef; } - return false; + return _instanceObj == other._instanceObj; } } diff --git a/packages/reactter/lib/src/core/state_management.dart b/packages/reactter/lib/src/core/state_management.dart index 3190be92..46ef6908 100644 --- a/packages/reactter/lib/src/core/state_management.dart +++ b/packages/reactter/lib/src/core/state_management.dart @@ -153,11 +153,8 @@ abstract class StateManagement implements IContext { } if (_deferredEvents.containsKey(notifier)) { - try { - notifier.notifyListeners(param); - } finally { - _deferredEvents.remove(notifier); - } + _deferredEvents.remove(notifier); + notifier.notifyListeners(param); } } } diff --git a/packages/reactter/lib/src/framework/rt_state_base.dart b/packages/reactter/lib/src/framework/rt_state_base.dart index ebc7fa96..43db8b24 100644 --- a/packages/reactter/lib/src/framework/rt_state_base.dart +++ b/packages/reactter/lib/src/framework/rt_state_base.dart @@ -33,16 +33,9 @@ abstract class RtStateBase> implements RtState { // ignore: unused_field final _debugAssertRegistering = debugAssertRegistering(); + bool _isRegistered = false; bool _isUpdating = false; - @override - @protected - @mustCallSuper - void _register() { - BindingZone.recollectState(this); - _notifyCreated(); - } - /// A label used for debugging purposes. @override String get debugLabel => "$runtimeType[$hashCode]"; @@ -65,6 +58,18 @@ abstract class RtStateBase> implements RtState { eventHandler._hasListeners(this) || (_boundInstance != null && eventHandler._hasListeners(_boundInstance)); + @override + @protected + @mustCallSuper + void _register() { + BindingZone.recollectState(this); + + if (_isRegistered) return; + + _notifyCreated(); + _isRegistered = true; + } + @mustCallSuper @override void bind(Object instance) { @@ -149,6 +154,8 @@ abstract class RtStateBase> implements RtState { eventHandler.emit(this, Lifecycle.deleted); eventHandler.offAll(this); + if (_isDisposed) return; + _notifyDisponsed(); _isDisposed = true; } @@ -183,9 +190,14 @@ abstract class RtStateBase> implements RtState { emit(this, event, this); - if (_boundInstance != null) { - emit(_boundInstance!, event, this); + if (_boundInstance == null) return; + + if (_boundInstance is RtStateBase && + !(_boundInstance as RtStateBase)._isDisposed) { + return (_boundInstance as RtStateBase)._notify(event); } + + emit(_boundInstance!, event, this); } void _notifyCreated() { From b151bd050839c0a319077063ead9ea318401e2fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 20 Sep 2024 01:16:22 -0600 Subject: [PATCH 024/141] refactor(framework, widgets): Update mount and unmount logic in `ProviderElement`. --- .../lib/src/framework/provider_impl.dart | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/packages/flutter_reactter/lib/src/framework/provider_impl.dart b/packages/flutter_reactter/lib/src/framework/provider_impl.dart index 5b7ea04e..09c6fb95 100644 --- a/packages/flutter_reactter/lib/src/framework/provider_impl.dart +++ b/packages/flutter_reactter/lib/src/framework/provider_impl.dart @@ -142,7 +142,9 @@ class ProvideImpl extends ProviderBase @internal class ProviderElement extends InheritedElement with ScopeElementMixin { - Widget? prevChild; + static final Map _instanceMountCount = {}; + + Widget? _prevChild; HashMap>? _inheritedElementsWithId; bool _isLazyInstanceObtained = false; @@ -193,20 +195,25 @@ class ProviderElement extends InheritedElement @override void mount(Element? parent, Object? newSlot) { + final dependency = RtDependency(widget.id); + var count = _instanceMountCount.putIfAbsent(dependency, () => 0); + _instanceMountCount[dependency] = ++count; + final shouldNotifyMount = count == 1; + if (!widget.init && !widget.isLazy) { Rt.get(widget.id, widget); } _updateInheritedElementWithId(parent); - if (isRoot) { - Rt.emit(instance!, Lifecycle.willMount); + if (shouldNotifyMount) { + Rt.emit(dependency, Lifecycle.willMount); } super.mount(parent, newSlot); - if (isRoot) { - Rt.emit(instance!, Lifecycle.didMount); + if (shouldNotifyMount) { + Rt.emit(dependency, Lifecycle.didMount); } } @@ -215,34 +222,40 @@ class ProviderElement extends InheritedElement if (hasDependenciesDirty) { notifyClients(widget); - if (prevChild != null) return prevChild!; + if (_prevChild != null) return _prevChild!; } - return prevChild = super.build(); + return _prevChild = super.build(); } @override void unmount() { final id = widget.id; final ref = widget; - final isRoot = this.isRoot; - final instance = this.instance; + final dependency = RtDependency(widget.id); + final count = (_instanceMountCount[dependency] ?? 0) - 1; + final shouldNotifyUnmount = count < 1; + + if (shouldNotifyUnmount) { + _instanceMountCount.remove(dependency); + } else { + _instanceMountCount[dependency] = count; + } try { - if (isRoot) { - Rt.emit(instance!, Lifecycle.willUnmount); + if (shouldNotifyUnmount) { + Rt.emit(dependency, Lifecycle.willUnmount); } return super.unmount(); } finally { - if (isRoot) { - Rt.emit(instance!, Lifecycle.didUnmount); + if (shouldNotifyUnmount) { + Rt.emit(dependency, Lifecycle.didUnmount); } Rt.delete(id, ref); - _inheritedElementsWithId = null; - prevChild = null; + _prevChild = null; } } From fe9e7c2ed9894209550c9b79e13aa3a0db317260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 20 Sep 2024 02:57:03 -0600 Subject: [PATCH 025/141] feature(reactter_lint): Add new rules and rename rules. - Add `rt_invalid_state_creation` and `rt_no_logic_create_state`. - Add `rt_` prefix to rule names. - Rename `hook_late_convention` to `rt_state_late_convention`. --- packages/reactter_lint/README.md | 190 ++++++++++++------ packages/reactter_lint/lib/reactter_lint.dart | 20 +- .../reactter_lint/lib/src/extensions.dart | 104 +++++++++- packages/reactter_lint/lib/src/helpers.dart | 123 +++++++++++- .../lib/src/rules/hook_late_convention.dart | 124 ------------ ...tion.dart => rt_hook_name_convention.dart} | 18 +- ...ion.dart => rt_invalid_hook_position.dart} | 15 +- ...ter.dart => rt_invalid_hook_register.dart} | 20 +- .../src/rules/rt_invalid_state_creation.dart | 135 +++++++++++++ .../rules/rt_no_logic_in_create_state.dart | 62 ++++++ .../src/rules/rt_state_late_convention.dart | 109 ++++++++++ packages/reactter_lint/lib/src/types.dart | 20 +- packages/reactter_lint/lib/src/visitors.dart | 0 packages/reactter_lint/pubspec.lock | 46 ++--- 14 files changed, 731 insertions(+), 255 deletions(-) delete mode 100644 packages/reactter_lint/lib/src/rules/hook_late_convention.dart rename packages/reactter_lint/lib/src/rules/{hook_name_convention.dart => rt_hook_name_convention.dart} (80%) rename packages/reactter_lint/lib/src/rules/{invalid_hook_position.dart => rt_invalid_hook_position.dart} (79%) rename packages/reactter_lint/lib/src/rules/{invalid_hook_register.dart => rt_invalid_hook_register.dart} (87%) create mode 100644 packages/reactter_lint/lib/src/rules/rt_invalid_state_creation.dart create mode 100644 packages/reactter_lint/lib/src/rules/rt_no_logic_in_create_state.dart create mode 100644 packages/reactter_lint/lib/src/rules/rt_state_late_convention.dart create mode 100644 packages/reactter_lint/lib/src/visitors.dart diff --git a/packages/reactter_lint/README.md b/packages/reactter_lint/README.md index f97e948f..9f856f96 100644 --- a/packages/reactter_lint/README.md +++ b/packages/reactter_lint/README.md @@ -9,24 +9,29 @@ ____ [![Pub points](https://img.shields.io/pub/points/reactter_lint?color=196959&labelColor=23967F&logo=dart)](https://pub.dev/packages/reactter_lint/score) [![MIT License](https://img.shields.io/github/license/2devs-team/reactter?color=a85f00&labelColor=F08700&logoColor=fff&logo=Open%20Source%20Initiative)](https://github.com/2devs-team/reactter/blob/master/LICENSE) -`Reactter_lint` is an analytics tool for [Reactter](https://pub.dev/packages/reactter) that helps developers to encourage good coding practices and preventing frequent problems using the conventions Reactter. +`Reactter_lint` is an analytics tool for [Reactter](https://pub.dev/packages/reactter) that helps developers to encourage good coding practices and preventing frequent problems using the conventions Rt. -## Contents +### Contents -- [Contents](#contents) - [Quickstart](#quickstart) - [Lints](#lints) - - [hook\_late\_convention](#hook_late_convention) + - [rt\_hook\_name\_convention](#rt_hook_name_convention) - [Bad](#bad) - [Good](#good) - - [hook\_name\_convention](#hook_name_convention) + - [rt\_invalid\_hook\_position](#rt_invalid_hook_position) - [Bad](#bad-1) - [Good](#good-1) - - [invalid\_hook\_position](#invalid_hook_position) + - [rt\_invalid\_hook\_register](#rt_invalid_hook_register) - [Bad](#bad-2) - [Good](#good-2) - - [invalid\_hook\_register](#invalid_hook_register) + - [rt\_invalid\_state\_creation](#rt_invalid_state_creation) - [Bad](#bad-3) + - [God](#god) + - [rt\_no\_logic\_in\_create\_state](#rt_no_logic_in_create_state) + - [Bad](#bad-4) + - [God](#god-1) + - [rt\_state\_late\_convention](#rt_state_late_convention) + - [Bad](#bad-5) - [Good](#good-3) ## Quickstart @@ -53,38 +58,7 @@ dart run custom_lint ## Lints -### hook_late_convention - -The hook late must be attached an instance. - -#### Bad - -Cause: `ReactterHook` cannot be late without attaching an instance. - -```dart -class AppController { - final otherState = UseState(0); - late final stateLate = UseState(otherState.value); - ... -} -``` - -#### Good - -Fix: Use `Reactter.lazyState` for attaching an instance. - -```dart -class AppController { - final otherState = UseState(0); - late final stateLate = Reactter.lazyState( - () => UseState(otherState.value), - this, - ); - ... -} -``` - -### hook_name_convention +### rt_hook_name_convention The hook name should be prefixed with `use`. @@ -93,7 +67,7 @@ The hook name should be prefixed with `use`. Cause: The hook name is not prefixed with `use`. ```dart -class MyHook extends ReactterHook { +> class MyHook extends RtHook { ... } ``` @@ -103,12 +77,12 @@ class MyHook extends ReactterHook { Fix: Name hook using `use` preffix. ```dart -class UseMyHook extends ReactterHook { +class UseMyHook extends RtHook { ... } ``` -### invalid_hook_position +### rt_invalid_hook_position The hook must be defined after the hook register. @@ -117,9 +91,9 @@ The hook must be defined after the hook register. Cause: The hook cannot be defined before the hook register. ```dart -class UseMyHook extends ReactterHook { +class UseMyHook extends RtHook { final stateHook = UseState(0); - final $ = ReactterHook.$register; +> final $ = RtHook.$register; ... } ``` @@ -129,14 +103,14 @@ class UseMyHook extends ReactterHook { Fix: Define it after the hook register. ```dart -class UseMyHook extends ReactterHook { - final $ = ReactterHook.$register; +class UseMyHook extends RtHook { + final $ = RtHook.$register; final stateHook = UseState(0); ... } ``` -### invalid_hook_register +### rt_invalid_hook_register The hook register('$' field) must be final only. @@ -145,8 +119,8 @@ The hook register('$' field) must be final only. Cause: The hook register cannot be defined using getter. ```dart -class MyHook extends ReactterHook { - get $ => ReactterHook.$register; +class MyHook extends RtHook { +> get $ => RtHook.$register; ... } ``` @@ -154,8 +128,8 @@ class MyHook extends ReactterHook { Cause: The hook register cannot be defined using setter. ```dart -class UseMyHook extends ReactterHook { - set $(_) => ReactterHook.$register; +class UseMyHook extends RtHook { +> set $(_) => RtHook.$register; ... } ``` @@ -163,8 +137,8 @@ class UseMyHook extends ReactterHook { Cause: The hook register cannot be defined using `var` keyword. ```dart -class UseMyHook extends ReactterHook { - var $ = ReactterHook.$register; +class UseMyHook extends RtHook { +> var $ = RtHook.$register; ... } ``` @@ -172,8 +146,8 @@ class UseMyHook extends ReactterHook { Cause: The hook register cannot be defined using a type. ```dart -class UseMyHook extends ReactterHook { - Object $ = ReactterHook.$register; +class UseMyHook extends RtHook { +> Object $ = RtHook.$register; ... } ``` @@ -183,8 +157,110 @@ class UseMyHook extends ReactterHook { Fix: Define it using `final` keyword. ```dart -class UseMyHook extends ReactterHook { - final $ = ReactterHook.$register; +class UseMyHook extends RtHook { + final $ = RtHook.$register; + ... +} +``` + +### rt_invalid_state_creation + +The state must be create under the Reactter context. + +#### Bad + +Cause: The state cannot be created outside Reactter context. + +```dart +class MyState with RtStateBase, RtContext {...} + +> final myState = MyState(); +``` + +#### God + +Fix: Use `Rt.createState` method for creating the state under the Reactter context. + +```dart +class MyState with RtStateBase, RtContext {...} + +final myState = Rt.createState(() => MyState()); +``` + +### rt_no_logic_in_create_state + +Don't put logic in `createState` method + +#### Bad + +Cause: The `createState` method includes additional logic. + +```dart +> final myState = Rt.createState(() { +> final inst = MyState(); +> inst.foo(); +> return inst; +> }); +``` + +```dart +> final myState = Rt.createState(() { +> if (flag) return MyState(); +> +> return OtherState(); +> }); +``` + +#### God + +Fix: Try moving the logic out of `createState` method. + +```dart +final myState = Rt.createState(() => MyState()); +myState.foo(); +``` + +```dart +final myState = flag + ? Rt.createState(() => MyState()) + : Rt.createState(() => OtherState()); +``` + +### rt_state_late_convention + +The state late must be attached an instance. + +#### Bad + +Cause: `RtHook` cannot be late without attaching an instance. + +```dart +class AppController { + final stateA = UseState(0); + final stateB = UseState(0); +> late final stateLate = UseCompute( +> () => stateA.value + stateB.value, +> [stateA, stateB], +> ); + ... +} +``` + +#### Good + +Fix: Use `Rt.lazyState` for attaching an instance. + +```dart +class AppController { + final stateA = UseState(0); + final stateB = UseState(0); + late final stateLate = Rt.lazyState( + () => UseCompute( + () => stateA.value + stateB.value, + [stateA, stateB], + ), + this, + ); ... } ``` diff --git a/packages/reactter_lint/lib/reactter_lint.dart b/packages/reactter_lint/lib/reactter_lint.dart index 36016d67..2fa80436 100644 --- a/packages/reactter_lint/lib/reactter_lint.dart +++ b/packages/reactter_lint/lib/reactter_lint.dart @@ -1,9 +1,11 @@ // This is the entrypoint of our custom linter import 'package:custom_lint_builder/custom_lint_builder.dart'; -import 'package:reactter_lint/src/rules/hook_late_convention.dart'; -import 'package:reactter_lint/src/rules/hook_name_convention.dart'; -import 'package:reactter_lint/src/rules/invalid_hook_position.dart'; -import 'package:reactter_lint/src/rules/invalid_hook_register.dart'; +import 'package:reactter_lint/src/rules/rt_state_late_convention.dart'; +import 'package:reactter_lint/src/rules/rt_hook_name_convention.dart'; +import 'package:reactter_lint/src/rules/rt_invalid_hook_position.dart'; +import 'package:reactter_lint/src/rules/rt_invalid_hook_register.dart'; +import 'package:reactter_lint/src/rules/rt_no_logic_in_create_state.dart'; +import 'package:reactter_lint/src/rules/rt_invalid_state_creation.dart'; /// The function creates an instance of the _ReactterLinter class and returns it as a PluginBase object. PluginBase createPlugin() => _ReactterLinter(); @@ -13,10 +15,12 @@ class _ReactterLinter extends PluginBase { @override List getLintRules(CustomLintConfigs configs) { return const [ - HookLateConvention(), - HookNameConvention(), - InvalidHookPosition(), - InvalidHookRegister(), + RtStateLateConvention(), + RtHookNameConvention(), + RtInvalidHookPosition(), + RtInvalidHookRegister(), + RtNoLogicInCreateState(), + RtInvalidStateCreation(), ]; } } diff --git a/packages/reactter_lint/lib/src/extensions.dart b/packages/reactter_lint/lib/src/extensions.dart index 08f1747a..0e1d754d 100644 --- a/packages/reactter_lint/lib/src/extensions.dart +++ b/packages/reactter_lint/lib/src/extensions.dart @@ -1,22 +1,41 @@ import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/src/dart/element/member.dart'; // ignore: implementation_imports -extension ElementExtension on Element? { +extension ElementExtension on Element { /// The `node` is a getter method defined as an extension on the `Element` class in /// Dart. It allows you to retrieve the AST (Abstract Syntax Tree) node associated with an element. AstNode? get node { - if (this == null) return null; + if (this.library == null) return null; - if (this?.library == null) return null; - - final parsedLibrary = this - ?.session - ?.getParsedLibraryByElement(this!.library!) as ParsedLibraryResult?; - final declaration = parsedLibrary?.getElementDeclaration(this!); + final parsedLibrary = this.session?.getParsedLibraryByElement(this.library!) + as ParsedLibraryResult?; + final declaration = parsedLibrary?.getElementDeclaration(this); return declaration?.node; } + + Element get canonicalElement { + var self = this; + if (self is PropertyAccessorElement) { + var variable = self.variable; + if (variable is FieldMember) { + // A field element defined in a parameterized type where the values of + // the type parameters are known. + // + // This concept should be invisible when comparing FieldElements, but a + // bug in the analyzer causes FieldElements to not evaluate as + // equivalent to equivalent FieldMembers. See + // https://github.com/dart-lang/sdk/issues/35343. + return variable.declaration; + } else { + return variable; + } + } else { + return self; + } + } } extension InterableExtension on Iterable { @@ -33,3 +52,72 @@ extension InterableExtension on Iterable { return null; } } + +extension BlockExtension on Block { + /// Returns the last statement of this block, or `null` if this is empty. + /// + /// If the last immediate statement of this block is a [Block], recurses into + /// it to find the last statement. + Statement? get lastStatement { + if (statements.isEmpty) { + return null; + } + var lastStatement = statements.last; + if (lastStatement is Block) { + return lastStatement.lastStatement; + } + return lastStatement; + } +} + +extension FunctionExpressionExtension on FunctionExpression { + Expression? get returnExpression { + return body.returnExpression; + } +} + +extension FunctionBodyExtension on FunctionBody { + Expression? get returnExpression { + if (this is BlockFunctionBody) { + var block = (this as BlockFunctionBody).block; + if (block.statements.isEmpty) return null; + var lastStatement = block.statements.last; + + if (lastStatement is ReturnStatement) { + return lastStatement.expression; + } + } + + if (this is ExpressionFunctionBody) { + return (this as ExpressionFunctionBody).expression; + } + + return null; + } +} + +/// Operations on iterables. +extension IterableExtensions on Iterable { + /// The first element of this iterator, or `null` if the iterable is empty. + T? get firstOrNull { + var iterator = this.iterator; + if (iterator.moveNext()) return iterator.current; + return null; + } + + /// The last element of this iterable, or `null` if the iterable is empty. + /// + /// This computation may not be efficient. + /// The last value is potentially found by iterating the entire iterable + /// and temporarily storing every value. + /// The process only iterates the iterable once. + /// If iterating more than once is not a problem, it may be more efficient + /// for some iterables to do: + /// ```dart + /// var lastOrNull = iterable.isEmpty ? null : iterable.last; + /// ``` + T? get lastOrNull { + if (isEmpty) return null; + return last; + } +} diff --git a/packages/reactter_lint/lib/src/helpers.dart b/packages/reactter_lint/lib/src/helpers.dart index 2f113257..3397fe32 100644 --- a/packages/reactter_lint/lib/src/helpers.dart +++ b/packages/reactter_lint/lib/src/helpers.dart @@ -1,12 +1,13 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:reactter_lint/src/consts.dart'; +import 'package:reactter_lint/src/extensions.dart'; /// The function checks if a given declaration is a hook register. /// /// Args: /// node (Declaration): The "node" parameter is a Declaration object. -bool isHookRegister(Declaration node) { +bool isRegisterDeclaration(Declaration node) { if (node is FieldDeclaration) { final name = node.fields.variables.first.name.toString(); return name == HOOK_REGISTER_VAR; @@ -31,3 +32,123 @@ Element? getElementFromDeclaration(Declaration node) { return node.declaredElement; } + +/// Returns whether the canonical elements of [element1] and [element2] are +/// equal. +bool canonicalElementsAreEqual(Element? element1, Element? element2) => + element1?.canonicalElement == element2?.canonicalElement; + +/// Returns whether the canonical elements from two nodes are equal. +/// +/// As in, [NullableAstNodeExtension.canonicalElement], the two nodes must be +/// [Expression]s in order to be compared (otherwise `false` is returned). +/// +/// The two nodes must both be a [SimpleIdentifier], [PrefixedIdentifier], or +/// [PropertyAccess] (otherwise `false` is returned). +/// +/// If the two nodes are PrefixedIdentifiers, or PropertyAccess nodes, then +/// `true` is returned only if their canonical elements are equal, in +/// addition to their prefixes' and targets' (respectfully) canonical +/// elements. +/// +/// There is an inherent assumption about pure getters. For example: +/// +/// A a1 = ... +/// A a2 = ... +/// a1.b.c; // statement 1 +/// a2.b.c; // statement 2 +/// a1.b.c; // statement 3 +/// +/// The canonical elements from statements 1 and 2 are different, because a1 +/// is not the same element as a2. The canonical elements from statements 1 +/// and 3 are considered to be equal, even though `A.b` may have side effects +/// which alter the returned value. +bool canonicalElementsFromIdentifiersAreEqual( + Expression? rawExpression1, Expression? rawExpression2) { + if (rawExpression1 == null || rawExpression2 == null) return false; + + var expression1 = rawExpression1.unParenthesized; + var expression2 = rawExpression2.unParenthesized; + + print("EXPRESSION 1: ${expression1.runtimeType}"); + print("EXPRESSION 2: ${expression2.runtimeType}"); + + if (expression1 is SimpleIdentifier) { + print("EXPRESSION SimpleIdentifier 1: $expression1"); + print("EXPRESSION SimpleIdentifier 2: $expression2"); + return expression2 is SimpleIdentifier && + canonicalElementsAreEqual(getWriteOrReadElement(expression1), + getWriteOrReadElement(expression2)); + } + + if (expression1 is PrefixedIdentifier) { + print("EXPRESSION PrefixedIdentifier 1: $expression1"); + print("EXPRESSION PrefixedIdentifier 2: $expression2"); + return expression2 is PrefixedIdentifier && + canonicalElementsAreEqual(expression1.prefix.staticElement, + expression2.prefix.staticElement) && + canonicalElementsAreEqual(getWriteOrReadElement(expression1.identifier), + getWriteOrReadElement(expression2.identifier)); + } + + if (expression1 is PropertyAccess && expression2 is PropertyAccess) { + print("EXPRESSION PropertyAccess 1: $expression1"); + print("EXPRESSION PropertyAccess 2: $expression2"); + var target1 = expression1.target; + var target2 = expression2.target; + return canonicalElementsFromIdentifiersAreEqual(target1, target2) && + canonicalElementsAreEqual( + getWriteOrReadElement(expression1.propertyName), + getWriteOrReadElement(expression2.propertyName)); + } + + if (expression1 is InstanceCreationExpression && + expression2 is InstanceCreationExpression) { + print( + "EXPRESSION InstanceCreationExpression 1: ${expression1.staticParameterElement}"); + print( + "EXPRESSION InstanceCreationExpression 2: ${expression2.staticParameterElement}"); + + return expression1 == expression2; + } + + return false; +} + +/// If the [node] is the finishing identifier of an assignment, return its +/// "writeElement", otherwise return its "staticElement", which might be +/// thought as the "readElement". +Element? getWriteOrReadElement(SimpleIdentifier node) { + var writeElement = _getWriteElement(node); + if (writeElement != null) { + return writeElement; + } + return node.staticElement; +} + +/// If the [node] is the target of a [CompoundAssignmentExpression], +/// return the corresponding "writeElement", which is the local variable, +/// the setter referenced with a [SimpleIdentifier] or a [PropertyAccess], +/// or the `[]=` operator. +Element? _getWriteElement(AstNode node) { + var parent = node.parent; + if (parent is AssignmentExpression && parent.leftHandSide == node) { + return parent.writeElement; + } + if (parent is PostfixExpression) { + return parent.writeElement; + } + if (parent is PrefixExpression) { + return parent.writeElement; + } + + if (parent is PrefixedIdentifier && parent.identifier == node) { + return _getWriteElement(parent); + } + + if (parent is PropertyAccess && parent.propertyName == node) { + return _getWriteElement(parent); + } + + return null; +} diff --git a/packages/reactter_lint/lib/src/rules/hook_late_convention.dart b/packages/reactter_lint/lib/src/rules/hook_late_convention.dart deleted file mode 100644 index 252587a8..00000000 --- a/packages/reactter_lint/lib/src/rules/hook_late_convention.dart +++ /dev/null @@ -1,124 +0,0 @@ -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/error/error.dart'; -import 'package:analyzer/error/listener.dart'; -import 'package:custom_lint_builder/custom_lint_builder.dart'; -import 'package:reactter_lint/src/extensions.dart'; -import 'package:reactter_lint/src/types.dart'; - -/// The `HookLateConvention` class is a DartLint rule that enforces a specific naming convention for -/// hooks. -class HookLateConvention extends DartLintRule { - const HookLateConvention() : super(code: _code); - - static const _code = LintCode( - name: "hook_late_convention", - errorSeverity: ErrorSeverity.WARNING, - problemMessage: "The '{0}' hook late must be attached an instance.", - correctionMessage: - "Try removing 'late' keyword or wrapping the hook using 'Rt.lazyState'.\n" - "Example: late final myHookLate = Rt.lazyState(() => UseState(0), this);", - ); - - @override - List getFixes() => [_HookLateFix()]; - - static whenIsInvalid({ - required CustomLintContext context, - required Function(Declaration node, Element element) onInvalid, - }) { - context.registry.addFieldDeclaration((node) { - final element = node.fields.variables.first.declaredElement; - - if (element == null) return; - - if (!reactterHookType.isAssignableFromType(element.type)) return; - - if (!element.isLate) return; - - bool checkIsUseLazy(MethodInvocation method) { - return method.target.toString() == "Rt" && - method.methodName.toString() == "lazyState"; - } - - bool checkIsVariableUseLazy(VariableDeclaration variable) { - return variable.childEntities.whereType().any( - checkIsUseLazy, - ); - } - - final isUseLazy = node.fields.childEntities - .whereType() - .any(checkIsVariableUseLazy); - - if (isUseLazy) return; - - onInvalid(node, element); - }); - } - - @override - void run( - CustomLintResolver resolver, - ErrorReporter reporter, - CustomLintContext context, - ) { - whenIsInvalid( - context: context, - onInvalid: (node, element) { - reporter.reportErrorForElement(code, element, [element.name!]); - }, - ); - } -} - -/// The `_HookLateFix` class is a Dart fix that provides a solution for a specific issue related to late -/// variables. -class _HookLateFix extends DartFix { - @override - void run( - CustomLintResolver resolver, - ChangeReporter reporter, - CustomLintContext context, - AnalysisError analysisError, - List others, - ) { - HookLateConvention.whenIsInvalid( - context: context, - onInvalid: (node, element) { - try { - if (node is! FieldDeclaration) return; - - bool checkIsInstanceCreationExpression(VariableDeclaration variable) { - return variable.childEntities - .whereType() - .isNotEmpty; - } - - final variable = node.fields.childEntities - .whereType() - .firstWhereOrNull(checkIsInstanceCreationExpression); - - final instanceCreationExpression = variable?.childEntities - .firstWhereOrNull((child) => child is InstanceCreationExpression); - - if (instanceCreationExpression == null) return; - - final changeBuilder = reporter.createChangeBuilder( - message: "Wrap with 'Rt.lazyState'.", - priority: 1, - ); - - changeBuilder.addDartFileEdit((builder) { - builder.addSimpleReplacement( - instanceCreationExpression.sourceRange, - "Rt.lazyState(() => $instanceCreationExpression, this)", - ); - }); - } catch (e) { - print(e); - } - }, - ); - } -} diff --git a/packages/reactter_lint/lib/src/rules/hook_name_convention.dart b/packages/reactter_lint/lib/src/rules/rt_hook_name_convention.dart similarity index 80% rename from packages/reactter_lint/lib/src/rules/hook_name_convention.dart rename to packages/reactter_lint/lib/src/rules/rt_hook_name_convention.dart index 4d9a021d..7a248811 100644 --- a/packages/reactter_lint/lib/src/rules/hook_name_convention.dart +++ b/packages/reactter_lint/lib/src/rules/rt_hook_name_convention.dart @@ -7,12 +7,11 @@ import 'package:custom_lint_builder/custom_lint_builder.dart'; import 'package:reactter_lint/src/consts.dart'; import 'package:reactter_lint/src/types.dart'; -/// The HookNameConvention class is a DartLintRule that enforces a specific naming convention for hooks. -class HookNameConvention extends DartLintRule { - const HookNameConvention() : super(code: _code); +class RtHookNameConvention extends DartLintRule { + const RtHookNameConvention() : super(code: _code); static const _code = LintCode( - name: "hook_name_convention", + name: "rt_hook_name_convention", errorSeverity: ErrorSeverity.WARNING, problemMessage: "The '{0}' hook name should be prefixed with '$HOOK_NAME_PREFIX'.", @@ -20,7 +19,7 @@ class HookNameConvention extends DartLintRule { ); @override - List getFixes() => [_HookNameFix()]; + List getFixes() => [_RtHookNameFix()]; static whenIsInvalid({ required CustomLintContext context, @@ -31,7 +30,7 @@ class HookNameConvention extends DartLintRule { if (declaredElement == null) return; - if (!reactterHookType.isAssignableFrom(declaredElement)) return; + if (!rtHookType.isAssignableFrom(declaredElement)) return; final name = declaredElement.name; @@ -66,8 +65,7 @@ class HookNameConvention extends DartLintRule { } } -/// The _HookNameFix class is a Dart fix that helps with fixing hook names. -class _HookNameFix extends DartFix { +class _RtHookNameFix extends DartFix { @override void run( CustomLintResolver resolver, @@ -76,12 +74,12 @@ class _HookNameFix extends DartFix { AnalysisError analysisError, List others, ) { - HookNameConvention.whenIsInvalid( + RtHookNameConvention.whenIsInvalid( context: context, onInvalid: (node, element) { try { final name = element.name!; - final nameUse = HookNameConvention.prefixUseName(name); + final nameUse = RtHookNameConvention.prefixUseName(name); final changeBuilder = reporter.createChangeBuilder( message: "Rename '$name' to '$nameUse'.", diff --git a/packages/reactter_lint/lib/src/rules/invalid_hook_position.dart b/packages/reactter_lint/lib/src/rules/rt_invalid_hook_position.dart similarity index 79% rename from packages/reactter_lint/lib/src/rules/invalid_hook_position.dart rename to packages/reactter_lint/lib/src/rules/rt_invalid_hook_position.dart index 769a54ab..85a23620 100644 --- a/packages/reactter_lint/lib/src/rules/invalid_hook_position.dart +++ b/packages/reactter_lint/lib/src/rules/rt_invalid_hook_position.dart @@ -8,13 +8,11 @@ import 'package:reactter_lint/src/extensions.dart'; import 'package:reactter_lint/src/helpers.dart'; import 'package:reactter_lint/src/types.dart'; -/// The InvalidHookPosition class is a DartLintRule that detects and reports invalid hook positions in -/// code. -class InvalidHookPosition extends DartLintRule { - const InvalidHookPosition() : super(code: _code); +class RtInvalidHookPosition extends DartLintRule { + const RtInvalidHookPosition() : super(code: _code); static const _code = LintCode( - name: "invalid_hook_position", + name: "rt_invalid_hook_position", errorSeverity: ErrorSeverity.WARNING, problemMessage: "The '{0}' must be defined after hook register('$HOOK_REGISTER_VAR').", @@ -31,9 +29,10 @@ class InvalidHookPosition extends DartLintRule { if (declaredElement == null) return; - if (!reactterHookType.isAssignableFrom(declaredElement)) return; + if (!rtHookType.isAssignableFrom(declaredElement)) return; - final hookRegisterNode = node.members.firstWhereOrNull(isHookRegister); + final hookRegisterNode = + node.members.firstWhereOrNull(isRegisterDeclaration); if (hookRegisterNode == null) return; @@ -42,7 +41,7 @@ class InvalidHookPosition extends DartLintRule { if (element == null) return false; - return reactterHookType.isAssignableFrom(element); + return rtHookType.isAssignableFrom(element); }).toList(); for (final hook in hooks) { diff --git a/packages/reactter_lint/lib/src/rules/invalid_hook_register.dart b/packages/reactter_lint/lib/src/rules/rt_invalid_hook_register.dart similarity index 87% rename from packages/reactter_lint/lib/src/rules/invalid_hook_register.dart rename to packages/reactter_lint/lib/src/rules/rt_invalid_hook_register.dart index b35c359d..0814ba86 100644 --- a/packages/reactter_lint/lib/src/rules/invalid_hook_register.dart +++ b/packages/reactter_lint/lib/src/rules/rt_invalid_hook_register.dart @@ -8,12 +8,11 @@ import 'package:reactter_lint/src/consts.dart'; import 'package:reactter_lint/src/helpers.dart'; import 'package:reactter_lint/src/types.dart'; -/// The InvalidHookRegister class is a DartLintRule that detects and reports invalid hook registrations. -class InvalidHookRegister extends DartLintRule { - const InvalidHookRegister() : super(code: _code); +class RtInvalidHookRegister extends DartLintRule { + const RtInvalidHookRegister() : super(code: _code); static const _code = LintCode( - name: "invalid_hook_register", + name: "rt_invalid_hook_register", errorSeverity: ErrorSeverity.ERROR, problemMessage: "The hook register('$HOOK_REGISTER_VAR' field) must be final only.", @@ -23,7 +22,7 @@ class InvalidHookRegister extends DartLintRule { ); @override - List getFixes() => [_HookRegisterFix()]; + List getFixes() => [_RtHookRegisterFix()]; static whenIsInvalid({ required CustomLintContext context, @@ -34,9 +33,8 @@ class InvalidHookRegister extends DartLintRule { if (declaredElement == null) return; - if (!reactterHookType.isAssignableFrom(declaredElement)) return; - - final hookRegisterNodes = node.members.where(isHookRegister); + if (!rtHookType.isAssignableFrom(declaredElement)) return; + final hookRegisterNodes = node.members.where(isRegisterDeclaration); if (hookRegisterNodes.isEmpty) return; @@ -73,9 +71,7 @@ class InvalidHookRegister extends DartLintRule { } } -/// The `_HookRegisterFix` class is a Dart fix that performs a specific action related to hook -/// registration. -class _HookRegisterFix extends DartFix { +class _RtHookRegisterFix extends DartFix { @override void run( CustomLintResolver resolver, @@ -84,7 +80,7 @@ class _HookRegisterFix extends DartFix { AnalysisError analysisError, List others, ) { - InvalidHookRegister.whenIsInvalid( + RtInvalidHookRegister.whenIsInvalid( context: context, onInvalid: (node, element) { try { diff --git a/packages/reactter_lint/lib/src/rules/rt_invalid_state_creation.dart b/packages/reactter_lint/lib/src/rules/rt_invalid_state_creation.dart new file mode 100644 index 00000000..ad01bbf3 --- /dev/null +++ b/packages/reactter_lint/lib/src/rules/rt_invalid_state_creation.dart @@ -0,0 +1,135 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/error/error.dart'; +import 'package:analyzer/error/listener.dart'; +import 'package:custom_lint_builder/custom_lint_builder.dart'; +import 'package:reactter_lint/src/extensions.dart'; +import 'package:reactter_lint/src/types.dart'; + +class RtInvalidStateCreation extends DartLintRule { + const RtInvalidStateCreation() : super(code: _code); + + static const _code = LintCode( + name: "rt_invalid_state_creation", + errorSeverity: ErrorSeverity.WARNING, + problemMessage: "The `{0}` state must be create under the Reactter context.", + correctionMessage: "Use `Rt.createState` method.", + ); + + @override + List getFixes() => [_RtInvalidStateCreationFix()]; + + static whenIsInvalid({ + required LintCode code, + required CustomLintContext context, + required Function(InstanceCreationExpression node) onInvalid, + }) { + context.registry.addInstanceCreationExpression((node) { + final element = node.constructorName.staticElement; + + if (element == null || + element.isFactory || + rtHookType.isAssignableFromType(element.returnType) || + !rtStateBaseType.isAssignableFromType(element.returnType) || + iAutoRegisterStateType.isAssignableFromType(element.returnType)) { + return; + } + + final methodInvocation = node.thisOrAncestorOfType(); + final targetType = methodInvocation?.realTarget?.staticType; + final methodName = methodInvocation?.methodName.staticElement; + final isCreateState = targetType != null && + methodName != null && + rtInterface.isAssignableFromType(targetType) && + createStateType.isAssignableFrom(methodName); + + if (methodInvocation == null || !isCreateState) return onInvalid(node); + + final functionArg = methodInvocation.argumentList.arguments + .whereType() + .firstOrNull; + final returnFunctionArg = functionArg?.returnExpression; + + if (returnFunctionArg == null) return onInvalid(node); + + bool isEqualReturnExpression; + + if (returnFunctionArg is SimpleIdentifier) { + isEqualReturnExpression = returnFunctionArg.staticElement == element; + } else { + isEqualReturnExpression = + returnFunctionArg.unParenthesized == node.unParenthesized; + } + + if (!isEqualReturnExpression) return onInvalid(node); + }); + } + + bool isUsedCreateStateMethod(Set nodes, FunctionBody body) { + final returnExpression = body.returnExpression; + print( + "RETURN EXPRESSION: $returnExpression, ${returnExpression.runtimeType}"); + if (returnExpression == null) return false; + + if (body is ExpressionFunctionBody) return true; + + if (returnExpression is SimpleIdentifier) { + final returnElement = returnExpression.staticElement; + final variableDeclaration = + nodes.whereType().firstOrNull; + final variableElement = variableDeclaration?.declaredElement; + + return returnElement == variableElement; + } + + return false; + } + + @override + void run( + CustomLintResolver resolver, + ErrorReporter reporter, + CustomLintContext context, + ) { + whenIsInvalid( + code: code, + context: context, + onInvalid: (node) { + reporter.reportErrorForNode(code, node, [node.toString()]); + }, + ); + } +} + +class _RtInvalidStateCreationFix extends DartFix { + @override + void run( + CustomLintResolver resolver, + ChangeReporter reporter, + CustomLintContext context, + AnalysisError analysisError, + List others, + ) { + RtInvalidStateCreation.whenIsInvalid( + code: RtInvalidStateCreation._code, + context: context, + onInvalid: (node) { + try { + final changeBuilder = reporter.createChangeBuilder( + message: + "Convert '${node.toString()}' to use `Rt.createState` method.", + priority: 1, + ); + + changeBuilder.addDartFileEdit((builder) { + builder.addSimpleReplacement( + node.sourceRange, + "Rt.createState(() => ${node.toString()})", + ); + }); + } catch (e) { + print(e); + } + }, + ); + } +} diff --git a/packages/reactter_lint/lib/src/rules/rt_no_logic_in_create_state.dart b/packages/reactter_lint/lib/src/rules/rt_no_logic_in_create_state.dart new file mode 100644 index 00000000..3d19b1a1 --- /dev/null +++ b/packages/reactter_lint/lib/src/rules/rt_no_logic_in_create_state.dart @@ -0,0 +1,62 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/error/error.dart'; +import 'package:analyzer/error/listener.dart'; +import 'package:custom_lint_builder/custom_lint_builder.dart'; +import 'package:reactter_lint/src/extensions.dart'; +import 'package:reactter_lint/src/types.dart'; + +class RtNoLogicInCreateState extends DartLintRule { + const RtNoLogicInCreateState() : super(code: _code); + + static const _code = LintCode( + name: "rt_no_logic_in_create_state", + errorSeverity: ErrorSeverity.WARNING, + problemMessage: "Don't put logic in `createState` method.", + correctionMessage: "Try moving the logic out of `createState` method.", + ); + + static whenIsInvalid({ + required LintCode code, + required CustomLintContext context, + required Function(MethodInvocation node) onInvalid, + }) { + context.registry.addMethodInvocation((node) { + final targetType = node.realTarget?.staticType; + final methodName = node.methodName.staticElement; + + if (targetType == null || methodName == null) return; + if (!rtInterface.isAssignableFromType(targetType)) return; + if (!createStateType.isAssignableFrom(methodName)) return; + final functionArg = _getFunctionFromArgument(node); + + if (functionArg == null || functionArg.body is! BlockFunctionBody) return; + + final functionBody = functionArg.body as BlockFunctionBody; + final statements = functionBody.block.statements; + + if (statements.length > 1) return onInvalid(node); + if (statements.firstOrNull is! ReturnStatement) return onInvalid(node); + }); + } + + @override + void run( + CustomLintResolver resolver, + ErrorReporter reporter, + CustomLintContext context, + ) { + whenIsInvalid( + code: code, + context: context, + onInvalid: (node) { + reporter.reportErrorForNode(code, node, [node.toString()]); + }, + ); + } + + static FunctionExpression? _getFunctionFromArgument(MethodInvocation node) { + return node.argumentList.arguments + .whereType() + .firstOrNull; + } +} diff --git a/packages/reactter_lint/lib/src/rules/rt_state_late_convention.dart b/packages/reactter_lint/lib/src/rules/rt_state_late_convention.dart new file mode 100644 index 00000000..d7773ff9 --- /dev/null +++ b/packages/reactter_lint/lib/src/rules/rt_state_late_convention.dart @@ -0,0 +1,109 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/error/error.dart'; +import 'package:analyzer/error/listener.dart'; +import 'package:custom_lint_builder/custom_lint_builder.dart'; +import 'package:reactter_lint/src/types.dart'; + +class RtStateLateConvention extends DartLintRule { + const RtStateLateConvention() : super(code: _code); + + static const _code = LintCode( + name: "rt_state_late_convention", + errorSeverity: ErrorSeverity.WARNING, + problemMessage: "The '{0}' state late must be attached an instance.", + correctionMessage: + "Try removing 'late' keyword or wrapping the state using 'Rt.lazyState'.\n" + "Example: late final myStateLate = Rt.lazyState(() => UseState(0), this);", + ); + + @override + List getFixes() => [_RtStateLateFix()]; + + static whenIsInvalid({ + required CustomLintContext context, + required Function(FieldDeclaration node) onInvalid, + }) { + context.registry.addFieldDeclaration((node) { + final variable = node.fields.variables.first; + final element = variable.declaredElement; + + if (element == null || + !element.isLate || + !rtStateType.isAssignableFromType(element.type)) { + return; + } + + final initializer = variable.initializer?.unParenthesized; + + if (initializer is! MethodInvocation) return onInvalid(node); + + final targetType = initializer.realTarget?.staticType; + final methodName = initializer.methodName.staticElement; + final isLazyState = targetType != null && + methodName != null && + rtInterface.isAssignableFromType(targetType) && + lazyStateType.isAssignableFrom(methodName); + + if (isLazyState) return; + + onInvalid(node); + }); + } + + @override + void run( + CustomLintResolver resolver, + ErrorReporter reporter, + CustomLintContext context, + ) { + whenIsInvalid( + context: context, + onInvalid: (node) { + final variable = node.fields.variables.first; + final initializer = variable.initializer?.unParenthesized; + + if (initializer == null) return; + + reporter + .reportErrorForNode(code, initializer, [initializer.toString()]); + }, + ); + } +} + +class _RtStateLateFix extends DartFix { + @override + void run( + CustomLintResolver resolver, + ChangeReporter reporter, + CustomLintContext context, + AnalysisError analysisError, + List others, + ) { + RtStateLateConvention.whenIsInvalid( + context: context, + onInvalid: (node) { + try { + final variable = node.fields.variables.first; + final initializer = variable.initializer?.unParenthesized; + + if (initializer == null) return; + + final changeBuilder = reporter.createChangeBuilder( + message: "Wrap with 'Rt.lazyState'.", + priority: 1, + ); + + changeBuilder.addDartFileEdit((builder) { + builder.addSimpleReplacement( + initializer.sourceRange, + "Rt.lazyState(() => $initializer, this)", + ); + }); + } catch (e) { + print(e); + } + }, + ); + } +} diff --git a/packages/reactter_lint/lib/src/types.dart b/packages/reactter_lint/lib/src/types.dart index e8868407..0d7c2684 100644 --- a/packages/reactter_lint/lib/src/types.dart +++ b/packages/reactter_lint/lib/src/types.dart @@ -1,7 +1,19 @@ import 'package:custom_lint_builder/custom_lint_builder.dart'; -final reactterHookType = - TypeChecker.fromName('RtHook', packageName: 'reactter'); +final rtStateType = TypeChecker.fromName('RtState', packageName: 'reactter'); +final rtStateBaseType = + TypeChecker.fromName('RtStateBase', packageName: 'reactter'); +final iAutoRegisterStateType = + TypeChecker.fromName('IAutoRegisterState', packageName: 'reactter'); +final rtHookType = TypeChecker.fromName('RtHook', packageName: 'reactter'); +final hookRegisterType = + TypeChecker.fromName('HookRegister', packageName: 'reactter'); -final hookRegister = - TypeChecker.fromName('_HookRegister', packageName: 'reactter'); +final reactterType = TypeChecker.fromPackage('reactter'); + +final rtInterface = + TypeChecker.fromName('RtInterface', packageName: 'reactter'); +final createStateType = + TypeChecker.fromName('createState', packageName: 'reactter'); +final lazyStateType = + TypeChecker.fromName('lazyState', packageName: 'reactter'); diff --git a/packages/reactter_lint/lib/src/visitors.dart b/packages/reactter_lint/lib/src/visitors.dart new file mode 100644 index 00000000..e69de29b diff --git a/packages/reactter_lint/pubspec.lock b/packages/reactter_lint/pubspec.lock index 96cfdcb7..2fe03a08 100644 --- a/packages/reactter_lint/pubspec.lock +++ b/packages/reactter_lint/pubspec.lock @@ -29,10 +29,10 @@ packages: dependency: transitive description: name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.5.0" async: dependency: transitive description: @@ -77,10 +77,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" convert: dependency: transitive description: @@ -93,10 +93,10 @@ packages: dependency: transitive description: name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27 url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" custom_lint: dependency: transitive description: @@ -149,10 +149,10 @@ packages: dependency: transitive description: name: freezed_annotation - sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.4" glob: dependency: transitive description: @@ -173,10 +173,10 @@ packages: dependency: transitive description: name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted - version: "4.8.1" + version: "4.9.0" logging: dependency: transitive description: @@ -197,10 +197,10 @@ packages: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" package_config: dependency: transitive description: @@ -229,10 +229,10 @@ packages: dependency: transitive description: name: pubspec_parse - sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 + sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 url: "https://pub.dev" source: hosted - version: "1.2.3" + version: "1.3.0" rxdart: dependency: transitive description: @@ -285,10 +285,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" term_glyph: dependency: transitive description: @@ -301,10 +301,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.3" typed_data: dependency: transitive description: @@ -317,18 +317,18 @@ packages: dependency: transitive description: name: uuid - sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 + sha256: f33d6bb662f0e4f79dcd7ada2e6170f3b3a2530c28fc41f49a411ddedd576a77 url: "https://pub.dev" source: hosted - version: "4.3.3" + version: "4.5.0" vm_service: dependency: transitive description: name: vm_service - sha256: e7d5ecd604e499358c5fe35ee828c0298a320d54455e791e9dcf73486bc8d9f0 + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.1.0" + version: "14.2.5" watcher: dependency: transitive description: @@ -346,4 +346,4 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.3.0 <4.0.0" + dart: ">=3.4.0 <4.0.0" From 5256710027330dbc1889733ed4eda6c20ffe5f00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Tue, 1 Oct 2024 01:12:24 -0600 Subject: [PATCH 026/141] refactor(framework, hooks, interfaces, signal): Update `debugLabel` and rename `debugProperties` to `debugInfo`. --- packages/reactter/lib/src/framework/rt_state_base.dart | 6 +++--- packages/reactter/lib/src/hooks/use_async_state.dart | 4 ++-- packages/reactter/lib/src/hooks/use_compute.dart | 4 ++-- packages/reactter/lib/src/hooks/use_dependency.dart | 4 ++-- packages/reactter/lib/src/hooks/use_effect.dart | 4 ++-- packages/reactter/lib/src/hooks/use_reducer.dart | 4 ++-- packages/reactter/lib/src/hooks/use_state.dart | 4 ++-- packages/reactter/lib/src/interfaces/state.dart | 6 +++--- packages/reactter/lib/src/signal/signal_impl.dart | 4 ++-- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/reactter/lib/src/framework/rt_state_base.dart b/packages/reactter/lib/src/framework/rt_state_base.dart index 43db8b24..beea144a 100644 --- a/packages/reactter/lib/src/framework/rt_state_base.dart +++ b/packages/reactter/lib/src/framework/rt_state_base.dart @@ -38,11 +38,11 @@ abstract class RtStateBase> implements RtState { /// A label used for debugging purposes. @override - String get debugLabel => "$runtimeType[$hashCode]"; + String? get debugLabel => null; - /// A map containing properties used for debugging purposes. + /// A map containing information about state for debugging purposes. @override - Map get debugProperties => {}; + Map get debugInfo => {}; /// The reference instance to the current state. @override diff --git a/packages/reactter/lib/src/hooks/use_async_state.dart b/packages/reactter/lib/src/hooks/use_async_state.dart index 43a4065e..565b0b95 100644 --- a/packages/reactter/lib/src/hooks/use_async_state.dart +++ b/packages/reactter/lib/src/hooks/use_async_state.dart @@ -33,9 +33,9 @@ abstract class UseAsyncStateBase extends RtHook { final String? _debugLabel; @override - String get debugLabel => _debugLabel ?? super.debugLabel; + String? get debugLabel => _debugLabel ?? super.debugLabel; @override - Map get debugProperties => { + Map get debugInfo => { 'value': value, 'error': error, 'status': status, diff --git a/packages/reactter/lib/src/hooks/use_compute.dart b/packages/reactter/lib/src/hooks/use_compute.dart index a496e662..42501539 100644 --- a/packages/reactter/lib/src/hooks/use_compute.dart +++ b/packages/reactter/lib/src/hooks/use_compute.dart @@ -46,9 +46,9 @@ class UseCompute extends RtHook { final String? _debugLabel; @override - String get debugLabel => _debugLabel ?? super.debugLabel; + String? get debugLabel => _debugLabel ?? super.debugLabel; @override - Map get debugProperties => { + Map get debugInfo => { 'value': value, 'dependencies': dependencies, }; diff --git a/packages/reactter/lib/src/hooks/use_dependency.dart b/packages/reactter/lib/src/hooks/use_dependency.dart index 97a1a867..0be1a08f 100644 --- a/packages/reactter/lib/src/hooks/use_dependency.dart +++ b/packages/reactter/lib/src/hooks/use_dependency.dart @@ -110,9 +110,9 @@ class UseDependency extends RtHook { final String? _debugLabel; @override - String get debugLabel => _debugLabel ?? super.debugLabel; + String? get debugLabel => _debugLabel ?? super.debugLabel; @override - Map get debugProperties => { + Map get debugInfo => { 'instance': instance, 'id': id, }; diff --git a/packages/reactter/lib/src/hooks/use_effect.dart b/packages/reactter/lib/src/hooks/use_effect.dart index 197310cd..fa17330a 100644 --- a/packages/reactter/lib/src/hooks/use_effect.dart +++ b/packages/reactter/lib/src/hooks/use_effect.dart @@ -144,9 +144,9 @@ class UseEffect extends RtHook { final String? _debugLabel; @override - String get debugLabel => _debugLabel ?? super.debugLabel; + String? get debugLabel => _debugLabel ?? super.debugLabel; @override - Map get debugProperties => { + Map get debugInfo => { 'dependencies': dependencies, }; diff --git a/packages/reactter/lib/src/hooks/use_reducer.dart b/packages/reactter/lib/src/hooks/use_reducer.dart index 321c9b3b..563eaca3 100644 --- a/packages/reactter/lib/src/hooks/use_reducer.dart +++ b/packages/reactter/lib/src/hooks/use_reducer.dart @@ -60,9 +60,9 @@ class UseReducer extends RtHook { final String? _debugLabel; @override - String get debugLabel => _debugLabel ?? super.debugLabel; + String? get debugLabel => _debugLabel ?? super.debugLabel; @override - Map get debugProperties => { + Map get debugInfo => { 'value': value, }; diff --git a/packages/reactter/lib/src/hooks/use_state.dart b/packages/reactter/lib/src/hooks/use_state.dart index d435d107..b8dc2b18 100644 --- a/packages/reactter/lib/src/hooks/use_state.dart +++ b/packages/reactter/lib/src/hooks/use_state.dart @@ -46,9 +46,9 @@ class UseState extends RtHook { final String? _debugLabel; @override - String get debugLabel => _debugLabel ?? super.debugLabel; + String? get debugLabel => _debugLabel ?? super.debugLabel; @override - Map get debugProperties => {'value': value}; + Map get debugInfo => {'value': value}; T _value; diff --git a/packages/reactter/lib/src/interfaces/state.dart b/packages/reactter/lib/src/interfaces/state.dart index 73dfe114..751edc64 100644 --- a/packages/reactter/lib/src/interfaces/state.dart +++ b/packages/reactter/lib/src/interfaces/state.dart @@ -10,10 +10,10 @@ abstract class IState implements IContext { Object? get boundInstance; /// A label used for debugging purposes. - String get debugLabel => "$runtimeType[$hashCode]"; + String? get debugLabel; - /// A map containing properties used for debugging purposes. - Map get debugProperties; + /// A map containing information about state for debugging purposes. + Map get debugInfo; /// This method is typically used for internal /// registration purposes within the state management system. diff --git a/packages/reactter/lib/src/signal/signal_impl.dart b/packages/reactter/lib/src/signal/signal_impl.dart index ed9475cb..1d39e03f 100644 --- a/packages/reactter/lib/src/signal/signal_impl.dart +++ b/packages/reactter/lib/src/signal/signal_impl.dart @@ -99,10 +99,10 @@ class Signal final String? _debugLabel; @override - String get debugLabel => _debugLabel ?? super.debugLabel; + String? get debugLabel => _debugLabel ?? super.debugLabel; @override - Map get debugProperties => {'value': value}; + Map get debugInfo => {'value': value}; /// Returns the [value] of the signal. @override From 7720c4017c9cb52b4e0e4b9e337486e3d2620dad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Tue, 1 Oct 2024 01:20:10 -0600 Subject: [PATCH 027/141] refactor: Add constants for release, profile, debug, web modes. --- packages/reactter/lib/src/constants.dart | 75 ++++++++++++++++++++++++ packages/reactter/lib/src/internals.dart | 1 + 2 files changed, 76 insertions(+) create mode 100644 packages/reactter/lib/src/constants.dart diff --git a/packages/reactter/lib/src/constants.dart b/packages/reactter/lib/src/constants.dart new file mode 100644 index 00000000..6d12cf82 --- /dev/null +++ b/packages/reactter/lib/src/constants.dart @@ -0,0 +1,75 @@ +part of 'internals.dart'; + +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// A constant that is true if the application was compiled in release mode. +/// +/// More specifically, this is a constant that is true if the application was +/// compiled in Dart with the '-Ddart.vm.product=true' flag. +/// +/// Since this is a const value, it can be used to indicate to the compiler that +/// a particular block of code will not be executed in release mode, and hence +/// can be removed. +/// +/// Generally it is better to use [kDebugMode] or `assert` to gate code, since +/// using [kReleaseMode] will introduce differences between release and profile +/// builds, which makes performance testing less representative. +/// +/// See also: +/// +/// * [kDebugMode], which is true in debug builds. +/// * [kProfileMode], which is true in profile builds. +const bool kReleaseMode = bool.fromEnvironment('dart.vm.product'); + +/// A constant that is true if the application was compiled in profile mode. +/// +/// More specifically, this is a constant that is true if the application was +/// compiled in Dart with the '-Ddart.vm.profile=true' flag. +/// +/// Since this is a const value, it can be used to indicate to the compiler that +/// a particular block of code will not be executed in profile mode, an hence +/// can be removed. +/// +/// See also: +/// +/// * [kDebugMode], which is true in debug builds. +/// * [kReleaseMode], which is true in release builds. +const bool kProfileMode = bool.fromEnvironment('dart.vm.profile'); + +/// A constant that is true if the application was compiled in debug mode. +/// +/// More specifically, this is a constant that is true if the application was +/// not compiled with '-Ddart.vm.product=true' and '-Ddart.vm.profile=true'. +/// +/// Since this is a const value, it can be used to indicate to the compiler that +/// a particular block of code will not be executed in debug mode, and hence +/// can be removed. +/// +/// An alternative strategy is to use asserts, as in: +/// +/// ```dart +/// assert(() { +/// // ...debug-only code here... +/// return true; +/// }()); +/// ``` +/// +/// See also: +/// +/// * [kReleaseMode], which is true in release builds. +/// * [kProfileMode], which is true in profile builds. +const bool kDebugMode = !kReleaseMode && !kProfileMode; + +/// A constant that is true if the application was compiled to run on the web. +/// +/// See also: +/// +/// * [defaultTargetPlatform], which is used by themes to find out which +/// platform the application is running on (or, in the case of a web app, +/// which platform the application's browser is running in). Can be overridden +/// in tests with [debugDefaultTargetPlatformOverride]. +/// * [dart:io.Platform], a way to find out the browser's platform that is not +/// overridable in tests. +const bool kIsWeb = bool.fromEnvironment('dart.library.js_util'); diff --git a/packages/reactter/lib/src/internals.dart b/packages/reactter/lib/src/internals.dart index ed49f04c..306d8b2e 100644 --- a/packages/reactter/lib/src/internals.dart +++ b/packages/reactter/lib/src/internals.dart @@ -26,3 +26,4 @@ part 'interfaces/context.dart'; part 'interfaces/hook.dart'; part 'interfaces/observer.dart'; part 'interfaces/state.dart'; +part 'constants.dart'; From 31672d1c3a55c41c920964cf2be91b2acbc2ea16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Tue, 1 Oct 2024 01:28:49 -0600 Subject: [PATCH 028/141] refactor: Move devtools from `flutter_reactter` to `reactter`. --- .../flutter_reactter/lib/src/devtools.dart | 112 ++++++++++------ .../extension/devtools/config.yaml | 0 packages/reactter/lib/src/devtools.dart | 126 ++++++++++++++++++ 3 files changed, 198 insertions(+), 40 deletions(-) rename packages/{flutter_reactter => reactter}/extension/devtools/config.yaml (100%) create mode 100644 packages/reactter/lib/src/devtools.dart diff --git a/packages/flutter_reactter/lib/src/devtools.dart b/packages/flutter_reactter/lib/src/devtools.dart index 15f8e2e6..293955bc 100644 --- a/packages/flutter_reactter/lib/src/devtools.dart +++ b/packages/flutter_reactter/lib/src/devtools.dart @@ -1,4 +1,4 @@ -import 'dart:convert'; +import 'dart:collection'; import 'dart:developer' as dev; import 'package:flutter/foundation.dart'; @@ -16,6 +16,7 @@ extension RtExt on RtInterface { @internal class ReactterDevTools extends RtStateObserver { static ReactterDevTools? _instance; + final LinkedHashMap states = LinkedHashMap(); static void _initializeDebugging() { if (kDebugMode) { @@ -23,84 +24,115 @@ class ReactterDevTools extends RtStateObserver { } } - final Map states = {}; - - List get stateIdRefs => states.keys.toList(); - ReactterDevTools._() { - print('ReactterDevTools initialized $hashCode'); Rt.addObserver(this); - _initDevTools(); } @override void onStateCreated(covariant RtState state) { - states[state.hashCode.toString()] = state; + final stateKey = state.hashCode.toString(); + states.putIfAbsent(stateKey, () => state); dev.postEvent('ext.reactter.onStateCreated', { - 'state': _getState(state), + 'stateKey': stateKey, }); - print("states len: ${states.length}"); - print("state created: ${state.debugLabel}"); } @override void onStateBound(covariant RtState state, Object instance) { + final stateKey = state.hashCode.toString(); dev.postEvent('ext.reactter.onStateBound', { - 'state': _getState(state), - 'instance': instance.hashCode, + 'stateKey': stateKey, }); } @override void onStateUnbound(covariant RtState state, Object instance) { + final stateKey = state.hashCode.toString(); dev.postEvent('ext.reactter.onStateUnbound', { - 'state': _getState(state), - 'instance': instance.hashCode, + 'stateKey': stateKey, }); } @override void onStateUpdated(covariant RtState state) { + final stateKey = state.hashCode.toString(); dev.postEvent('ext.reactter.onStateUpdated', { - 'state': _getState(state), + 'stateKey': stateKey, }); } @override void onStateDisposed(covariant RtState state) { - states.remove(state.hashCode.toString()); + final stateKey = state.hashCode.toString(); + states.remove(stateKey); dev.postEvent('ext.reactter.onStateDisposed', { - 'state': _getState(state), + 'stateKey': stateKey, }); - print("states len: ${states.length}"); - print("state disposed: ${state.debugLabel}"); } - void _initDevTools() { - dev.registerExtension( - 'ext.reactter.getStateIdRefs', - (method, parameters) async { - return dev.ServiceExtensionResponse.result( - json.encode(stateIdRefs), - ); - }, - ); - } + // List> _getPropertiesNode(RtState state) { + // return state.debugInfo.entries.map((entry) { + // var valueEncode = entry.value; + + // valueEncode = valueEncode.toString(); + + // return { + // 'key': entry.key, + // 'value': valueEncode, + // }; + // }).toList(); + // } + + Map? getStateDetails(String stateKey) { + final state = states[stateKey]; + + if (state == null) return null; - Map _getState(RtState state) { return { - 'id': state.hashCode, 'type': state.runtimeType.toString(), 'label': state.debugLabel, - 'properties': state.debugProperties.map( - (key, value) { - var valueEncode = value; + }; + } - valueEncode = value.toString(); + Map? getDebugInfo(String stateKey) { + final state = states[stateKey]; - return MapEntry(key, valueEncode); - }, - ), - }; + return state?.debugInfo; + } + + String getPropertyValue(value) { + if (value is List) { + return getListString(value); + } else if (value is Map) { + return getMapString(value); + } else if (value is Set) { + return getSetString(value); + } else { + return value.toString(); + } + } + + String getListString(List list) { + var listString = list.toString(); + if (listString.length > 60) { + listString = '${listString.substring(0, 60)}...]'; + } + return listString; + } + + String getMapString(Map map) { + var mapString = map.toString(); + if (mapString.length > 60) { + mapString = '${mapString.substring(0, 60)}...}'; + } + return mapString; + } + + String getSetString(Set set) { + var setString = set.toString(); + if (setString.length > 60) { + setString = '${setString.substring(0, 60)}...}'; + } + return setString; } } diff --git a/packages/flutter_reactter/extension/devtools/config.yaml b/packages/reactter/extension/devtools/config.yaml similarity index 100% rename from packages/flutter_reactter/extension/devtools/config.yaml rename to packages/reactter/extension/devtools/config.yaml diff --git a/packages/reactter/lib/src/devtools.dart b/packages/reactter/lib/src/devtools.dart new file mode 100644 index 00000000..47714a57 --- /dev/null +++ b/packages/reactter/lib/src/devtools.dart @@ -0,0 +1,126 @@ +import 'dart:collection'; +import 'dart:developer' as dev; + +import 'package:meta/meta.dart'; + +import 'framework.dart'; +import 'internals.dart'; + +extension RtExt on RtInterface { + void initializeDebugging() { + if (kDebugMode) { + ReactterDevTools._initializeDebugging(); + } + } +} + +@internal +class ReactterDevTools extends RtStateObserver { + static ReactterDevTools? _instance; + final LinkedHashMap states = LinkedHashMap(); + + static void _initializeDebugging() { + if (kDebugMode) { + _instance ??= ReactterDevTools._(); + } + } + + ReactterDevTools._() { + Rt.addObserver(this); + } + + @override + void onStateCreated(covariant RtState state) { + final stateKey = state.hashCode.toString(); + states.putIfAbsent(stateKey, () => state); + dev.postEvent('ext.reactter.onStateCreated', { + 'stateKey': stateKey, + }); + } + + @override + void onStateBound(covariant RtState state, Object instance) { + final stateKey = state.hashCode.toString(); + dev.postEvent('ext.reactter.onStateBound', { + 'stateKey': stateKey, + }); + } + + @override + void onStateUnbound(covariant RtState state, Object instance) { + final stateKey = state.hashCode.toString(); + dev.postEvent('ext.reactter.onStateUnbound', { + 'stateKey': stateKey, + }); + } + + @override + void onStateUpdated(covariant RtState state) { + final stateKey = state.hashCode.toString(); + dev.postEvent('ext.reactter.onStateUpdated', { + 'stateKey': stateKey, + }); + } + + @override + void onStateDisposed(covariant RtState state) { + final stateKey = state.hashCode.toString(); + states.remove(stateKey); + dev.postEvent('ext.reactter.onStateDisposed', { + 'stateKey': stateKey, + }); + } + + Map? getStateDetails(String stateKey) { + final state = states[stateKey]; + + if (state == null) return null; + + return { + 'type': state.runtimeType.toString(), + 'label': state.debugLabel, + }; + } + + Map? getDebugInfo(String stateKey) { + final state = states[stateKey]; + + return state?.debugInfo; + } + + String getPropertyValue(value) { + if (value is List) { + return getListString(value); + } else if (value is Map) { + return getMapString(value); + } else if (value is Set) { + return getSetString(value); + } else { + return value.toString(); + } + } + + String getListString(List list) { + var listString = list.toString(); + if (listString.length > 60) { + listString = '${listString.substring(0, 60)}...]'; + } + return listString; + } + + String getMapString(Map map) { + var mapString = map.toString(); + if (mapString.length > 60) { + mapString = '${mapString.substring(0, 60)}...}'; + } + return mapString; + } + + String getSetString(Set set) { + var setString = set.toString(); + if (setString.length > 60) { + setString = '${setString.substring(0, 60)}...}'; + } + return setString; + } +} From d9a890da8971c27b7985b470f749eaee19c32f51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Wed, 23 Oct 2024 23:35:32 -0600 Subject: [PATCH 029/141] build: Remove devtools from `flutter_reactter` package. --- .../lib/flutter_reactter.dart | 1 - .../flutter_reactter/lib/src/devtools.dart | 138 ------------------ 2 files changed, 139 deletions(-) delete mode 100644 packages/flutter_reactter/lib/src/devtools.dart diff --git a/packages/flutter_reactter/lib/flutter_reactter.dart b/packages/flutter_reactter/lib/flutter_reactter.dart index d9f5c690..a8921473 100644 --- a/packages/flutter_reactter/lib/flutter_reactter.dart +++ b/packages/flutter_reactter/lib/flutter_reactter.dart @@ -5,5 +5,4 @@ export 'src/framework.dart' export 'src/extensions.dart'; export 'src/types.dart'; export 'src/widgets.dart' hide ProviderWrapper; -export 'src/devtools.dart' hide ReactterDevTools; export 'reactter.dart'; diff --git a/packages/flutter_reactter/lib/src/devtools.dart b/packages/flutter_reactter/lib/src/devtools.dart deleted file mode 100644 index 293955bc..00000000 --- a/packages/flutter_reactter/lib/src/devtools.dart +++ /dev/null @@ -1,138 +0,0 @@ -import 'dart:collection'; -import 'dart:developer' as dev; - -import 'package:flutter/foundation.dart'; -import 'package:meta/meta.dart'; -import 'package:reactter/reactter.dart'; - -extension RtExt on RtInterface { - void initializeDebugging() { - if (kDebugMode) { - ReactterDevTools._initializeDebugging(); - } - } -} - -@internal -class ReactterDevTools extends RtStateObserver { - static ReactterDevTools? _instance; - final LinkedHashMap states = LinkedHashMap(); - - static void _initializeDebugging() { - if (kDebugMode) { - _instance ??= ReactterDevTools._(); - } - } - - ReactterDevTools._() { - Rt.addObserver(this); - } - - @override - void onStateCreated(covariant RtState state) { - final stateKey = state.hashCode.toString(); - states.putIfAbsent(stateKey, () => state); - dev.postEvent('ext.reactter.onStateCreated', { - 'stateKey': stateKey, - }); - } - - @override - void onStateBound(covariant RtState state, Object instance) { - final stateKey = state.hashCode.toString(); - dev.postEvent('ext.reactter.onStateBound', { - 'stateKey': stateKey, - }); - } - - @override - void onStateUnbound(covariant RtState state, Object instance) { - final stateKey = state.hashCode.toString(); - dev.postEvent('ext.reactter.onStateUnbound', { - 'stateKey': stateKey, - }); - } - - @override - void onStateUpdated(covariant RtState state) { - final stateKey = state.hashCode.toString(); - dev.postEvent('ext.reactter.onStateUpdated', { - 'stateKey': stateKey, - }); - } - - @override - void onStateDisposed(covariant RtState state) { - final stateKey = state.hashCode.toString(); - states.remove(stateKey); - dev.postEvent('ext.reactter.onStateDisposed', { - 'stateKey': stateKey, - }); - } - - // List> _getPropertiesNode(RtState state) { - // return state.debugInfo.entries.map((entry) { - // var valueEncode = entry.value; - - // valueEncode = valueEncode.toString(); - - // return { - // 'key': entry.key, - // 'value': valueEncode, - // }; - // }).toList(); - // } - - Map? getStateDetails(String stateKey) { - final state = states[stateKey]; - - if (state == null) return null; - - return { - 'type': state.runtimeType.toString(), - 'label': state.debugLabel, - }; - } - - Map? getDebugInfo(String stateKey) { - final state = states[stateKey]; - - return state?.debugInfo; - } - - String getPropertyValue(value) { - if (value is List) { - return getListString(value); - } else if (value is Map) { - return getMapString(value); - } else if (value is Set) { - return getSetString(value); - } else { - return value.toString(); - } - } - - String getListString(List list) { - var listString = list.toString(); - if (listString.length > 60) { - listString = '${listString.substring(0, 60)}...]'; - } - return listString; - } - - String getMapString(Map map) { - var mapString = map.toString(); - if (mapString.length > 60) { - mapString = '${mapString.substring(0, 60)}...}'; - } - return mapString; - } - - String getSetString(Set set) { - var setString = set.toString(); - if (setString.length > 60) { - setString = '${setString.substring(0, 60)}...}'; - } - return setString; - } -} From 56a02ceccd3b03414cd9c0202e91e71c29f181c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Wed, 23 Oct 2024 23:49:55 -0600 Subject: [PATCH 030/141] feature(core, framework, hooks, interfaces): Implement `RtDependencyObserver` and remove unused `logger` from interfaces. --- .../reactter/lib/src/core/binding_zone.dart | 4 +- .../lib/src/core/dependency_injection.dart | 112 ++++++++++-------- .../reactter/lib/src/core/dependency_ref.dart | 10 +- .../lib/src/core/dependency_register.dart | 12 +- .../reactter/lib/src/core/event_handler.dart | 72 ++++++++--- .../reactter/lib/src/core/event_notifier.dart | 6 +- .../lib/src/core/lifecycle_observer.dart | 6 +- packages/reactter/lib/src/core/logger.dart | 13 -- .../lib/src/{constants.dart => env.dart} | 0 packages/reactter/lib/src/framework.dart | 1 - .../lib/src/framework/rt_context.dart | 4 - .../src/framework/rt_dependency_observer.dart | 66 +++++++++++ .../lib/src/framework/rt_interface.dart | 25 ++-- .../lib/src/framework/rt_state_base.dart | 19 +-- .../reactter/lib/src/hooks/use_effect.dart | 22 ++-- .../reactter/lib/src/interfaces/context.dart | 3 - .../reactter/lib/src/interfaces/state.dart | 2 - packages/reactter/lib/src/internals.dart | 5 +- packages/reactter/lib/src/types.dart | 8 -- 19 files changed, 223 insertions(+), 167 deletions(-) delete mode 100644 packages/reactter/lib/src/core/logger.dart rename packages/reactter/lib/src/{constants.dart => env.dart} (100%) create mode 100644 packages/reactter/lib/src/framework/rt_dependency_observer.dart diff --git a/packages/reactter/lib/src/core/binding_zone.dart b/packages/reactter/lib/src/core/binding_zone.dart index 61e0ac1b..3ac3fee7 100644 --- a/packages/reactter/lib/src/core/binding_zone.dart +++ b/packages/reactter/lib/src/core/binding_zone.dart @@ -62,9 +62,7 @@ class BindingZone { } for (final state in states.toList(growable: false)) { - if (state.boundInstance != null) { - state._validateInstanceBinded(); - } else if (state != instance) { + if (state != instance && state.boundInstance == null) { state.bind(instance); } } diff --git a/packages/reactter/lib/src/core/dependency_injection.dart b/packages/reactter/lib/src/core/dependency_injection.dart index c9cd5fdf..3f56c86a 100644 --- a/packages/reactter/lib/src/core/dependency_injection.dart +++ b/packages/reactter/lib/src/core/dependency_injection.dart @@ -35,9 +35,11 @@ abstract class DependencyInjection implements IContext { var dependencyRegister = _getDependencyRegister(id); if (dependencyRegister != null) { - logger.log( - 'The "$dependencyRegister" builder already registered as `${dependencyRegister.mode}`.', + _notifyDependencyFailed( + dependencyRegister, + DependencyFail.alreadyRegistered, ); + return false; } @@ -50,9 +52,6 @@ abstract class DependencyInjection implements IContext { _dependencyRegisters.add(dependencyRegister); eventHandler.emit(dependencyRegister, Lifecycle.registered); - logger.log( - 'The "$dependencyRegister" builder has been registered as `$mode`.', - ); return true; } @@ -311,7 +310,10 @@ abstract class DependencyInjection implements IContext { if (dependencyRegister?.instance == null) { final dependencyRef = DependencyRef(id); - logger.log('The "$dependencyRef" dependency already deleted.'); + _notifyDependencyFailed( + dependencyRef, + DependencyFail.alreadyDeleted, + ); return false; } @@ -330,15 +332,17 @@ abstract class DependencyInjection implements IContext { return true; case DependencyMode.factory: _removeInstance(dependencyRegister); - logger.log( - 'The "$dependencyRegister" builder has been retained ' - 'because it\'s `${DependencyMode.factory}`.', + + _notifyDependencyFailed( + dependencyRegister, + DependencyFail.builderRetainedAsFactory, ); + return true; case DependencyMode.singleton: - logger.log( - 'The "$dependencyRegister" dependency has been retained ' - 'because it\'s `${DependencyMode.singleton}`.', + _notifyDependencyFailed( + dependencyRegister, + DependencyFail.dependencyRetainedAsSingleton, ); } @@ -350,27 +354,22 @@ abstract class DependencyInjection implements IContext { /// Returns `true` when dependency has been unregistered. bool unregister([String? id]) { final dependencyRegister = _getDependencyRegister(id); - final typeLabel = - dependencyRegister?.mode.label ?? DependencyMode.builder.label; if (dependencyRegister == null) { final dependencyRef = DependencyRef(id); - logger.log('The "$dependencyRef" $typeLabel already deregistered.'); + _notifyDependencyFailed( + dependencyRef, + DependencyFail.alreadyUnregistered, + ); return false; } if (dependencyRegister._instance != null) { - final idParam = id != null ? "id: '$id, '" : ''; - - logger.log( - 'The "$T" builder couldn\'t deregister ' - 'because the "$dependencyRegister" dependency is active.\n' - 'You should delete the instance before with:\n' - '`Rt.delete<$T>(${id ?? ''});` or \n' - '`Rt.destroy<$T>($idParam, onlyInstance: true);`\n', - level: LogLevel.warning, + _notifyDependencyFailed( + dependencyRegister, + DependencyFail.cannotUnregisterActiveInstance, ); return false; @@ -380,7 +379,6 @@ abstract class DependencyInjection implements IContext { eventHandler.emit(dependencyRegister, Lifecycle.unregistered); eventHandler.offAll(dependencyRegister); - logger.log('The "$dependencyRegister" $typeLabel has been deregistered.'); return true; } @@ -400,7 +398,10 @@ abstract class DependencyInjection implements IContext { if (dependencyRegister?.instance == null && onlyInstance) { final dependencyRef = DependencyRef(id); - logger.log('The "$dependencyRef" instance already deleted.'); + _notifyDependencyFailed( + dependencyRef, + DependencyFail.alreadyDeleted, + ); return false; } @@ -483,17 +484,13 @@ abstract class DependencyInjection implements IContext { if (instanceRegister == null) { final dependencyRef = DependencyRef(id); - final idParam = id != null ? ", id: '$id'" : ''; - - logger.log( - 'The "$dependencyRef" builder is not registered.\n' - 'You should register the instance build with: \n' - '`Rt.register<$T>(() => $T()$idParam);` or \n' - '`Rt.create<$T>(() => $T()$idParam);`.', - level: LogLevel.warning, + + _notifyDependencyFailed( + dependencyRef, + DependencyFail.missingInstanceBuilder, ); - return _getDependencyRegisterByRef(dependencyRef); + return getDependencyRegisterByRef(dependencyRef); } if (ref != null) { @@ -501,7 +498,10 @@ abstract class DependencyInjection implements IContext { } if (instanceRegister.instance != null) { - logger.log('The "$instanceRegister" instance already created.'); + _notifyDependencyFailed( + instanceRegister, + DependencyFail.alreadyCreated, + ); return instanceRegister; } @@ -509,10 +509,16 @@ abstract class DependencyInjection implements IContext { BindingZone.autoBinding(() => _createInstance(instanceRegister)); // ignore: deprecated_member_use_from_same_package - eventHandler.emit(instanceRegister, Lifecycle.initialized); - eventHandler.emit(instanceRegister, Lifecycle.created); - - logger.log('The "$instanceRegister" instance has been created.'); + eventHandler.emit( + instanceRegister, + Lifecycle.initialized, + instanceRegister.instance, + ); + eventHandler.emit( + instanceRegister, + Lifecycle.created, + instanceRegister.instance, + ); return instanceRegister; } @@ -530,20 +536,18 @@ abstract class DependencyInjection implements IContext { /// Removes an instance of a generic type from a [DependencyRegister]. void _removeInstance(DependencyRegister dependencyRegister) { - final log = 'The "$dependencyRegister" instance has been deleted.'; final instance = dependencyRegister.instance; dependencyRegister._instance = null; _instances.remove(instance); // ignore: deprecated_member_use_from_same_package - eventHandler.emit(instance, Lifecycle.destroyed); - eventHandler.emit(instance, Lifecycle.deleted); + eventHandler.emit(instance, Lifecycle.destroyed, instance); + eventHandler.emit(instance, Lifecycle.deleted, instance); // ignore: deprecated_member_use_from_same_package - eventHandler.emit(dependencyRegister, Lifecycle.destroyed); - eventHandler.emit(dependencyRegister, Lifecycle.deleted); - logger.log(log); + eventHandler.emit(dependencyRegister, Lifecycle.destroyed, instance); + eventHandler.emit(dependencyRegister, Lifecycle.deleted, instance); if (instance is IState && !(instance as IState).isDisposed) { instance.dispose(); @@ -552,7 +556,7 @@ abstract class DependencyInjection implements IContext { /// Returns the [DependencyRef] associated with the given instance. /// If the instance is null or not found, returns null. - DependencyRef? _getDependencyRef(Object? instance) { + DependencyRef? getDependencyRef(Object? instance) { return _instances[instance] as DependencyRef?; } @@ -565,9 +569,19 @@ abstract class DependencyInjection implements IContext { } /// Returns the [DependencyRegister] of [T] type with a given [dependencyRef]. - DependencyRegister? _getDependencyRegisterByRef( - DependencyRef? dependencyRef, + DependencyRegister? getDependencyRegisterByRef( + DependencyRef? dependencyRef, ) { return _dependencyRegisters.lookup(dependencyRef) as DependencyRegister?; } + + void _notifyDependencyFailed( + DependencyRef dependencyRef, + DependencyFail fail, + ) { + for (final observer + in RtDependencyObserver._observers.toList(growable: false)) { + observer.onDependencyFailed(dependencyRef, fail); + } + } } diff --git a/packages/reactter/lib/src/core/dependency_ref.dart b/packages/reactter/lib/src/core/dependency_ref.dart index 8d635e2a..e6be791b 100644 --- a/packages/reactter/lib/src/core/dependency_ref.dart +++ b/packages/reactter/lib/src/core/dependency_ref.dart @@ -8,15 +8,9 @@ part of '../internals.dart'; class DependencyRef { final String? id; - const DependencyRef([this.id]); - - @override - String toString() { - final type = T.toString().replaceAll(RegExp(r'\?'), ''); - final id = this.id != null ? "[id='${this.id}']" : ""; + Type get type => T; - return '$type$id'; - } + const DependencyRef([this.id]); int _getTypeHashCode() => TT.hashCode; diff --git a/packages/reactter/lib/src/core/dependency_register.dart b/packages/reactter/lib/src/core/dependency_register.dart index 38f3c21c..515ddf79 100644 --- a/packages/reactter/lib/src/core/dependency_register.dart +++ b/packages/reactter/lib/src/core/dependency_register.dart @@ -28,16 +28,8 @@ class DependencyRegister extends DependencyRef { this.mode = DependencyMode.builder, }) : super(id); + /// Creates an instance of the dependency. T? builder() { - _instance = _builder(); - - return _instance; - } - - @override - String toString() { - final hashCode = instance != null ? "(${instance.hashCode})" : ""; - - return '${super.toString()}$hashCode'; + return _instance = _builder(); } } diff --git a/packages/reactter/lib/src/core/event_handler.dart b/packages/reactter/lib/src/core/event_handler.dart index e9d8f9d9..4167098f 100644 --- a/packages/reactter/lib/src/core/event_handler.dart +++ b/packages/reactter/lib/src/core/event_handler.dart @@ -65,11 +65,14 @@ abstract class EventHandler implements IContext { if (notifier?._dependencyRef == null) { notifier?.notifyListeners(param); notifierPartner?.notifyListeners(param); - return; + } else { + notifierPartner?.notifyListeners(param); + notifier?.notifyListeners(param); } - notifierPartner?.notifyListeners(param); - notifier?.notifyListeners(param); + if (eventName is Lifecycle) { + _notifyToObservers(instance, eventName, param); + } } /// Removes all instance's events @@ -114,8 +117,8 @@ abstract class EventHandler implements IContext { /// If the [EventNotifier] does not exist in the lookup table, it creates a new one. EventNotifier? _getEventNotifierPartner(Object? instance, Enum eventName) { final instancePartner = instance is DependencyRef - ? dependencyInjection._getDependencyRegisterByRef(instance)?.instance - : dependencyInjection._getDependencyRef(instance); + ? dependencyInjection.getDependencyRegisterByRef(instance)?.instance + : dependencyInjection.getDependencyRef(instance); if (instancePartner == null) return null; @@ -134,17 +137,58 @@ abstract class EventHandler implements IContext { void _resolveLifecycleEvent( Object? instance, Lifecycle lifecycle, [ - IState? state, + dynamic param, ]) { - if (instance is LifecycleObserver) { - return _executeLifecycleObserver(instance, lifecycle, state); - } + final instanceObj = instance is DependencyRef + ? dependencyInjection.getDependencyRegisterByRef(instance)?.instance + : instance; - if (instance is! DependencyRef) return; - - final instanceObj = - dependencyInjection._getDependencyRegisterByRef(instance)?.instance; + if (instanceObj is LifecycleObserver) { + _executeLifecycleObserver(instanceObj, lifecycle, param); + } + } - return _resolveLifecycleEvent(instanceObj, lifecycle, state); + void _notifyToObservers( + Object? instanceOrDependencyRef, + Lifecycle lifecycle, + dynamic param, + ) { + final dependencyRef = instanceOrDependencyRef is DependencyRef + ? instanceOrDependencyRef + : dependencyInjection.getDependencyRef(instanceOrDependencyRef); + + final instance = param is Object + ? param + : dependencyInjection + .getDependencyRegisterByRef(dependencyRef) + ?.instance; + + if (dependencyRef == null) return; + + for (final observer + in RtDependencyObserver._observers.toList(growable: false)) { + switch (lifecycle) { + case Lifecycle.registered: + observer.onDependencyRegistered(dependencyRef); + break; + case Lifecycle.created: + observer.onDependencyCreated(dependencyRef, instance); + break; + case Lifecycle.didMount: + observer.onDependencyMounted(dependencyRef, instance); + break; + case Lifecycle.didUnmount: + observer.onDependencyUnmounted(dependencyRef, instance); + break; + case Lifecycle.deleted: + observer.onDependencyDeleted(dependencyRef, instance); + break; + case Lifecycle.unregistered: + observer.onDependencyUnregistered(dependencyRef); + break; + default: + break; + } + } } } diff --git a/packages/reactter/lib/src/core/event_notifier.dart b/packages/reactter/lib/src/core/event_notifier.dart index c335f73b..d3e385a5 100644 --- a/packages/reactter/lib/src/core/event_notifier.dart +++ b/packages/reactter/lib/src/core/event_notifier.dart @@ -60,11 +60,11 @@ class EventNotifier extends EventNotifierRef with Notifier { final void Function(EventNotifier notifier) onNotifyComplete; DependencyRef? get instanceRef => - _dependencyRef ?? dependencyInjection._getDependencyRef(_instanceObj); + _dependencyRef ?? dependencyInjection.getDependencyRef(_instanceObj); Object? get instanceObj => _instanceObj ?? - dependencyInjection._getDependencyRegisterByRef(_dependencyRef)?.instance; + dependencyInjection.getDependencyRegisterByRef(_dependencyRef)?.instance; EventNotifier( Object? instanceOrObj, @@ -93,7 +93,7 @@ class EventNotifier extends EventNotifierRef with Notifier { final instanceRefSelf = instanceRef; if (instanceRefSelf != null) { - return instanceRefSelf == dependencyInjection._getDependencyRef(other); + return instanceRefSelf == dependencyInjection.getDependencyRef(other); } return instanceObj == other; diff --git a/packages/reactter/lib/src/core/lifecycle_observer.dart b/packages/reactter/lib/src/core/lifecycle_observer.dart index dd2d9bb4..5687862f 100644 --- a/packages/reactter/lib/src/core/lifecycle_observer.dart +++ b/packages/reactter/lib/src/core/lifecycle_observer.dart @@ -66,7 +66,7 @@ abstract class LifecycleObserver { void _executeLifecycleObserver( LifecycleObserver observer, Lifecycle lifecycle, [ - IState? state, + dynamic param, ]) { switch (lifecycle) { // ignore: deprecated_member_use_from_same_package @@ -84,10 +84,10 @@ void _executeLifecycleObserver( observer.onDidMount(); break; case Lifecycle.willUpdate: - observer.onWillUpdate(state); + observer.onWillUpdate(param); break; case Lifecycle.didUpdate: - observer.onDidUpdate(state); + observer.onDidUpdate(param); break; case Lifecycle.willUnmount: observer.onWillUnmount(); diff --git a/packages/reactter/lib/src/core/logger.dart b/packages/reactter/lib/src/core/logger.dart deleted file mode 100644 index 35c4988c..00000000 --- a/packages/reactter/lib/src/core/logger.dart +++ /dev/null @@ -1,13 +0,0 @@ -part of '../internals.dart'; - -enum LogLevel { info, warning, error } - -@internal -abstract class Logger { - /// It's used to determine whether logging is enabled or disabled. - bool isLogEnable = true; - - /// It's used as a callback function for logging purposes in - /// the [RtInterface] class. - LogWriterCallback get log; -} diff --git a/packages/reactter/lib/src/constants.dart b/packages/reactter/lib/src/env.dart similarity index 100% rename from packages/reactter/lib/src/constants.dart rename to packages/reactter/lib/src/env.dart diff --git a/packages/reactter/lib/src/framework.dart b/packages/reactter/lib/src/framework.dart index c1245466..7f7d18f2 100644 --- a/packages/reactter/lib/src/framework.dart +++ b/packages/reactter/lib/src/framework.dart @@ -6,7 +6,6 @@ export 'internals.dart' Lifecycle, LifecycleObserver, DependencyMode, - LogLevel, RtInterface, RtStateObserver, RtHook, diff --git a/packages/reactter/lib/src/framework/rt_context.dart b/packages/reactter/lib/src/framework/rt_context.dart index 04b44f4f..3cc354bd 100644 --- a/packages/reactter/lib/src/framework/rt_context.dart +++ b/packages/reactter/lib/src/framework/rt_context.dart @@ -20,10 +20,6 @@ mixin RtContext implements IContext { @override @internal EventHandler get eventHandler => Rt; - - @override - @internal - Logger get logger => Rt; } /// {@macro reactter.rt} diff --git a/packages/reactter/lib/src/framework/rt_dependency_observer.dart b/packages/reactter/lib/src/framework/rt_dependency_observer.dart new file mode 100644 index 00000000..33ab450a --- /dev/null +++ b/packages/reactter/lib/src/framework/rt_dependency_observer.dart @@ -0,0 +1,66 @@ +part of '../internals.dart'; + +enum DependencyFail { + alreadyRegistered, + alreadyCreated, + alreadyDeleted, + alreadyUnregistered, + missingInstanceBuilder, + builderRetainedAsFactory, + dependencyRetainedAsSingleton, + cannotUnregisterActiveInstance, +} + +/// {@template reactter.rt_dependency_observer} +/// An abstract class that defines the interface for observing dependency changes. +/// Implementations of this class can be used to monitor the lifecycle of dependencies. +/// {@endtemplate} +abstract class RtDependencyObserver implements IObserver { + /// A set of all registered dependency observers. + static final _observers = {}; + + /// Called when a dependency is registered. + /// [dependency] - The dependency that was registered. + void onDependencyRegistered(covariant DependencyRef dependency); + + /// Called when a dependency is created. + /// [dependency] - The dependency that was created. + /// [instance] - The instance of the dependency. + void onDependencyCreated( + covariant DependencyRef dependency, + Object? instance, + ); + + /// Called when a dependency is mounted. + /// [dependency] - The dependency that was mounted. + /// [instance] - The instance of the dependency. + void onDependencyMounted( + covariant DependencyRef dependency, + Object? instance, + ); + + /// Called when a dependency is unmounted. + /// [dependency] - The dependency that was unmounted. + /// [instance] - The instance of the dependency. + void onDependencyUnmounted( + covariant DependencyRef dependency, + Object? instance, + ); + + /// Called when a dependency is deleted. + /// [dependency] - The dependency that was deleted. + /// [instance] - The instance of the dependency. + void onDependencyDeleted( + covariant DependencyRef dependency, + Object? instance, + ); + + /// Called when a dependency is unregistered. + /// [dependency] - The dependency that was unregistered. + void onDependencyUnregistered(covariant DependencyRef dependency); + + void onDependencyFailed( + covariant DependencyRef dependency, + DependencyFail fail, + ); +} diff --git a/packages/reactter/lib/src/framework/rt_interface.dart b/packages/reactter/lib/src/framework/rt_interface.dart index 9f941adb..752babfb 100644 --- a/packages/reactter/lib/src/framework/rt_interface.dart +++ b/packages/reactter/lib/src/framework/rt_interface.dart @@ -1,15 +1,5 @@ part of '../internals.dart'; -void defaultLogWriterCallback( - String value, { - Object? error, - LogLevel? level = LogLevel.info, -}) { - if (Rt.isLogEnable || error != null) { - dev.log(value, name: 'REACTTER', error: error); - } -} - ///{@template reactter.rt_interface} /// A class that represents the interface for Rt. /// @@ -20,7 +10,6 @@ class RtInterface StateManagement, DependencyInjection, EventHandler, - Logger, ObserverManager { @override @internal @@ -31,18 +20,16 @@ class RtInterface @override @internal EventHandler get eventHandler => this; - @override - @internal - Logger get logger => this; - - @override - LogWriterCallback get log => defaultLogWriterCallback; @override void addObserver(covariant IObserver observer) { if (observer is RtStateObserver) { RtStateObserver._observers.add(observer); } + + if (observer is RtDependencyObserver) { + RtDependencyObserver._observers.add(observer); + } } @override @@ -50,6 +37,10 @@ class RtInterface if (observer is RtStateObserver) { RtStateObserver._observers.remove(observer); } + + if (observer is RtDependencyObserver) { + RtDependencyObserver._observers.remove(observer); + } } } diff --git a/packages/reactter/lib/src/framework/rt_state_base.dart b/packages/reactter/lib/src/framework/rt_state_base.dart index beea144a..6749bfa7 100644 --- a/packages/reactter/lib/src/framework/rt_state_base.dart +++ b/packages/reactter/lib/src/framework/rt_state_base.dart @@ -83,8 +83,6 @@ abstract class RtStateBase> implements RtState { eventHandler.one(instance, Lifecycle.deleted, _onInstanceDeleted); _boundInstance = instance; - if (BindingZone.currentZone == null) _validateInstanceBinded(); - _notifyBound(instance); } @@ -147,8 +145,7 @@ abstract class RtStateBase> implements RtState { assert(!_isDisposed, "Can't dispose when it's been disposed"); if (_boundInstance != null) { - eventHandler.off(_boundInstance!, Lifecycle.deleted, _onInstanceDeleted); - _boundInstance = null; + unbind(); } eventHandler.emit(this, Lifecycle.deleted); @@ -160,20 +157,6 @@ abstract class RtStateBase> implements RtState { _isDisposed = true; } - @override - void _validateInstanceBinded() { - if (dependencyInjection.isActive(boundInstance)) return; - - logger.log( - "The instance binded($boundInstance) to $this is not in Reactter's context and cannot be disposed automatically.\n" - "You can solve this problem in two ways:\n" - "1. Call the 'dispose' method manually when $this is no longer needed.\n" - "2. Create $boundInstance using the dependency injection methods.\n" - "**Ignore this message if you are sure that it will be disposed.**", - level: LogLevel.warning, - ); - } - /// When the instance is destroyed, this object is dispose. void _onInstanceDeleted(_, __) => dispose(); diff --git a/packages/reactter/lib/src/hooks/use_effect.dart b/packages/reactter/lib/src/hooks/use_effect.dart index fa17330a..6e9e0423 100644 --- a/packages/reactter/lib/src/hooks/use_effect.dart +++ b/packages/reactter/lib/src/hooks/use_effect.dart @@ -279,10 +279,13 @@ class UseEffect extends RtHook { _cleanupCallback = cleanupCallback; } } catch (error) { - logger.log( - 'An error occurred while executing the effect', - level: LogLevel.error, - ); + assert(() { + throw AssertionError( + 'An error occurred while executing the effect in $this${debugLabel != null ? '($debugLabel)' : ''}.\n' + 'The error thrown was:\n' + ' $error\n', + ); + }()); rethrow; } finally { @@ -294,10 +297,13 @@ class UseEffect extends RtHook { try { _cleanupCallback?.call(); } catch (error) { - logger.log( - 'An error occurred while executing the cleanup effect', - level: LogLevel.error, - ); + assert(() { + throw AssertionError( + 'An error occurred while executing the cleanup effect in $this${debugLabel != null ? '($debugLabel)' : ''}.\n' + 'The error thrown was:\n' + ' $error\n', + ); + }()); rethrow; } finally { diff --git a/packages/reactter/lib/src/interfaces/context.dart b/packages/reactter/lib/src/interfaces/context.dart index 8d10d986..b1d5b78f 100644 --- a/packages/reactter/lib/src/interfaces/context.dart +++ b/packages/reactter/lib/src/interfaces/context.dart @@ -17,7 +17,4 @@ abstract class IContext { @internal EventHandler get eventHandler; - - @internal - Logger get logger; } diff --git a/packages/reactter/lib/src/interfaces/state.dart b/packages/reactter/lib/src/interfaces/state.dart index 751edc64..09946f73 100644 --- a/packages/reactter/lib/src/interfaces/state.dart +++ b/packages/reactter/lib/src/interfaces/state.dart @@ -55,6 +55,4 @@ abstract class IState implements IContext { /// Called when this object is removed @mustCallSuper void dispose(); - - void _validateInstanceBinded(); } diff --git a/packages/reactter/lib/src/internals.dart b/packages/reactter/lib/src/internals.dart index 306d8b2e..2516a15a 100644 --- a/packages/reactter/lib/src/internals.dart +++ b/packages/reactter/lib/src/internals.dart @@ -1,4 +1,3 @@ -import 'dart:developer' as dev; import 'dart:async'; import 'dart:collection'; import 'package:meta/meta.dart'; @@ -13,10 +12,10 @@ part 'core/event_handler.dart'; part 'core/event_notifier.dart'; part 'core/lifecycle_observer.dart'; part 'core/lifecycle.dart'; -part 'core/logger.dart'; part 'core/notifier.dart'; part 'core/observer_manager.dart'; part 'core/state_management.dart'; +part 'framework/rt_dependency_observer.dart'; part 'framework/rt_hook.dart'; part 'framework/rt_interface.dart'; part 'framework/rt_state.dart'; @@ -26,4 +25,4 @@ part 'interfaces/context.dart'; part 'interfaces/hook.dart'; part 'interfaces/observer.dart'; part 'interfaces/state.dart'; -part 'constants.dart'; +part 'env.dart'; diff --git a/packages/reactter/lib/src/types.dart b/packages/reactter/lib/src/types.dart index c80bc506..8e35f15c 100644 --- a/packages/reactter/lib/src/types.dart +++ b/packages/reactter/lib/src/types.dart @@ -1,20 +1,12 @@ import 'dart:async'; import 'args.dart'; -import 'internals.dart' show LogLevel; import 'hooks/hooks.dart'; import 'memo/memo.dart' show Memo; /// A function to generate the instance of [T] dependency. typedef InstanceBuilder = T Function(); -/// Rt.log type -typedef LogWriterCallback = void Function( - String text, { - Object error, - LogLevel level, -}); - /// UseAsyncState.when's parameter type for representing a value typedef WhenValueReturn = R Function(T value); From f0bb881a3a0f2c0681f5461b7aeb1e62e607aabd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Wed, 23 Oct 2024 23:54:24 -0600 Subject: [PATCH 031/141] feature: Implement a new logger and log state and dependency events. --- packages/reactter/lib/reactter.dart | 3 +- packages/reactter/lib/src/logger.dart | 336 ++++++++++++++++++++++++++ packages/reactter/lib/src/types.dart | 15 ++ 3 files changed, 353 insertions(+), 1 deletion(-) create mode 100644 packages/reactter/lib/src/logger.dart diff --git a/packages/reactter/lib/reactter.dart b/packages/reactter/lib/reactter.dart index e754dd59..69e3d895 100644 --- a/packages/reactter/lib/reactter.dart +++ b/packages/reactter/lib/reactter.dart @@ -1,9 +1,10 @@ library reactter; +export 'src/args.dart'; export 'src/framework.dart'; export 'src/hooks/hooks.dart' hide UseAsyncStateBase; export 'src/memo/memo.dart'; export 'src/obj/obj.dart'; export 'src/signal/signal.dart'; -export 'src/args.dart'; +export 'src/logger.dart' hide RtLogger; export 'src/types.dart'; diff --git a/packages/reactter/lib/src/logger.dart b/packages/reactter/lib/src/logger.dart new file mode 100644 index 00000000..cc775f17 --- /dev/null +++ b/packages/reactter/lib/src/logger.dart @@ -0,0 +1,336 @@ +import 'dart:developer' as dev; + +import 'package:meta/meta.dart'; + +import 'framework.dart'; +import 'internals.dart'; +import 'types.dart'; + +extension RtLoggerExt on RtInterface { + /// Initializes the logger. + /// + /// This function sets up the logger for the application. You can provide + /// a custom name for the logger and specify the output destination for + /// the log messages. + /// + /// If no name is provided, the default name 'REACTTER' will be used. + /// + /// If no output is provided, the default output will be used. + /// + /// Parameters: + /// - [name]: An optional name for the logger. Defaults to 'REACTTER'. + /// - [output]: An optional [LogOutput] function to specify the log output destination. + /// The default output is the `dart:developer`'s `log` function. + void initializeLogger({ + String name = 'REACTTER', + LogOutput output = dev.log, + }) { + RtLogger.initialize(name: name, output: output); + } +} + +@internal +class RtLogger with RtStateObserver, RtDependencyObserver { + static RtLogger? _instance; + + static void initialize({ + String name = 'REACTTER', + LogOutput output = dev.log, + }) { + assert(_instance == null, 'The logger has already been initialized.'); + + if (kDebugMode) { + _instance ??= RtLogger._(name: name, output: output); + } + } + + final LogOutput output; + final String name; + + RtLogger._({ + required this.name, + required this.output, + }) { + Rt.addObserver(this); + } + + void log(String message, {int level = 0, StackTrace? stackTrace}) { + output.call( + message, + name: name, + level: level, + stackTrace: stackTrace ?? StackTrace.current, + ); + } + + String _prettyFormat(Object? instance) { + if (instance is DependencyRef) { + final type = instance.type.toString().replaceAll('?', ''); + final id = instance.id; + final idStr = id != null ? "id: '$id'" : null; + final mode = instance is DependencyRegister + ? instance.mode.label + : Rt.getDependencyRegisterByRef(instance)?.mode.label; + final modeStr = mode != null ? "mode: '$mode'" : null; + final params = [ + if (idStr != null) idStr, + if (modeStr != null) modeStr, + ].join(', '); + final paramsStr = params.isNotEmpty ? '($params)' : ''; + + return '[DEPENDENCY | $type$paramsStr]'; + } + + if (instance is RtState) { + final type = instance.runtimeType.toString(); + final label = instance.debugLabel; + final labelStr = label != null ? "(label: '$label')" : ''; + + if (instance is RtHook) { + return '[HOOK | $type$labelStr | #${instance.hashCode}]'; + } + + return '[STATE | $type$labelStr | #${instance.hashCode}]'; + } + + return '[UNKNOWN | ${instance.runtimeType} | #${instance.hashCode}]'; + } + + @override + void onStateCreated(RtState state) { + log( + '${_prettyFormat(state)} created.', + level: LogLevel.finer, + stackTrace: StackTrace.current, + ); + } + + @override + void onStateBound(RtState state, Object instance) { + log( + '${_prettyFormat(state)} bound to ${_prettyFormat(instance)}.', + level: LogLevel.finer, + stackTrace: StackTrace.current, + ); + + if (instance is! RtState && !Rt.isActive(instance)) { + final T = instance.runtimeType; + + log( + 'The bound instance(${_prettyFormat(instance)}) to state(${_prettyFormat(state)}) is not in Reactter\'s context and cannot be disposed automatically.\n' + 'You can solve this problem in one of the following ways:\n' + '\t- Call `dispose` method manually when state is no longer needed:\n' + '\t\t`state.dispose();`\n' + '\t- Create bound instance using the dependency injection methods:\n' + '\t\t`Rt.register<$T>(() => $T(...));`\n' + '\t\t`Rt.create<$T>(() => $T(...));`\n' + '**Ignore this message if you are sure that it will be disposed.**', + level: LogLevel.warning, + stackTrace: StackTrace.current, + ); + } + } + + @override + void onStateUnbound(RtState state, Object instance) { + log( + '${_prettyFormat(state)} unbound from ${_prettyFormat(instance)}.', + level: LogLevel.finer, + stackTrace: StackTrace.current, + ); + } + + @override + void onStateUpdated(RtState state) { + log( + '${_prettyFormat(state)} updated.', + level: LogLevel.finer, + stackTrace: StackTrace.current, + ); + } + + @override + void onStateDisposed(RtState state) { + log( + '${_prettyFormat(state)} disposed.', + level: LogLevel.finer, + stackTrace: StackTrace.current, + ); + } + + @override + void onDependencyRegistered(DependencyRef dependency) { + log( + '${_prettyFormat(dependency)} registered.', + level: LogLevel.fine, + stackTrace: StackTrace.current, + ); + } + + @override + void onDependencyCreated(DependencyRef dependency, Object? instance) { + log( + '${_prettyFormat(dependency)} created. Its instance: ${_prettyFormat(instance)}.', + level: LogLevel.fine, + stackTrace: StackTrace.current, + ); + } + + @override + void onDependencyMounted(DependencyRef dependency, Object? instance) { + log( + '${_prettyFormat(dependency)} mounted. Its instance: ${_prettyFormat(instance)}.', + level: LogLevel.fine, + stackTrace: StackTrace.current, + ); + } + + @override + void onDependencyUnmounted(DependencyRef dependency, Object? instance) { + log( + '${_prettyFormat(dependency)} unmounted. Its instance: ${_prettyFormat(instance)}.', + level: LogLevel.fine, + stackTrace: StackTrace.current, + ); + } + + @override + void onDependencyDeleted(DependencyRef dependency, Object? instance) { + log( + '${_prettyFormat(dependency)} deleted. Its instance: ${_prettyFormat(instance)}.', + level: LogLevel.fine, + stackTrace: StackTrace.current, + ); + } + + @override + void onDependencyUnregistered(DependencyRef dependency) { + log( + '${_prettyFormat(dependency)} unregistered.', + level: LogLevel.fine, + stackTrace: StackTrace.current, + ); + } + + @override + void onDependencyFailed( + covariant DependencyRef dependency, + DependencyFail fail, + ) { + final T = dependency.type; + final id = dependency.id; + final idParam = id != null ? "id: '$id, '" : ''; + + switch (fail) { + case DependencyFail.alreadyRegistered: + log( + '${_prettyFormat(dependency)} already registered.', + level: LogLevel.info, + stackTrace: StackTrace.current, + ); + break; + case DependencyFail.alreadyCreated: + log( + '${_prettyFormat(dependency)} already created.', + level: LogLevel.info, + stackTrace: StackTrace.current, + ); + break; + case DependencyFail.alreadyDeleted: + log( + '${_prettyFormat(dependency)} already deleted.', + level: LogLevel.info, + stackTrace: StackTrace.current, + ); + break; + case DependencyFail.alreadyUnregistered: + log( + '${_prettyFormat(dependency)} already unregistered.', + level: LogLevel.info, + stackTrace: StackTrace.current, + ); + break; + case DependencyFail.builderRetainedAsFactory: + log( + '${_prettyFormat(dependency)}\'s instance retained because it\'s factory mode.', + level: LogLevel.info, + stackTrace: StackTrace.current, + ); + break; + case DependencyFail.dependencyRetainedAsSingleton: + log( + '${_prettyFormat(dependency)} retained because it\'s singleton mode.', + level: LogLevel.info, + stackTrace: StackTrace.current, + ); + break; + case DependencyFail.missingInstanceBuilder: + log( + '${_prettyFormat(dependency)} couldn\'t register.\n' + 'You should register the instance build with: \n' + '\t`Rt.register<$T>(() => $T(...)$idParam);` \n' + '\t`Rt.create<$T>(() => $T(...)$idParam);`', + level: LogLevel.warning, + stackTrace: StackTrace.current, + ); + break; + case DependencyFail.cannotUnregisterActiveInstance: + final instance = dependency is DependencyRegister + ? dependency.instance + : Rt.getDependencyRegisterByRef(dependency)?.instance; + + log( + '${_prettyFormat(dependency)} couldn\'t unregister ' + 'because ${_prettyFormat(instance)} is active.\n' + 'You should delete the instance before with:\n' + '\t`Rt.delete<$T>(${id ?? ''});`\n' + '\t`Rt.destroy<$T>($idParam, onlyInstance: true);`\n', + level: LogLevel.severe, + stackTrace: StackTrace.current, + ); + break; + } + } +} + +/// Copy from `package:logging`. +/// [Level]s to control logging output. Logging can be enabled to include all +/// levels above certain [Level]. The predefined [Level] constants below are sorted as +/// follows (in descending order): [Level.shout], [Level.severe], +/// [Level.warning], [Level.info], [Level.config], [Level.fine], [Level.finer], +/// [Level.finest], and [Level.all]. +/// +/// We recommend using one of the predefined logging levels. If you define your +/// own level, make sure you use a value between those used in [Level.all] and +/// [Level.off]. +class LogLevel { + /// Special key to turn on logging for all levels (0). + static const int all = 0; + + /// Key for highly detailed tracing (300). + static const int finest = 300; + + /// Key for fairly detailed tracing (400). + static const int finer = 400; + + /// Key for tracing information (500). + static const int fine = 500; + + /// Key for static configuration messages (700). + static const int config = 700; + + /// Key for informational messages (800). + static const int info = 800; + + /// Key for potential problems (900). + static const int warning = 900; + + /// Key for serious failures (1000). + static const int severe = 1000; + + /// Key for extra debugging loudness (1200). + static const int shout = 1200; + + /// Special key to turn off all logging (2000). + static const int off = 2000; +} diff --git a/packages/reactter/lib/src/types.dart b/packages/reactter/lib/src/types.dart index 8e35f15c..8366b237 100644 --- a/packages/reactter/lib/src/types.dart +++ b/packages/reactter/lib/src/types.dart @@ -4,6 +4,21 @@ import 'args.dart'; import 'hooks/hooks.dart'; import 'memo/memo.dart' show Memo; +/// {@template log_output} +/// An function to specify the log output destination. +/// +/// - [message]: The log message. +/// - [name]: The name of the logger. +/// - [level]: The integer value of the [LogLevel]. +/// - [stackTrace]: The stack trace of the log message. +/// {@endtemplate} +typedef LogOutput = void Function( + String message, { + String name, + int level, + StackTrace? stackTrace, +}); + /// A function to generate the instance of [T] dependency. typedef InstanceBuilder = T Function(); From 2a243184e2145fa69cd4cfebe556a254848a5713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Thu, 24 Oct 2024 00:23:10 -0600 Subject: [PATCH 032/141] docs: Add API title to core concepts documentation and add a dependency injection example. --- .../core_concepts/dependency_injection.mdx | 25 +++++++++++++++++-- .../docs/core_concepts/event_handler.mdx | 2 ++ .../src/content/docs/core_concepts/hooks.mdx | 2 ++ .../docs/core_concepts/rendering_control.mdx | 5 +++- .../docs/core_concepts/state_management.mdx | 4 ++- website/src/content/docs/getting_started.mdx | 1 - 6 files changed, 34 insertions(+), 5 deletions(-) diff --git a/website/src/content/docs/core_concepts/dependency_injection.mdx b/website/src/content/docs/core_concepts/dependency_injection.mdx index ee0093fe..ba30f43a 100644 --- a/website/src/content/docs/core_concepts/dependency_injection.mdx +++ b/website/src/content/docs/core_concepts/dependency_injection.mdx @@ -17,6 +17,8 @@ Dependency injection offers several benefits. - **Inversion of Control**: It adheres to the principle of inversion of control, where the responsibility for object creation and management is delegated to Reactter. This results in improved code _modularity_, _reusability_, and _testability_. - **Simplified Code**: By offloading the responsibility of creating dependencies from individual classes, dependency injection simplifies code, allowing classes to focus more on their core functionality. +## API + Reactter provides the following dependencies injection mechanisms: - Hooks @@ -243,11 +245,30 @@ void main() async { print('Count: ${counter?.count ?? 'Counter instance not found'}'); } ``` -In this case, the countdown will work as before, but when trying to get the `Counter` instance to print itsvalue, +In this case, the countdown will work as before, but when trying to get the `Counter` instance to print its value, the ouput will be _“Counter instance not found”_. This occurs because `Counter` was registered as `DependencyMode.builder`(the default mode), so when it was deleted at the end of the countdown its registration was also deleted. -If we want to get the `Counter` instance to print its value, we need to register using the `DependencyMode.singleton` mode. +If we want to get the `Counter` instance to print its value, we need to register using the `DependencyMode.singleton` mode, looking like this: + +```dart title="main.dart" {7} collapse={8-15} "Rt.register" "Rt.create" "Rt.get" "DependencyMode.singleton" /counter(?!\\.)/ "counter?.count" /Counter(?!\u0060)(?! )/ /Countdown(?!\u0060)/ /(countdown) =/ "countdown.run" +import 'package:reactter/reactter.dart'; +import 'countdown.dart'; +import 'counter.dart'; + +void main() async { + // Register the `Counter` class as singleton mode with an initial value of 20 + Rt.register(() => Counter(20), mode: DependencyMode.singleton); + // Create an instance of the `Countdown` class + final countdown = Rt.create(() => Countdown())!; + // Start the countdown + await countdown.run(); + // Get the instance of the `Counter` class + final counter = Rt.get(); + // Try to print the current count value + print('Count: ${counter?.count ?? 'Counter instance not found'}'); +} +``` Let's now delve into the **modes** of dependency registration. diff --git a/website/src/content/docs/core_concepts/event_handler.mdx b/website/src/content/docs/core_concepts/event_handler.mdx index c1bbadba..7dab9d74 100644 --- a/website/src/content/docs/core_concepts/event_handler.mdx +++ b/website/src/content/docs/core_concepts/event_handler.mdx @@ -11,6 +11,8 @@ and coordination between various components within the application. Designed to ensure efficient state management and dependency injection, fostering a cohesive ecosystem where different parts of the application can interact harmoniously. +## API + Reactter offers the following event handler mechanisms: - Hooks diff --git a/website/src/content/docs/core_concepts/hooks.mdx b/website/src/content/docs/core_concepts/hooks.mdx index aa977edc..7820c3ed 100644 --- a/website/src/content/docs/core_concepts/hooks.mdx +++ b/website/src/content/docs/core_concepts/hooks.mdx @@ -20,6 +20,8 @@ import UseTextInputCode from '@/examples/custom_hook/lib/use_text_input.dart?raw Hooks are classes with the ability to use states and manage side effects. They are a fundamental concept in Reactter and are used to encapsulate logic that can be reused across the application. +## API + Reactter provider some hooks to manage the state and side effects of the application, which are: - [`UseState`](/reactter/hooks/use_state) diff --git a/website/src/content/docs/core_concepts/rendering_control.mdx b/website/src/content/docs/core_concepts/rendering_control.mdx index 6f7d1fe3..9820dd62 100644 --- a/website/src/content/docs/core_concepts/rendering_control.mdx +++ b/website/src/content/docs/core_concepts/rendering_control.mdx @@ -20,6 +20,9 @@ import counterMainCode from '@/examples/counter/lib/main.dart?raw'; In Flutter, efficient rendering control is essential for crafting high-performance, responsive, and scalable applications. Reactter provides a way to easily control the rendering of components in the widget tree behavior effortlessly, using the _**flutter_reactter**_ package. + +## API + This package provides a collection of classes, widgets and some methods: - Classes @@ -45,7 +48,7 @@ The rendering control in Reactter is based on two core concepts of Flutter: - **InheritedWidget**: This powerful mechanism efficiently shares data across the widget tree. Reactter extends this capability with the `RtProvider` widget, which stores dependencies using the dependency injection system. This allows descendant widgets to access these dependencies as needed. -- **BuildContext Methods**: These methods facilitate dependency access and rendering control within the widget tree. +- **BuildContext Extensions**: These methods facilitate dependency access and rendering control within the widget tree. Reactter widgets like `RtConsumer`, `RtSelector`, and `RtComponent` use these methods to observe dependencies or states. Whenever the dependency or any observed state undergoes a change, these widgets promptly trigger the rebuilding of the widget tree to reflect the updated state. diff --git a/website/src/content/docs/core_concepts/state_management.mdx b/website/src/content/docs/core_concepts/state_management.mdx index e305fb44..ff04b9c4 100644 --- a/website/src/content/docs/core_concepts/state_management.mdx +++ b/website/src/content/docs/core_concepts/state_management.mdx @@ -10,6 +10,8 @@ import StateMethods from '@/content/docs/shareds/state_methods.mdx'; State management is a critical aspect of any application. It allows you to manage the state of your application, and facilitates seamless tracking and handling of changes to it. +## API + Reactter provides a variety of mechanisms for state management, including classes, hooks, and methods: - Classes @@ -33,7 +35,7 @@ Reactter provides a variety of mechanisms for state management, including classe Reactter's state management system is based on the concept of _reactivity_. Contrary to the prevailing notion that implementing reactive programming in Dart can be challenging, Reactter greatly simplifies this process. -To dive into the concept, let's start by exploring what constitutes a state in Rt. +To dive into the concept, let's start by exploring what constitutes a state in Reactter. ### State diff --git a/website/src/content/docs/getting_started.mdx b/website/src/content/docs/getting_started.mdx index 4e6a3942..4cbdf33d 100644 --- a/website/src/content/docs/getting_started.mdx +++ b/website/src/content/docs/getting_started.mdx @@ -3,7 +3,6 @@ title: Getting started description: A step-by-step guide to installing and how to use Reactter. --- -import { Icon } from "astro-icon/components"; import { Code } from "@astrojs/starlight/components"; import reactterVersion from "@/data/reactter_version"; From 477bdd50f4132fc95db9aa8d9f00c541fbbcf544 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 25 Oct 2024 00:33:21 -0600 Subject: [PATCH 033/141] refactor: Remove unused imports and update export statement in reactter.dart --- packages/flutter_reactter/lib/reactter.dart | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/flutter_reactter/lib/reactter.dart b/packages/flutter_reactter/lib/reactter.dart index 90f847af..27832dcc 100644 --- a/packages/flutter_reactter/lib/reactter.dart +++ b/packages/flutter_reactter/lib/reactter.dart @@ -1,6 +1 @@ -export 'package:reactter/reactter.dart' hide Rt; -import 'package:flutter/foundation.dart'; -import 'package:reactter/reactter.dart' as r show Rt; - -// ignore: non_constant_identifier_names -final Rt = r.Rt..isLogEnable = kDebugMode; +export 'package:reactter/reactter.dart'; From d2f3357109126aada5135d333def4a13d6718da4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Sun, 27 Oct 2024 00:41:50 -0600 Subject: [PATCH 034/141] build(website): Add Footer component and update styles --- website/astro.config.mjs | 1 + website/src/components/Footer.astro | 28 ++++++++++++++++++++++++++ website/src/components/SiteTitle.astro | 2 +- website/src/content/docs/index.mdx | 27 +++++++++++++------------ website/src/styles/custom.css | 17 +++++++++------- website/tailwind.config.mjs | 2 +- 6 files changed, 55 insertions(+), 22 deletions(-) create mode 100644 website/src/components/Footer.astro diff --git a/website/astro.config.mjs b/website/astro.config.mjs index 0a3b252f..eda4a7a7 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -32,6 +32,7 @@ export default defineConfig({ }, components: { Head: "./src/components/Head.astro", + Footer: "./src/components/Footer.astro", SocialIcons: "./src/components/SocialIcons.astro", Search: "./src/components/Search.astro", SiteTitle: "./src/components/SiteTitle.astro", diff --git a/website/src/components/Footer.astro b/website/src/components/Footer.astro new file mode 100644 index 00000000..6d29c31a --- /dev/null +++ b/website/src/components/Footer.astro @@ -0,0 +1,28 @@ +--- +import type { Props } from "@astrojs/starlight/props"; +import Default from "@astrojs/starlight/components/Footer.astro"; + +const year = new Date().getFullYear(); +--- + +<> + { + Astro.props.entry.data.hero ? ( + <> +
+

Released under the MIT License.

+

+ Copyright © {year} Reactter. +

+
+ + + + ) : ( + + ) + } + diff --git a/website/src/components/SiteTitle.astro b/website/src/components/SiteTitle.astro index 31f2e2c9..6b4834f5 100644 --- a/website/src/components/SiteTitle.astro +++ b/website/src/components/SiteTitle.astro @@ -32,7 +32,7 @@ const href = Astro.props.locale ?? "/"; } diff --git a/website/src/content/docs/index.mdx b/website/src/content/docs/index.mdx index 85b525b8..4c8ab979 100644 --- a/website/src/content/docs/index.mdx +++ b/website/src/content/docs/index.mdx @@ -3,19 +3,20 @@ title: Reactter description: A light, powerful and quick Reactive State Management, Dependency Injection and Event Handler for Dart/Flutter. template: splash hero: - title:

Reactter

+ title:

Reactter

tagline: -

+

A - lightweight, - powerful, and - reactive -

-

- State Management, - Dependency Injection and - Event Handler for Dart/Flutter. -

+ lightweight, + powerful, and + reactive +

+

+ State Management, + Dependency Injection and + Event Handler for + Dart/Flutter. +

image: html: actions: @@ -28,6 +29,7 @@ hero: icon: external attrs: target: _blank + footer: TEST --- import { CardGrid } from "@astrojs/starlight/components"; @@ -81,5 +83,4 @@ import Card from "@/components/Card.astro"; Reduce code significantly and simplify your app. - - + \ No newline at end of file diff --git a/website/src/styles/custom.css b/website/src/styles/custom.css index 2f384038..2c0999a5 100644 --- a/website/src/styles/custom.css +++ b/website/src/styles/custom.css @@ -11,7 +11,7 @@ } body { - background-color: hsl(from var(--sl-color-accent-low) h s 05% / 100%); + background-color: hsl(from var(--sl-color-accent-low) h s 08% / 100%); } [data-has-hero] header { @@ -48,21 +48,24 @@ body { background-position: center; width: 100%; height: 100%; + max-width: 300px; } [data-has-hero] .hero #hero_logo::after { content: ""; - background: inherit; - width: 100%; - height: 100%; position: absolute; - filter: drop-shadow(0px 0px) blur(80px); + width: 100%; + aspect-ratio: 1/1; + border-radius: 50%; + background: linear-gradient(45deg, var(--sl-color-accent) 15%, var(--sl-color-accent-low) 75%); + filter: blur(72px); z-index: -1; } [data-has-hero] .page { - background: - linear-gradient(215deg, transparent 0%, rgb(from var(--sl-color-accent-low) r g b / 0.5) 45%, rgb(from var(--sl-color-accent-low) r g b / 0.5) 55%, transparent 100%); + background: + radial-gradient(var(--sl-color-accent-low),transparent 50%) no-repeat -60vw -40vh / 105vw 200vh, + radial-gradient(var(--sl-color-accent), var(--sl-color-accent-low), transparent 60%) no-repeat 50% calc(100% + 21rem) / 62rem 32rem; } [data-has-hero] .action.primary { diff --git a/website/tailwind.config.mjs b/website/tailwind.config.mjs index 6d827905..49c576c6 100644 --- a/website/tailwind.config.mjs +++ b/website/tailwind.config.mjs @@ -34,7 +34,7 @@ export default { gray, }, fontFamily: { - sans: ['"Atkinson Hyperlegible"'], + sans: ['"Inter"'], mono: ['"IBM Plex Mono"'], }, }, From d2a6771a34f643dbf7a8fc4d9e149b7a01b5a806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Thu, 31 Oct 2024 23:40:26 -0600 Subject: [PATCH 035/141] feat(core): Add `untrackedAsync` and `batchAsync` methods for asynchronous state management. --- .../lib/src/core/state_management.dart | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/packages/reactter/lib/src/core/state_management.dart b/packages/reactter/lib/src/core/state_management.dart index 46ef6908..ef76a4fa 100644 --- a/packages/reactter/lib/src/core/state_management.dart +++ b/packages/reactter/lib/src/core/state_management.dart @@ -101,6 +101,37 @@ abstract class StateManagement implements IContext { } } + /// {@template reactter.untracked_async} + /// Executes the given [callback] function asynchronously without tracking any state changes. + /// This means that any state changes that occur inside the [callback] function + /// will not trigger any side effects. + /// + /// The [callback] function should return a [Future] of type [T]. + /// The returned [Future] will be the result of the untracked operation. + /// + /// Example usage: + /// ```dart + /// final state = UseAsyncState(0, () async => Future.value(1)); + /// final computed = UseCompute(() => state.value + 1, [state]); + /// + /// await Rt.untrackedAsync(() async { + /// await state.resolve(); + /// + /// print(computed.value); // 1 -> because the state change is not tracked + /// }); + /// + /// print(computed.value); // 1 -> because the state change is not tracked + /// ``` + /// {@endtemplate} + Future untrackedAsync(Future Function() callback) async { + try { + _untrackedRunningCount++; + return await callback(); + } finally { + _untrackedRunningCount--; + } + } + /// {@template reactter.batch} /// Executes the given [callback] function within a batch operation. /// @@ -143,6 +174,47 @@ abstract class StateManagement implements IContext { } } + /// {@template reactter.batch_async} + /// Executes the given [callback] function within a batch operation asynchronously. + /// + /// This method is similar to [batch], but it allows the [callback] function to be asynchronous. + /// + /// The [callback] function should return a [Future] of type [T]. + /// The returned [Future] will be the result of the batch operation. + /// + /// Example usage: + /// ```dart + /// final stateA = Signal(0); + /// final stateB = UseAsyncState(0, () async => Future.value(2)); + /// final computed = UseCompute( + /// () => stateA.value + stateB.value, + /// [stateA, stateB], + /// ); + /// + /// await Rt.batchAsync(() async { + /// stateA.value = 1; + /// await stateB.resolve(); + /// + /// print(computed.value); // 0 -> because the batch operation is not completed yet. + /// }); + /// + /// print(computed.value); // 3 -> because the batch operation is completed. + /// ``` + /// + /// {@endtemplate} + Future batchAsync(Future Function() callback) async { + try { + _batchRunningCount++; + return await callback(); + } finally { + _batchRunningCount--; + + if (_batchRunningCount == 0) { + _endBatch(); + } + } + } + void _endBatch() { for (final event in _deferredEvents.entries.toList(growable: false)) { final notifier = event.key; From 64ffd33a2b22097eeca0ade1e465d936c9837809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Thu, 31 Oct 2024 23:46:08 -0600 Subject: [PATCH 036/141] refactor: Export `devtools` module while hiding `RtDevTools` in reactter.dart. --- packages/reactter/lib/reactter.dart | 1 + packages/reactter/lib/src/devtools.dart | 503 ++++++++++++++++++++++-- 2 files changed, 472 insertions(+), 32 deletions(-) diff --git a/packages/reactter/lib/reactter.dart b/packages/reactter/lib/reactter.dart index 69e3d895..4ebc98a4 100644 --- a/packages/reactter/lib/reactter.dart +++ b/packages/reactter/lib/reactter.dart @@ -1,6 +1,7 @@ library reactter; export 'src/args.dart'; +export 'src/devtools.dart' hide RtDevTools; export 'src/framework.dart'; export 'src/hooks/hooks.dart' hide UseAsyncStateBase; export 'src/memo/memo.dart'; diff --git a/packages/reactter/lib/src/devtools.dart b/packages/reactter/lib/src/devtools.dart index 47714a57..9c7f1457 100644 --- a/packages/reactter/lib/src/devtools.dart +++ b/packages/reactter/lib/src/devtools.dart @@ -5,87 +5,357 @@ import 'package:meta/meta.dart'; import 'framework.dart'; import 'internals.dart'; +import 'signal/signal.dart'; -extension RtExt on RtInterface { - void initializeDebugging() { - if (kDebugMode) { - ReactterDevTools._initializeDebugging(); - } +extension RtDevToolsExt on RtInterface { + void initializeDevTools() { + RtDevTools.initialize(); } } @internal -class ReactterDevTools extends RtStateObserver { - static ReactterDevTools? _instance; - final LinkedHashMap states = LinkedHashMap(); +class RtDevTools with RtStateObserver, RtDependencyObserver { + static RtDevTools? _instance; + + final LinkedList<_Node> _nodes = LinkedList(); + final LinkedHashMap _nodesByKey = LinkedHashMap(); + + static void initialize() { + assert(_instance == null, 'The devtools has already been initialized.'); - static void _initializeDebugging() { if (kDebugMode) { - _instance ??= ReactterDevTools._(); + _instance ??= RtDevTools._(); } } - ReactterDevTools._() { + RtDevTools._() { Rt.addObserver(this); } @override - void onStateCreated(covariant RtState state) { + void onStateCreated(RtState state) { final stateKey = state.hashCode.toString(); - states.putIfAbsent(stateKey, () => state); + + _addNode(state); + + // print("++ onStateCreated ++"); + // print("stateNode: ${stateNode.toJson()}"); + // print("++++++++++++++"); + // for (var e in nodes) { + // print("${e.toJson()}"); + // } + // print("______________"); + dev.postEvent('ext.reactter.onStateCreated', { 'stateKey': stateKey, }); } @override - void onStateBound(covariant RtState state, Object instance) { + void onStateBound(RtState state, Object instance) { final stateKey = state.hashCode.toString(); + final instanceKey = instance.hashCode.toString(); + final stateNode = _nodesByKey[stateKey]; + final instanceNode = _addNode(instance); + + if (stateNode != null) instanceNode.addChild(stateNode); + + // print("++ onStateBound ++"); + // print("stateNode: ${stateNode?.toJson()}"); + // print("instanceNode: ${instanceNode.toJson()}"); + // print("++++++++++++++"); + // for (var e in nodes) { + // print("${e.toJson()}"); + // } + // print("______________"); + dev.postEvent('ext.reactter.onStateBound', { 'stateKey': stateKey, + 'instanceKey': instanceKey, }); } @override - void onStateUnbound(covariant RtState state, Object instance) { + void onStateUnbound(RtState state, Object instance) { final stateKey = state.hashCode.toString(); + final instanceKey = instance.hashCode.toString(); + final stateNode = _nodesByKey[stateKey]; + final instanceNode = _nodesByKey[instanceKey]; + + if (stateNode != null) instanceNode?.removeChild(stateNode); + + final isInstanceRemoved = _removeNode(instanceKey); + + // print("++ onStateUnbound ++"); + // print("stateNode: ${stateNode?.toJson()}"); + // print("instanceNode: ${instanceNode?.toJson()}"); + // print("++++++++++++++"); + // for (var e in nodes) { + // print("${e.toJson()}"); + // } + // print("______________"); + dev.postEvent('ext.reactter.onStateUnbound', { 'stateKey': stateKey, + 'instanceKey': instanceKey, + 'isInstanceRemoved': isInstanceRemoved, }); } @override - void onStateUpdated(covariant RtState state) { + void onStateUpdated(RtState state) { final stateKey = state.hashCode.toString(); + dev.postEvent('ext.reactter.onStateUpdated', { 'stateKey': stateKey, }); } @override - void onStateDisposed(covariant RtState state) { + void onStateDisposed(RtState state) { final stateKey = state.hashCode.toString(); - states.remove(stateKey); + + final isStateRemoved = _removeNode(stateKey); + dev.postEvent('ext.reactter.onStateDisposed', { 'stateKey': stateKey, + 'isStateRemoved': isStateRemoved, + }); + } + + @override + void onDependencyRegistered(DependencyRef dependency) { + final dependencyKey = dependency.hashCode.toString(); + + _addNode(dependency); + + // print("++ onDependencyRegistered ++"); + // print("dependencyNode: ${dependencyNode.toJson()}"); + // print("++++++++++++++"); + // for (var e in nodes) { + // print("${e.toJson()}"); + // } + // print("______________"); + + dev.postEvent('ext.reactter.onDependencyRegistered', { + 'dependencyKey': dependencyKey, + }); + } + + @override + void onDependencyCreated(DependencyRef dependency, Object? instance) { + final dependencyKey = dependency.hashCode.toString(); + final instanceKey = instance.hashCode.toString(); + + if (instance != null) _addNode(instance); + + // print("++ onDependencyCreated ++"); + // if (instance != null) { + // final instanceNode = ; + // print("instanceNode: ${instanceNode.toJson()}"); + // } + // print("++++++++++++++"); + // for (var e in nodes) { + // print("${e.toJson()}"); + // } + // print("______________"); + + dev.postEvent('ext.reactter.onDependencyCreated', { + 'dependencyKey': dependencyKey, + 'instanceKey': instanceKey, + }); + } + + @override + void onDependencyMounted(DependencyRef dependency, Object? instance) { + final dependencyKey = dependency.hashCode.toString(); + final instanceKey = instance.hashCode.toString(); + + dev.postEvent('ext.reactter.onDependencyMounted', { + 'dependencyKey': dependencyKey, + 'instanceKey': instanceKey, }); } - Map? getStateDetails(String stateKey) { - final state = states[stateKey]; + @override + void onDependencyUnmounted(DependencyRef dependency, Object? instance) { + final dependencyKey = dependency.hashCode.toString(); + final instanceKey = instance.hashCode.toString(); - if (state == null) return null; + dev.postEvent('ext.reactter.onDependencyUnmounted', { + 'dependencyKey': dependencyKey, + 'instanceKey': instanceKey, + }); + } + + @override + void onDependencyDeleted(DependencyRef dependency, Object? instance) { + final dependencyKey = dependency.hashCode.toString(); + final instanceObj = + instance ?? _nodesByKey[dependencyKey]?.children.first.instance; + final instanceKey = instanceObj.hashCode.toString(); + final dependencyNode = _nodesByKey[dependencyKey]; + final instanceNode = _nodesByKey[instanceKey]; + + if (instanceNode != null) dependencyNode?.removeChild(instanceNode); + + final isInstanceRemoved = _removeNode(instanceKey); + + dev.postEvent('ext.reactter.onDependencyDeleted', { + 'dependencyKey': dependencyKey, + 'instanceKey': instanceKey, + 'isInstanceRemoved': isInstanceRemoved, + }); + } + + @override + void onDependencyUnregistered(DependencyRef dependency) { + final dependencyKey = dependency.hashCode.toString(); + + _removeNode(dependencyKey); + + dev.postEvent('ext.reactter.onDependencyUnregistered', { + 'dependencyKey': dependencyKey, + }); + } + + @override + void onDependencyFailed( + covariant DependencyRef dependency, + DependencyFail fail, + ) {} + + Map getNodes(int page, int pageSize) { + final nodesList = _nodes.toList(); + final length = nodesList.length; + final start = page * pageSize; + final end = length < (start + pageSize) ? length : start + pageSize; + final totalPages = (length / pageSize).ceil(); + + final data = + nodesList.sublist(start, end).map((node) => node.toJson()).toList(); return { - 'type': state.runtimeType.toString(), - 'label': state.debugLabel, + 'nodes': data, + 'total': length, + 'start': start, + 'end': end, + 'page': page, + 'pageSize': pageSize, + 'totalPages': totalPages, + }; + } + + _Node _addNode(Object instance) { + _Node node; + + if (instance is DependencyRef) { + node = _addDependencyNode(instance); + } else if (instance is RtState) { + node = _addStateNode(instance); + } else { + node = _addInstanceNode(instance); + } + + _nodesByKey[node.key] = node; + + if (node.list == null) { + _nodes.add(node); + node.moveChildren(); + } + + return node; + } + + bool _removeNode(String nodeKey) { + final node = _nodesByKey[nodeKey]; + + if (node == null) return false; + + final isRemoved = node.remove(); + + if (isRemoved) _nodesByKey.remove(nodeKey); + + return isRemoved; + } + + _StateNode _addStateNode(RtState state) { + final stateKey = state.hashCode.toString(); + final stateNode = _nodesByKey.putIfAbsent( + stateKey, + () => _StateNode(instance: state), + ) as _StateNode; + + final boundInstance = state.boundInstance; + + if (boundInstance != null) { + final boundInstanceNode = _addNode(boundInstance); + boundInstanceNode.addChild(stateNode); + } + + return stateNode; + } + + _DependencyNode _addDependencyNode(DependencyRef dependencyRef) { + final dependencyKey = dependencyRef.hashCode.toString(); + final dependencyNode = _nodesByKey.putIfAbsent( + dependencyKey, + () => _DependencyNode(instance: dependencyRef), + ) as _DependencyNode; + + final instance = Rt.getDependencyRegisterByRef(dependencyRef)?.instance; + + if (instance != null) { + final instanceNode = _addNode(instance); + dependencyNode.addChild(instanceNode); + } + + return dependencyNode; + } + + _InstanceNode _addInstanceNode(Object instance) { + return _nodesByKey.putIfAbsent( + instance.hashCode.toString(), + () => _InstanceNode(instance: instance), + ) as _InstanceNode; + } + + Map getDynamicInfo(Object instance) { + if (instance is DependencyRef) { + return getDependencyInfo(instance); + } else if (instance is RtState) { + return getStateInfo(instance); + } else { + return getInstanceInfo(instance); + } + } + + Map getDependencyInfo(DependencyRef dependencyRef) { + return { + 'kind': _NodeKind.dependency, + 'key': dependencyRef.hashCode.toString(), + 'type': dependencyRef.type.toString(), + 'id': dependencyRef.id, }; } - Map? getDebugInfo(String stateKey) { - final state = states[stateKey]; + Map getStateInfo(RtState state) { + return { + 'kind': _NodeKind.state, + 'key': state.hashCode.toString(), + 'type': state.runtimeType.toString(), + 'debugLabel': state.debugLabel, + 'debugInfo': state.debugInfo, + 'boundInstanceKey': state.boundInstance?.hashCode.toString(), + }; + } - return state?.debugInfo; + Map getInstanceInfo(Object instance) { + return { + 'kind': _NodeKind.instance, + 'key': instance.hashCode.toString(), + 'type': instance.runtimeType.toString(), + }; } String getPropertyValue(value) { @@ -100,27 +370,196 @@ class ReactterDevTools extends RtStateObserver { } } - String getListString(List list) { - var listString = list.toString(); + String getListString(List data) { + var listString = data.toString(); if (listString.length > 60) { listString = '${listString.substring(0, 60)}...]'; } return listString; } - String getMapString(Map map) { - var mapString = map.toString(); + String getMapString(Map data) { + var mapString = data.toString(); if (mapString.length > 60) { mapString = '${mapString.substring(0, 60)}...}'; } return mapString; } - String getSetString(Set set) { - var setString = set.toString(); + String getSetString(Set data) { + var setString = data.toString(); if (setString.length > 60) { setString = '${setString.substring(0, 60)}...}'; } return setString; } } + +abstract class _NodeKind { + static const String state = 'state'; + static const String hook = 'hook'; + static const String signal = 'signal'; + static const String instance = 'instance'; + static const String dependency = 'dependency'; +} + +abstract class _Node extends LinkedListEntry<_Node> { + String get key => instance.hashCode.toString(); + + _Node? _parent; + _Node? get parent => _parent; + + final T instance; + final LinkedHashSet<_Node> children = LinkedHashSet(); + + _Node({required this.instance}); + + Map toJson(); + + _Node? get lastDescendant => + children.isEmpty ? this : children.last.lastDescendant as _Node; + + void addChild(_Node node) { + if (node._parent == this) return; + + if (node.list != null) node.unlink(); + + node._parent?.children.remove(node); + + if (lastDescendant?.list != null) { + lastDescendant?.insertAfter(node); + } else { + insertAfter(node); + } + + node.moveChildren(); + + children.add(node); + node._parent = this; + } + + void moveChildren() { + _Node? prevChild; + + for (final child in children) { + if (child.list != null) child.unlink(); + + if (prevChild != null) { + prevChild.insertAfter(child); + } else { + insertAfter(child); + } + + child.moveChildren(); + + prevChild = child; + } + } + + void removeChild(_Node node) { + // for (final child in node.children.toList()) { + // if (child.list != null) { + // node.removeChild(child); + // } + // } + + if (node.list != null) { + node.unlink(); + } + + if (node._parent == this) { + children.remove(this); + node._parent = null; + } + + if (children.isEmpty) { + unlink(); + } + } + + bool remove() { + if (children.isEmpty) { + _parent?.removeChild(this); + + if (list != null) unlink(); + } + + return list == null; + } +} + +class _InstanceNode extends _Node { + _InstanceNode({required Object instance}) : super(instance: instance); + + @override + Map toJson() { + return { + 'kind': _NodeKind.instance, + 'key': key, + 'type': instance.runtimeType.toString(), + 'dependencyRef': Rt.getDependencyRef(instance)?.hashCode.toString(), + }; + } + + @override + bool remove() { + final json = toJson(); + + if (json['dependencyRef'] == null) { + return super.remove(); + } + + return false; + } +} + +class _StateNode extends _Node { + _StateNode({required RtState instance}) : super(instance: instance); + + static String resolveKind(RtState instance) { + if (instance is Signal) return _NodeKind.signal; + if (instance is RtHook) return _NodeKind.hook; + return _NodeKind.state; + } + + @override + Map toJson() { + return { + 'kind': resolveKind(instance), + 'key': key, + 'type': instance.runtimeType.toString(), + 'debugLabel': instance.debugLabel, + 'debugInfo': instance.debugInfo, + 'boundInstanceKey': instance.boundInstance?.hashCode.toString(), + 'dependencyRef': Rt.getDependencyRef(instance)?.hashCode.toString(), + }; + } + + @override + bool remove() { + final json = toJson(); + + if (json['dependencyRef'] == null) { + return super.remove(); + } + + return false; + } +} + +class _DependencyNode extends _Node { + _DependencyNode({required DependencyRef instance}) + : super(instance: instance); + + @override + Map toJson() { + return { + 'kind': _NodeKind.dependency, + 'key': key, + 'type': instance.type.toString(), + 'id': instance.id, + 'instanceKey': + Rt.getDependencyRegisterByRef(instance)?.instance.hashCode.toString(), + }; + } +} From c1853da89962d893cf103ab67aa6ee527b4fa653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Thu, 31 Oct 2024 23:46:36 -0600 Subject: [PATCH 037/141] feat(devtools): Introduce new data structures and interfaces for enhanced state management in the devtools extension. --- .../devtools_options.yaml | 4 + .../reactter_devtools_extension/lib/main.dart | 2 +- .../lib/src/controllers/nodes_controller.dart | 227 ++++++++++++++++++ .../lib/src/data/dependency_info.dart | 7 + .../lib/src/data/dependency_node.dart | 87 +++++++ .../lib/src/data/instance_info.dart | 5 + .../lib/src/data/instance_node.dart | 32 +++ .../lib/src/data/property_node.dart | 63 +++++ .../lib/src/data/slot_node.dart | 26 ++ .../lib/src/data/state_info.dart | 19 ++ .../lib/src/data/state_node.dart | 126 ++++++++++ .../lib/src/data/tree_list.dart | 34 +++ .../lib/src/data/tree_node.dart | 168 +++++++++++++ .../lib/src/interfaces/node.dart | 23 ++ .../lib/src/interfaces/node_info.dart | 5 + .../lib/src/reactter_devtools_extension.dart | 97 +++----- .../lib/src/services/devtools_service.dart | 131 ++++++++++ .../lib/src/services/eval_service.dart | 19 ++ .../lib/src/vm.dart | 0 .../lib/src/widgets/loading.dart | 16 ++ .../lib/src/widgets/node_tile.dart | 201 ++++++++++++++++ .../lib/src/widgets/nodes_list.dart | 71 ++++++ .../lib/src/widgets/offset_scrollbar.dart | 100 ++++++++ .../lib/src/widgets/properties_list.dart | 59 +++++ .../lib/src/widgets/property_tile.dart | 43 ++++ .../reactter_devtools_extension/pubspec.lock | 130 +++++++--- .../reactter_devtools_extension/pubspec.yaml | 14 +- .../web/index.html | 70 ++---- 28 files changed, 1637 insertions(+), 142 deletions(-) create mode 100644 packages/reactter_devtools_extension/devtools_options.yaml create mode 100644 packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart create mode 100644 packages/reactter_devtools_extension/lib/src/data/dependency_info.dart create mode 100644 packages/reactter_devtools_extension/lib/src/data/dependency_node.dart create mode 100644 packages/reactter_devtools_extension/lib/src/data/instance_info.dart create mode 100644 packages/reactter_devtools_extension/lib/src/data/instance_node.dart create mode 100644 packages/reactter_devtools_extension/lib/src/data/property_node.dart create mode 100644 packages/reactter_devtools_extension/lib/src/data/slot_node.dart create mode 100644 packages/reactter_devtools_extension/lib/src/data/state_info.dart create mode 100644 packages/reactter_devtools_extension/lib/src/data/state_node.dart create mode 100644 packages/reactter_devtools_extension/lib/src/data/tree_list.dart create mode 100644 packages/reactter_devtools_extension/lib/src/data/tree_node.dart create mode 100644 packages/reactter_devtools_extension/lib/src/interfaces/node.dart create mode 100644 packages/reactter_devtools_extension/lib/src/interfaces/node_info.dart create mode 100644 packages/reactter_devtools_extension/lib/src/services/devtools_service.dart create mode 100644 packages/reactter_devtools_extension/lib/src/services/eval_service.dart create mode 100644 packages/reactter_devtools_extension/lib/src/vm.dart create mode 100644 packages/reactter_devtools_extension/lib/src/widgets/loading.dart create mode 100644 packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart create mode 100644 packages/reactter_devtools_extension/lib/src/widgets/nodes_list.dart create mode 100644 packages/reactter_devtools_extension/lib/src/widgets/offset_scrollbar.dart create mode 100644 packages/reactter_devtools_extension/lib/src/widgets/properties_list.dart create mode 100644 packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart diff --git a/packages/reactter_devtools_extension/devtools_options.yaml b/packages/reactter_devtools_extension/devtools_options.yaml new file mode 100644 index 00000000..995fadae --- /dev/null +++ b/packages/reactter_devtools_extension/devtools_options.yaml @@ -0,0 +1,4 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: + - reactter: true \ No newline at end of file diff --git a/packages/reactter_devtools_extension/lib/main.dart b/packages/reactter_devtools_extension/lib/main.dart index 809cd645..681a08d7 100644 --- a/packages/reactter_devtools_extension/lib/main.dart +++ b/packages/reactter_devtools_extension/lib/main.dart @@ -3,5 +3,5 @@ import 'package:flutter/material.dart'; import 'package:reactter_devtools_extension/src/reactter_devtools_extension.dart'; void main() { - runApp(const DevToolsExtension(child: ReactterDevtoolsExtension())); + runApp(const DevToolsExtension(child: RtDevToolsExtension())); } diff --git a/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart b/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart new file mode 100644 index 00000000..a6e184ec --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart @@ -0,0 +1,227 @@ +import 'dart:async'; +import 'dart:collection'; + +import 'package:devtools_extensions/devtools_extensions.dart'; +import 'package:flutter_reactter/reactter.dart'; + +import 'package:reactter_devtools_extension/src/data/instance_info.dart'; +import 'package:reactter_devtools_extension/src/data/instance_node.dart'; +import 'package:reactter_devtools_extension/src/data/slot_node.dart'; +import 'package:reactter_devtools_extension/src/data/state_info.dart'; +import 'package:reactter_devtools_extension/src/data/tree_list.dart'; +import 'package:reactter_devtools_extension/src/interfaces/node.dart'; +import 'package:reactter_devtools_extension/src/services/devtools_service.dart'; +import 'package:reactter_devtools_extension/src/data/state_node.dart'; + +enum NodeType { + dependency, + state, + instance, +} + +class NodesController { + StreamSubscription? extEventSubscription; + + final devtoolsSevices = DevtoolsService(); + final nodesList = TreeList(); + final uNodes = UseState(LinkedHashMap()); + final uCurrentNodeKey = UseState(null); + + INode? get currentNode => uNodes.value[uCurrentNodeKey.value]; + + NodesController() { + init(); + } + + Future init() async { + await getAllNodes(); + + final vmService = await serviceManager.onServiceAvailable; + + extEventSubscription = vmService.onExtensionEvent.listen((event) { + if (!(event.extensionKind?.startsWith('ext.reactter.') ?? false)) { + return; + } + + final eventData = event.extensionData?.data ?? {}; + + switch (event.extensionKind) { + case 'ext.reactter.onStateCreated': + final stateKey = eventData['stateKey'] as String; + addNodeByKey(stateKey); + break; + case 'ext.reactter.onStateBound': + final instanceKey = eventData['instanceKey'] as String; + final stateKey = eventData['stateKey'] as String; + addNodeByKey(instanceKey); + addNodeByKey(stateKey); + break; + case 'ext.reactter.onStateUnbound': + final instanceKey = eventData['instanceKey'] as String; + final isInstanceRemoved = eventData['isInstanceRemoved'] as bool; + if (isInstanceRemoved) removeNodeByKey(instanceKey); + break; + case 'ext.reactter.onStateDisposed': + final stateKey = eventData['stateKey'] as String; + final isStateRemoved = eventData['isStateRemoved'] as bool; + if (isStateRemoved) removeNodeByKey(stateKey); + break; + case 'ext.reactter.onDependencyCreated': + final instanceKey = eventData['instanceKey'] as String; + addNodeByKey(instanceKey); + break; + case 'ext.reactter.onDependencyDeleted': + final instanceKey = eventData['instanceKey'] as String; + final isInstanceRemoved = eventData['isInstanceRemoved'] as bool; + if (isInstanceRemoved) removeNodeByKey(instanceKey); + } + }); + } + + Future getAllNodes() async { + final nodesInfo = await devtoolsSevices.getAllNodes(); + addNodes(nodesInfo); + } + + Future addNodeByKey(String nodeKey) async { + final nodeInfo = await devtoolsSevices.getNodeBykey(nodeKey); + addNodeByMapInfo(nodeInfo); + print('addNodeByKey $nodeKey ${nodeInfo['boundInstanceKey']}'); + } + + void addNodes(List> nodesInfo) { + Rt.batch(() { + for (final nodeInfo in nodesInfo) { + addNodeByMapInfo(nodeInfo); + // print( + // "key: ${nodeInfo['key']}, label: ${nodeInfo['debugLabel']}, type: ${nodeInfo['type']}, kind: ${nodeInfo['kind']}, boundInstanceKey: ${nodeInfo['boundInstanceKey']}", + // ); + } + }); + } + + void addNodeByMapInfo(Map nodeInfo) { + final kind = nodeInfo['kind']; + final key = nodeInfo['key']; + final type = nodeInfo['type']; + + switch (kind) { + case 'dependency': + break; + case 'state': + case 'hook': + case 'signal': + final nodePrev = uNodes.value[key]; + final node = nodePrev is StateNode + ? nodePrev + : StateNode( + key: key, + kind: kind, + type: type, + ); + final debugLabel = nodeInfo['debugLabel']; + final boundInstanceKey = nodeInfo['boundInstanceKey']; + + node.uInfo.value = StateInfo( + label: debugLabel, + boundInstanceKey: boundInstanceKey, + ); + + if (boundInstanceKey != null) { + final boundInstanceNodePrev = uNodes.value[boundInstanceKey]; + final boundInstanceNode = boundInstanceNodePrev ?? + SlotNode( + key: boundInstanceKey, + kind: kind, + type: 'Unknown', + ); + + addNode(boundInstanceNode); + + boundInstanceNode.addChild(node); + } + + addNode(node); + + break; + case 'instance': + final nodePrev = uNodes.value[key]; + final node = nodePrev is InstanceNode + ? nodePrev + : InstanceNode( + key: key, + kind: kind, + type: type, + ); + + node.uInfo.value = InstanceInfo(); + + addNode(node); + + break; + } + } + + void addNode(INode node) { + // if (node is SlotNode) { + // print("SloteNode(key: ${node.key})"); + // } else if (node is StateNode) { + // print("StateNode(key: ${node.key})"); + // } else if (node is InstanceNode) { + // print("InstanceNode(key: ${node.key})"); + // } else { + // print("Node(key: ${node.key})"); + // } + + final nodePrev = uNodes.value[node.key]; + + if (nodePrev == node) return; + + uNodes.value[node.key] = node; + uNodes.notify(); + + nodePrev?.replace(node); + + if (node is SlotNode) return; + + if (node.list == null) nodesList.add(node); + } + + void removeNodeByKey(String nodeKey) { + devtoolsSevices.disposeNodeByKey(nodeKey); + + if (uCurrentNodeKey.value == nodeKey) { + uCurrentNodeKey.value = null; + } + + uNodes + ..value[nodeKey]?.remove() + ..value.remove(nodeKey) + ..notify(); + } + + Future selectNode(INode node) async { + currentNode?.uIsSelected.value = false; + uCurrentNodeKey.value = node.key; + node.uIsSelected.value = true; + node.loadDetails(); + } +} + +class RtDevToolsInstanceException implements Exception { + RtDevToolsInstanceException(); + + @override + String toString() { + return ''' + RtDevTools is not available. + Make sure you have initialized RtDevTools before using this service. + You can do this by calling `Rt.initializeDebugging()` in your app entry point. eg: + + void main() { + Rt.initializeDebugging(); + runApp(MyApp()); + } + '''; + } +} diff --git a/packages/reactter_devtools_extension/lib/src/data/dependency_info.dart b/packages/reactter_devtools_extension/lib/src/data/dependency_info.dart new file mode 100644 index 00000000..39090df3 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/data/dependency_info.dart @@ -0,0 +1,7 @@ +import 'package:reactter_devtools_extension/src/interfaces/node_info.dart'; + +class DependencyInfo extends INodeInfo { + final String? id; + + DependencyInfo({required this.id}); +} diff --git a/packages/reactter_devtools_extension/lib/src/data/dependency_node.dart b/packages/reactter_devtools_extension/lib/src/data/dependency_node.dart new file mode 100644 index 00000000..a6f8c288 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/data/dependency_node.dart @@ -0,0 +1,87 @@ +import 'package:devtools_app_shared/service.dart'; +import 'package:flutter_reactter/reactter.dart'; +import 'package:reactter_devtools_extension/src/data/dependency_info.dart'; +import 'package:reactter_devtools_extension/src/interfaces/node.dart'; +import 'package:reactter_devtools_extension/src/services/eval_service.dart'; +import 'package:vm_service/vm_service.dart'; + +base class DependencyNode extends INode { + final uIsLoading = UseState(false); + + DependencyNode._({ + required super.key, + required super.kind, + required super.type, + }) { + _loadDependencyNode(); + } + + factory DependencyNode({ + required String key, + required String kind, + required String type, + }) { + return Rt.createState( + () => DependencyNode._( + key: key, + kind: kind, + type: type, + ), + ); + } + + Future _loadDependencyNode() async { + uIsLoading.value = true; + + final eval = await EvalService.devtoolsEval; + final isAlive = Disposable(); + + final dependencyNode = await eval.evalInstance( + 'RtDevTools._instance?.getDependencyInfo("$key")', + isAlive: isAlive, + ); + + assert(dependencyNode.kind == InstanceKind.kMap); + + final dependencyNodeAssociations = + dependencyNode.associations?.cast(); + + assert(dependencyNodeAssociations != null); + + String? type; + String? id; + + for (var element in dependencyNodeAssociations!) { + assert(element.key != null && element.value != null); + + final eKey = element.key!.valueAsString!; + final eValue = element.value!.valueAsString; + + if (element.value!.kind == InstanceKind.kNull) continue; + + if (eValue == null) continue; + + switch (eKey) { + case 'type': + type = eValue; + break; + case 'id': + id = eValue; + break; + } + } + + assert(type != null); + + uInfo.value = DependencyInfo( + id: id, + ); + + uIsLoading.value = false; + } + + @override + void loadDetails() { + // TODO: implement loadDetails + } +} diff --git a/packages/reactter_devtools_extension/lib/src/data/instance_info.dart b/packages/reactter_devtools_extension/lib/src/data/instance_info.dart new file mode 100644 index 00000000..372c743b --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/data/instance_info.dart @@ -0,0 +1,5 @@ +import 'package:reactter_devtools_extension/src/interfaces/node_info.dart'; + +class InstanceInfo extends INodeInfo { + InstanceInfo(); +} diff --git a/packages/reactter_devtools_extension/lib/src/data/instance_node.dart b/packages/reactter_devtools_extension/lib/src/data/instance_node.dart new file mode 100644 index 00000000..d8f85722 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/data/instance_node.dart @@ -0,0 +1,32 @@ +import 'package:flutter_reactter/reactter.dart'; +import 'package:reactter_devtools_extension/src/interfaces/node.dart'; +import 'package:reactter_devtools_extension/src/interfaces/node_info.dart'; + +base class InstanceNode extends INode { + InstanceNode._({ + required super.key, + required super.kind, + required super.type, + }) { + // _loadStateNode(); + } + + factory InstanceNode({ + required String key, + required String kind, + required String type, + }) { + return Rt.createState( + () => InstanceNode._( + key: key, + kind: kind, + type: type, + ), + ); + } + + @override + void loadDetails() { + // TODO: implement loadDetails + } +} diff --git a/packages/reactter_devtools_extension/lib/src/data/property_node.dart b/packages/reactter_devtools_extension/lib/src/data/property_node.dart new file mode 100644 index 00000000..ab46819d --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/data/property_node.dart @@ -0,0 +1,63 @@ +import 'package:devtools_app_shared/service.dart'; +import 'package:flutter_reactter/reactter.dart'; +import 'package:reactter_devtools_extension/src/data/tree_node.dart'; +import 'package:reactter_devtools_extension/src/services/eval_service.dart'; +import 'package:vm_service/vm_service.dart'; + +base class PropertyNode extends TreeNode { + final String key; + final InstanceRef valueRef; + + String? _value; + String? get value => _value; + + PropertyNode._({ + required this.key, + required this.valueRef, + }) { + if (valueRef.kind == InstanceKind.kString) { + _value = '"${valueRef.valueAsString}"'; + } else { + _value = valueRef.valueAsString; + } + + if (_value == null) { + _loadValue(); + } + } + + factory PropertyNode({ + PropertyNode? parent, + required String key, + required InstanceRef valueRef, + }) { + return Rt.createState( + () => PropertyNode._( + key: key, + valueRef: valueRef, + ), + ); + } + + Future _loadValue() async { + final eval = await EvalService.devtoolsEval; + final isAlive = Disposable(); + + final value = await eval.evalInstance( + 'RtDevTools._instance?.getPropertyValue(value)', + isAlive: isAlive, + scope: {'value': valueRef.id!}, + ); + + final isIterable = await eval.evalInstance( + 'value is Iterable', + isAlive: isAlive, + scope: {'value': valueRef.id!}, + ); + + print(isIterable); + + _value = value.valueAsString; + notify(); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/data/slot_node.dart b/packages/reactter_devtools_extension/lib/src/data/slot_node.dart new file mode 100644 index 00000000..fa35f0d9 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/data/slot_node.dart @@ -0,0 +1,26 @@ +import 'package:flutter_reactter/reactter.dart'; +import 'package:reactter_devtools_extension/src/interfaces/node.dart'; +import 'package:reactter_devtools_extension/src/interfaces/node_info.dart'; + +base class SlotNode extends INode { + SlotNode._({required super.key, required super.kind, required super.type}); + + factory SlotNode({ + required String key, + required String kind, + required String type, + }) { + return Rt.createState( + () => SlotNode._( + key: key, + kind: kind, + type: type, + ), + ); + } + + @override + void loadDetails() { + // TODO: implement loadDetails + } +} diff --git a/packages/reactter_devtools_extension/lib/src/data/state_info.dart b/packages/reactter_devtools_extension/lib/src/data/state_info.dart new file mode 100644 index 00000000..c0fbff3a --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/data/state_info.dart @@ -0,0 +1,19 @@ +import 'package:reactter_devtools_extension/src/interfaces/node_info.dart'; + +class StateInfo extends INodeInfo { + final String? boundInstanceKey; + final List kinds; + + StateInfo({ + super.label, + this.boundInstanceKey, + this.kinds = const ['RtState'], + }); + + StateInfo copyWith({String? label, String? boundInstanceKey}) { + return StateInfo( + label: label ?? this.label, + boundInstanceKey: boundInstanceKey ?? this.boundInstanceKey, + ); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/data/state_node.dart b/packages/reactter_devtools_extension/lib/src/data/state_node.dart new file mode 100644 index 00000000..6d14bac7 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/data/state_node.dart @@ -0,0 +1,126 @@ +import 'package:devtools_app_shared/service.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/interfaces/node.dart'; +import 'package:reactter_devtools_extension/src/data/property_node.dart'; +import 'package:reactter_devtools_extension/src/data/state_info.dart'; +import 'package:reactter_devtools_extension/src/services/eval_service.dart'; + +import 'package:vm_service/vm_service.dart'; + +base class StateNode extends INode { + final uIsLoading = UseState(false); + + StateNode._({ + required super.key, + required super.kind, + required super.type, + }) { + // _loadStateNode(); + } + + factory StateNode({ + required String key, + required String kind, + required String type, + }) { + return Rt.createState( + () => StateNode._( + key: key, + kind: kind, + type: type, + ), + ); + } + + Future _loadStateNode() async { + uIsLoading.value = true; + + final eval = await EvalService.devtoolsEval; + final isAlive = Disposable(); + + final stateNode = await eval.evalInstance( + 'RtDevTools._instance?.getStateInfo("$key")', + isAlive: isAlive, + ); + + assert(stateNode.kind == InstanceKind.kMap); + + final stateNodeAssociations = + stateNode.associations?.cast(); + + assert(stateNodeAssociations != null); + + String? label; + + for (var element in stateNodeAssociations!) { + assert(element.key != null && element.value != null); + + final eKey = element.key!.valueAsString!; + final eValue = element.value!.valueAsString!; + + if (element.value!.kind == InstanceKind.kNull) continue; + + switch (eKey) { + case 'label': + label = eValue; + break; + } + } + + uInfo.value = StateInfo( + label: label, + ); + + uIsLoading.value = false; + } + + @override + Future loadDetails() async { + await Future.wait([loadBoundInstance(), loadProperties()]); + } + + Future loadBoundInstance() async { + // final eval = await EvalService.devtoolsEval; + // final isAlive = Disposable(); + // final valueInst = await eval.safeGetInstance(ref, isAlive); + + // final inst = await eval.evalInstance( + // 'state.boundInstance', + // isAlive: isAlive, + // scope: {'state': ref.id!}, + // ); + } + + Future loadProperties() async { + final eval = await EvalService.devtoolsEval; + final isAlive = Disposable(); + + final propertiesInst = await eval.evalInstance( + 'RtDevTools._instance?.getDebugInfo("$key")', + isAlive: isAlive, + ); + + assert(propertiesInst.kind == InstanceKind.kMap); + + final associations = propertiesInst.associations?.cast(); + + assert(associations != null); + + final properties = []; + + for (final association in associations!) { + final key = association.key!.valueAsString!; + final valueRef = association.value; + + properties.add( + PropertyNode( + key: key, + valueRef: valueRef, + ), + ); + } + + propertyNodes.clear(); + propertyNodes.addAll(properties); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/data/tree_list.dart b/packages/reactter_devtools_extension/lib/src/data/tree_list.dart new file mode 100644 index 00000000..7b952534 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/data/tree_list.dart @@ -0,0 +1,34 @@ +import 'dart:collection'; + +import 'package:flutter_reactter/reactter.dart'; +import 'package:reactter_devtools_extension/src/data/tree_node.dart'; + +final class TreeList> extends LinkedList + with RtContext, RtStateBase> { + TreeList._(); + + factory TreeList() => Rt.createState(() => TreeList._()); + + @override + void add(E entry) => update(() => super.add(entry)); + + @override + void addFirst(E entry) => update(() => super.addFirst(entry)); + + @override + void addAll(Iterable entries) => update(() => super.addAll(entries)); + + @override + bool remove(E entry) => entry.remove(); + + @override + void clear() { + Rt.batch(() { + for (final entry in this) { + (entry as RtState).dispose(); + } + + update(() => super.clear()); + }); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/data/tree_node.dart b/packages/reactter_devtools_extension/lib/src/data/tree_node.dart new file mode 100644 index 00000000..dbb6ff48 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/data/tree_node.dart @@ -0,0 +1,168 @@ +import 'dart:collection'; +import 'package:flutter_reactter/reactter.dart'; + +abstract base class TreeNode> extends LinkedListEntry + with RtStateBase, RtContext { + final uChildren = UseState(LinkedHashSet()); + final uIsExpanded = UseState(true); + + E? _parent; + E? get parent => _parent; + + int get depth => (parent?.depth ?? 0) + 1; + + E get lastDescendant { + if (!uIsExpanded.value) return this as E; + if (uChildren.value.isEmpty) return this as E; + + return uChildren.value.last.lastDescendant; + } + + TreeNode() { + UseEffect( + _onIsExpandedChanged, + [uIsExpanded], + ); + + if (list != null) bind(list!); + } + + @override + void unlink() { + if (list == null) return; + + final rList = list; + super.unlink(); + if (rList is RtState) (rList as RtState).notify(); + } + + @override + void insertAfter(E entry) { + if (list == null) return; + + super.insertAfter(entry); + if (list is RtState) (list as RtState).notify(); + } + + @override + void insertBefore(E entry) { + if (list == null) return; + + super.insertBefore(entry); + if (list is RtState) (list as RtState).notify(); + } + + void replace(E entry) { + Rt.batch(() { + if (parent != null) { + entry._parent = parent; + insertBefore(entry); + } + + if (entry.list == null) { + list?.add(entry); + } + + entry.uIsExpanded.value = uIsExpanded.value; + + final children = uChildren.value; + + for (final child in [...children]) { + entry.addChild(child); + } + + children.clear(); + + remove(); + }); + } + + void addChild(E entry) { + Rt.batch(() { + if (entry.parent != this) { + entry.parent?.uChildren.value.remove(entry); + entry.parent?.uChildren.notify(); + entry._parent = this as E; + entry + ..unbind() + ..bind(this); + } + + uChildren.value.add(entry); + uChildren.notify(); + + if (uIsExpanded.value) { + _showChildren(); + } else { + _hideChildren(); + } + }); + } + + bool remove() { + Rt.batch(() { + final children = uChildren.value.toList(); + + for (final node in children) { + node.remove(); + } + + unlink(); + + parent?.uChildren + ?..value.remove(this) + ..notify(); + + if (!isDisposed) dispose(); + }); + + return list == null; + } + + void _onIsExpandedChanged() { + Rt.batch(() { + if (uIsExpanded.value) { + _showChildren(); + } else { + _hideChildren(); + } + }); + } + + void _showChildren() { + Rt.batch(() { + E? prevChild; + + for (final node in uChildren.value) { + node.unlink(); + + if (prevChild == null) { + insertAfter(node); + } else { + prevChild.lastDescendant.insertAfter(node); + } + + if (node.uIsExpanded.value) { + node._showChildren(); + } else { + node._hideChildren(); + } + + prevChild = node; + } + }); + } + + void _hideChildren() { + Rt.batch(() { + for (final node in uChildren.value) { + if (node.list == null) { + continue; + } + + node._hideChildren(); + node.unlink(); + } + }); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/interfaces/node.dart b/packages/reactter_devtools_extension/lib/src/interfaces/node.dart new file mode 100644 index 00000000..f1640f68 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/interfaces/node.dart @@ -0,0 +1,23 @@ +import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/data/tree_node.dart'; +import 'package:reactter_devtools_extension/src/interfaces/node_info.dart'; +import 'package:reactter_devtools_extension/src/data/property_node.dart'; +import 'package:reactter_devtools_extension/src/data/tree_list.dart'; + +abstract base class INode extends TreeNode { + final String key; + final String kind; + final String type; + + final uInfo = UseState(null); + final uIsSelected = UseState(false); + final propertyNodes = TreeList(); + + INode({ + required this.key, + required this.kind, + required this.type, + }); + + void loadDetails(); +} diff --git a/packages/reactter_devtools_extension/lib/src/interfaces/node_info.dart b/packages/reactter_devtools_extension/lib/src/interfaces/node_info.dart new file mode 100644 index 00000000..5cd5eb40 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/interfaces/node_info.dart @@ -0,0 +1,5 @@ +abstract class INodeInfo { + final String? label; + + INodeInfo({this.label}); +} diff --git a/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart b/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart index 8850047c..fcfd96e4 100644 --- a/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart +++ b/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart @@ -1,64 +1,47 @@ -import 'dart:async'; +import 'package:devtools_app_shared/ui.dart'; +import 'package:flutter/material.dart' hide Split; +import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/widgets/nodes_list.dart'; +import 'package:reactter_devtools_extension/src/widgets/properties_list.dart'; +import 'controllers/nodes_controller.dart'; -import 'package:devtools_app_shared/service.dart'; -import 'package:devtools_extensions/devtools_extensions.dart'; -import 'package:flutter/widgets.dart'; - -class ReactterDevtoolsExtension extends StatefulWidget { - const ReactterDevtoolsExtension({super.key}); - - @override - State createState() => - _ReactterDevtoolsExtensionState(); -} - -class _ReactterDevtoolsExtensionState extends State { - late final StreamSubscription extensionEventSubscription; - - @override - void initState() { - super.initState(); - init(); - } - - Future init() async { - final vmService = await serviceManager.onServiceAvailable; - - final evalOnDartLibrary = EvalOnDartLibrary( - 'package:flutter_reactter/src/devtools.dart', - vmService, - serviceManager: serviceManager, - ); - - try { - final instanceRef = await evalOnDartLibrary.safeEval( - 'ReactterDevTools.debugInstance', - isAlive: null, - ); - - print( - 'ReactterDevTools.debugInstance: ${instanceRef.identityHashCode}', - ); - } catch (e) { - print('Error: $e'); - } - - extensionEventSubscription = vmService.onExtensionEvent.listen((event) { - if (!(event.extensionKind?.startsWith('ext.reactter.') ?? false)) { - return; - } - - print( - "Received event: ${event.extensionKind}" - " with data: ${event.extensionData}", - ); - }); - } +class RtDevToolsExtension extends StatelessWidget { + const RtDevToolsExtension({super.key}); @override Widget build(BuildContext context) { - return Container( - child: const Text('Reactter Devtools Extension'), + return RtProvider( + () => NodesController(), + builder: (context, inst, child) { + return SplitPane( + axis: SplitPane.axisFor(context, 0.8), + initialFractions: const [0.40, 0.60], + children: const [ + RoundedOutlinedBorder( + clip: true, + child: Column( + children: [ + AreaPaneHeader(title: Text("Instance Tree")), + Expanded( + child: NodesList(), + ), + ], + ), + ), + RoundedOutlinedBorder( + clip: true, + child: Column( + children: [ + AreaPaneHeader(title: Text("Details")), + Expanded( + child: PropertiesList(), + ), + ], + ), + ), + ], + ); + }, ); } } diff --git a/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart b/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart new file mode 100644 index 00000000..dd05aeef --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart @@ -0,0 +1,131 @@ +import 'package:devtools_app_shared/service.dart'; +import 'package:queue/queue.dart'; +import 'package:reactter_devtools_extension/src/services/eval_service.dart'; +import 'package:vm_service/vm_service.dart'; + +class DevtoolsService { + static final evalsQueue = Queue(parallel: 5); + static final _nodesDisposables = {}; + + Future>> getAllNodes({ + int page = 0, + int pageSize = 20, + }) async { + final eval = await EvalService.devtoolsEval; + final isAlive = Disposable(); + + final pageNodes = await evalsQueue.add( + () => eval.evalInstance( + 'RtDevTools._instance?.getNodes($page, $pageSize)', + isAlive: isAlive, + ), + ); + + assert(pageNodes.kind == InstanceKind.kMap); + + final pageInfo = {}; + + for (final entry in pageNodes.associations!) { + final key = entry.key!.valueAsString!; + final valueRef = entry.value!; + + if (valueRef.kind == InstanceKind.kString) { + pageInfo[key] = valueRef.valueAsString; + } else if (valueRef.kind == InstanceKind.kInt) { + pageInfo[key] = int.tryParse(valueRef.valueAsString); + } else { + pageInfo[key] = valueRef; + } + } + + final totalPages = pageInfo['totalPages'] as int; + final nodesInst = pageInfo['nodes'] as InstanceRef; + final nodes = await eval.safeGetInstance(nodesInst, isAlive); + + assert(nodes.kind == InstanceKind.kList); + + final nodeRefs = nodes.elements!.cast(); + final nodeInfos = >[]; + + for (final nodeRef in nodeRefs) { + final nodeInst = await eval.safeGetInstance(nodeRef, isAlive); + + nodeInfos.add(await getNodeInfo(nodeInst)); + } + + if (page >= totalPages - 1) return nodeInfos; + + final nextNodeInfos = await getAllNodes(page: page + 1, pageSize: pageSize); + + return nodeInfos + nextNodeInfos; + } + + Future> getNodeBykey(String nodeKey) async { + final eval = await EvalService.devtoolsEval; + + final isAlive = _nodesDisposables.putIfAbsent(nodeKey, () => Disposable()); + + final nodeInst = await evalsQueue.add( + () => eval.evalInstance( + 'RtDevTools._instance?._nodesByKey["$nodeKey"]?.toJson()', + isAlive: isAlive, + ), + ); + + if (nodeInst.kind == InstanceKind.kNull) return {}; + + assert(nodeInst.kind == InstanceKind.kMap); + + return getNodeInfo(nodeInst); + } + + void disposeNodeByKey(String nodeKey) { + _nodesDisposables.remove(nodeKey)?.dispose(); + } + + Future> getNodeInfo(Instance nodeInst) async { + final nodeInfo = {}; + + for (final entry in nodeInst.associations!) { + final key = entry.key!.valueAsString!; + final valueRef = entry.value!; + + nodeInfo[key] = await getValueOfInstanceRef(valueRef); + } + + return nodeInfo; + } + + Future getValueOfInstanceRef(InstanceRef instanceRef) async { + final eval = await EvalService.devtoolsEval; + final isAlive = Disposable(); + + final instance = await evalsQueue.add( + () => eval.safeGetInstance(instanceRef, isAlive), + ); + + return await getValueOfInstance(instance); + } + + Future getValueOfInstance(Instance instance) async { + switch (instance.kind) { + case InstanceKind.kNull: + return null; + case InstanceKind.kString: + return instance.valueAsString; + case InstanceKind.kMap: + return await getNodeInfo(instance); + case InstanceKind.kList: + final list = instance.elements!.cast(); + final listValues = []; + + for (final e in list) { + listValues.add(await getValueOfInstanceRef(e)); + } + + return listValues; + default: + return instance.valueAsString; + } + } +} diff --git a/packages/reactter_devtools_extension/lib/src/services/eval_service.dart b/packages/reactter_devtools_extension/lib/src/services/eval_service.dart new file mode 100644 index 00000000..23b3afe6 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/services/eval_service.dart @@ -0,0 +1,19 @@ +import 'package:devtools_app_shared/service.dart'; +import 'package:devtools_extensions/devtools_extensions.dart'; + +class EvalService { + static final devtoolsEval = _getEvalOnDartLibrary( + 'package:reactter/src/devtools.dart', + ); + static final dartEval = _getEvalOnDartLibrary('dart:io'); +} + +Future _getEvalOnDartLibrary(String path) async { + final vmService = await serviceManager.onServiceAvailable; + + return EvalOnDartLibrary( + path, + vmService, + serviceManager: serviceManager, + ); +} diff --git a/packages/reactter_devtools_extension/lib/src/vm.dart b/packages/reactter_devtools_extension/lib/src/vm.dart new file mode 100644 index 00000000..e69de29b diff --git a/packages/reactter_devtools_extension/lib/src/widgets/loading.dart b/packages/reactter_devtools_extension/lib/src/widgets/loading.dart new file mode 100644 index 00000000..7480d31b --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/widgets/loading.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; + +class Loading extends StatelessWidget { + const Loading({super.key}); + + @override + Widget build(BuildContext context) { + return const SizedBox.square( + dimension: 18, + child: FittedBox( + fit: BoxFit.scaleDown, + child: CircularProgressIndicator(), + ), + ); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart new file mode 100644 index 00000000..1a2839ac --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart @@ -0,0 +1,201 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/interfaces/node.dart'; + +enum NodeKind { + instance(label: 'I', color: Colors.orange), + state(label: 'S', color: Colors.blue), + hook(label: 'H', color: Colors.purple), + signal(label: 'S', color: Colors.green); + + const NodeKind({ + required this.label, + required this.color, + }); + + final String label; + final Color color; + + static NodeKind getKind(String kind) { + switch (kind) { + case 'instance': + return instance; + case 'state': + return state; + case 'hook': + return hook; + case 'signal': + return signal; + default: + return instance; + } + } +} + +class NodeTile extends StatelessWidget { + final INode node; + final void Function()? onTap; + + const NodeTile({ + super.key, + required this.node, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return RtWatcher((context, watch) { + final isSelected = watch(node.uIsSelected).value; + + return ListTile( + dense: true, + visualDensity: const VisualDensity(vertical: 0, horizontal: 0), + horizontalTitleGap: 0, + minVerticalPadding: 0, + contentPadding: EdgeInsets.zero, + minTileHeight: 0, + selected: isSelected, + selectedTileColor: Theme.of(context).focusColor, + onTap: onTap, + // leading: NodeTileLeading(node: node), + title: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + ...List.generate( + node.depth.toInt() - 1, + (index) => Padding( + padding: EdgeInsets.only( + left: 20, + right: (index == node.depth.toInt() - 2) ? 0 : 0, + ), + child: const VerticalDivider(width: 1), + ), + ), + NodeTileLeading(node: node), + NodeTileTitle(node: node), + ], + ), + ); + }); + } +} + +class NodeTileLeading extends StatelessWidget { + final INode node; + + const NodeTileLeading({super.key, required this.node}); + + @override + Widget build(BuildContext context) { + return RtWatcher((context, watch) { + final children = watch(node.uChildren).value; + + if (children.isEmpty) { + return const SizedBox(width: 11); + } + + final isExpanded = watch(node.uIsExpanded).value; + + return Padding( + padding: const EdgeInsets.only(left: 8), + child: IconButton( + padding: EdgeInsets.zero, + splashRadius: 18, + iconSize: 24, + constraints: const BoxConstraints.tightForFinite(), + icon: Icon( + isExpanded ? Icons.keyboard_arrow_down : Icons.keyboard_arrow_right, + ), + onPressed: () { + node.uIsExpanded.value = !isExpanded; + }, + ), + ); + }); + } +} + +class NodeTileIcon extends StatelessWidget { + final String kind; + + const NodeTileIcon({super.key, required this.kind}); + + @override + Widget build(BuildContext context) { + final nodeKind = NodeKind.getKind(kind); + + return CircleAvatar( + backgroundColor: nodeKind.color, + child: Text( + nodeKind.label, + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + ); + } +} + +class NodeTileTitle extends StatelessWidget { + final INode node; + + const NodeTileTitle({ + super.key, + required this.node, + }); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + SizedBox.square( + dimension: 24, + child: Padding( + padding: const EdgeInsets.all(4).copyWith(left: 0, right: 4), + child: NodeTileIcon(kind: node.kind), + ), + ), + RtWatcher((context, watch) { + final info = watch(node.uInfo).value; + + return RichText( + selectionRegistrar: SelectionContainer.maybeOf(context), + selectionColor: Theme.of(context).highlightColor, + text: TextSpan( + style: Theme.of(context).textTheme.labelSmall, + children: [ + TextSpan( + text: node.type, + style: Theme.of(context) + .textTheme + .labelSmall + ?.copyWith(color: Colors.amber), + ), + if (info?.label != null) + TextSpan( + children: [ + const TextSpan(text: "("), + TextSpan( + text: info?.label!, + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: Theme.of(context).colorScheme.primary, + ), + ), + const TextSpan(text: ")"), + ], + ), + TextSpan( + text: " #${node.key}", + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ), + ], + ), + ); + }), + ], + ); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/widgets/nodes_list.dart b/packages/reactter_devtools_extension/lib/src/widgets/nodes_list.dart new file mode 100644 index 00000000..a64561bf --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/widgets/nodes_list.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/controllers/nodes_controller.dart'; +import 'package:reactter_devtools_extension/src/widgets/node_tile.dart'; +import 'package:reactter_devtools_extension/src/widgets/offset_scrollbar.dart'; + +class NodesList extends StatelessWidget { + const NodesList({super.key}); + + @override + Widget build(BuildContext context) { + final listKey = GlobalKey(); + final scrollControllerX = ScrollController(); + final scrollControllerY = ScrollController(); + final focusNode = FocusNode(); + final nodesController = context.use(); + + return LayoutBuilder( + builder: (context, constraints) { + final viewportWidth = constraints.maxWidth; + + return Scrollbar( + controller: scrollControllerX, + thumbVisibility: true, + child: SingleChildScrollView( + controller: scrollControllerX, + scrollDirection: Axis.horizontal, + child: Material( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 1000.00), + child: OffsetScrollbar( + isAlwaysShown: true, + axis: Axis.vertical, + controller: scrollControllerY, + offsetController: scrollControllerX, + offsetControllerViewportDimension: viewportWidth, + child: SelectableRegion( + focusNode: focusNode, + selectionControls: materialTextSelectionControls, + child: RtWatcher((context, watch) { + final length = watch(nodesController.nodesList).length; + + return ListView.custom( + key: listKey, + controller: scrollControllerY, + itemExtent: 24, + childrenDelegate: SliverChildBuilderDelegate( + (context, index) { + final node = + nodesController.nodesList.elementAt(index); + + return NodeTile( + key: Key(node.key), + node: node, + onTap: () => nodesController.selectNode(node), + ); + }, + childCount: length, + ), + ); + }), + ), + ), + ), + ), + ), + ); + }, + ); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/widgets/offset_scrollbar.dart b/packages/reactter_devtools_extension/lib/src/widgets/offset_scrollbar.dart new file mode 100644 index 00000000..ec471eff --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/widgets/offset_scrollbar.dart @@ -0,0 +1,100 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; + +/// Scrollbar that is offset by the amount specified by an [offsetController]. +/// +/// This makes it possible to create a [ListView] with both vertical and +/// horizontal scrollbars by wrapping the [ListView] in a +/// [SingleChildScrollView] that handles horizontal scrolling. The +/// [offsetController] is the offset of the parent [SingleChildScrollView] in +/// this example. +/// +/// This class could be optimized if performance was a concern using a +/// [CustomPainter] instead of an [AnimatedBuilder] so that the +/// [OffsetScrollbar] widget does not need to build on each change to the +/// [offsetController]. +class OffsetScrollbar extends StatefulWidget { + const OffsetScrollbar({ + super.key, + this.isAlwaysShown = false, + required this.axis, + required this.controller, + required this.offsetController, + required this.child, + required this.offsetControllerViewportDimension, + }); + + final bool isAlwaysShown; + final Axis axis; + final ScrollController controller; + final ScrollController offsetController; + final Widget child; + + /// The current viewport dimension of the offsetController may not be + /// available at build time as it is not updated until later so we require + /// that the known correct viewport dimension is passed into this class. + /// + /// This is a workaround because we use an AnimatedBuilder to listen for + /// changes to the offsetController rather than displaying the scrollbar at + /// paint time which would be more difficult. + final double offsetControllerViewportDimension; + + @override + State createState() => _OffsetScrollbarState(); +} + +class _OffsetScrollbarState extends State { + @override + Widget build(BuildContext context) { + if (!widget.offsetController.position.hasContentDimensions) { + SchedulerBinding.instance.addPostFrameCallback((timeStamp) { + if (widget.offsetController.position.hasViewportDimension && mounted) { + // TODO(jacobr): find a cleaner way to be notified that the + // offsetController now has a valid dimension. We would probably + // have to implement our own ScrollbarPainter instead of being able + // to use the existing Scrollbar widget. + setState(() {}); + } + }); + } + return AnimatedBuilder( + animation: widget.offsetController, + builder: (context, child) { + // Compute a delta to move the scrollbar from where it is by default to + // where it should be given the viewport dimension of the + // offsetController not the viewport that is the entire scroll extent + // of the offsetController because this controller is nested within the + // offset controller. + double delta = 0.0; + if (widget.offsetController.position.hasContentDimensions) { + delta = widget.offsetController.offset - + widget.offsetController.position.maxScrollExtent + + widget.offsetController.position.minScrollExtent; + if (widget.offsetController.position.hasViewportDimension) { + // TODO(jacobr): this is a bit of a hack. + // The viewport dimension from the offsetController may be one frame + // behind the true viewport dimension. We add this delta so the + // scrollbar always appears stuck to the side of the viewport. + delta += widget.offsetControllerViewportDimension - + widget.offsetController.position.viewportDimension; + } + } + final offset = widget.axis == Axis.vertical + ? Offset(delta, 0.0) + : Offset(0.0, delta); + return Transform.translate( + offset: offset, + child: Scrollbar( + thumbVisibility: widget.isAlwaysShown, + controller: widget.controller, + child: Transform.translate( + offset: -offset, + child: child, + ), + ), + ); + }, + child: widget.child, + ); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/widgets/properties_list.dart b/packages/reactter_devtools_extension/lib/src/widgets/properties_list.dart new file mode 100644 index 00000000..fe362942 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/widgets/properties_list.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/controllers/nodes_controller.dart'; +import 'package:reactter_devtools_extension/src/widgets/property_tile.dart'; + +class PropertiesList extends StatelessWidget { + const PropertiesList({super.key}); + + @override + Widget build(BuildContext context) { + final focusNode = FocusNode(); + final inst = context.use(); + + return SelectableRegion( + focusNode: focusNode, + selectionControls: materialTextSelectionControls, + child: Scrollbar( + child: SizedBox.expand( + child: RtWatcher( + (context, watch) { + watch(inst.uCurrentNodeKey).value; + + if (inst.currentNode == null) { + return const SizedBox(); + } + + final properties = watch(inst.currentNode!.propertyNodes); + + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Padding( + // padding: const EdgeInsets.all(8.0).copyWith(bottom: 4), + // child: Text("Debug info", + // style: Theme.of(context).textTheme.bodyMedium), + // ), + Expanded( + child: ListView.builder( + itemCount: properties.length, + itemBuilder: (context, index) { + final propertyNode = properties.elementAt(index); + + return PropertyTile( + key: ObjectKey(propertyNode), + propertyNode: propertyNode, + ); + }, + ), + ), + ], + ); + }, + ), + ), + ), + ); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart new file mode 100644 index 00000000..b357eb36 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/data/property_node.dart'; + +class PropertyTile extends StatelessWidget { + const PropertyTile({ + super.key, + required this.propertyNode, + }); + + final PropertyNode propertyNode; + + @override + Widget build(BuildContext context) { + return RtWatcher((context, watch) { + return ListTile( + dense: true, + visualDensity: const VisualDensity(vertical: 0, horizontal: 2), + contentPadding: const EdgeInsets.symmetric(horizontal: 8), + minVerticalPadding: 4, + minTileHeight: 0, + title: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "${watch(propertyNode).key}: ", + style: Theme.of(context).textTheme.labelSmall, + ), + Flexible( + child: Text( + "${watch(propertyNode).value}", + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: Theme.of(context).colorScheme.primary, + ), + ), + ), + ], + ), + onTap: () {}, + ); + }); + } +} diff --git a/packages/reactter_devtools_extension/pubspec.lock b/packages/reactter_devtools_extension/pubspec.lock index f0bb5e22..01cc07bd 100644 --- a/packages/reactter_devtools_extension/pubspec.lock +++ b/packages/reactter_devtools_extension/pubspec.lock @@ -57,38 +57,38 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 - url: "https://pub.dev" - source: hosted - version: "1.0.8" devtools_app_shared: dependency: "direct main" description: name: devtools_app_shared - sha256: b8dbeb8352ffaff88fc1e00e0a5f1a659003fb75e0ff1250ec2add6ad6cd54c3 + sha256: ce205ca88895e2d44321e6f92f33db761132e2927f4edcc63221cc10267c2f61 url: "https://pub.dev" source: hosted - version: "0.0.9" + version: "0.1.1" devtools_extensions: dependency: "direct main" description: name: devtools_extensions - sha256: "1d1ece240f182f42374694ca743eb0bb10838c801d680d8523151a10f5464901" + sha256: "109355b1e66c6a806d9166953c0ce0cd45a008c5b53b5a0479dfb428887d18c6" url: "https://pub.dev" source: hosted - version: "0.0.13" + version: "0.1.1" devtools_shared: dependency: transitive description: name: devtools_shared - sha256: "1f46a18cf389dd47b10e1b963cff463c0fede747b8c0fc787d2455eb034135e8" + sha256: e5fa35a08b7728bb835982784f4b261f35cec0eec897698cc91e815caa92d10e + url: "https://pub.dev" + source: hosted + version: "8.1.1" + dtd: + dependency: transitive + description: + name: dtd + sha256: "0d4a51ab223090d2d6b86477f414052db78cad1b2de020619f454a2a39369fec" url: "https://pub.dev" source: hosted - version: "6.0.4" + version: "2.1.0" extension_discovery: dependency: transitive description: @@ -105,6 +105,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" flutter: dependency: "direct main" description: flutter @@ -114,15 +122,30 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "4.0.0" + flutter_reactter: + dependency: "direct main" + description: + path: "../flutter_reactter" + relative: true + source: path + version: "7.3.1" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + http: + dependency: transitive + description: + name: http + sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba + url: "https://pub.dev" + source: hosted + version: "1.2.0" http_parser: dependency: transitive description: @@ -131,6 +154,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + intl: + dependency: transitive + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.dev" + source: hosted + version: "0.19.0" io: dependency: transitive description: @@ -147,38 +178,46 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" + json_rpc_2: + dependency: transitive + description: + name: json_rpc_2 + sha256: "5e469bffa23899edacb7b22787780068d650b106a21c76db3c49218ab7ca447e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "4.0.0" logging: dependency: transitive description: @@ -199,18 +238,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.15.0" path: dependency: transitive description: @@ -235,6 +274,21 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" + queue: + dependency: "direct main" + description: + name: queue + sha256: "9a41ecadc15db79010108c06eae229a45c56b18db699760f34e8c9ac9b831ff9" + url: "https://pub.dev" + source: hosted + version: "3.1.0+2" + reactter: + dependency: transitive + description: + path: "../reactter" + relative: true + source: path + version: "7.3.0" shelf: dependency: transitive description: @@ -300,10 +354,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.2" typed_data: dependency: transitive description: @@ -312,6 +366,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + unified_analytics: + dependency: transitive + description: + name: unified_analytics + sha256: "099bb7f0d7fe4d1cc551a81cb1d13ff45005443f871a1429d18ccdbdabee4c9b" + url: "https://pub.dev" + source: hosted + version: "5.8.8+2" usage: dependency: transitive description: @@ -332,10 +394,10 @@ packages: dependency: "direct main" description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.5" web: dependency: transitive description: @@ -377,5 +439,5 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=3.3.1 <4.0.0" - flutter: ">=3.17.0-0.0.pre" + dart: ">=3.4.0-282.1.beta <4.0.0" + flutter: ">=3.22.0-0.1.pre" diff --git a/packages/reactter_devtools_extension/pubspec.yaml b/packages/reactter_devtools_extension/pubspec.yaml index 2fa825c5..ea7ae01d 100644 --- a/packages/reactter_devtools_extension/pubspec.yaml +++ b/packages/reactter_devtools_extension/pubspec.yaml @@ -9,15 +9,17 @@ environment: dependencies: flutter: sdk: flutter - cupertino_icons: ^1.0.6 - devtools_extensions: ^0.0.13 - devtools_app_shared: ^0.0.9 - vm_service: ^13.0.0 + devtools_extensions: ^0.1.1 + devtools_app_shared: ^0.1.1 + vm_service: ^14.2.5 + queue: ^3.1.0+2 + flutter_reactter: + path: ../flutter_reactter dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^3.0.0 + flutter_lints: ^4.0.0 flutter: uses-material-design: true @@ -26,4 +28,4 @@ scripts: dev: flutter run -d chrome --dart-define=use_simulated_environment=true build: $before: flutter pub get - $script: dart run devtools_extensions build_and_copy --source=. --dest=../flutter_reactter/extension/devtools + $script: dart run devtools_extensions build_and_copy --source=. --dest=../reactter/extension/devtools diff --git a/packages/reactter_devtools_extension/web/index.html b/packages/reactter_devtools_extension/web/index.html index 07c43f45..3b19c58e 100644 --- a/packages/reactter_devtools_extension/web/index.html +++ b/packages/reactter_devtools_extension/web/index.html @@ -1,7 +1,7 @@ - - - - - - - - - - - - - - - - - - reactter_devtools_extension - - - - - - - - - + + + + + + + + + + + + + + + + reactter_devtools_extension + + + + + From aeb5c47253c1e9d2031b327508f79e6cf4f21601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Thu, 31 Oct 2024 23:47:06 -0600 Subject: [PATCH 038/141] refactor(devtools): Add devtools_options.yaml for configuration settings. --- packages/reactter/devtools_options.yaml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 packages/reactter/devtools_options.yaml diff --git a/packages/reactter/devtools_options.yaml b/packages/reactter/devtools_options.yaml new file mode 100644 index 00000000..fa0b357c --- /dev/null +++ b/packages/reactter/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: From 91db2fe3c071b041fddc46a5a1b1b8946ef3e22b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Sun, 3 Nov 2024 00:20:55 -0600 Subject: [PATCH 039/141] feat(devtools): Enhance `PropertyNode` with asynchronous value loading and improved value resolution methods. --- packages/reactter/lib/src/devtools.dart | 22 +- .../lib/src/controllers/nodes_controller.dart | 30 +-- .../lib/src/data/constants.dart | 16 ++ .../lib/src/data/property_node.dart | 234 ++++++++++++++++-- .../lib/src/services/devtools_service.dart | 89 +------ .../lib/src/services/eval_service.dart | 2 + .../lib/src/utils/extensions.dart | 108 ++++++++ .../lib/src/widgets/property_tile.dart | 8 +- 8 files changed, 378 insertions(+), 131 deletions(-) create mode 100644 packages/reactter_devtools_extension/lib/src/data/constants.dart create mode 100644 packages/reactter_devtools_extension/lib/src/utils/extensions.dart diff --git a/packages/reactter/lib/src/devtools.dart b/packages/reactter/lib/src/devtools.dart index 9c7f1457..0767dbcd 100644 --- a/packages/reactter/lib/src/devtools.dart +++ b/packages/reactter/lib/src/devtools.dart @@ -320,6 +320,14 @@ class RtDevTools with RtStateObserver, RtDependencyObserver { ) as _InstanceNode; } + Map? getDebugInfo(String stateKey) { + final state = _nodesByKey[stateKey]; + + if (state is! _StateNode) return null; + + return state.instance.debugInfo; + } + Map getDynamicInfo(Object instance) { if (instance is DependencyRef) { return getDependencyInfo(instance); @@ -345,7 +353,6 @@ class RtDevTools with RtStateObserver, RtDependencyObserver { 'key': state.hashCode.toString(), 'type': state.runtimeType.toString(), 'debugLabel': state.debugLabel, - 'debugInfo': state.debugInfo, 'boundInstanceKey': state.boundInstance?.hashCode.toString(), }; } @@ -370,6 +377,18 @@ class RtDevTools with RtStateObserver, RtDependencyObserver { } } + Map getPlainInstanceInfo(Object instance) { + if (instance is DependencyRef) { + return getDependencyInfo(instance); + } + + if (instance is RtState) { + return getStateInfo(instance); + } + + return getInstanceInfo(instance); + } + String getListString(List data) { var listString = data.toString(); if (listString.length > 60) { @@ -529,7 +548,6 @@ class _StateNode extends _Node { 'key': key, 'type': instance.runtimeType.toString(), 'debugLabel': instance.debugLabel, - 'debugInfo': instance.debugInfo, 'boundInstanceKey': instance.boundInstance?.hashCode.toString(), 'dependencyRef': Rt.getDependencyRef(instance)?.hashCode.toString(), }; diff --git a/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart b/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart index a6e184ec..d7ac7f71 100644 --- a/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart +++ b/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart @@ -13,12 +13,6 @@ import 'package:reactter_devtools_extension/src/interfaces/node.dart'; import 'package:reactter_devtools_extension/src/services/devtools_service.dart'; import 'package:reactter_devtools_extension/src/data/state_node.dart'; -enum NodeType { - dependency, - state, - instance, -} - class NodesController { StreamSubscription? extEventSubscription; @@ -47,32 +41,32 @@ class NodesController { switch (event.extensionKind) { case 'ext.reactter.onStateCreated': - final stateKey = eventData['stateKey'] as String; + final String stateKey = eventData['stateKey']; addNodeByKey(stateKey); break; case 'ext.reactter.onStateBound': - final instanceKey = eventData['instanceKey'] as String; - final stateKey = eventData['stateKey'] as String; + final String instanceKey = eventData['instanceKey']; + final String stateKey = eventData['stateKey']; addNodeByKey(instanceKey); addNodeByKey(stateKey); break; case 'ext.reactter.onStateUnbound': - final instanceKey = eventData['instanceKey'] as String; - final isInstanceRemoved = eventData['isInstanceRemoved'] as bool; + final String instanceKey = eventData['instanceKey']; + final bool isInstanceRemoved = eventData['isInstanceRemoved']; if (isInstanceRemoved) removeNodeByKey(instanceKey); break; case 'ext.reactter.onStateDisposed': - final stateKey = eventData['stateKey'] as String; - final isStateRemoved = eventData['isStateRemoved'] as bool; + final String stateKey = eventData['stateKey']; + final bool isStateRemoved = eventData['isStateRemoved']; if (isStateRemoved) removeNodeByKey(stateKey); break; case 'ext.reactter.onDependencyCreated': - final instanceKey = eventData['instanceKey'] as String; + final String instanceKey = eventData['instanceKey']; addNodeByKey(instanceKey); break; case 'ext.reactter.onDependencyDeleted': - final instanceKey = eventData['instanceKey'] as String; - final isInstanceRemoved = eventData['isInstanceRemoved'] as bool; + final String instanceKey = eventData['instanceKey']; + final bool isInstanceRemoved = eventData['isInstanceRemoved']; if (isInstanceRemoved) removeNodeByKey(instanceKey); } }); @@ -89,7 +83,7 @@ class NodesController { print('addNodeByKey $nodeKey ${nodeInfo['boundInstanceKey']}'); } - void addNodes(List> nodesInfo) { + void addNodes(List nodesInfo) { Rt.batch(() { for (final nodeInfo in nodesInfo) { addNodeByMapInfo(nodeInfo); @@ -100,7 +94,7 @@ class NodesController { }); } - void addNodeByMapInfo(Map nodeInfo) { + void addNodeByMapInfo(Map nodeInfo) { final kind = nodeInfo['kind']; final key = nodeInfo['key']; final type = nodeInfo['type']; diff --git a/packages/reactter_devtools_extension/lib/src/data/constants.dart b/packages/reactter_devtools_extension/lib/src/data/constants.dart new file mode 100644 index 00000000..edb4e133 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/data/constants.dart @@ -0,0 +1,16 @@ +enum NodeType { + dependency('dependency'), + state('state'), + instance('instance'); + + final String value; + + const NodeType(this.value); + + factory NodeType.fromString(String value) { + return NodeType.values.firstWhere( + (e) => e.value == value, + orElse: () => NodeType.instance, + ); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/data/property_node.dart b/packages/reactter_devtools_extension/lib/src/data/property_node.dart index ab46819d..9e23f21e 100644 --- a/packages/reactter_devtools_extension/lib/src/data/property_node.dart +++ b/packages/reactter_devtools_extension/lib/src/data/property_node.dart @@ -1,29 +1,32 @@ import 'package:devtools_app_shared/service.dart'; import 'package:flutter_reactter/reactter.dart'; +import 'package:reactter_devtools_extension/src/data/constants.dart'; import 'package:reactter_devtools_extension/src/data/tree_node.dart'; import 'package:reactter_devtools_extension/src/services/eval_service.dart'; +import 'package:reactter_devtools_extension/src/utils/extensions.dart'; import 'package:vm_service/vm_service.dart'; +const _kMaxValueLength = 80; + base class PropertyNode extends TreeNode { final String key; final InstanceRef valueRef; + final instanceInfo = UseState?>(null); + + final uIsLoading = UseState(false); String? _value; - String? get value => _value; + String? get value { + if (_value == null) _loadValueAsync(); + + return _value; + } PropertyNode._({ required this.key, required this.valueRef, }) { - if (valueRef.kind == InstanceKind.kString) { - _value = '"${valueRef.valueAsString}"'; - } else { - _value = valueRef.valueAsString; - } - - if (_value == null) { - _loadValue(); - } + uIsExpanded.value = false; } factory PropertyNode({ @@ -39,25 +42,204 @@ base class PropertyNode extends TreeNode { ); } - Future _loadValue() async { - final eval = await EvalService.devtoolsEval; - final isAlive = Disposable(); + Future _loadValueAsync() async { + if (uIsLoading.value) return; - final value = await eval.evalInstance( - 'RtDevTools._instance?.getPropertyValue(value)', - isAlive: isAlive, - scope: {'value': valueRef.id!}, - ); + uIsLoading.value = true; - final isIterable = await eval.evalInstance( - 'value is Iterable', - isAlive: isAlive, - scope: {'value': valueRef.id!}, - ); + await Rt.batchAsync(() async { + try { + switch (valueRef.kind) { + case InstanceKind.kNull: + _value = 'null'; + break; + case InstanceKind.kString: + _value = '"${valueRef.valueAsString}"'; + break; + case InstanceKind.kMap: + await _resolveValueByMap(); + break; + case InstanceKind.kList: + await _resolveValueByList(); + break; + case InstanceKind.kPlainInstance: + await _resolveValueByPlainInstance(); + break; + default: + _value = valueRef.valueAsString; + break; + } + } catch (e) { + _value = 'Error: $e'; + } finally { + uIsLoading.value = false; + notify(); + } + }); + } + + Future _resolveValueByList() async { + assert(valueRef.kind == InstanceKind.kList); + + try { + final isAlive = Disposable(); + final instance = await valueRef.safeGetInstance(isAlive); + final elements = instance?.elements?.cast(); + + _value = '[...]'; + + if (elements?.isEmpty ?? true) return; + + for (var i = 0; i < elements!.length; i++) { + addChild(PropertyNode( + key: i.toString(), + valueRef: elements[i], + )); + } + + await _resolveValueByChildren( + buildValue: (node) => '${node.value}', + prefix: '[', + suffix: ']', + ); + } catch (e) { + print('_resolveValueByList error: $e'); + } finally { + notify(); + } + } + + Future _resolveValueByMap() async { + assert(valueRef.kind == InstanceKind.kMap); + + try { + final isAlive = Disposable(); + final instance = await valueRef.safeGetInstance(isAlive); + final associations = instance?.associations; + + _value = '{...}'; + + if (associations == null) return; + + for (final entry in associations) { + final InstanceRef keyRef = entry.key; + final InstanceRef valueRef = entry.value; + + final key = await keyRef.evalValueFirstLevel(isAlive); + + addChild(PropertyNode( + key: key.toString(), + valueRef: valueRef, + )); + } - print(isIterable); + await _resolveValueByChildren( + buildValue: (node) => '${node.key}: ${node.value}', + prefix: '{', + suffix: '}', + ); + } catch (e) { + print('_resolveMap error: $e'); + } finally { + notify(); + } + + return; + } + + Future _resolveValueByChildren({ + required String Function(PropertyNode node) buildValue, + String prefix = '{', + String suffix = '}', + }) async { + final children = uChildren.value.toList(); + + _value = '$prefix...$suffix'; + + if (children.isEmpty) return; + + final childrenValueBuffer = StringBuffer(); + var isFull = true; + + for (var i = 0; i < children.length; i++) { + final child = children[i]; + final isLast = i == children.length - 1; + await child._loadValueAsync(); + + childrenValueBuffer.write("${buildValue(child)}${isLast ? '' : ', '}"); + + if (childrenValueBuffer.length > _kMaxValueLength) { + isFull = isLast; + break; + } + } + final moreEllipsis = isFull ? '' : ', ...'; + final maxValueBufferLength = + _kMaxValueLength - prefix.length - moreEllipsis.length - suffix.length; + final childrenValue = childrenValueBuffer.toString(); + final shouldBeCutted = childrenValue.length > maxValueBufferLength; + final cuttedEllipsis = shouldBeCutted ? '...' : ''; + final childrenValueCutted = shouldBeCutted + ? childrenValue.substring( + 0, + maxValueBufferLength - cuttedEllipsis.length, + ) + : childrenValue; + + _value = "$prefix$childrenValueCutted$cuttedEllipsis$moreEllipsis$suffix"; + } + + Future _resolveValueByPlainInstance() async { + assert(valueRef.kind == InstanceKind.kPlainInstance); + + try { + final isAlive = Disposable(); + final eval = await EvalService.devtoolsEval; + final valueInfo = await EvalService.evalsQueue.add( + () => eval.evalInstance( + 'RtDevTools._instance?.getPlainInstanceInfo(value)', + isAlive: isAlive, + scope: {'value': valueRef.id!}, + ), + ); + + if (valueInfo.kind != InstanceKind.kMap) return; + + final valueInfoMap = await valueInfo.evalValue(isAlive); + + if (valueInfoMap is! Map) return; + + instanceInfo.value = valueInfoMap.cast(); + + final String kind = valueInfoMap['kind']; + final String key = valueInfoMap['key']; + final String type = valueInfoMap['type']; + + switch (NodeType.fromString(kind)) { + case NodeType.state: + final String? debugLabel = valueInfoMap['debugLabel']; + + if (debugLabel?.isNotEmpty ?? false) { + _value = "$type($debugLabel)#$key"; + break; + } + case NodeType.dependency: + final String? id = valueInfoMap['id']; + + if (id?.isNotEmpty ?? false) { + _value = "$type($id)#$key"; + break; + } + default: + _value = "$type#$key"; + break; + } + } catch (e) { + print('loadValueFromPlainInstance error: $e'); + } finally { + notify(); + } - _value = value.valueAsString; - notify(); + return; } } diff --git a/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart b/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart index dd05aeef..1646cebf 100644 --- a/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart +++ b/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart @@ -1,20 +1,19 @@ import 'package:devtools_app_shared/service.dart'; -import 'package:queue/queue.dart'; import 'package:reactter_devtools_extension/src/services/eval_service.dart'; +import 'package:reactter_devtools_extension/src/utils/extensions.dart'; import 'package:vm_service/vm_service.dart'; class DevtoolsService { - static final evalsQueue = Queue(parallel: 5); static final _nodesDisposables = {}; - Future>> getAllNodes({ + Future> getAllNodes({ int page = 0, int pageSize = 20, }) async { final eval = await EvalService.devtoolsEval; final isAlive = Disposable(); - final pageNodes = await evalsQueue.add( + final pageNodes = await EvalService.evalsQueue.add( () => eval.evalInstance( 'RtDevTools._instance?.getNodes($page, $pageSize)', isAlive: isAlive, @@ -23,35 +22,9 @@ class DevtoolsService { assert(pageNodes.kind == InstanceKind.kMap); - final pageInfo = {}; - - for (final entry in pageNodes.associations!) { - final key = entry.key!.valueAsString!; - final valueRef = entry.value!; - - if (valueRef.kind == InstanceKind.kString) { - pageInfo[key] = valueRef.valueAsString; - } else if (valueRef.kind == InstanceKind.kInt) { - pageInfo[key] = int.tryParse(valueRef.valueAsString); - } else { - pageInfo[key] = valueRef; - } - } - + final Map pageInfo = await pageNodes.evalValue(isAlive); final totalPages = pageInfo['totalPages'] as int; - final nodesInst = pageInfo['nodes'] as InstanceRef; - final nodes = await eval.safeGetInstance(nodesInst, isAlive); - - assert(nodes.kind == InstanceKind.kList); - - final nodeRefs = nodes.elements!.cast(); - final nodeInfos = >[]; - - for (final nodeRef in nodeRefs) { - final nodeInst = await eval.safeGetInstance(nodeRef, isAlive); - - nodeInfos.add(await getNodeInfo(nodeInst)); - } + final nodeInfos = (pageInfo['nodes'] as List).cast(); if (page >= totalPages - 1) return nodeInfos; @@ -60,12 +33,12 @@ class DevtoolsService { return nodeInfos + nextNodeInfos; } - Future> getNodeBykey(String nodeKey) async { + Future getNodeBykey(String nodeKey) async { final eval = await EvalService.devtoolsEval; final isAlive = _nodesDisposables.putIfAbsent(nodeKey, () => Disposable()); - final nodeInst = await evalsQueue.add( + final nodeInst = await EvalService.evalsQueue.add( () => eval.evalInstance( 'RtDevTools._instance?._nodesByKey["$nodeKey"]?.toJson()', isAlive: isAlive, @@ -76,56 +49,10 @@ class DevtoolsService { assert(nodeInst.kind == InstanceKind.kMap); - return getNodeInfo(nodeInst); + return await nodeInst.evalValue(isAlive); } void disposeNodeByKey(String nodeKey) { _nodesDisposables.remove(nodeKey)?.dispose(); } - - Future> getNodeInfo(Instance nodeInst) async { - final nodeInfo = {}; - - for (final entry in nodeInst.associations!) { - final key = entry.key!.valueAsString!; - final valueRef = entry.value!; - - nodeInfo[key] = await getValueOfInstanceRef(valueRef); - } - - return nodeInfo; - } - - Future getValueOfInstanceRef(InstanceRef instanceRef) async { - final eval = await EvalService.devtoolsEval; - final isAlive = Disposable(); - - final instance = await evalsQueue.add( - () => eval.safeGetInstance(instanceRef, isAlive), - ); - - return await getValueOfInstance(instance); - } - - Future getValueOfInstance(Instance instance) async { - switch (instance.kind) { - case InstanceKind.kNull: - return null; - case InstanceKind.kString: - return instance.valueAsString; - case InstanceKind.kMap: - return await getNodeInfo(instance); - case InstanceKind.kList: - final list = instance.elements!.cast(); - final listValues = []; - - for (final e in list) { - listValues.add(await getValueOfInstanceRef(e)); - } - - return listValues; - default: - return instance.valueAsString; - } - } } diff --git a/packages/reactter_devtools_extension/lib/src/services/eval_service.dart b/packages/reactter_devtools_extension/lib/src/services/eval_service.dart index 23b3afe6..01385827 100644 --- a/packages/reactter_devtools_extension/lib/src/services/eval_service.dart +++ b/packages/reactter_devtools_extension/lib/src/services/eval_service.dart @@ -1,7 +1,9 @@ import 'package:devtools_app_shared/service.dart'; import 'package:devtools_extensions/devtools_extensions.dart'; +import 'package:queue/queue.dart'; class EvalService { + static final evalsQueue = Queue(parallel: 5); static final devtoolsEval = _getEvalOnDartLibrary( 'package:reactter/src/devtools.dart', ); diff --git a/packages/reactter_devtools_extension/lib/src/utils/extensions.dart b/packages/reactter_devtools_extension/lib/src/utils/extensions.dart new file mode 100644 index 00000000..79f185e6 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/utils/extensions.dart @@ -0,0 +1,108 @@ +import 'package:devtools_app_shared/service.dart'; +import 'package:reactter_devtools_extension/src/services/eval_service.dart'; +import 'package:vm_service/vm_service.dart'; + +extension InstanceExt on Instance { + Future evalValue([Disposable? isAlive]) async { + switch (kind) { + case InstanceKind.kNull: + return null; + case InstanceKind.kString: + return valueAsString; + case InstanceKind.kDouble: + return double.tryParse(valueAsString!); + case InstanceKind.kInt: + return int.tryParse(valueAsString!); + case InstanceKind.kBool: + return bool.tryParse(valueAsString!); + case InstanceKind.kMap: + final nodeInfo = {}; + + for (final entry in associations!) { + final InstanceRef keyRef = entry.key; + final InstanceRef valueRef = entry.value; + + nodeInfo[await keyRef.evalValue(isAlive)] = + await valueRef.evalValue(isAlive); + } + + return nodeInfo; + case InstanceKind.kList: + final list = elements!.cast(); + final listValues = []; + + for (final e in list) { + final value = await e.evalValue(isAlive); + listValues.add(value); + } + + return listValues; + default: + return valueAsString; + } + } + + dynamic evalValueFirstLevel() { + switch (kind) { + case InstanceKind.kNull: + return null; + case InstanceKind.kString: + return valueAsString; + case InstanceKind.kDouble: + return double.tryParse(valueAsString!); + case InstanceKind.kInt: + return int.tryParse(valueAsString!); + case InstanceKind.kBool: + return bool.tryParse(valueAsString!); + default: + return this; + } + } + + String safeValue() { + switch (kind) { + case InstanceKind.kNull: + return 'null'; + case InstanceKind.kString: + return '"$valueAsString"'; + case InstanceKind.kMap: + return '{...}'; + case InstanceKind.kList: + return '[...]'; + default: + return valueAsString ?? 'unknown'; + } + } +} + +extension InstanceRefExt on InstanceRef { + Future safeGetInstance([Disposable? isAlive]) async { + try { + final eval = await EvalService.devtoolsEval; + + final instance = await EvalService.evalsQueue.add( + () => eval.safeGetInstance(this, isAlive), + ); + + return instance; + } catch (e) { + print('safeGetInstance error: $e'); + return null; + } + } + + Future evalValue([Disposable? isAlive]) async { + final instance = await safeGetInstance(isAlive); + return await instance?.evalValue(isAlive); + } + + Future evalValueFirstLevel([Disposable? isAlive]) async { + final instance = await safeGetInstance(isAlive); + return instance?.evalValueFirstLevel(); + } + + Future safeValue([Disposable? isAlive]) async { + final instance = await safeGetInstance(isAlive); + return instance?.safeValue() ?? 'unknown'; + } +} diff --git a/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart index b357eb36..58ee35c5 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart @@ -24,14 +24,14 @@ class PropertyTile extends StatelessWidget { children: [ Text( "${watch(propertyNode).key}: ", - style: Theme.of(context).textTheme.labelSmall, + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: Theme.of(context).colorScheme.primary, + ), ), Flexible( child: Text( "${watch(propertyNode).value}", - style: Theme.of(context).textTheme.labelSmall?.copyWith( - color: Theme.of(context).colorScheme.primary, - ), + style: Theme.of(context).textTheme.labelSmall, ), ), ], From 93e7aaae2c93986460049ade8f9eac0773181a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Mon, 4 Nov 2024 17:15:24 -0600 Subject: [PATCH 040/141] feat(core): Update `untracked` and `batch` methods to support asynchronous callbacks. --- .../lib/src/core/state_management.dart | 79 ++----------------- 1 file changed, 6 insertions(+), 73 deletions(-) diff --git a/packages/reactter/lib/src/core/state_management.dart b/packages/reactter/lib/src/core/state_management.dart index ef76a4fa..632b68b9 100644 --- a/packages/reactter/lib/src/core/state_management.dart +++ b/packages/reactter/lib/src/core/state_management.dart @@ -92,40 +92,10 @@ abstract class StateManagement implements IContext { /// print(computed.value); // 1 -> because the state change is not tracked /// ``` /// {@endtemplate} - T untracked(T Function() callback) { + FutureOr untracked(FutureOr Function() callback) async { try { _untrackedRunningCount++; - return callback(); - } finally { - _untrackedRunningCount--; - } - } - /// {@template reactter.untracked_async} - /// Executes the given [callback] function asynchronously without tracking any state changes. - /// This means that any state changes that occur inside the [callback] function - /// will not trigger any side effects. - /// - /// The [callback] function should return a [Future] of type [T]. - /// The returned [Future] will be the result of the untracked operation. - /// - /// Example usage: - /// ```dart - /// final state = UseAsyncState(0, () async => Future.value(1)); - /// final computed = UseCompute(() => state.value + 1, [state]); - /// - /// await Rt.untrackedAsync(() async { - /// await state.resolve(); - /// - /// print(computed.value); // 1 -> because the state change is not tracked - /// }); - /// - /// print(computed.value); // 1 -> because the state change is not tracked - /// ``` - /// {@endtemplate} - Future untrackedAsync(Future Function() callback) async { - try { - _untrackedRunningCount++; return await callback(); } finally { _untrackedRunningCount--; @@ -161,50 +131,10 @@ abstract class StateManagement implements IContext { /// print(computed.value); // 3 -> because the batch operation is completed. /// ``` /// {@endtemplate} - T batch(T Function() callback) { + FutureOr batch(FutureOr Function() callback) async { try { _batchRunningCount++; - return callback(); - } finally { - _batchRunningCount--; - - if (_batchRunningCount == 0) { - _endBatch(); - } - } - } - /// {@template reactter.batch_async} - /// Executes the given [callback] function within a batch operation asynchronously. - /// - /// This method is similar to [batch], but it allows the [callback] function to be asynchronous. - /// - /// The [callback] function should return a [Future] of type [T]. - /// The returned [Future] will be the result of the batch operation. - /// - /// Example usage: - /// ```dart - /// final stateA = Signal(0); - /// final stateB = UseAsyncState(0, () async => Future.value(2)); - /// final computed = UseCompute( - /// () => stateA.value + stateB.value, - /// [stateA, stateB], - /// ); - /// - /// await Rt.batchAsync(() async { - /// stateA.value = 1; - /// await stateB.resolve(); - /// - /// print(computed.value); // 0 -> because the batch operation is not completed yet. - /// }); - /// - /// print(computed.value); // 3 -> because the batch operation is completed. - /// ``` - /// - /// {@endtemplate} - Future batchAsync(Future Function() callback) async { - try { - _batchRunningCount++; return await callback(); } finally { _batchRunningCount--; @@ -226,7 +156,10 @@ abstract class StateManagement implements IContext { if (_deferredEvents.containsKey(notifier)) { _deferredEvents.remove(notifier); - notifier.notifyListeners(param); + + if (!notifier._debugDisposed) { + notifier.notifyListeners(param); + } } } } From 3f7b66a4f5b2bd35f1f7cced543b52ef46b43014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Mon, 4 Nov 2024 17:24:19 -0600 Subject: [PATCH 041/141] feat(devtools): Enhance state management with new NodeKind enum and improve property tile rendering. --- .../lib/src/controllers/nodes_controller.dart | 6 + .../lib/src/data/constants.dart | 34 ++++++ .../lib/src/data/property_node.dart | 112 +++++++++++++++--- .../lib/src/data/state_node.dart | 4 + .../lib/src/data/tree_list.dart | 6 +- .../lib/src/widgets/node_tile.dart | 100 ++-------------- .../lib/src/widgets/properties_list.dart | 103 +++++++++------- .../lib/src/widgets/property_tile.dart | 35 +++--- .../lib/src/widgets/tile_builder.dart | 86 ++++++++++++++ 9 files changed, 318 insertions(+), 168 deletions(-) create mode 100644 packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart diff --git a/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart b/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart index d7ac7f71..5519fa99 100644 --- a/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart +++ b/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart @@ -55,6 +55,12 @@ class NodesController { final bool isInstanceRemoved = eventData['isInstanceRemoved']; if (isInstanceRemoved) removeNodeByKey(instanceKey); break; + case 'ext.reactter.onStateUpdated': + final String stateKey = eventData['stateKey']; + if (uCurrentNodeKey.value == stateKey) { + print('refresh state'); + } + break; case 'ext.reactter.onStateDisposed': final String stateKey = eventData['stateKey']; final bool isStateRemoved = eventData['isStateRemoved']; diff --git a/packages/reactter_devtools_extension/lib/src/data/constants.dart b/packages/reactter_devtools_extension/lib/src/data/constants.dart index edb4e133..41b83fee 100644 --- a/packages/reactter_devtools_extension/lib/src/data/constants.dart +++ b/packages/reactter_devtools_extension/lib/src/data/constants.dart @@ -1,3 +1,5 @@ +import 'package:flutter/material.dart'; + enum NodeType { dependency('dependency'), state('state'), @@ -14,3 +16,35 @@ enum NodeType { ); } } + +enum NodeKind { + instance(label: 'Instance', abbr: 'I', color: Colors.orange), + state(label: 'State', abbr: 'S', color: Colors.blue), + hook(label: 'Hook', abbr: 'H', color: Colors.purple), + signal(label: 'Signal', abbr: 'S', color: Colors.green); + + const NodeKind({ + required this.label, + required this.abbr, + required this.color, + }); + + final String label; + final String abbr; + final Color color; + + static NodeKind getKind(String kind) { + switch (kind) { + case 'instance': + return instance; + case 'state': + return state; + case 'hook': + return hook; + case 'signal': + return signal; + default: + return instance; + } + } +} diff --git a/packages/reactter_devtools_extension/lib/src/data/property_node.dart b/packages/reactter_devtools_extension/lib/src/data/property_node.dart index 9e23f21e..7b35746d 100644 --- a/packages/reactter_devtools_extension/lib/src/data/property_node.dart +++ b/packages/reactter_devtools_extension/lib/src/data/property_node.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:devtools_app_shared/service.dart'; import 'package:flutter_reactter/reactter.dart'; import 'package:reactter_devtools_extension/src/data/constants.dart'; @@ -6,7 +8,7 @@ import 'package:reactter_devtools_extension/src/services/eval_service.dart'; import 'package:reactter_devtools_extension/src/utils/extensions.dart'; import 'package:vm_service/vm_service.dart'; -const _kMaxValueLength = 80; +const _kMaxValueLength = 50; base class PropertyNode extends TreeNode { final String key; @@ -17,7 +19,7 @@ base class PropertyNode extends TreeNode { String? _value; String? get value { - if (_value == null) _loadValueAsync(); + if (_value == null) Future.microtask(_loadValueAsync); return _value; } @@ -47,7 +49,7 @@ base class PropertyNode extends TreeNode { uIsLoading.value = true; - await Rt.batchAsync(() async { + await Rt.batch(() async { try { switch (valueRef.kind) { case InstanceKind.kNull: @@ -56,11 +58,17 @@ base class PropertyNode extends TreeNode { case InstanceKind.kString: _value = '"${valueRef.valueAsString}"'; break; + case InstanceKind.kList: + await _resolveValueByList(); + break; case InstanceKind.kMap: await _resolveValueByMap(); break; - case InstanceKind.kList: - await _resolveValueByList(); + case InstanceKind.kRecord: + await _resolveValueByRecord(); + break; + case InstanceKind.kSet: + await _resolveValueBySet(); break; case InstanceKind.kPlainInstance: await _resolveValueByPlainInstance(); @@ -70,7 +78,7 @@ base class PropertyNode extends TreeNode { break; } } catch (e) { - _value = 'Error: $e'; + _value = 'Unknown - Cannot load value'; } finally { uIsLoading.value = false; notify(); @@ -78,6 +86,35 @@ base class PropertyNode extends TreeNode { }); } + Future _resolveValueBySet() async { + assert(valueRef.kind == InstanceKind.kSet); + + try { + final isAlive = Disposable(); + final instance = await valueRef.safeGetInstance(isAlive); + final elements = instance?.elements?.cast(); + + _value = '{}'; + + if (elements?.isEmpty ?? true) return; + + for (var i = 0; i < elements!.length; i++) { + addChild(PropertyNode( + key: i.toString(), + valueRef: elements[i], + )); + } + + await _resolveValueByChildren( + buildValue: (node) => '${node.value}', + prefix: '{', + suffix: '}', + ); + } finally { + notify(); + } + } + Future _resolveValueByList() async { assert(valueRef.kind == InstanceKind.kList); @@ -86,7 +123,7 @@ base class PropertyNode extends TreeNode { final instance = await valueRef.safeGetInstance(isAlive); final elements = instance?.elements?.cast(); - _value = '[...]'; + _value = '[]'; if (elements?.isEmpty ?? true) return; @@ -102,8 +139,6 @@ base class PropertyNode extends TreeNode { prefix: '[', suffix: ']', ); - } catch (e) { - print('_resolveValueByList error: $e'); } finally { notify(); } @@ -117,7 +152,7 @@ base class PropertyNode extends TreeNode { final instance = await valueRef.safeGetInstance(isAlive); final associations = instance?.associations; - _value = '{...}'; + _value = '{}'; if (associations == null) return; @@ -138,8 +173,6 @@ base class PropertyNode extends TreeNode { prefix: '{', suffix: '}', ); - } catch (e) { - print('_resolveMap error: $e'); } finally { notify(); } @@ -147,6 +180,35 @@ base class PropertyNode extends TreeNode { return; } + Future _resolveValueByRecord() async { + assert(valueRef.kind == InstanceKind.kRecord); + + try { + final isAlive = Disposable(); + final instance = await valueRef.safeGetInstance(isAlive); + final fields = instance?.fields?.cast(); + + _value = '()'; + + for (var i = 0; i < fields!.length; i++) { + final field = fields[i]; + + addChild(PropertyNode( + key: field.name.toString(), + valueRef: field.value, + )); + } + + await _resolveValueByChildren( + buildValue: (node) => '${node.key}: ${node.value}', + prefix: '(', + suffix: ')', + ); + } finally { + notify(); + } + } + Future _resolveValueByChildren({ required String Function(PropertyNode node) buildValue, String prefix = '{', @@ -164,15 +226,28 @@ base class PropertyNode extends TreeNode { for (var i = 0; i < children.length; i++) { final child = children[i]; final isLast = i == children.length - 1; - await child._loadValueAsync(); - childrenValueBuffer.write("${buildValue(child)}${isLast ? '' : ', '}"); + switch (child.valueRef.kind) { + case InstanceKind.kMap: + case InstanceKind.kSet: + childrenValueBuffer.write("{...}${isLast ? '' : ', '}"); + break; + case InstanceKind.kList: + childrenValueBuffer.write("[...]${isLast ? '' : ', '}"); + break; + default: + await child._loadValueAsync(); + childrenValueBuffer + .write("${buildValue(child)}${isLast ? '' : ', '}"); + break; + } if (childrenValueBuffer.length > _kMaxValueLength) { isFull = isLast; break; } } + final moreEllipsis = isFull ? '' : ', ...'; final maxValueBufferLength = _kMaxValueLength - prefix.length - moreEllipsis.length - suffix.length; @@ -202,6 +277,13 @@ base class PropertyNode extends TreeNode { scope: {'value': valueRef.id!}, ), ); + final dartEval = await EvalService.dartEval; + final records = await EvalService.evalsQueue.add( + () => dartEval.evalInstance( + "('first', a: 2, b: true, 'last')", + isAlive: isAlive, + ), + ); if (valueInfo.kind != InstanceKind.kMap) return; @@ -234,8 +316,6 @@ base class PropertyNode extends TreeNode { _value = "$type#$key"; break; } - } catch (e) { - print('loadValueFromPlainInstance error: $e'); } finally { notify(); } diff --git a/packages/reactter_devtools_extension/lib/src/data/state_node.dart b/packages/reactter_devtools_extension/lib/src/data/state_node.dart index 6d14bac7..8a72d312 100644 --- a/packages/reactter_devtools_extension/lib/src/data/state_node.dart +++ b/packages/reactter_devtools_extension/lib/src/data/state_node.dart @@ -100,6 +100,10 @@ base class StateNode extends INode { isAlive: isAlive, ); + if (propertiesInst.kind == InstanceKind.kNull) { + return; + } + assert(propertiesInst.kind == InstanceKind.kMap); final associations = propertiesInst.associations?.cast(); diff --git a/packages/reactter_devtools_extension/lib/src/data/tree_list.dart b/packages/reactter_devtools_extension/lib/src/data/tree_list.dart index 7b952534..6841d4d7 100644 --- a/packages/reactter_devtools_extension/lib/src/data/tree_list.dart +++ b/packages/reactter_devtools_extension/lib/src/data/tree_list.dart @@ -25,10 +25,12 @@ final class TreeList> extends LinkedList void clear() { Rt.batch(() { for (final entry in this) { - (entry as RtState).dispose(); + if (!entry.isDisposed) entry.dispose(); } - update(() => super.clear()); + super.clear(); + + notify(); }); } } diff --git a/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart index 1a2839ac..32f0f2a5 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart @@ -1,36 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/data/constants.dart'; import 'package:reactter_devtools_extension/src/interfaces/node.dart'; - -enum NodeKind { - instance(label: 'I', color: Colors.orange), - state(label: 'S', color: Colors.blue), - hook(label: 'H', color: Colors.purple), - signal(label: 'S', color: Colors.green); - - const NodeKind({ - required this.label, - required this.color, - }); - - final String label; - final Color color; - - static NodeKind getKind(String kind) { - switch (kind) { - case 'instance': - return instance; - case 'state': - return state; - case 'hook': - return hook; - case 'signal': - return signal; - default: - return instance; - } - } -} +import 'package:reactter_devtools_extension/src/widgets/tile_builder.dart'; class NodeTile extends StatelessWidget { final INode node; @@ -47,69 +19,11 @@ class NodeTile extends StatelessWidget { return RtWatcher((context, watch) { final isSelected = watch(node.uIsSelected).value; - return ListTile( - dense: true, - visualDensity: const VisualDensity(vertical: 0, horizontal: 0), - horizontalTitleGap: 0, - minVerticalPadding: 0, - contentPadding: EdgeInsets.zero, - minTileHeight: 0, - selected: isSelected, - selectedTileColor: Theme.of(context).focusColor, + return TreeNodeTileBuilder( + treeNode: node, + title: NodeTileTitle(node: node), + isSelected: isSelected, onTap: onTap, - // leading: NodeTileLeading(node: node), - title: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - ...List.generate( - node.depth.toInt() - 1, - (index) => Padding( - padding: EdgeInsets.only( - left: 20, - right: (index == node.depth.toInt() - 2) ? 0 : 0, - ), - child: const VerticalDivider(width: 1), - ), - ), - NodeTileLeading(node: node), - NodeTileTitle(node: node), - ], - ), - ); - }); - } -} - -class NodeTileLeading extends StatelessWidget { - final INode node; - - const NodeTileLeading({super.key, required this.node}); - - @override - Widget build(BuildContext context) { - return RtWatcher((context, watch) { - final children = watch(node.uChildren).value; - - if (children.isEmpty) { - return const SizedBox(width: 11); - } - - final isExpanded = watch(node.uIsExpanded).value; - - return Padding( - padding: const EdgeInsets.only(left: 8), - child: IconButton( - padding: EdgeInsets.zero, - splashRadius: 18, - iconSize: 24, - constraints: const BoxConstraints.tightForFinite(), - icon: Icon( - isExpanded ? Icons.keyboard_arrow_down : Icons.keyboard_arrow_right, - ), - onPressed: () { - node.uIsExpanded.value = !isExpanded; - }, - ), ); }); } @@ -127,7 +41,7 @@ class NodeTileIcon extends StatelessWidget { return CircleAvatar( backgroundColor: nodeKind.color, child: Text( - nodeKind.label, + nodeKind.abbr, style: Theme.of(context).textTheme.labelSmall?.copyWith( color: Colors.white, fontWeight: FontWeight.bold, diff --git a/packages/reactter_devtools_extension/lib/src/widgets/properties_list.dart b/packages/reactter_devtools_extension/lib/src/widgets/properties_list.dart index fe362942..ccbf1912 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/properties_list.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/properties_list.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; import 'package:reactter_devtools_extension/src/controllers/nodes_controller.dart'; +import 'package:reactter_devtools_extension/src/widgets/offset_scrollbar.dart'; import 'package:reactter_devtools_extension/src/widgets/property_tile.dart'; class PropertiesList extends StatelessWidget { @@ -8,52 +9,68 @@ class PropertiesList extends StatelessWidget { @override Widget build(BuildContext context) { + final listKey = GlobalKey(); + final scrollControllerX = ScrollController(); + final scrollControllerY = ScrollController(); final focusNode = FocusNode(); - final inst = context.use(); - - return SelectableRegion( - focusNode: focusNode, - selectionControls: materialTextSelectionControls, - child: Scrollbar( - child: SizedBox.expand( - child: RtWatcher( - (context, watch) { - watch(inst.uCurrentNodeKey).value; - - if (inst.currentNode == null) { - return const SizedBox(); - } - - final properties = watch(inst.currentNode!.propertyNodes); - - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Padding( - // padding: const EdgeInsets.all(8.0).copyWith(bottom: 4), - // child: Text("Debug info", - // style: Theme.of(context).textTheme.bodyMedium), - // ), - Expanded( - child: ListView.builder( - itemCount: properties.length, - itemBuilder: (context, index) { - final propertyNode = properties.elementAt(index); - - return PropertyTile( - key: ObjectKey(propertyNode), - propertyNode: propertyNode, - ); - }, - ), + final nodesController = context.use(); + + return LayoutBuilder( + builder: (context, constraints) { + final viewportWidth = constraints.maxWidth; + + return Scrollbar( + controller: scrollControllerX, + thumbVisibility: true, + child: SingleChildScrollView( + controller: scrollControllerX, + scrollDirection: Axis.horizontal, + child: Material( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 1000.00), + child: OffsetScrollbar( + isAlwaysShown: true, + axis: Axis.vertical, + controller: scrollControllerY, + offsetController: scrollControllerX, + offsetControllerViewportDimension: viewportWidth, + child: SelectableRegion( + focusNode: focusNode, + selectionControls: materialTextSelectionControls, + child: RtWatcher((context, watch) { + watch(nodesController.uCurrentNodeKey); + + final propertyNodes = nodesController.currentNode != null + ? watch(nodesController.currentNode!.propertyNodes) + : null; + + final length = propertyNodes?.length ?? 0; + + return ListView.custom( + key: listKey, + controller: scrollControllerY, + itemExtent: 24, + childrenDelegate: SliverChildBuilderDelegate( + (context, index) { + final propertyNode = + propertyNodes!.elementAt(index); + + return PropertyTile( + key: Key(propertyNode.key), + propertyNode: propertyNode, + ); + }, + childCount: length, + ), + ); + }), ), - ], - ); - }, + ), + ), + ), ), - ), - ), + ); + }, ); } } diff --git a/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart index 58ee35c5..7b339440 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; import 'package:reactter_devtools_extension/src/data/property_node.dart'; +import 'package:reactter_devtools_extension/src/widgets/loading.dart'; +import 'package:reactter_devtools_extension/src/widgets/tile_builder.dart'; class PropertyTile extends StatelessWidget { const PropertyTile({ @@ -13,29 +15,34 @@ class PropertyTile extends StatelessWidget { @override Widget build(BuildContext context) { return RtWatcher((context, watch) { - return ListTile( - dense: true, - visualDensity: const VisualDensity(vertical: 0, horizontal: 2), - contentPadding: const EdgeInsets.symmetric(horizontal: 8), - minVerticalPadding: 4, - minTileHeight: 0, + return TreeNodeTileBuilder( + treeNode: propertyNode, title: Row( - crossAxisAlignment: CrossAxisAlignment.start, children: [ + if (watch(propertyNode.uChildren).value.isEmpty) + const SizedBox(width: 22), Text( "${watch(propertyNode).key}: ", - style: Theme.of(context).textTheme.labelSmall?.copyWith( - color: Theme.of(context).colorScheme.primary, - ), + style: Theme.of(context) + .textTheme + .labelSmall + ?.copyWith(color: Theme.of(context).colorScheme.primary), ), - Flexible( - child: Text( + RtWatcher((context, watch) { + final isLoading = watch(propertyNode.uIsLoading).value; + + if (isLoading || watch(propertyNode).value == null) { + return const Loading(); + } + + return Text( "${watch(propertyNode).value}", style: Theme.of(context).textTheme.labelSmall, - ), - ), + ); + }), ], ), + isSelected: false, onTap: () {}, ); }); diff --git a/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart b/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart new file mode 100644 index 00000000..37a0cefe --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/data/tree_node.dart'; + +class TreeNodeTileBuilder extends StatelessWidget { + final TreeNode treeNode; + final bool isSelected; + final Widget title; + final void Function()? onTap; + + const TreeNodeTileBuilder({ + super.key, + required this.treeNode, + required this.title, + this.isSelected = false, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return ListTile( + dense: true, + visualDensity: const VisualDensity(vertical: 0, horizontal: 0), + horizontalTitleGap: 0, + minVerticalPadding: 0, + contentPadding: EdgeInsets.zero, + minTileHeight: 24, + selected: isSelected, + selectedTileColor: Theme.of(context).focusColor, + onTap: onTap, + title: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + ...List.generate( + treeNode.depth.toInt() - 1, + (index) => Padding( + padding: EdgeInsets.only( + left: 20, + right: (index == treeNode.depth.toInt() - 2) ? 0 : 0, + ), + child: const VerticalDivider(width: 1), + ), + ), + TileExpandable(treeNode: treeNode), + title, + ], + ), + ); + } +} + +class TileExpandable extends StatelessWidget { + final TreeNode treeNode; + + const TileExpandable({super.key, required this.treeNode}); + + @override + Widget build(BuildContext context) { + return RtWatcher((context, watch) { + final children = watch(treeNode.uChildren).value; + + if (children.isEmpty) { + return const SizedBox(width: 11); + } + + final isExpanded = watch(treeNode.uIsExpanded).value; + + return Padding( + padding: const EdgeInsets.only(left: 8), + child: IconButton( + padding: EdgeInsets.zero, + splashRadius: 18, + iconSize: 24, + constraints: const BoxConstraints.tightForFinite(), + icon: Icon( + isExpanded ? Icons.keyboard_arrow_down : Icons.keyboard_arrow_right, + ), + onPressed: () { + treeNode.uIsExpanded.value = !isExpanded; + }, + ), + ); + }); + } +} From f77d0bfa3e8409cf1dd3db32fbf7a0731d1825c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Wed, 6 Nov 2024 00:06:02 -0600 Subject: [PATCH 042/141] feat(devtools): Reload state details when state is updated --- .../lib/src/controllers/nodes_controller.dart | 4 +- .../lib/src/data/dependency_node.dart | 2 +- .../lib/src/data/instance_node.dart | 2 +- .../lib/src/data/property_node.dart | 354 +++++++++--------- .../lib/src/data/slot_node.dart | 2 +- .../lib/src/data/state_node.dart | 82 +--- .../lib/src/interfaces/node.dart | 2 +- .../lib/src/widgets/properties_list.dart | 2 +- .../lib/src/widgets/property_tile.dart | 68 ++-- 9 files changed, 228 insertions(+), 290 deletions(-) diff --git a/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart b/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart index 5519fa99..e2b5ab4d 100644 --- a/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart +++ b/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart @@ -57,9 +57,7 @@ class NodesController { break; case 'ext.reactter.onStateUpdated': final String stateKey = eventData['stateKey']; - if (uCurrentNodeKey.value == stateKey) { - print('refresh state'); - } + if (uCurrentNodeKey.value == stateKey) currentNode?.loadDetails(); break; case 'ext.reactter.onStateDisposed': final String stateKey = eventData['stateKey']; diff --git a/packages/reactter_devtools_extension/lib/src/data/dependency_node.dart b/packages/reactter_devtools_extension/lib/src/data/dependency_node.dart index a6f8c288..dcd1d1a0 100644 --- a/packages/reactter_devtools_extension/lib/src/data/dependency_node.dart +++ b/packages/reactter_devtools_extension/lib/src/data/dependency_node.dart @@ -81,7 +81,7 @@ base class DependencyNode extends INode { } @override - void loadDetails() { + Future loadDetails() async { // TODO: implement loadDetails } } diff --git a/packages/reactter_devtools_extension/lib/src/data/instance_node.dart b/packages/reactter_devtools_extension/lib/src/data/instance_node.dart index d8f85722..33c19ab3 100644 --- a/packages/reactter_devtools_extension/lib/src/data/instance_node.dart +++ b/packages/reactter_devtools_extension/lib/src/data/instance_node.dart @@ -26,7 +26,7 @@ base class InstanceNode extends INode { } @override - void loadDetails() { + Future loadDetails() async { // TODO: implement loadDetails } } diff --git a/packages/reactter_devtools_extension/lib/src/data/property_node.dart b/packages/reactter_devtools_extension/lib/src/data/property_node.dart index 7b35746d..a721463b 100644 --- a/packages/reactter_devtools_extension/lib/src/data/property_node.dart +++ b/packages/reactter_devtools_extension/lib/src/data/property_node.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:collection'; import 'package:devtools_app_shared/service.dart'; import 'package:flutter_reactter/reactter.dart'; @@ -12,236 +13,244 @@ const _kMaxValueLength = 50; base class PropertyNode extends TreeNode { final String key; - final InstanceRef valueRef; - final instanceInfo = UseState?>(null); - final uIsLoading = UseState(false); + InstanceRef _valueRef; + InstanceRef get valueRef => _valueRef; + Disposable? isAlive; + bool _isResolved = false; - String? _value; - String? get value { - if (_value == null) Future.microtask(_loadValueAsync); + FutureOr? _valueFuture; - return _value; - } + final LinkedHashMap _childNodeRefs = + LinkedHashMap(); + + final uValue = UseState(null); + final uInstanceInfo = UseState?>(null); + final uIsLoading = UseState(false); PropertyNode._({ required this.key, - required this.valueRef, - }) { - uIsExpanded.value = false; + required InstanceRef valueRef, + bool isExpanded = false, + }) : _valueRef = valueRef { + uIsExpanded.value = isExpanded; } factory PropertyNode({ PropertyNode? parent, required String key, required InstanceRef valueRef, + bool isExpanded = false, }) { return Rt.createState( () => PropertyNode._( key: key, valueRef: valueRef, + isExpanded: isExpanded, ), ); } - Future _loadValueAsync() async { - if (uIsLoading.value) return; + Future reassignValueRef(InstanceRef valueRef) async { + isAlive?.dispose(); + _isResolved = false; + _valueRef = valueRef; + _valueFuture = null; + uIsLoading.value = false; + + if (list == null) return; + + await getValueAsync(); + } - uIsLoading.value = true; + Future getValueAsync() async { + if (_valueFuture != null) return _valueFuture!; + if (_isResolved) return Future.value(uValue.value); + if (uIsLoading.value) return Future.value(uValue.value); + + return _valueFuture = Rt.batch(() async { + uIsLoading.value = true; + isAlive = Disposable(); + String? value; - await Rt.batch(() async { try { switch (valueRef.kind) { - case InstanceKind.kNull: - _value = 'null'; - break; - case InstanceKind.kString: - _value = '"${valueRef.valueAsString}"'; - break; case InstanceKind.kList: - await _resolveValueByList(); + value = await _resolveValueByList(); break; case InstanceKind.kMap: - await _resolveValueByMap(); + value = await _resolveValueByMap(); break; case InstanceKind.kRecord: - await _resolveValueByRecord(); + value = await _resolveValueByRecord(); break; case InstanceKind.kSet: - await _resolveValueBySet(); + value = await _resolveValueBySet(); break; case InstanceKind.kPlainInstance: - await _resolveValueByPlainInstance(); + value = await _resolveValueByPlainInstance(); + break; + case InstanceKind.kNull: + value = 'null'; + uChildren.value.clear(); + break; + case InstanceKind.kString: + value = '"${valueRef.valueAsString}"'; + uChildren.value.clear(); break; default: - _value = valueRef.valueAsString; + value = valueRef.valueAsString; + uChildren.value.clear(); break; } } catch (e) { - _value = 'Unknown - Cannot load value'; - } finally { - uIsLoading.value = false; - notify(); + value = null; } + + value ??= 'Unknown - Cannot load value'; + + _isResolved = true; + uIsLoading.value = false; + return uValue.value = value; }); } - Future _resolveValueBySet() async { + Future _resolveValueBySet() async { assert(valueRef.kind == InstanceKind.kSet); - try { - final isAlive = Disposable(); - final instance = await valueRef.safeGetInstance(isAlive); - final elements = instance?.elements?.cast(); + final instance = await valueRef.safeGetInstance(isAlive); + final elements = instance?.elements?.cast() ?? []; + final children = { + for (var i = 0; i < elements.length; i++) i.toString(): elements[i], + }; - _value = '{}'; + await _addChildren(children); - if (elements?.isEmpty ?? true) return; - - for (var i = 0; i < elements!.length; i++) { - addChild(PropertyNode( - key: i.toString(), - valueRef: elements[i], - )); - } - - await _resolveValueByChildren( - buildValue: (node) => '${node.value}', - prefix: '{', - suffix: '}', - ); - } finally { - notify(); - } + return await _resolveValueByChildren( + buildValue: (key, value) => '$value', + prefix: '{', + suffix: '}', + ); } - Future _resolveValueByList() async { + Future _resolveValueByList() async { assert(valueRef.kind == InstanceKind.kList); - try { - final isAlive = Disposable(); - final instance = await valueRef.safeGetInstance(isAlive); - final elements = instance?.elements?.cast(); + final instance = await valueRef.safeGetInstance(isAlive); + final elements = instance?.elements?.cast() ?? []; + final children = { + for (var i = 0; i < elements.length; i++) i.toString(): elements[i], + }; - _value = '[]'; + await _addChildren(children); - if (elements?.isEmpty ?? true) return; - - for (var i = 0; i < elements!.length; i++) { - addChild(PropertyNode( - key: i.toString(), - valueRef: elements[i], - )); - } - - await _resolveValueByChildren( - buildValue: (node) => '${node.value}', - prefix: '[', - suffix: ']', - ); - } finally { - notify(); - } + return await _resolveValueByChildren( + buildValue: (key, value) => "$value", + prefix: '[', + suffix: ']', + ); } - Future _resolveValueByMap() async { + Future _resolveValueByMap() async { assert(valueRef.kind == InstanceKind.kMap); - try { - final isAlive = Disposable(); - final instance = await valueRef.safeGetInstance(isAlive); - final associations = instance?.associations; - - _value = '{}'; - - if (associations == null) return; + final instance = await valueRef.safeGetInstance(isAlive); + final associations = instance?.associations?.cast() ?? []; + final children = {}; - for (final entry in associations) { - final InstanceRef keyRef = entry.key; - final InstanceRef valueRef = entry.value; + for (final entry in associations) { + final keyRef = entry.key as InstanceRef; + final key = await keyRef.evalValueFirstLevel(isAlive); - final key = await keyRef.evalValueFirstLevel(isAlive); - - addChild(PropertyNode( - key: key.toString(), - valueRef: valueRef, - )); - } - - await _resolveValueByChildren( - buildValue: (node) => '${node.key}: ${node.value}', - prefix: '{', - suffix: '}', - ); - } finally { - notify(); + children[key.toString()] = entry.value; } - return; + await _addChildren(children); + + return await _resolveValueByChildren( + buildValue: (key, value) => '$key: $value', + prefix: '{', + suffix: '}', + ); } - Future _resolveValueByRecord() async { + Future _resolveValueByRecord() async { assert(valueRef.kind == InstanceKind.kRecord); - try { - final isAlive = Disposable(); - final instance = await valueRef.safeGetInstance(isAlive); - final fields = instance?.fields?.cast(); + final instance = await valueRef.safeGetInstance(isAlive); + final fields = instance?.fields?.cast() ?? []; + final children = { + for (final field in fields) + field.name.toString(): field.value as InstanceRef, + }; - _value = '()'; + await _addChildren(children); - for (var i = 0; i < fields!.length; i++) { - final field = fields[i]; + return await _resolveValueByChildren( + buildValue: (key, value) => '$key: $value', + prefix: '(', + suffix: ')', + ); + } - addChild(PropertyNode( - key: field.name.toString(), - valueRef: field.value, - )); - } + Future _addChildren(Map children) async { + final childrenToRemove = _childNodeRefs.keys.toSet(); - await _resolveValueByChildren( - buildValue: (node) => '${node.key}: ${node.value}', - prefix: '(', - suffix: ')', + for (final child in children.entries) { + final isRemoved = childrenToRemove.remove(child.key); + final childNode = _childNodeRefs.putIfAbsent( + child.key, + () => PropertyNode( + key: child.key, + valueRef: child.value, + ), ); - } finally { - notify(); + + if (isRemoved) { + childNode.reassignValueRef(child.value); + } else { + addChild(childNode); + } + } + + for (final childKey in childrenToRemove) { + final childNode = _childNodeRefs.remove(childKey); + childNode?.remove(); } } - Future _resolveValueByChildren({ - required String Function(PropertyNode node) buildValue, + Future _resolveValueByChildren({ + required String Function(String key, String? value) buildValue, String prefix = '{', String suffix = '}', }) async { final children = uChildren.value.toList(); - - _value = '$prefix...$suffix'; - - if (children.isEmpty) return; - final childrenValueBuffer = StringBuffer(); var isFull = true; for (var i = 0; i < children.length; i++) { final child = children[i]; final isLast = i == children.length - 1; + String? value; switch (child.valueRef.kind) { case InstanceKind.kMap: case InstanceKind.kSet: - childrenValueBuffer.write("{...}${isLast ? '' : ', '}"); + value = '{...}'; break; case InstanceKind.kList: - childrenValueBuffer.write("[...]${isLast ? '' : ', '}"); + value = '[...]'; break; default: - await child._loadValueAsync(); - childrenValueBuffer - .write("${buildValue(child)}${isLast ? '' : ', '}"); + await child.getValueAsync(); + value = child.uValue.value; break; } + childrenValueBuffer.write( + "${buildValue(child.key, value)}${isLast ? '' : ', '}", + ); + if (childrenValueBuffer.length > _kMaxValueLength) { isFull = isLast; break; @@ -261,65 +270,38 @@ base class PropertyNode extends TreeNode { ) : childrenValue; - _value = "$prefix$childrenValueCutted$cuttedEllipsis$moreEllipsis$suffix"; + return "$prefix$childrenValueCutted$cuttedEllipsis$moreEllipsis$suffix"; } - Future _resolveValueByPlainInstance() async { + Future _resolveValueByPlainInstance() async { assert(valueRef.kind == InstanceKind.kPlainInstance); - try { - final isAlive = Disposable(); - final eval = await EvalService.devtoolsEval; - final valueInfo = await EvalService.evalsQueue.add( - () => eval.evalInstance( - 'RtDevTools._instance?.getPlainInstanceInfo(value)', - isAlive: isAlive, - scope: {'value': valueRef.id!}, - ), - ); - final dartEval = await EvalService.dartEval; - final records = await EvalService.evalsQueue.add( - () => dartEval.evalInstance( - "('first', a: 2, b: true, 'last')", - isAlive: isAlive, - ), - ); - - if (valueInfo.kind != InstanceKind.kMap) return; - - final valueInfoMap = await valueInfo.evalValue(isAlive); - - if (valueInfoMap is! Map) return; + final eval = await EvalService.devtoolsEval; + final valueInfo = await EvalService.evalsQueue.add( + () => eval.evalInstance( + 'RtDevTools._instance?.getPlainInstanceInfo(value)', + isAlive: isAlive, + scope: {'value': valueRef.id!}, + ), + ); - instanceInfo.value = valueInfoMap.cast(); + if (valueInfo.kind != InstanceKind.kMap) return null; - final String kind = valueInfoMap['kind']; - final String key = valueInfoMap['key']; - final String type = valueInfoMap['type']; + final valueInfoMap = await valueInfo.evalValue(isAlive); - switch (NodeType.fromString(kind)) { - case NodeType.state: - final String? debugLabel = valueInfoMap['debugLabel']; + if (valueInfoMap is! Map) return null; - if (debugLabel?.isNotEmpty ?? false) { - _value = "$type($debugLabel)#$key"; - break; - } - case NodeType.dependency: - final String? id = valueInfoMap['id']; + uInstanceInfo.value = valueInfoMap.cast(); - if (id?.isNotEmpty ?? false) { - _value = "$type($id)#$key"; - break; - } - default: - _value = "$type#$key"; - break; - } - } finally { - notify(); - } + final String kind = valueInfoMap['kind']; + final String key = valueInfoMap['key']; + final String type = valueInfoMap['type']; + final String? id = valueInfoMap['id']; + final String? debugLabel = valueInfoMap['debugLabel']; + final String? idOrDebugLabel = id ?? debugLabel; - return; + return idOrDebugLabel != null + ? "$type($idOrDebugLabel)#$key" + : "$type#$key"; } } diff --git a/packages/reactter_devtools_extension/lib/src/data/slot_node.dart b/packages/reactter_devtools_extension/lib/src/data/slot_node.dart index fa35f0d9..5f9b0d51 100644 --- a/packages/reactter_devtools_extension/lib/src/data/slot_node.dart +++ b/packages/reactter_devtools_extension/lib/src/data/slot_node.dart @@ -20,7 +20,7 @@ base class SlotNode extends INode { } @override - void loadDetails() { + Future loadDetails() async { // TODO: implement loadDetails } } diff --git a/packages/reactter_devtools_extension/lib/src/data/state_node.dart b/packages/reactter_devtools_extension/lib/src/data/state_node.dart index 8a72d312..020a7d61 100644 --- a/packages/reactter_devtools_extension/lib/src/data/state_node.dart +++ b/packages/reactter_devtools_extension/lib/src/data/state_node.dart @@ -14,9 +14,7 @@ base class StateNode extends INode { required super.key, required super.kind, required super.type, - }) { - // _loadStateNode(); - } + }); factory StateNode({ required String key, @@ -32,48 +30,6 @@ base class StateNode extends INode { ); } - Future _loadStateNode() async { - uIsLoading.value = true; - - final eval = await EvalService.devtoolsEval; - final isAlive = Disposable(); - - final stateNode = await eval.evalInstance( - 'RtDevTools._instance?.getStateInfo("$key")', - isAlive: isAlive, - ); - - assert(stateNode.kind == InstanceKind.kMap); - - final stateNodeAssociations = - stateNode.associations?.cast(); - - assert(stateNodeAssociations != null); - - String? label; - - for (var element in stateNodeAssociations!) { - assert(element.key != null && element.value != null); - - final eKey = element.key!.valueAsString!; - final eValue = element.value!.valueAsString!; - - if (element.value!.kind == InstanceKind.kNull) continue; - - switch (eKey) { - case 'label': - label = eValue; - break; - } - } - - uInfo.value = StateInfo( - label: label, - ); - - uIsLoading.value = false; - } - @override Future loadDetails() async { await Future.wait([loadBoundInstance(), loadProperties()]); @@ -95,36 +51,30 @@ base class StateNode extends INode { final eval = await EvalService.devtoolsEval; final isAlive = Disposable(); - final propertiesInst = await eval.evalInstance( + final debugInfoValue = await eval.evalInstance( 'RtDevTools._instance?.getDebugInfo("$key")', isAlive: isAlive, ); - if (propertiesInst.kind == InstanceKind.kNull) { - return; - } + PropertyNode? propertyNode; - assert(propertiesInst.kind == InstanceKind.kMap); - - final associations = propertiesInst.associations?.cast(); - - assert(associations != null); - - final properties = []; - - for (final association in associations!) { - final key = association.key!.valueAsString!; - final valueRef = association.value; + for (final node in propertyNodes) { + if (node.key == 'debugInfo') { + propertyNode = node; + break; + } + } - properties.add( + if (propertyNode == null) { + propertyNodes.add( PropertyNode( - key: key, - valueRef: valueRef, + key: 'debugInfo', + valueRef: debugInfoValue, + isExpanded: true, ), ); + } else { + propertyNode.reassignValueRef(debugInfoValue); } - - propertyNodes.clear(); - propertyNodes.addAll(properties); } } diff --git a/packages/reactter_devtools_extension/lib/src/interfaces/node.dart b/packages/reactter_devtools_extension/lib/src/interfaces/node.dart index f1640f68..06eff8b3 100644 --- a/packages/reactter_devtools_extension/lib/src/interfaces/node.dart +++ b/packages/reactter_devtools_extension/lib/src/interfaces/node.dart @@ -19,5 +19,5 @@ abstract base class INode extends TreeNode { required this.type, }); - void loadDetails(); + Future loadDetails(); } diff --git a/packages/reactter_devtools_extension/lib/src/widgets/properties_list.dart b/packages/reactter_devtools_extension/lib/src/widgets/properties_list.dart index ccbf1912..a9c7dd2f 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/properties_list.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/properties_list.dart @@ -56,7 +56,7 @@ class PropertiesList extends StatelessWidget { propertyNodes!.elementAt(index); return PropertyTile( - key: Key(propertyNode.key), + key: ObjectKey(propertyNode), propertyNode: propertyNode, ); }, diff --git a/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart index 7b339440..013f0f65 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart @@ -14,37 +14,45 @@ class PropertyTile extends StatelessWidget { @override Widget build(BuildContext context) { - return RtWatcher((context, watch) { - return TreeNodeTileBuilder( - treeNode: propertyNode, - title: Row( - children: [ - if (watch(propertyNode.uChildren).value.isEmpty) - const SizedBox(width: 22), - Text( - "${watch(propertyNode).key}: ", - style: Theme.of(context) - .textTheme - .labelSmall - ?.copyWith(color: Theme.of(context).colorScheme.primary), - ), - RtWatcher((context, watch) { - final isLoading = watch(propertyNode.uIsLoading).value; + return TreeNodeTileBuilder( + key: key, + treeNode: propertyNode, + title: Row( + children: [ + RtWatcher((context, watch) { + return SizedBox( + width: watch(propertyNode.uChildren).value.isEmpty ? 22 : 0, + ); + }), + Text( + "${propertyNode.key}: ", + style: Theme.of(context) + .textTheme + .labelSmall + ?.copyWith(color: Theme.of(context).colorScheme.primary), + ), + FutureBuilder( + future: propertyNode.getValueAsync(), + builder: (context, snapshot) { + return RtWatcher((context, watch) { + final isLoading = watch(propertyNode.uIsLoading).value; + final value = watch(propertyNode.uValue).value; - if (isLoading || watch(propertyNode).value == null) { - return const Loading(); - } + if (isLoading) { + return const Loading(); + } - return Text( - "${watch(propertyNode).value}", - style: Theme.of(context).textTheme.labelSmall, - ); - }), - ], - ), - isSelected: false, - onTap: () {}, - ); - }); + return Text( + value ?? '...', + style: Theme.of(context).textTheme.labelSmall, + ); + }); + }, + ), + ], + ), + isSelected: false, + onTap: () {}, + ); } } From e12689b3999bd397822d7fdcee2136f9eb624019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Sat, 9 Nov 2024 01:55:47 -0600 Subject: [PATCH 043/141] feat(core): Support asynchronous callbacks in untracked and batch methods and add test for it. --- .../lib/src/core/state_management.dart | 4 +- .../test/core/state_management_test.dart | 90 ++++++++++++++++++- 2 files changed, 91 insertions(+), 3 deletions(-) diff --git a/packages/reactter/lib/src/core/state_management.dart b/packages/reactter/lib/src/core/state_management.dart index 632b68b9..e5b31849 100644 --- a/packages/reactter/lib/src/core/state_management.dart +++ b/packages/reactter/lib/src/core/state_management.dart @@ -96,7 +96,7 @@ abstract class StateManagement implements IContext { try { _untrackedRunningCount++; - return await callback(); + return callback is Future Function() ? await callback() : callback(); } finally { _untrackedRunningCount--; } @@ -135,7 +135,7 @@ abstract class StateManagement implements IContext { try { _batchRunningCount++; - return await callback(); + return callback is Future Function() ? await callback() : callback(); } finally { _batchRunningCount--; diff --git a/packages/reactter/test/core/state_management_test.dart b/packages/reactter/test/core/state_management_test.dart index ac88ceb4..c2da29da 100644 --- a/packages/reactter/test/core/state_management_test.dart +++ b/packages/reactter/test/core/state_management_test.dart @@ -63,10 +63,49 @@ void main() { expect(computed.value, 1); }); + + test("should run the async callback without tracking the changes", + () async { + final state = UseState(0); + final computed = UseCompute(() => state.value + 1, [state]); + + await Rt.untracked(() async { + await Future.delayed(Duration(milliseconds: 10)); + + state.value = 2; + + expect(computed.value, 1); + }); + + expect(computed.value, 1); + }); + + test("should run the async callback as nested untracked", () async { + final state = UseState(0); + final computed = UseCompute(() => state.value + 1, [state]); + + await Rt.untracked(() async { + await Future.delayed(Duration(milliseconds: 10)); + + await Rt.untracked(() async { + await Future.delayed(Duration(milliseconds: 10)); + + state.value = 2; + + expect(computed.value, 1); + }); + + state.value += 1; + + expect(computed.value, 1); + }); + + expect(computed.value, 1); + }); }); group("batch", () { - test("should run the callback and change the state after the batch", () { + test("should run the callback and reflect the state after the batch", () { final state = UseState(0); final computed = UseCompute(() => state.value + 1, [state]); @@ -119,5 +158,54 @@ void main() { // stateA(2) + stateB(3) + stateC(1) expect(computed.value, 6); }); + + test("should run the async callback and reflect the state after the batch", + () async { + final stateA = UseState(0); + final stateB = UseState(0); + final computed = UseCompute( + () => stateA.value + stateB.value, + [stateA, stateB], + ); + + await Rt.batch(() async { + stateA.value += 1; // 1 + + await Future.delayed(Duration(milliseconds: 10)); + + stateB.value += 2; // 2 + + expect(computed.value, 0); + }); + + // stateA(1) + stateB(2) + expect(computed.value, 3); + }); + + test("should run the async callback as nested batch", () async { + final stateA = UseState(0); + final stateB = UseState(0); + final computed = UseCompute( + () => stateA.value + stateB.value, + [stateA, stateB], + ); + + await Rt.batch(() async { + stateA.value += 1; // 1 + + await Future.delayed(Duration(milliseconds: 10)); + + await Rt.batch(() async { + stateB.value += 2; // 2 + + expect(computed.value, 0); + }); + + expect(computed.value, 0); + }); + + // stateA(1) + stateB(2) + expect(computed.value, 3); + }); }); } From 7266307458cb7b7a557bc94221cd38aa93068ee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Sat, 9 Nov 2024 01:58:51 -0600 Subject: [PATCH 044/141] fix(framework): Fix to propagate state param to boundInstance. --- packages/reactter/lib/src/devtools.dart | 9 +++++---- packages/reactter/lib/src/framework/rt_state_base.dart | 10 ++++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/reactter/lib/src/devtools.dart b/packages/reactter/lib/src/devtools.dart index 0767dbcd..dae0e83b 100644 --- a/packages/reactter/lib/src/devtools.dart +++ b/packages/reactter/lib/src/devtools.dart @@ -368,13 +368,14 @@ class RtDevTools with RtStateObserver, RtDependencyObserver { String getPropertyValue(value) { if (value is List) { return getListString(value); - } else if (value is Map) { + } + if (value is Map) { return getMapString(value); - } else if (value is Set) { + } + if (value is Set) { return getSetString(value); - } else { - return value.toString(); } + return value.toString(); } Map getPlainInstanceInfo(Object instance) { diff --git a/packages/reactter/lib/src/framework/rt_state_base.dart b/packages/reactter/lib/src/framework/rt_state_base.dart index 6749bfa7..78a568ce 100644 --- a/packages/reactter/lib/src/framework/rt_state_base.dart +++ b/packages/reactter/lib/src/framework/rt_state_base.dart @@ -164,23 +164,25 @@ abstract class RtStateBase> implements RtState { /// If [Rt._isUntrackedRunning] is true, the notification is skipped. /// If [Rt._isBatchRunning] is true, the notification is deferred until the batch is completed. /// The [event] is emitted using [Rt.emit] for the current instance and [_boundInstance]. - void _notify(Enum event) { + void _notify(Enum event, [dynamic param]) { if (stateManagement._isUntrackedRunning) return; final emit = stateManagement._isBatchRunning ? stateManagement._emitDefferred : eventHandler.emit; - emit(this, event, this); + final finalParam = param ?? this; + + emit(this, event, finalParam); if (_boundInstance == null) return; if (_boundInstance is RtStateBase && !(_boundInstance as RtStateBase)._isDisposed) { - return (_boundInstance as RtStateBase)._notify(event); + return (_boundInstance as RtStateBase)._notify(event, finalParam); } - emit(_boundInstance!, event, this); + emit(_boundInstance!, event, finalParam); } void _notifyCreated() { From 8b1a169840c16ebb0f6e2ce082d5f881689d3449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Sat, 9 Nov 2024 02:07:13 -0600 Subject: [PATCH 045/141] feat(framework): Enhance dependency management to ensure to re-build when is detected changes while building. --- .../lib/src/framework/dependency.dart | 244 ++++++++++++++++-- .../lib/src/framework/provider_impl.dart | 4 +- .../src/framework/scope_element_mixin.dart | 184 ++++--------- .../lib/src/widgets/rt_selector.dart | 2 +- 4 files changed, 270 insertions(+), 164 deletions(-) diff --git a/packages/flutter_reactter/lib/src/framework/dependency.dart b/packages/flutter_reactter/lib/src/framework/dependency.dart index 89b39ab2..5ecdaf6c 100644 --- a/packages/flutter_reactter/lib/src/framework/dependency.dart +++ b/packages/flutter_reactter/lib/src/framework/dependency.dart @@ -1,12 +1,53 @@ part of '../framework.dart'; +typedef DependencyListener = void Function( + Dependency dependency, + dynamic instanceOrState, +); + abstract class Dependency { T? _instance; final Set _states; + bool _isDirty = false; + bool get isDirty => _isDirty; + CallbackEvent? _listener; + + Dependency(this._instance, this._states) { + listen((dependency, instanceOrState) => dependency.markDirty()); + } + + MasterDependency toMaster(); + + void commitToMaster(MasterDependency masterDependency); + + @mustCallSuper + void markDirty() { + _isDirty = true; + unlisten(); + } + + void listen(covariant DependencyListener callback); + + @mustBeOverridden + @mustCallSuper + void unlisten() { + _listener = null; + } + + @mustCallSuper + void dispose() { + unlisten(); + } - Dependency(this._instance, this._states); + CallbackEvent _buildListenerCallback(DependencyListener callback) { + unlisten(); - MasterDependency makeMaster(); + return _listener = (instanceOrState, _) { + callback(this, instanceOrState); + markDirty(); + unlisten(); + }; + } } class MasterDependency extends Dependency { @@ -19,28 +60,66 @@ class MasterDependency extends Dependency { }) : _selects = selects.toSet(), super(instance, states.toSet()); - void putDependency(Dependency dependency) { - if (dependency is InstanceDependency) { - _instance = dependency._instance as T; - return; + void putDependency(Dependency dependency) { + dependency.commitToMaster(this); + } + + @override + void dispose() { + unlisten(); + + for (final select in _selects) { + select.unlisten(); + } + + super.dispose(); + } + + @override + void commitToMaster(MasterDependency masterDependency) { + if (_instance != null) masterDependency._instance = _instance; + + masterDependency._states.addAll(_states); + masterDependency._selects.addAll(_selects); + } + + @override + void listen(DependencyListener callback) { + final eventListener = _buildListenerCallback(callback); + + if (_instance != null) { + Rt.one(_instance, Lifecycle.didUpdate, eventListener); } - if (dependency is StatesDependency) { - _states.addAll(dependency._states); - return; + for (final state in _states) { + Rt.one(state, Lifecycle.didUpdate, eventListener); } - if (dependency is SelectDependency) { - _instance = dependency._instance as T; - _selects.add(dependency); + for (final select in _selects) { + select.listen(callback); } } - // coverage:ignore-start @override - MasterDependency makeMaster() { - throw UnimplementedError(); + void unlisten() { + if (_listener == null) return; + + if (_instance != null) { + Rt.off(_instance, Lifecycle.didUpdate, _listener!); + _listener = null; + } + + for (final state in _states) { + if (_listener == null) return; + Rt.off(state, Lifecycle.didUpdate, _listener!); + } + + super.unlisten(); } + + // coverage:ignore-start + @override + MasterDependency toMaster() => this; // coverage:ignore-end } @@ -48,29 +127,86 @@ class InstanceDependency extends Dependency { InstanceDependency(T instance) : super(instance, const {}); @override - MasterDependency makeMaster() => MasterDependency( - instance: _instance, - ); + MasterDependency toMaster() { + final masterDependency = MasterDependency( + instance: _instance, + ); + + if (isDirty) masterDependency.markDirty(); + + return masterDependency; + } + + @override + void commitToMaster(MasterDependency masterDependency) { + if (_instance != null) masterDependency._instance = _instance; + } + + @override + void listen(DependencyListener callback) { + final eventListener = _buildListenerCallback(callback); + + Rt.one(_instance, Lifecycle.didUpdate, eventListener); + } + + @override + void unlisten() { + if (_listener == null) return; + + Rt.off(_instance, Lifecycle.didUpdate, _listener!); + super.unlisten(); + } } class StatesDependency extends Dependency { StatesDependency(Set states) : super(null, states); @override - MasterDependency makeMaster() => MasterDependency( - states: _states, - ); + MasterDependency toMaster() { + final masterDependency = MasterDependency( + states: _states, + ); + + if (isDirty) masterDependency.markDirty(); + + return masterDependency; + } + + @override + void commitToMaster(MasterDependency masterDependency) { + masterDependency._states.addAll(_states); + } + + @override + void listen(DependencyListener callback) { + final eventListener = _buildListenerCallback(callback); + + for (final state in _states) { + Rt.one(state, Lifecycle.didUpdate, eventListener); + } + } + + @override + void unlisten() { + if (_listener == null) return; + + for (final state in _states) { + Rt.off(state, Lifecycle.didUpdate, _listener!); + } + + super.unlisten(); + } } class SelectDependency extends Dependency { - final T _instanceSelect; + final T instanceSelect; final Selector computeValue; late final dynamic value; SelectDependency({ required T instance, required this.computeValue, - }) : _instanceSelect = instance, + }) : instanceSelect = instance, super(null, {}) { value = resolve(true); if (_states.isEmpty) _instance = instance; @@ -78,7 +214,7 @@ class SelectDependency extends Dependency { dynamic resolve([bool isWatchState = false]) { return computeValue( - _instanceSelect, + instanceSelect, isWatchState ? watchState : skipWatchState, ); } @@ -88,10 +224,62 @@ class SelectDependency extends Dependency { return state; } - static S skipWatchState(S s) => s; + S skipWatchState(S s) => s; + + @override + MasterDependency toMaster() { + return MasterDependency(selects: {this}); + } @override - MasterDependency makeMaster() => MasterDependency( - selects: {this}, - ); + void commitToMaster(MasterDependency masterDependency) { + if (_instance != null) masterDependency._instance = _instance; + + masterDependency._selects.add(this); + } + + @override + CallbackEvent _buildListenerCallback(DependencyListener callback) { + unlisten(); + + _listener = (instanceOrState, _) { + if (value == resolve()) return; + + callback(this, instanceOrState); + markDirty(); + unlisten(); + }; + + return _listener!; + } + + @override + void listen(DependencyListener callback) { + final eventListener = _buildListenerCallback(callback); + + if (_instance != null) { + Rt.on(_instance, Lifecycle.didUpdate, eventListener); + _listener = null; + } + + for (final state in _states) { + Rt.on(state, Lifecycle.didUpdate, eventListener); + } + } + + @override + void unlisten() { + if (_listener == null) return; + + if (_instance != null) { + Rt.off(_instance, Lifecycle.didUpdate, _listener!); + _listener = null; + } + + for (final state in _states) { + Rt.off(state, Lifecycle.didUpdate, _listener!); + } + + super.unlisten(); + } } diff --git a/packages/flutter_reactter/lib/src/framework/provider_impl.dart b/packages/flutter_reactter/lib/src/framework/provider_impl.dart index 09c6fb95..a500bf56 100644 --- a/packages/flutter_reactter/lib/src/framework/provider_impl.dart +++ b/packages/flutter_reactter/lib/src/framework/provider_impl.dart @@ -99,8 +99,8 @@ class ProvideImpl extends ProviderBase context.dependOnInheritedElement( providerInheritedElement!, aspect: listenStates == null - ? InstanceDependency(instance) - : StatesDependency(listenStates(instance).toSet()), + ? InstanceDependency(instance) + : StatesDependency(listenStates(instance).toSet()), ); return instance; diff --git a/packages/flutter_reactter/lib/src/framework/scope_element_mixin.dart b/packages/flutter_reactter/lib/src/framework/scope_element_mixin.dart index 32d7fce3..43ff995b 100644 --- a/packages/flutter_reactter/lib/src/framework/scope_element_mixin.dart +++ b/packages/flutter_reactter/lib/src/framework/scope_element_mixin.dart @@ -4,14 +4,14 @@ part of '../framework.dart'; /// and notify when should be updated its dependencies. @internal mixin ScopeElementMixin on InheritedElement { - bool _isFlushDependentsScheduled = false; - bool _updatedShouldNotify = false; - final _dependenciesDirty = HashSet(); final _dependents = HashMap(); - final _instancesAndStatesDependencies = HashMap>(); final _dependentsFlushReady = {}; + final _dependenciesDirty = HashSet(); bool get hasDependenciesDirty => _dependenciesDirty.isNotEmpty; + bool _isFlushDependentsScheduled = false; + bool _updatedShouldNotify = false; + bool _isMarkNeedsBuild = false; @override void update(InheritedWidget newWidget) { @@ -31,9 +31,7 @@ mixin ScopeElementMixin on InheritedElement { @override void unmount() { - _dependents.clear(); - _dependenciesDirty.clear(); - _removeListeners(); + _disposeDependencies(); return super.unmount(); } @@ -45,18 +43,20 @@ mixin ScopeElementMixin on InheritedElement { @override void updateDependencies(Element dependent, Object? aspect) { // coverage:ignore-start - if (aspect is! Dependency) { + if (aspect is! Dependency || _isMarkNeedsBuild) { return super.updateDependencies(dependent, aspect); } - var masterDependency = getDependencies(dependent); + var dependency = getDependencies(dependent); - if (masterDependency != null && masterDependency is! MasterDependency) { + if (dependency != null && dependency is! MasterDependency) { return super.updateDependencies(dependent, aspect); } // coverage:ignore-end - _scheduleflushDependents(); + assert(dependency is MasterDependency || dependency == null); + + var masterDependency = dependency is MasterDependency ? dependency : null; masterDependency = _flushDependent(dependent, masterDependency); @@ -64,8 +64,13 @@ mixin ScopeElementMixin on InheritedElement { masterDependency.putDependency(aspect); } - masterDependency ??= aspect.makeMaster(); - _addListener(masterDependency as MasterDependency); + masterDependency ??= aspect.toMaster(); + + if (masterDependency.isDirty) { + markNeedsNotifyDependents(masterDependency, null); + } else { + masterDependency.listen(markNeedsNotifyDependents); + } return setDependencies(dependent, masterDependency); } @@ -92,80 +97,12 @@ mixin ScopeElementMixin on InheritedElement { if (dependency is MasterDependency) ...dependency._selects, }; - if (dependencies.any(_dependenciesDirty.contains)) { - dependent.didChangeDependencies(); - _removeDependencies(dependent); - _dependenciesDirty.removeAll(dependencies); - } - } - - void _addListener(Dependency dependency) { - if (dependency._instance != null) { - _addInstanceListener(dependency._instance!, dependency); - } - - if (dependency._states.isNotEmpty) { - _addStatesListener(dependency._states, dependency); - } - - if (dependency is MasterDependency && dependency._selects.isNotEmpty) { - for (final dependency in dependency._selects) { - _addListener(dependency); - } - } - } - - void _addInstanceListener( - Object instance, - Dependency dependency, - ) { - if (!_instancesAndStatesDependencies.containsKey(instance)) { - Rt.on(instance, Lifecycle.didUpdate, _markNeedsNotifyDependents); - } - - _instancesAndStatesDependencies[instance] ??= {}; - _instancesAndStatesDependencies[instance]?.add(dependency); - } - - void _addStatesListener( - Set states, - Dependency dependency, - ) { - for (final state in states) { - if (!_instancesAndStatesDependencies.containsKey(state)) { - Rt.on(state, Lifecycle.didUpdate, _markNeedsNotifyDependents); - } - - _instancesAndStatesDependencies[state] ??= {}; - _instancesAndStatesDependencies[state]?.add(dependency); - } - } - - void _markNeedsNotifyDependents(Object? instanceOrState, _) { - assert(instanceOrState != null); - - final dependencies = _instancesAndStatesDependencies[instanceOrState]; - - if (dependencies?.isEmpty ?? true) return; - - final dependenciesDirty = [ - for (final dependency in dependencies!) - if (dependency is! SelectDependency) - dependency - else if (dependency.value != dependency.resolve()) - dependency - ]; - - if (dependenciesDirty.isEmpty) return; + if (!dependencies.any(_dependenciesDirty.contains)) return; - _dependenciesDirty.addAll(dependenciesDirty); - dependencies.removeAll(dependenciesDirty); - - if (dependencies.isEmpty) { - _clearInstanceOrStateDependencies(instanceOrState); - } - - markNeedsBuild(); + dependent.didChangeDependencies(); + _removeDependencies(dependent); + _dependenciesDirty.removeAll(dependencies); + _isMarkNeedsBuild = false; } void _scheduleflushDependents() { @@ -179,69 +116,50 @@ mixin ScopeElementMixin on InheritedElement { }); } - Object? _flushDependent(Element dependent, Object? dependency) { + MasterDependency? _flushDependent( + Element dependent, + MasterDependency? dependency, + ) { + _scheduleflushDependents(); + if (_dependentsFlushReady.contains(dependent)) return dependency; _dependentsFlushReady.add(dependent); - if (dependency is! MasterDependency) return dependency; - - _clearDependency(dependency); + if (dependency is MasterDependency) dependency.dispose(); return null; } - void _removeDependencies(Element dependent) { - setDependencies(dependent, null); - _dependents.remove(dependent); + void markNeedsNotifyDependents( + Dependency dependency, + dynamic instanceOrState, + ) { + _dependenciesDirty.add(dependency); + markNeedsBuild(); } - void _removeListeners() { - for (final instancesOrStates in _instancesAndStatesDependencies.keys) { - Rt.off( - instancesOrStates, - Lifecycle.didUpdate, - _markNeedsNotifyDependents, - ); - } + @override + void markNeedsBuild() { + if (_isMarkNeedsBuild) return; - _instancesAndStatesDependencies.clear(); + _isMarkNeedsBuild = true; + super.markNeedsBuild(); } - void _clearDependency(Dependency dependency) { - if (dependency._instance != null) { - final dependencies = - _instancesAndStatesDependencies[dependency._instance]; - dependencies?.remove(dependency); - - if (dependencies?.isEmpty ?? false) { - _clearInstanceOrStateDependencies(dependency._instance); - } - } - - for (final state in dependency._states) { - final dependenciesOfState = _instancesAndStatesDependencies[state]; - dependenciesOfState?.remove(dependency); + void _removeDependencies(Element dependent) { + setDependencies(dependent, null); + _dependents.remove(dependent); + } - if (dependenciesOfState?.isEmpty ?? false) { - _clearInstanceOrStateDependencies(state); - } - } + void _disposeDependencies() { + final dependencies = _dependents.values; - if (dependency is MasterDependency) { - for (final select in dependency._selects) { - _clearDependency(select); - } + for (final dependency in dependencies) { + if (dependency is Dependency) dependency.dispose(); } - } - - void _clearInstanceOrStateDependencies(Object? instanceOrState) { - Rt.off( - instanceOrState, - Lifecycle.didUpdate, - _markNeedsNotifyDependents, - ); - _instancesAndStatesDependencies.remove(instanceOrState); + _dependents.clear(); + _dependenciesDirty.clear(); } } diff --git a/packages/flutter_reactter/lib/src/widgets/rt_selector.dart b/packages/flutter_reactter/lib/src/widgets/rt_selector.dart index 3fd725ef..1b7c3da3 100644 --- a/packages/flutter_reactter/lib/src/widgets/rt_selector.dart +++ b/packages/flutter_reactter/lib/src/widgets/rt_selector.dart @@ -163,7 +163,7 @@ class RtSelector extends StatelessWidget { ? inheritedElement.instance : null; - final dependency = SelectDependency( + final dependency = SelectDependency( instance: instance as T, computeValue: selector as dynamic, ); From 298beb8ef72b018dc99add2a1164514014778171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Sun, 10 Nov 2024 23:24:25 -0600 Subject: [PATCH 046/141] refactor(framework): Simplify dependency listener callbacks and improve state management checks --- .../lib/src/framework/dependency.dart | 41 +++---- .../lib/src/framework/provider_impl.dart | 2 +- .../src/framework/scope_element_mixin.dart | 112 +++++++++++------- .../lib/src/widgets/rt_scope.dart | 2 +- 4 files changed, 86 insertions(+), 71 deletions(-) diff --git a/packages/flutter_reactter/lib/src/framework/dependency.dart b/packages/flutter_reactter/lib/src/framework/dependency.dart index 5ecdaf6c..82e071a7 100644 --- a/packages/flutter_reactter/lib/src/framework/dependency.dart +++ b/packages/flutter_reactter/lib/src/framework/dependency.dart @@ -1,10 +1,5 @@ part of '../framework.dart'; -typedef DependencyListener = void Function( - Dependency dependency, - dynamic instanceOrState, -); - abstract class Dependency { T? _instance; final Set _states; @@ -13,7 +8,7 @@ abstract class Dependency { CallbackEvent? _listener; Dependency(this._instance, this._states) { - listen((dependency, instanceOrState) => dependency.markDirty()); + listen(markDirty); } MasterDependency toMaster(); @@ -26,7 +21,7 @@ abstract class Dependency { unlisten(); } - void listen(covariant DependencyListener callback); + void listen(covariant void Function() callback); @mustBeOverridden @mustCallSuper @@ -39,11 +34,11 @@ abstract class Dependency { unlisten(); } - CallbackEvent _buildListenerCallback(DependencyListener callback) { + CallbackEvent _buildListenerCallback(void Function() callback) { unlisten(); return _listener = (instanceOrState, _) { - callback(this, instanceOrState); + callback(); markDirty(); unlisten(); }; @@ -84,15 +79,15 @@ class MasterDependency extends Dependency { } @override - void listen(DependencyListener callback) { + void listen(void Function() callback) { final eventListener = _buildListenerCallback(callback); if (_instance != null) { - Rt.one(_instance, Lifecycle.didUpdate, eventListener); + Rt.on(_instance, Lifecycle.didUpdate, eventListener); } for (final state in _states) { - Rt.one(state, Lifecycle.didUpdate, eventListener); + Rt.on(state, Lifecycle.didUpdate, eventListener); } for (final select in _selects) { @@ -106,11 +101,9 @@ class MasterDependency extends Dependency { if (_instance != null) { Rt.off(_instance, Lifecycle.didUpdate, _listener!); - _listener = null; } for (final state in _states) { - if (_listener == null) return; Rt.off(state, Lifecycle.didUpdate, _listener!); } @@ -143,10 +136,10 @@ class InstanceDependency extends Dependency { } @override - void listen(DependencyListener callback) { + void listen(void Function() callback) { final eventListener = _buildListenerCallback(callback); - Rt.one(_instance, Lifecycle.didUpdate, eventListener); + Rt.on(_instance, Lifecycle.didUpdate, eventListener); } @override @@ -178,11 +171,11 @@ class StatesDependency extends Dependency { } @override - void listen(DependencyListener callback) { + void listen(void Function() callback) { final eventListener = _buildListenerCallback(callback); for (final state in _states) { - Rt.one(state, Lifecycle.didUpdate, eventListener); + Rt.on(state, Lifecycle.didUpdate, eventListener); } } @@ -239,27 +232,24 @@ class SelectDependency extends Dependency { } @override - CallbackEvent _buildListenerCallback(DependencyListener callback) { + CallbackEvent _buildListenerCallback(void Function() callback) { unlisten(); - _listener = (instanceOrState, _) { + return _listener = (instanceOrState, _) { if (value == resolve()) return; - callback(this, instanceOrState); + callback(); markDirty(); unlisten(); }; - - return _listener!; } @override - void listen(DependencyListener callback) { + void listen(void Function() callback) { final eventListener = _buildListenerCallback(callback); if (_instance != null) { Rt.on(_instance, Lifecycle.didUpdate, eventListener); - _listener = null; } for (final state in _states) { @@ -273,7 +263,6 @@ class SelectDependency extends Dependency { if (_instance != null) { Rt.off(_instance, Lifecycle.didUpdate, _listener!); - _listener = null; } for (final state in _states) { diff --git a/packages/flutter_reactter/lib/src/framework/provider_impl.dart b/packages/flutter_reactter/lib/src/framework/provider_impl.dart index a500bf56..701c9c28 100644 --- a/packages/flutter_reactter/lib/src/framework/provider_impl.dart +++ b/packages/flutter_reactter/lib/src/framework/provider_impl.dart @@ -219,7 +219,7 @@ class ProviderElement extends InheritedElement @override Widget build() { - if (hasDependenciesDirty) { + if (hasDirtyDependencies) { notifyClients(widget); if (_prevChild != null) return _prevChild!; diff --git a/packages/flutter_reactter/lib/src/framework/scope_element_mixin.dart b/packages/flutter_reactter/lib/src/framework/scope_element_mixin.dart index 43ff995b..63f121a0 100644 --- a/packages/flutter_reactter/lib/src/framework/scope_element_mixin.dart +++ b/packages/flutter_reactter/lib/src/framework/scope_element_mixin.dart @@ -4,15 +4,15 @@ part of '../framework.dart'; /// and notify when should be updated its dependencies. @internal mixin ScopeElementMixin on InheritedElement { - final _dependents = HashMap(); + final _dependents = HashMap(); final _dependentsFlushReady = {}; - final _dependenciesDirty = HashSet(); - - bool get hasDependenciesDirty => _dependenciesDirty.isNotEmpty; bool _isFlushDependentsScheduled = false; bool _updatedShouldNotify = false; bool _isMarkNeedsBuild = false; + bool get hasDirtyDependencies => + _isMarkNeedsBuild || _dependents.values.any((dep) => dep.isDirty); + @override void update(InheritedWidget newWidget) { _updatedShouldNotify = true; @@ -22,89 +22,107 @@ mixin ScopeElementMixin on InheritedElement { @override void updated(InheritedWidget oldWidget) { - super.updated(oldWidget); - + // This point is generally reached when applying the hot reload. if (_updatedShouldNotify) { + // If the widget tree is updated, we need to reset the state + // to avoid memory leaks. + _resetState(); notifyClients(oldWidget); + return; } + + super.updated(oldWidget); } @override void unmount() { - _disposeDependencies(); + _resetState(); return super.unmount(); } + /// Returns the value of the dependency of the dependent. + /// If a MasterDepndency is stored in this scope, it will be returned. + /// Otherwise, it will return the value of the dependency of the dependent in + /// the parent scope. @override Object? getDependencies(Element dependent) { return _dependents[dependent] ?? super.getDependencies(dependent); } + /// Sets the value of the dependency of the dependent. + /// If a MasterDepndency is stored in this scope, it will be set. + /// Otherwise, it will set the value of the dependency of the dependent in + /// the parent scope. + @override + void setDependencies(Element dependent, Object? value) { + if (value is MasterDependency) _dependents[dependent] = value; + + super.setDependencies(dependent, value); + } + @override void updateDependencies(Element dependent, Object? aspect) { - // coverage:ignore-start + // If the aspect is not a Dependency or if the widget tree is marked as + // needing build, we can skip the update of the dependencies. if (aspect is! Dependency || _isMarkNeedsBuild) { return super.updateDependencies(dependent, aspect); } var dependency = getDependencies(dependent); + // If no MasterDependency is stored, we can skip the update of the dependencies. if (dependency != null && dependency is! MasterDependency) { return super.updateDependencies(dependent, aspect); } - // coverage:ignore-end - assert(dependency is MasterDependency || dependency == null); + assert(dependency is MasterDependency?); - var masterDependency = dependency is MasterDependency ? dependency : null; + MasterDependency? masterDependency = dependency as MasterDependency?; + // Flush the dependent element and dispose of the dependency if it exists. masterDependency = _flushDependent(dependent, masterDependency); + // If the dependency is a MasterDependency, add the aspect to it. if (masterDependency is MasterDependency) { masterDependency.putDependency(aspect); } + // If the dependency is not a MasterDependency, create a new MasterDependency. masterDependency ??= aspect.toMaster(); if (masterDependency.isDirty) { - markNeedsNotifyDependents(masterDependency, null); + markNeedsBuild(); } else { - masterDependency.listen(markNeedsNotifyDependents); + masterDependency.listen(markNeedsBuild); } return setDependencies(dependent, masterDependency); } - @override - void setDependencies(Element dependent, Object? value) { - if (value is MasterDependency) { - _dependents[dependent] = value; - } - - super.setDependencies(dependent, value); - } - @override void notifyDependent(InheritedWidget oldWidget, Element dependent) { - // select can never be used inside `didChangeDependencies`, so if the - // dependent is already marked as needed build, there is no point - // in executing the selectors. + // if the dependent is already marked as needed build, there is no point + // in executing the evaluations about the dirty dependencies, because + // the dependent will be rebuild anyway. if (dependent.dirty) return; + if (!_isMarkNeedsBuild) return super.notifyDependent(oldWidget, dependent); + final dependency = getDependencies(dependent); - final dependencies = { - dependency, - if (dependency is MasterDependency) ...dependency._selects, - }; - if (!dependencies.any(_dependenciesDirty.contains)) return; + if (dependency is! MasterDependency) return; + + final isDirty = + dependency.isDirty || dependency._selects.any((dep) => dep.isDirty); + + if (!isDirty) return; - dependent.didChangeDependencies(); _removeDependencies(dependent); - _dependenciesDirty.removeAll(dependencies); _isMarkNeedsBuild = false; + dependent.didChangeDependencies(); } + /// Schedules a callback to flush dependents after the current frame. void _scheduleflushDependents() { if (_isFlushDependentsScheduled) return; @@ -116,6 +134,15 @@ mixin ScopeElementMixin on InheritedElement { }); } + /// Flushes the dependent element and disposes of the dependency if it exists. + /// + /// This method schedules the flushing of dependents and ensures that the + /// dependent element is marked as ready for flushing. If the dependent element + /// is already marked as ready, it returns the existing dependency. + /// + /// If the dependency is of type [MasterDependency], it is disposed of. + /// + /// Returns `null` after disposing of the dependency. MasterDependency? _flushDependent( Element dependent, MasterDependency? dependency, @@ -131,14 +158,6 @@ mixin ScopeElementMixin on InheritedElement { return null; } - void markNeedsNotifyDependents( - Dependency dependency, - dynamic instanceOrState, - ) { - _dependenciesDirty.add(dependency); - markNeedsBuild(); - } - @override void markNeedsBuild() { if (_isMarkNeedsBuild) return; @@ -149,17 +168,24 @@ mixin ScopeElementMixin on InheritedElement { void _removeDependencies(Element dependent) { setDependencies(dependent, null); - _dependents.remove(dependent); + _dependents.remove(dependent)?.dispose(); + } + + void _resetState() { + _disposeDependencies(); + _dependentsFlushReady.clear(); + _isFlushDependentsScheduled = false; + _updatedShouldNotify = false; + _isMarkNeedsBuild = false; } void _disposeDependencies() { final dependencies = _dependents.values; for (final dependency in dependencies) { - if (dependency is Dependency) dependency.dispose(); + dependency.dispose(); } _dependents.clear(); - _dependenciesDirty.clear(); } } diff --git a/packages/flutter_reactter/lib/src/widgets/rt_scope.dart b/packages/flutter_reactter/lib/src/widgets/rt_scope.dart index 85bc7dfb..aee745f3 100644 --- a/packages/flutter_reactter/lib/src/widgets/rt_scope.dart +++ b/packages/flutter_reactter/lib/src/widgets/rt_scope.dart @@ -57,7 +57,7 @@ class RtScopeElement extends InheritedElement with ScopeElementMixin { @override Widget build() { - if (hasDependenciesDirty) { + if (hasDirtyDependencies) { notifyClients(widget); if (prevChild != null) return prevChild!; From e75a6412e4c3466524ddb44804203289ab1fc9c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Sun, 10 Nov 2024 23:25:08 -0600 Subject: [PATCH 047/141] fix(core): Remove listeners correctly from single-use listeners and handle null cases --- packages/reactter/lib/src/core/notifier.dart | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/reactter/lib/src/core/notifier.dart b/packages/reactter/lib/src/core/notifier.dart index 23f60211..29168f02 100644 --- a/packages/reactter/lib/src/core/notifier.dart +++ b/packages/reactter/lib/src/core/notifier.dart @@ -181,6 +181,10 @@ abstract class Notifier { /// * [addListener], which registers a closure to be called when the object /// changes. void removeListener(Function listener) { + if (_listenersSingleUse.contains(listener)) { + _listenersSingleUse.remove(listener); + } + // This method is allowed to be called on disposed instances for usability // reasons. Due to how our frame scheduling logic between render objects and // overlays, it is common that the owner of this instance would be disposed a @@ -195,6 +199,7 @@ abstract class Notifier { // effectively resize the list at the end of all notifyListeners // iterations. _listeners[i] = null; + _reentrantlyRemovedListeners++; } else { // When we are outside the notifyListeners iterations we can @@ -272,12 +277,13 @@ abstract class Notifier { final int end = _count; for (int i = 0; i < end; i++) { final listener = _listeners[i]; - assert(listener != null, 'Unexpected null listener in $target.'); + + if (listener == null) continue; try { if (_listenersSingleUse.contains(listener)) { - removeListener(listener!); _listenersSingleUse.remove(listener); + removeListener(listener); } listenerCall(listener, param); From 77e7057d9a36420592c6c73b9d6681ab0d59a290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Mon, 11 Nov 2024 00:45:48 -0600 Subject: [PATCH 048/141] refactor(devtools): Clean up widget structure and improve performance by removing unused keys and optimizing layouts --- .../lib/src/controllers/nodes_controller.dart | 1 - .../lib/src/data/property_node.dart | 229 ++++++++++-------- .../lib/src/data/state_node.dart | 4 +- .../lib/src/widgets/properties_list.dart | 4 +- .../lib/src/widgets/property_tile.dart | 35 +-- .../lib/src/widgets/tile_builder.dart | 22 +- 6 files changed, 153 insertions(+), 142 deletions(-) diff --git a/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart b/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart index e2b5ab4d..6385ac58 100644 --- a/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart +++ b/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart @@ -84,7 +84,6 @@ class NodesController { Future addNodeByKey(String nodeKey) async { final nodeInfo = await devtoolsSevices.getNodeBykey(nodeKey); addNodeByMapInfo(nodeInfo); - print('addNodeByKey $nodeKey ${nodeInfo['boundInstanceKey']}'); } void addNodes(List nodesInfo) { diff --git a/packages/reactter_devtools_extension/lib/src/data/property_node.dart b/packages/reactter_devtools_extension/lib/src/data/property_node.dart index a721463b..806cf8e6 100644 --- a/packages/reactter_devtools_extension/lib/src/data/property_node.dart +++ b/packages/reactter_devtools_extension/lib/src/data/property_node.dart @@ -2,8 +2,8 @@ import 'dart:async'; import 'dart:collection'; import 'package:devtools_app_shared/service.dart'; + import 'package:flutter_reactter/reactter.dart'; -import 'package:reactter_devtools_extension/src/data/constants.dart'; import 'package:reactter_devtools_extension/src/data/tree_node.dart'; import 'package:reactter_devtools_extension/src/services/eval_service.dart'; import 'package:reactter_devtools_extension/src/utils/extensions.dart'; @@ -16,17 +16,17 @@ base class PropertyNode extends TreeNode { InstanceRef _valueRef; InstanceRef get valueRef => _valueRef; - Disposable? isAlive; - bool _isResolved = false; - - FutureOr? _valueFuture; + Disposable? _isAlive; + bool _isResolved = false; + bool _isValueUpdating = false; final LinkedHashMap _childNodeRefs = LinkedHashMap(); + final uValueFuture = UseState?>(null); final uValue = UseState(null); - final uInstanceInfo = UseState?>(null); final uIsLoading = UseState(false); + final uInstanceInfo = UseState?>(null); PropertyNode._({ required this.key, @@ -51,149 +51,165 @@ base class PropertyNode extends TreeNode { ); } - Future reassignValueRef(InstanceRef valueRef) async { - isAlive?.dispose(); - _isResolved = false; - _valueRef = valueRef; - _valueFuture = null; - uIsLoading.value = false; + Future updateValueRef(InstanceRef valueRef) async { + if (uValueFuture.value == null || _isValueUpdating) return; - if (list == null) return; + _isValueUpdating = true; - await getValueAsync(); + await uValueFuture.value; + + uValueFuture.value = null; + _valueRef = valueRef; + _isResolved = false; + uIsLoading.value = false; } Future getValueAsync() async { - if (_valueFuture != null) return _valueFuture!; + if (uValueFuture.value != null) return uValueFuture.value!; if (_isResolved) return Future.value(uValue.value); if (uIsLoading.value) return Future.value(uValue.value); - return _valueFuture = Rt.batch(() async { - uIsLoading.value = true; - isAlive = Disposable(); - String? value; - - try { - switch (valueRef.kind) { - case InstanceKind.kList: - value = await _resolveValueByList(); - break; - case InstanceKind.kMap: - value = await _resolveValueByMap(); - break; - case InstanceKind.kRecord: - value = await _resolveValueByRecord(); - break; - case InstanceKind.kSet: - value = await _resolveValueBySet(); - break; - case InstanceKind.kPlainInstance: - value = await _resolveValueByPlainInstance(); - break; - case InstanceKind.kNull: - value = 'null'; - uChildren.value.clear(); - break; - case InstanceKind.kString: - value = '"${valueRef.valueAsString}"'; - uChildren.value.clear(); - break; - default: - value = valueRef.valueAsString; - uChildren.value.clear(); - break; + try { + return uValueFuture.value = Rt.batch(() async { + uIsLoading.value = true; + _isAlive = Disposable(); + String? value; + + try { + switch (valueRef.kind) { + case InstanceKind.kList: + value = await resolveValueByList(); + break; + case InstanceKind.kSet: + value = await resolveValueBySet(); + break; + case InstanceKind.kMap: + value = await resolveValueByMap(); + break; + case InstanceKind.kRecord: + value = await resolveValueByRecord(); + break; + case InstanceKind.kPlainInstance: + value = await resolveValueByPlainInstance(); + break; + case InstanceKind.kNull: + value = 'null'; + uChildren.value.clear(); + break; + case InstanceKind.kString: + value = '"${valueRef.valueAsString}"'; + uChildren.value.clear(); + break; + default: + value = valueRef.valueAsString; + uChildren.value.clear(); + break; + } + } catch (e) { + value = null; } - } catch (e) { - value = null; - } - value ??= 'Unknown - Cannot load value'; + value ??= 'Unknown - Cannot load value'; + + uIsLoading.value = false; + uValue.value = value; + return value; + }); + } finally { _isResolved = true; - uIsLoading.value = false; - return uValue.value = value; - }); + _isValueUpdating = false; + } } - Future _resolveValueBySet() async { - assert(valueRef.kind == InstanceKind.kSet); + Future resolveValueByList() async { + assert(valueRef.kind == InstanceKind.kList); - final instance = await valueRef.safeGetInstance(isAlive); + final instance = await valueRef.safeGetInstance(_isAlive); final elements = instance?.elements?.cast() ?? []; - final children = { - for (var i = 0; i < elements.length; i++) i.toString(): elements[i], - }; + final SplayTreeMap children = SplayTreeMap( + (a, b) => int.parse(a).compareTo(int.parse(b)), + ); - await _addChildren(children); + for (var i = 0; i < elements.length; i++) { + children[i.toString()] = elements[i]; + } - return await _resolveValueByChildren( - buildValue: (key, value) => '$value', - prefix: '{', - suffix: '}', + await addChildren(children); + + return await resolveValueByChildren( + buildValue: (key, value) => "$value", + prefix: '[', + suffix: ']', ); } - Future _resolveValueByList() async { - assert(valueRef.kind == InstanceKind.kList); + Future resolveValueBySet() async { + assert(valueRef.kind == InstanceKind.kSet); - final instance = await valueRef.safeGetInstance(isAlive); + final instance = await valueRef.safeGetInstance(_isAlive); final elements = instance?.elements?.cast() ?? []; - final children = { - for (var i = 0; i < elements.length; i++) i.toString(): elements[i], - }; + final SplayTreeMap children = SplayTreeMap( + (a, b) => int.parse(a).compareTo(int.parse(b)), + ); - await _addChildren(children); + for (var i = 0; i < elements.length; i++) { + children[i.toString()] = elements[i]; + } - return await _resolveValueByChildren( - buildValue: (key, value) => "$value", - prefix: '[', - suffix: ']', + await addChildren(children); + + return await resolveValueByChildren( + buildValue: (key, value) => '$value', + prefix: '{', + suffix: '}', ); } - Future _resolveValueByMap() async { + Future resolveValueByMap() async { assert(valueRef.kind == InstanceKind.kMap); - final instance = await valueRef.safeGetInstance(isAlive); + final instance = await valueRef.safeGetInstance(_isAlive); final associations = instance?.associations?.cast() ?? []; - final children = {}; + final SplayTreeMap children = SplayTreeMap(); for (final entry in associations) { final keyRef = entry.key as InstanceRef; - final key = await keyRef.evalValueFirstLevel(isAlive); + final key = await keyRef.evalValueFirstLevel(_isAlive); children[key.toString()] = entry.value; } - await _addChildren(children); + await addChildren(children); - return await _resolveValueByChildren( + return await resolveValueByChildren( buildValue: (key, value) => '$key: $value', prefix: '{', suffix: '}', ); } - Future _resolveValueByRecord() async { + Future resolveValueByRecord() async { assert(valueRef.kind == InstanceKind.kRecord); - final instance = await valueRef.safeGetInstance(isAlive); + final instance = await valueRef.safeGetInstance(_isAlive); final fields = instance?.fields?.cast() ?? []; - final children = { - for (final field in fields) - field.name.toString(): field.value as InstanceRef, - }; + final SplayTreeMap children = SplayTreeMap(); + + for (final field in fields) { + children[field.name.toString()] = field.value as InstanceRef; + } - await _addChildren(children); + await addChildren(children); - return await _resolveValueByChildren( + return await resolveValueByChildren( buildValue: (key, value) => '$key: $value', prefix: '(', suffix: ')', ); } - Future _addChildren(Map children) async { + Future addChildren(SplayTreeMap children) async { final childrenToRemove = _childNodeRefs.keys.toSet(); for (final child in children.entries) { @@ -207,7 +223,7 @@ base class PropertyNode extends TreeNode { ); if (isRemoved) { - childNode.reassignValueRef(child.value); + childNode.updateValueRef(child.value); } else { addChild(childNode); } @@ -219,7 +235,7 @@ base class PropertyNode extends TreeNode { } } - Future _resolveValueByChildren({ + Future resolveValueByChildren({ required String Function(String key, String? value) buildValue, String prefix = '{', String suffix = '}', @@ -273,27 +289,38 @@ base class PropertyNode extends TreeNode { return "$prefix$childrenValueCutted$cuttedEllipsis$moreEllipsis$suffix"; } - Future _resolveValueByPlainInstance() async { + Future resolveValueByPlainInstance() async { assert(valueRef.kind == InstanceKind.kPlainInstance); final eval = await EvalService.devtoolsEval; final valueInfo = await EvalService.evalsQueue.add( () => eval.evalInstance( 'RtDevTools._instance?.getPlainInstanceInfo(value)', - isAlive: isAlive, + isAlive: _isAlive, scope: {'value': valueRef.id!}, ), ); + final instance = await valueRef.safeGetInstance(_isAlive); + final fields = instance?.fields?.cast() ?? []; + final SplayTreeMap children = SplayTreeMap(); + + for (final field in fields) { + if (field.value is InstanceRef) { + children[field.name] = field.value as InstanceRef; + } + } + + await addChildren(children); + if (valueInfo.kind != InstanceKind.kMap) return null; - final valueInfoMap = await valueInfo.evalValue(isAlive); + final valueInfoMap = await valueInfo.evalValue(_isAlive); if (valueInfoMap is! Map) return null; uInstanceInfo.value = valueInfoMap.cast(); - final String kind = valueInfoMap['kind']; final String key = valueInfoMap['key']; final String type = valueInfoMap['type']; final String? id = valueInfoMap['id']; @@ -301,7 +328,7 @@ base class PropertyNode extends TreeNode { final String? idOrDebugLabel = id ?? debugLabel; return idOrDebugLabel != null - ? "$type($idOrDebugLabel)#$key" - : "$type#$key"; + ? "$type($idOrDebugLabel) #$key" + : "$type #$key"; } } diff --git a/packages/reactter_devtools_extension/lib/src/data/state_node.dart b/packages/reactter_devtools_extension/lib/src/data/state_node.dart index 020a7d61..b213c85f 100644 --- a/packages/reactter_devtools_extension/lib/src/data/state_node.dart +++ b/packages/reactter_devtools_extension/lib/src/data/state_node.dart @@ -5,8 +5,6 @@ import 'package:reactter_devtools_extension/src/data/property_node.dart'; import 'package:reactter_devtools_extension/src/data/state_info.dart'; import 'package:reactter_devtools_extension/src/services/eval_service.dart'; -import 'package:vm_service/vm_service.dart'; - base class StateNode extends INode { final uIsLoading = UseState(false); @@ -74,7 +72,7 @@ base class StateNode extends INode { ), ); } else { - propertyNode.reassignValueRef(debugInfoValue); + propertyNode.updateValueRef(debugInfoValue); } } } diff --git a/packages/reactter_devtools_extension/lib/src/widgets/properties_list.dart b/packages/reactter_devtools_extension/lib/src/widgets/properties_list.dart index a9c7dd2f..2c10910f 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/properties_list.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/properties_list.dart @@ -9,7 +9,6 @@ class PropertiesList extends StatelessWidget { @override Widget build(BuildContext context) { - final listKey = GlobalKey(); final scrollControllerX = ScrollController(); final scrollControllerY = ScrollController(); final focusNode = FocusNode(); @@ -47,7 +46,7 @@ class PropertiesList extends StatelessWidget { final length = propertyNodes?.length ?? 0; return ListView.custom( - key: listKey, + key: ObjectKey(propertyNodes), controller: scrollControllerY, itemExtent: 24, childrenDelegate: SliverChildBuilderDelegate( @@ -56,7 +55,6 @@ class PropertiesList extends StatelessWidget { propertyNodes!.elementAt(index); return PropertyTile( - key: ObjectKey(propertyNode), propertyNode: propertyNode, ); }, diff --git a/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart index 013f0f65..64d27ac2 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart @@ -15,15 +15,9 @@ class PropertyTile extends StatelessWidget { @override Widget build(BuildContext context) { return TreeNodeTileBuilder( - key: key, treeNode: propertyNode, title: Row( children: [ - RtWatcher((context, watch) { - return SizedBox( - width: watch(propertyNode.uChildren).value.isEmpty ? 22 : 0, - ); - }), Text( "${propertyNode.key}: ", style: Theme.of(context) @@ -31,24 +25,21 @@ class PropertyTile extends StatelessWidget { .labelSmall ?.copyWith(color: Theme.of(context).colorScheme.primary), ), - FutureBuilder( - future: propertyNode.getValueAsync(), - builder: (context, snapshot) { - return RtWatcher((context, watch) { - final isLoading = watch(propertyNode.uIsLoading).value; - final value = watch(propertyNode.uValue).value; + RtWatcher((context, watch) { + final isLoading = watch(propertyNode.uIsLoading).value; - if (isLoading) { - return const Loading(); - } + if (isLoading) return const Loading(); - return Text( - value ?? '...', - style: Theme.of(context).textTheme.labelSmall, - ); - }); - }, - ), + watch(propertyNode.uValueFuture).value ?? + propertyNode.getValueAsync(); + + final value = watch(propertyNode.uValue).value; + + return Text( + value ?? '...', + style: Theme.of(context).textTheme.labelSmall, + ); + }), ], ), isSelected: false, diff --git a/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart b/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart index 37a0cefe..01e7d271 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart @@ -34,9 +34,9 @@ class TreeNodeTileBuilder extends StatelessWidget { children: [ ...List.generate( treeNode.depth.toInt() - 1, - (index) => Padding( + (index) => Container( padding: EdgeInsets.only( - left: 20, + left: 10, right: (index == treeNode.depth.toInt() - 2) ? 0 : 0, ), child: const VerticalDivider(width: 1), @@ -61,22 +61,20 @@ class TileExpandable extends StatelessWidget { final children = watch(treeNode.uChildren).value; if (children.isEmpty) { - return const SizedBox(width: 11); + return Container( + width: 24, + ); } final isExpanded = watch(treeNode.uIsExpanded).value; - return Padding( - padding: const EdgeInsets.only(left: 8), - child: IconButton( - padding: EdgeInsets.zero, - splashRadius: 18, - iconSize: 24, - constraints: const BoxConstraints.tightForFinite(), - icon: Icon( + return Container( + padding: const EdgeInsets.only(left: 0), + child: InkWell( + child: Icon( isExpanded ? Icons.keyboard_arrow_down : Icons.keyboard_arrow_right, ), - onPressed: () { + onTap: () { treeNode.uIsExpanded.value = !isExpanded; }, ), From c3f77a3964a1906eaa9bfce2cbb56b333ed1630b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Mon, 11 Nov 2024 01:27:16 -0600 Subject: [PATCH 049/141] feat(devtools): Add hot-restart listener to re-fetch nodes and reset state before fetching --- .../lib/src/controllers/nodes_controller.dart | 10 ++++++++++ .../lib/src/services/devtools_service.dart | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart b/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart index 6385ac58..0dc8156a 100644 --- a/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart +++ b/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart @@ -32,6 +32,9 @@ class NodesController { final vmService = await serviceManager.onServiceAvailable; + // Listen hot-restart for re-fetching all nodes + serviceManager.isolateManager.selectedIsolate.addListener(getAllNodes); + extEventSubscription = vmService.onExtensionEvent.listen((event) { if (!(event.extensionKind?.startsWith('ext.reactter.') ?? false)) { return; @@ -77,10 +80,17 @@ class NodesController { } Future getAllNodes() async { + resetState(); final nodesInfo = await devtoolsSevices.getAllNodes(); addNodes(nodesInfo); } + void resetState() { + nodesList.clear(); + uNodes.value.clear(); + uCurrentNodeKey.value = null; + } + Future addNodeByKey(String nodeKey) async { final nodeInfo = await devtoolsSevices.getNodeBykey(nodeKey); addNodeByMapInfo(nodeInfo); diff --git a/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart b/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart index 1646cebf..bd552292 100644 --- a/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart +++ b/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart @@ -20,7 +20,7 @@ class DevtoolsService { ), ); - assert(pageNodes.kind == InstanceKind.kMap); + if (pageNodes.kind == InstanceKind.kNull) return []; final Map pageInfo = await pageNodes.evalValue(isAlive); final totalPages = pageInfo['totalPages'] as int; From e3a86a5008ea5f475aed237298c5dc1073cfb7cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Mon, 11 Nov 2024 23:32:06 -0600 Subject: [PATCH 050/141] feat(devtools): Implement node selection and scrolling functionality; add InstanceTitle widget for consistent node representation --- .../lib/src/controllers/nodes_controller.dart | 23 ++++++++ .../lib/src/data/constants.dart | 2 + .../lib/src/data/tree_node.dart | 2 + .../lib/src/widgets/instance_title.dart | 53 +++++++++++++++++++ .../lib/src/widgets/node_tile.dart | 39 ++------------ .../lib/src/widgets/nodes_list.dart | 8 +-- .../lib/src/widgets/property_tile.dart | 27 ++++++++++ 7 files changed, 116 insertions(+), 38 deletions(-) create mode 100644 packages/reactter_devtools_extension/lib/src/widgets/instance_title.dart diff --git a/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart b/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart index 0dc8156a..0cddd2ef 100644 --- a/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart +++ b/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart @@ -2,7 +2,9 @@ import 'dart:async'; import 'dart:collection'; import 'package:devtools_extensions/devtools_extensions.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_reactter/reactter.dart'; +import 'package:reactter_devtools_extension/src/data/constants.dart'; import 'package:reactter_devtools_extension/src/data/instance_info.dart'; import 'package:reactter_devtools_extension/src/data/instance_node.dart'; @@ -21,6 +23,9 @@ class NodesController { final uNodes = UseState(LinkedHashMap()); final uCurrentNodeKey = UseState(null); + final listViewKey = GlobalKey(); + final scrollControllerY = ScrollController(); + INode? get currentNode => uNodes.value[uCurrentNodeKey.value]; NodesController() { @@ -91,6 +96,24 @@ class NodesController { uCurrentNodeKey.value = null; } + void selectNodeByKey(String nodeKey) { + final node = uNodes.value[nodeKey]; + + if (node != null) selectNode(node); + + final index = node?.getIndex(); + + if (index == null) return; + + final offset = index * kNodeTileHeight; + + scrollControllerY.animateTo( + offset, + duration: const Duration(milliseconds: 300), + curve: Curves.bounceIn, + ); + } + Future addNodeByKey(String nodeKey) async { final nodeInfo = await devtoolsSevices.getNodeBykey(nodeKey); addNodeByMapInfo(nodeInfo); diff --git a/packages/reactter_devtools_extension/lib/src/data/constants.dart b/packages/reactter_devtools_extension/lib/src/data/constants.dart index 41b83fee..28747d00 100644 --- a/packages/reactter_devtools_extension/lib/src/data/constants.dart +++ b/packages/reactter_devtools_extension/lib/src/data/constants.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +const double kNodeTileHeight = 24; + enum NodeType { dependency('dependency'), state('state'), diff --git a/packages/reactter_devtools_extension/lib/src/data/tree_node.dart b/packages/reactter_devtools_extension/lib/src/data/tree_node.dart index dbb6ff48..8a029b96 100644 --- a/packages/reactter_devtools_extension/lib/src/data/tree_node.dart +++ b/packages/reactter_devtools_extension/lib/src/data/tree_node.dart @@ -18,6 +18,8 @@ abstract base class TreeNode> extends LinkedListEntry return uChildren.value.last.lastDescendant; } + int? getIndex() => list?.indexed.firstWhere((e) => e.$2 == this).$1; + TreeNode() { UseEffect( _onIsExpandedChanged, diff --git a/packages/reactter_devtools_extension/lib/src/widgets/instance_title.dart b/packages/reactter_devtools_extension/lib/src/widgets/instance_title.dart new file mode 100644 index 00000000..9965023d --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/widgets/instance_title.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; + +class InstanceTitle extends StatelessWidget { + final String type; + final String? label; + final String nKey; + + const InstanceTitle({ + super.key, + required this.type, + this.label, + required this.nKey, + }); + + @override + Widget build(BuildContext context) { + return RichText( + selectionRegistrar: SelectionContainer.maybeOf(context), + selectionColor: Theme.of(context).highlightColor, + text: TextSpan( + style: Theme.of(context).textTheme.labelSmall, + children: [ + TextSpan( + text: type, + style: Theme.of(context) + .textTheme + .labelSmall + ?.copyWith(color: Colors.amber), + ), + if (label != null) + TextSpan( + children: [ + const TextSpan(text: "("), + TextSpan( + text: label!, + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: Theme.of(context).colorScheme.primary, + ), + ), + const TextSpan(text: ")"), + ], + ), + TextSpan( + text: " #$nKey", + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ), + ], + ), + ); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart index 32f0f2a5..4a7935f8 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; import 'package:reactter_devtools_extension/src/data/constants.dart'; import 'package:reactter_devtools_extension/src/interfaces/node.dart'; +import 'package:reactter_devtools_extension/src/widgets/instance_title.dart'; import 'package:reactter_devtools_extension/src/widgets/tile_builder.dart'; class NodeTile extends StatelessWidget { @@ -73,40 +74,10 @@ class NodeTileTitle extends StatelessWidget { RtWatcher((context, watch) { final info = watch(node.uInfo).value; - return RichText( - selectionRegistrar: SelectionContainer.maybeOf(context), - selectionColor: Theme.of(context).highlightColor, - text: TextSpan( - style: Theme.of(context).textTheme.labelSmall, - children: [ - TextSpan( - text: node.type, - style: Theme.of(context) - .textTheme - .labelSmall - ?.copyWith(color: Colors.amber), - ), - if (info?.label != null) - TextSpan( - children: [ - const TextSpan(text: "("), - TextSpan( - text: info?.label!, - style: Theme.of(context).textTheme.labelSmall?.copyWith( - color: Theme.of(context).colorScheme.primary, - ), - ), - const TextSpan(text: ")"), - ], - ), - TextSpan( - text: " #${node.key}", - style: Theme.of(context).textTheme.labelSmall?.copyWith( - color: Theme.of(context).colorScheme.secondary, - ), - ), - ], - ), + return InstanceTitle( + type: node.type, + label: info?.label, + nKey: node.key, ); }), ], diff --git a/packages/reactter_devtools_extension/lib/src/widgets/nodes_list.dart b/packages/reactter_devtools_extension/lib/src/widgets/nodes_list.dart index a64561bf..26d394b7 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/nodes_list.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/nodes_list.dart @@ -9,11 +9,11 @@ class NodesList extends StatelessWidget { @override Widget build(BuildContext context) { - final listKey = GlobalKey(); + final nodesController = context.use(); + final listViewKey = nodesController.listViewKey; final scrollControllerX = ScrollController(); - final scrollControllerY = ScrollController(); + final scrollControllerY = nodesController.scrollControllerY; final focusNode = FocusNode(); - final nodesController = context.use(); return LayoutBuilder( builder: (context, constraints) { @@ -41,7 +41,7 @@ class NodesList extends StatelessWidget { final length = watch(nodesController.nodesList).length; return ListView.custom( - key: listKey, + key: listViewKey, controller: scrollControllerY, itemExtent: 24, childrenDelegate: SliverChildBuilderDelegate( diff --git a/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart index 64d27ac2..a7ad8e2c 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/controllers/nodes_controller.dart'; import 'package:reactter_devtools_extension/src/data/property_node.dart'; +import 'package:reactter_devtools_extension/src/widgets/instance_title.dart'; import 'package:reactter_devtools_extension/src/widgets/loading.dart'; import 'package:reactter_devtools_extension/src/widgets/tile_builder.dart'; @@ -35,6 +37,31 @@ class PropertyTile extends StatelessWidget { final value = watch(propertyNode.uValue).value; + final instanceInfo = watch(propertyNode.uInstanceInfo).value; + + if (instanceInfo != null) { + final nKey = instanceInfo['key'] as String; + final nodesController = context.use(); + final node = nodesController.uNodes.value[nKey]; + + return Row( + children: [ + if (node != null) + InkWell( + child: const Icon(Icons.reply), + onTap: () { + nodesController.selectNodeByKey(nKey); + }, + ), + InstanceTitle( + type: instanceInfo['type'], + label: instanceInfo['id'] ?? instanceInfo['debugLabel'], + nKey: nKey, + ), + ], + ); + } + return Text( value ?? '...', style: Theme.of(context).textTheme.labelSmall, From 396c0aa80d67bdf3517fa76c6525402174f1933e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Wed, 13 Nov 2024 00:10:39 -0600 Subject: [PATCH 051/141] feat(devtools): Enhance node to show a dependency indicator --- packages/reactter/lib/src/devtools.dart | 14 +++- .../lib/src/controllers/nodes_controller.dart | 9 ++- .../lib/src/data/dependency_node.dart | 3 + .../lib/src/data/instance_info.dart | 3 +- .../lib/src/data/instance_node.dart | 7 +- .../lib/src/data/slot_node.dart | 3 + .../lib/src/data/state_info.dart | 13 +++- .../lib/src/data/state_node.dart | 3 + .../lib/src/interfaces/node.dart | 2 + .../lib/src/interfaces/node_info.dart | 4 +- .../lib/src/widgets/node_tile.dart | 67 ++++++++++++------- 11 files changed, 90 insertions(+), 38 deletions(-) diff --git a/packages/reactter/lib/src/devtools.dart b/packages/reactter/lib/src/devtools.dart index dae0e83b..2c04382c 100644 --- a/packages/reactter/lib/src/devtools.dart +++ b/packages/reactter/lib/src/devtools.dart @@ -434,7 +434,15 @@ abstract class _Node extends LinkedListEntry<_Node> { _Node({required this.instance}); - Map toJson(); + Map toJson() { + final dependencyRef = Rt.getDependencyRef(instance); + final dependencyId = dependencyRef?.id; + + return { + 'dependencyId': dependencyId, + 'dependencyRef': dependencyRef?.hashCode.toString(), + }; + } _Node? get lastDescendant => children.isEmpty ? this : children.last.lastDescendant as _Node; @@ -517,7 +525,7 @@ class _InstanceNode extends _Node { 'kind': _NodeKind.instance, 'key': key, 'type': instance.runtimeType.toString(), - 'dependencyRef': Rt.getDependencyRef(instance)?.hashCode.toString(), + ...super.toJson(), }; } @@ -550,7 +558,7 @@ class _StateNode extends _Node { 'type': instance.runtimeType.toString(), 'debugLabel': instance.debugLabel, 'boundInstanceKey': instance.boundInstance?.hashCode.toString(), - 'dependencyRef': Rt.getDependencyRef(instance)?.hashCode.toString(), + ...super.toJson(), }; } diff --git a/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart b/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart index 0cddd2ef..e9ddaa97 100644 --- a/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart +++ b/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart @@ -134,6 +134,7 @@ class NodesController { final kind = nodeInfo['kind']; final key = nodeInfo['key']; final type = nodeInfo['type']; + final dependencyRef = nodeInfo['dependencyRef']; switch (kind) { case 'dependency': @@ -149,11 +150,13 @@ class NodesController { kind: kind, type: type, ); + final debugLabel = nodeInfo['debugLabel']; final boundInstanceKey = nodeInfo['boundInstanceKey']; node.uInfo.value = StateInfo( - label: debugLabel, + dependencyRef: dependencyRef, + debugLabel: debugLabel, boundInstanceKey: boundInstanceKey, ); @@ -184,7 +187,9 @@ class NodesController { type: type, ); - node.uInfo.value = InstanceInfo(); + node.uInfo.value = InstanceInfo( + dependencyRef: dependencyRef, + ); addNode(node); diff --git a/packages/reactter_devtools_extension/lib/src/data/dependency_node.dart b/packages/reactter_devtools_extension/lib/src/data/dependency_node.dart index dcd1d1a0..b3f39ddb 100644 --- a/packages/reactter_devtools_extension/lib/src/data/dependency_node.dart +++ b/packages/reactter_devtools_extension/lib/src/data/dependency_node.dart @@ -8,6 +8,9 @@ import 'package:vm_service/vm_service.dart'; base class DependencyNode extends INode { final uIsLoading = UseState(false); + @override + String? get label => uInfo.value?.id; + DependencyNode._({ required super.key, required super.kind, diff --git a/packages/reactter_devtools_extension/lib/src/data/instance_info.dart b/packages/reactter_devtools_extension/lib/src/data/instance_info.dart index 372c743b..6aca46bf 100644 --- a/packages/reactter_devtools_extension/lib/src/data/instance_info.dart +++ b/packages/reactter_devtools_extension/lib/src/data/instance_info.dart @@ -1,5 +1,6 @@ import 'package:reactter_devtools_extension/src/interfaces/node_info.dart'; class InstanceInfo extends INodeInfo { - InstanceInfo(); + + const InstanceInfo({super.dependencyRef}); } diff --git a/packages/reactter_devtools_extension/lib/src/data/instance_node.dart b/packages/reactter_devtools_extension/lib/src/data/instance_node.dart index 33c19ab3..05987915 100644 --- a/packages/reactter_devtools_extension/lib/src/data/instance_node.dart +++ b/packages/reactter_devtools_extension/lib/src/data/instance_node.dart @@ -1,8 +1,11 @@ import 'package:flutter_reactter/reactter.dart'; +import 'package:reactter_devtools_extension/src/data/instance_info.dart'; import 'package:reactter_devtools_extension/src/interfaces/node.dart'; -import 'package:reactter_devtools_extension/src/interfaces/node_info.dart'; -base class InstanceNode extends INode { +base class InstanceNode extends INode { + @override + final label = null; + InstanceNode._({ required super.key, required super.kind, diff --git a/packages/reactter_devtools_extension/lib/src/data/slot_node.dart b/packages/reactter_devtools_extension/lib/src/data/slot_node.dart index 5f9b0d51..7ac5f2b0 100644 --- a/packages/reactter_devtools_extension/lib/src/data/slot_node.dart +++ b/packages/reactter_devtools_extension/lib/src/data/slot_node.dart @@ -3,6 +3,9 @@ import 'package:reactter_devtools_extension/src/interfaces/node.dart'; import 'package:reactter_devtools_extension/src/interfaces/node_info.dart'; base class SlotNode extends INode { + @override + final String? label = null; + SlotNode._({required super.key, required super.kind, required super.type}); factory SlotNode({ diff --git a/packages/reactter_devtools_extension/lib/src/data/state_info.dart b/packages/reactter_devtools_extension/lib/src/data/state_info.dart index c0fbff3a..a4edca9d 100644 --- a/packages/reactter_devtools_extension/lib/src/data/state_info.dart +++ b/packages/reactter_devtools_extension/lib/src/data/state_info.dart @@ -1,18 +1,25 @@ import 'package:reactter_devtools_extension/src/interfaces/node_info.dart'; class StateInfo extends INodeInfo { + final String? debugLabel; final String? boundInstanceKey; final List kinds; StateInfo({ - super.label, + super.dependencyRef, + this.debugLabel, this.boundInstanceKey, this.kinds = const ['RtState'], }); - StateInfo copyWith({String? label, String? boundInstanceKey}) { + StateInfo copyWith({ + String? dependencyRef, + String? debugLabel, + String? boundInstanceKey, + }) { return StateInfo( - label: label ?? this.label, + dependencyRef: dependencyRef ?? this.dependencyRef, + debugLabel: debugLabel ?? this.debugLabel, boundInstanceKey: boundInstanceKey ?? this.boundInstanceKey, ); } diff --git a/packages/reactter_devtools_extension/lib/src/data/state_node.dart b/packages/reactter_devtools_extension/lib/src/data/state_node.dart index b213c85f..1064c0dd 100644 --- a/packages/reactter_devtools_extension/lib/src/data/state_node.dart +++ b/packages/reactter_devtools_extension/lib/src/data/state_node.dart @@ -8,6 +8,9 @@ import 'package:reactter_devtools_extension/src/services/eval_service.dart'; base class StateNode extends INode { final uIsLoading = UseState(false); + @override + String? get label => uInfo.value?.debugLabel; + StateNode._({ required super.key, required super.kind, diff --git a/packages/reactter_devtools_extension/lib/src/interfaces/node.dart b/packages/reactter_devtools_extension/lib/src/interfaces/node.dart index 06eff8b3..d9b10deb 100644 --- a/packages/reactter_devtools_extension/lib/src/interfaces/node.dart +++ b/packages/reactter_devtools_extension/lib/src/interfaces/node.dart @@ -9,6 +9,8 @@ abstract base class INode extends TreeNode { final String kind; final String type; + String? get label; + final uInfo = UseState(null); final uIsSelected = UseState(false); final propertyNodes = TreeList(); diff --git a/packages/reactter_devtools_extension/lib/src/interfaces/node_info.dart b/packages/reactter_devtools_extension/lib/src/interfaces/node_info.dart index 5cd5eb40..8726b2d2 100644 --- a/packages/reactter_devtools_extension/lib/src/interfaces/node_info.dart +++ b/packages/reactter_devtools_extension/lib/src/interfaces/node_info.dart @@ -1,5 +1,5 @@ abstract class INodeInfo { - final String? label; + final String? dependencyRef; - INodeInfo({this.label}); + const INodeInfo({this.dependencyRef}); } diff --git a/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart index 4a7935f8..55688c09 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart @@ -32,21 +32,34 @@ class NodeTile extends StatelessWidget { class NodeTileIcon extends StatelessWidget { final String kind; + final bool isDependency; - const NodeTileIcon({super.key, required this.kind}); + const NodeTileIcon({ + super.key, + required this.kind, + this.isDependency = false, + }); @override Widget build(BuildContext context) { final nodeKind = NodeKind.getKind(kind); - return CircleAvatar( - backgroundColor: nodeKind.color, - child: Text( - nodeKind.abbr, - style: Theme.of(context).textTheme.labelSmall?.copyWith( - color: Colors.white, - fontWeight: FontWeight.bold, + return Badge( + backgroundColor: isDependency ? Colors.teal : Colors.transparent, + child: Padding( + padding: const EdgeInsets.all(2.0), + child: Center( + child: CircleAvatar( + backgroundColor: nodeKind.color, + child: Text( + nodeKind.abbr, + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: Colors.white, + fontWeight: FontWeight.bold, + ), ), + ), + ), ), ); } @@ -62,25 +75,29 @@ class NodeTileTitle extends StatelessWidget { @override Widget build(BuildContext context) { - return Row( - children: [ - SizedBox.square( - dimension: 24, - child: Padding( - padding: const EdgeInsets.all(4).copyWith(left: 0, right: 4), - child: NodeTileIcon(kind: node.kind), - ), - ), - RtWatcher((context, watch) { - final info = watch(node.uInfo).value; + return RtWatcher((context, watch) { + final info = watch(node.uInfo).value; + final dependencyRef = info?.dependencyRef; - return InstanceTitle( + return Row( + children: [ + SizedBox.square( + dimension: 24, + child: Padding( + padding: const EdgeInsets.all(1), + child: NodeTileIcon( + kind: node.kind, + isDependency: dependencyRef != null, + ), + ), + ), + InstanceTitle( type: node.type, - label: info?.label, + label: node.label, nKey: node.key, - ); - }), - ], - ); + ), + ], + ); + }); } } From ee5c3b6969b3eb370f1909342d3115932dc0509f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 15 Nov 2024 22:18:04 -0600 Subject: [PATCH 052/141] feat(devtools): Enhance node dependency handling --- packages/reactter/lib/src/devtools.dart | 105 ++++++----- .../lib/src/data/constants.dart | 42 ++++- .../lib/src/data/instance_node.dart | 9 +- .../lib/src/data/property_node.dart | 164 ++++++++++++++---- .../lib/src/data/state_node.dart | 53 ++++-- .../lib/src/interfaces/node.dart | 46 ++++- .../lib/src/reactter_devtools_extension.dart | 35 +++- .../lib/src/utils/extensions.dart | 21 ++- .../lib/src/vm.dart | 0 .../lib/src/widgets/instance_icon.dart | 44 +++++ .../lib/src/widgets/instance_title.dart | 83 +++++---- .../lib/src/widgets/node_tile.dart | 59 +------ .../lib/src/widgets/property_tile.dart | 52 +++--- 13 files changed, 494 insertions(+), 219 deletions(-) delete mode 100644 packages/reactter_devtools_extension/lib/src/vm.dart create mode 100644 packages/reactter_devtools_extension/lib/src/widgets/instance_icon.dart diff --git a/packages/reactter/lib/src/devtools.dart b/packages/reactter/lib/src/devtools.dart index 2c04382c..2fd78b02 100644 --- a/packages/reactter/lib/src/devtools.dart +++ b/packages/reactter/lib/src/devtools.dart @@ -328,41 +328,41 @@ class RtDevTools with RtStateObserver, RtDependencyObserver { return state.instance.debugInfo; } - Map getDynamicInfo(Object instance) { - if (instance is DependencyRef) { - return getDependencyInfo(instance); - } else if (instance is RtState) { - return getStateInfo(instance); - } else { - return getInstanceInfo(instance); - } - } + dynamic getBoundInstance(String stateKey) { + final state = _nodesByKey[stateKey]; - Map getDependencyInfo(DependencyRef dependencyRef) { - return { - 'kind': _NodeKind.dependency, - 'key': dependencyRef.hashCode.toString(), - 'type': dependencyRef.type.toString(), - 'id': dependencyRef.id, - }; + if (state is! _StateNode) return null; + + return state.instance.boundInstance; } - Map getStateInfo(RtState state) { - return { - 'kind': _NodeKind.state, - 'key': state.hashCode.toString(), - 'type': state.runtimeType.toString(), - 'debugLabel': state.debugLabel, - 'boundInstanceKey': state.boundInstance?.hashCode.toString(), - }; + dynamic getDependencyRef(String dependencyKey) { + final dependencyRef = _nodesByKey[dependencyKey]; + + if (dependencyRef is! _DependencyNode) return null; + + return dependencyRef.instance; } Map getInstanceInfo(Object instance) { - return { - 'kind': _NodeKind.instance, - 'key': instance.hashCode.toString(), - 'type': instance.runtimeType.toString(), - }; + if (instance is Enum) { + return { + ..._InstanceNode.getInstanceInfo(instance), + 'type': instance.toString(), + }; + } + + if (instance is Iterable) { + return { + ..._InstanceNode.getInstanceInfo(instance), + 'fields': { + 'length': instance.length, + 'items': instance.toList(), + }, + }; + } + + return _InstanceNode.getInstanceInfo(instance); } String getPropertyValue(value) { @@ -380,11 +380,11 @@ class RtDevTools with RtStateObserver, RtDependencyObserver { Map getPlainInstanceInfo(Object instance) { if (instance is DependencyRef) { - return getDependencyInfo(instance); + return _DependencyNode.getInstanceInfo(instance); } if (instance is RtState) { - return getStateInfo(instance); + return _StateNode.getInstanceInfo(instance); } return getInstanceInfo(instance); @@ -439,6 +439,7 @@ abstract class _Node extends LinkedListEntry<_Node> { final dependencyId = dependencyRef?.id; return { + 'key': key, 'dependencyId': dependencyId, 'dependencyRef': dependencyRef?.hashCode.toString(), }; @@ -519,12 +520,18 @@ abstract class _Node extends LinkedListEntry<_Node> { class _InstanceNode extends _Node { _InstanceNode({required Object instance}) : super(instance: instance); - @override - Map toJson() { + static Map getInstanceInfo(Object instance) { return { 'kind': _NodeKind.instance, - 'key': key, + 'key': instance.hashCode.toString(), 'type': instance.runtimeType.toString(), + }; + } + + @override + Map toJson() { + return { + ...getInstanceInfo(instance), ...super.toJson(), }; } @@ -550,14 +557,20 @@ class _StateNode extends _Node { return _NodeKind.state; } - @override - Map toJson() { + static Map getInstanceInfo(RtState instance) { return { 'kind': resolveKind(instance), - 'key': key, + 'key': instance.hashCode.toString(), 'type': instance.runtimeType.toString(), 'debugLabel': instance.debugLabel, 'boundInstanceKey': instance.boundInstance?.hashCode.toString(), + }; + } + + @override + Map toJson() { + return { + ...getInstanceInfo(instance), ...super.toJson(), }; } @@ -578,15 +591,23 @@ class _DependencyNode extends _Node { _DependencyNode({required DependencyRef instance}) : super(instance: instance); - @override - Map toJson() { + static Map getInstanceInfo(DependencyRef instance) { return { 'kind': _NodeKind.dependency, - 'key': key, + 'key': instance.hashCode.toString(), 'type': instance.type.toString(), 'id': instance.id, - 'instanceKey': - Rt.getDependencyRegisterByRef(instance)?.instance.hashCode.toString(), + 'instanceKey': Rt.getDependencyRegisterByRef( + instance, + )?.instance.hashCode.toString(), + }; + } + + @override + Map toJson() { + return { + ...getInstanceInfo(instance), + ...super.toJson(), }; } } diff --git a/packages/reactter_devtools_extension/lib/src/data/constants.dart b/packages/reactter_devtools_extension/lib/src/data/constants.dart index 28747d00..6ded9f47 100644 --- a/packages/reactter_devtools_extension/lib/src/data/constants.dart +++ b/packages/reactter_devtools_extension/lib/src/data/constants.dart @@ -20,23 +20,53 @@ enum NodeType { } enum NodeKind { - instance(label: 'Instance', abbr: 'I', color: Colors.orange), - state(label: 'State', abbr: 'S', color: Colors.blue), - hook(label: 'Hook', abbr: 'H', color: Colors.purple), - signal(label: 'Signal', abbr: 'S', color: Colors.green); + dependency( + key: 'dependency', + label: 'Dependency', + abbr: 'D', + color: Colors.teal, + ), + instance( + key: 'instance', + label: 'Instance', + abbr: 'I', + color: Colors.orange, + ), + state( + key: 'state', + label: 'State', + abbr: 'S', + color: Colors.blue, + ), + hook( + key: 'hook', + label: 'Hook', + abbr: 'H', + color: Colors.purple, + ), + signal( + key: 'signal', + label: 'Signal', + abbr: 'S', + color: Colors.green, + ); const NodeKind({ + required this.key, required this.label, required this.abbr, required this.color, }); + final String key; final String label; final String abbr; final Color color; - static NodeKind getKind(String kind) { + static NodeKind? getKind(String kind) { switch (kind) { + case 'dependency': + return dependency; case 'instance': return instance; case 'state': @@ -46,7 +76,7 @@ enum NodeKind { case 'signal': return signal; default: - return instance; + return null; } } } diff --git a/packages/reactter_devtools_extension/lib/src/data/instance_node.dart b/packages/reactter_devtools_extension/lib/src/data/instance_node.dart index 05987915..3dc1365f 100644 --- a/packages/reactter_devtools_extension/lib/src/data/instance_node.dart +++ b/packages/reactter_devtools_extension/lib/src/data/instance_node.dart @@ -10,9 +10,7 @@ base class InstanceNode extends INode { required super.key, required super.kind, required super.type, - }) { - // _loadStateNode(); - } + }); factory InstanceNode({ required String key, @@ -27,9 +25,4 @@ base class InstanceNode extends INode { ), ); } - - @override - Future loadDetails() async { - // TODO: implement loadDetails - } } diff --git a/packages/reactter_devtools_extension/lib/src/data/property_node.dart b/packages/reactter_devtools_extension/lib/src/data/property_node.dart index 806cf8e6..040093b8 100644 --- a/packages/reactter_devtools_extension/lib/src/data/property_node.dart +++ b/packages/reactter_devtools_extension/lib/src/data/property_node.dart @@ -11,39 +11,73 @@ import 'package:vm_service/vm_service.dart'; const _kMaxValueLength = 50; -base class PropertyNode extends TreeNode { +abstract base class IPropertyNode extends TreeNode { final String key; + final uValue = UseState(null); + final uInstanceInfo = UseState?>(null); + + IPropertyNode({required this.key, String? value, bool isExpanded = false}) { + uValue.value = value; + uIsExpanded.value = isExpanded; + } +} + +base class PropertyNode extends IPropertyNode { + PropertyNode._({ + required super.key, + required super.value, + required super.isExpanded, + }); + factory PropertyNode({ + IPropertyNode? parent, + required String key, + required String value, + bool isExpanded = false, + }) { + return Rt.createState( + () => PropertyNode._( + key: key, + value: value, + isExpanded: isExpanded, + ), + ); + } + + void updateValue(String value) { + uValue.value = value; + } +} + +base class PropertyAsyncNode extends IPropertyNode { InstanceRef _valueRef; InstanceRef get valueRef => _valueRef; Disposable? _isAlive; bool _isResolved = false; bool _isValueUpdating = false; - final LinkedHashMap _childNodeRefs = - LinkedHashMap(); + final LinkedHashMap _childNodeRefs = + LinkedHashMap(); final uValueFuture = UseState?>(null); - final uValue = UseState(null); final uIsLoading = UseState(false); - final uInstanceInfo = UseState?>(null); - PropertyNode._({ - required this.key, + PropertyAsyncNode._({ + required super.key, required InstanceRef valueRef, bool isExpanded = false, }) : _valueRef = valueRef { uIsExpanded.value = isExpanded; } - factory PropertyNode({ - PropertyNode? parent, + factory PropertyAsyncNode({ + PropertyAsyncNode? parent, required String key, required InstanceRef valueRef, bool isExpanded = false, }) { return Rt.createState( - () => PropertyNode._( + () => PropertyAsyncNode._( key: key, valueRef: valueRef, isExpanded: isExpanded, @@ -99,6 +133,34 @@ base class PropertyNode extends TreeNode { case InstanceKind.kString: value = '"${valueRef.valueAsString}"'; uChildren.value.clear(); + break; + case InstanceKind.kClosure: + final instance = await valueRef.safeGetInstance(_isAlive); + + final children = SplayTreeMap(); + + final name = instance?.closureFunction?.name; + final location = instance?.closureFunction?.location?.script?.uri; + final locationLine = instance?.closureFunction?.location?.line; + final locationColumn = + instance?.closureFunction?.location?.column; + + children['location'] = location; + children['locationLine'] = locationLine; + children['locationColumn'] = locationColumn; + + await addChildren(children); + + uInstanceInfo.value = { + 'name': name, + 'key': valueRef.identityHashCode.toString(), + 'type': 'Function', + 'kind': 'closure', + ...children, + }; + + value = "Function($name) $location $locationLine:$locationColumn"; + break; default: value = valueRef.valueAsString; @@ -209,21 +271,44 @@ base class PropertyNode extends TreeNode { ); } - Future addChildren(SplayTreeMap children) async { + Future addChildren(SplayTreeMap children) async { final childrenToRemove = _childNodeRefs.keys.toSet(); for (final child in children.entries) { - final isRemoved = childrenToRemove.remove(child.key); + final childCurrent = _childNodeRefs[child.key]; + final isAsyncValue = child.value is InstanceRef; + final isValueTypeSame = + (childCurrent is PropertyAsyncNode && isAsyncValue) || + (childCurrent is PropertyNode && !isAsyncValue); + + if (!isValueTypeSame) _childNodeRefs.remove(child.key); + + final isRemoveSkip = + isValueTypeSame && childrenToRemove.remove(child.key); + final childNode = _childNodeRefs.putIfAbsent( child.key, - () => PropertyNode( - key: child.key, - valueRef: child.value, - ), + () { + if (isAsyncValue) { + return PropertyAsyncNode( + key: child.key, + valueRef: child.value, + ); + } + + return PropertyNode( + key: child.key, + value: child.value.toString(), + ); + }, ); - if (isRemoved) { - childNode.updateValueRef(child.value); + if (isRemoveSkip) { + if (childNode is PropertyAsyncNode) { + childNode.updateValueRef(child.value); + } else if (childNode is PropertyNode) { + childNode.updateValue(child.value.toString()); + } } else { addChild(childNode); } @@ -247,9 +332,14 @@ base class PropertyNode extends TreeNode { for (var i = 0; i < children.length; i++) { final child = children[i]; final isLast = i == children.length - 1; + + if (child is PropertyNode) continue; + + assert(child is PropertyAsyncNode); + String? value; - switch (child.valueRef.kind) { + switch ((child as PropertyAsyncNode).valueRef.kind) { case InstanceKind.kMap: case InstanceKind.kSet: value = '{...}'; @@ -301,21 +391,9 @@ base class PropertyNode extends TreeNode { ), ); - final instance = await valueRef.safeGetInstance(_isAlive); - final fields = instance?.fields?.cast() ?? []; - final SplayTreeMap children = SplayTreeMap(); - - for (final field in fields) { - if (field.value is InstanceRef) { - children[field.name] = field.value as InstanceRef; - } - } - - await addChildren(children); - if (valueInfo.kind != InstanceKind.kMap) return null; - final valueInfoMap = await valueInfo.evalValue(_isAlive); + final valueInfoMap = await valueInfo.evalValue(_isAlive, 2); if (valueInfoMap is! Map) return null; @@ -325,8 +403,30 @@ base class PropertyNode extends TreeNode { final String type = valueInfoMap['type']; final String? id = valueInfoMap['id']; final String? debugLabel = valueInfoMap['debugLabel']; + final Map? valueInfoFields = valueInfoMap['fields']; final String? idOrDebugLabel = id ?? debugLabel; + final instance = await valueRef.safeGetInstance(_isAlive); + final fields = instance?.fields?.cast() ?? []; + final SplayTreeMap children = SplayTreeMap(); + + for (final field in fields) { + if (field.value is InstanceRef) { + if (field.name.startsWith('_') || field.name.startsWith('\$')) { + continue; + } + children[field.name] = field.value as InstanceRef; + } + } + + if (valueInfoFields != null) { + for (final entry in valueInfoFields.entries) { + children[entry.key] = entry.value; + } + } + + await addChildren(children); + return idOrDebugLabel != null ? "$type($idOrDebugLabel) #$key" : "$type #$key"; diff --git a/packages/reactter_devtools_extension/lib/src/data/state_node.dart b/packages/reactter_devtools_extension/lib/src/data/state_node.dart index 1064c0dd..4249dea6 100644 --- a/packages/reactter_devtools_extension/lib/src/data/state_node.dart +++ b/packages/reactter_devtools_extension/lib/src/data/state_node.dart @@ -15,7 +15,9 @@ base class StateNode extends INode { required super.key, required super.kind, required super.type, - }); + }) { + UseEffect(loadDependencyRef, [uInfo]); + } factory StateNode({ required String key, @@ -33,22 +35,45 @@ base class StateNode extends INode { @override Future loadDetails() async { - await Future.wait([loadBoundInstance(), loadProperties()]); + await Future.wait([ + super.loadDetails(), + loadBoundInstance(), + loadDebugInfo(), + ]); } Future loadBoundInstance() async { - // final eval = await EvalService.devtoolsEval; - // final isAlive = Disposable(); - // final valueInst = await eval.safeGetInstance(ref, isAlive); + final eval = await EvalService.devtoolsEval; + final isAlive = Disposable(); + + final boundInstanceValue = await eval.evalInstance( + 'RtDevTools._instance?.getBoundInstance("$key")', + isAlive: isAlive, + ); + + PropertyAsyncNode? propertyNode; + + for (final node in propertyNodes) { + if (node.key == 'boundInstance' && node is PropertyAsyncNode) { + propertyNode = node; + break; + } + } - // final inst = await eval.evalInstance( - // 'state.boundInstance', - // isAlive: isAlive, - // scope: {'state': ref.id!}, - // ); + if (propertyNode == null) { + propertyNodes.add( + PropertyAsyncNode( + key: 'boundInstance', + valueRef: boundInstanceValue, + isExpanded: false, + ), + ); + } else { + propertyNode.updateValueRef(boundInstanceValue); + } } - Future loadProperties() async { + Future loadDebugInfo() async { final eval = await EvalService.devtoolsEval; final isAlive = Disposable(); @@ -57,10 +82,10 @@ base class StateNode extends INode { isAlive: isAlive, ); - PropertyNode? propertyNode; + PropertyAsyncNode? propertyNode; for (final node in propertyNodes) { - if (node.key == 'debugInfo') { + if (node.key == 'debugInfo' && node is PropertyAsyncNode) { propertyNode = node; break; } @@ -68,7 +93,7 @@ base class StateNode extends INode { if (propertyNode == null) { propertyNodes.add( - PropertyNode( + PropertyAsyncNode( key: 'debugInfo', valueRef: debugInfoValue, isExpanded: true, diff --git a/packages/reactter_devtools_extension/lib/src/interfaces/node.dart b/packages/reactter_devtools_extension/lib/src/interfaces/node.dart index d9b10deb..73fd5127 100644 --- a/packages/reactter_devtools_extension/lib/src/interfaces/node.dart +++ b/packages/reactter_devtools_extension/lib/src/interfaces/node.dart @@ -1,8 +1,11 @@ +import 'package:devtools_app_shared/service.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; import 'package:reactter_devtools_extension/src/data/tree_node.dart'; import 'package:reactter_devtools_extension/src/interfaces/node_info.dart'; import 'package:reactter_devtools_extension/src/data/property_node.dart'; import 'package:reactter_devtools_extension/src/data/tree_list.dart'; +import 'package:reactter_devtools_extension/src/services/eval_service.dart'; +import 'package:vm_service/vm_service.dart'; abstract base class INode extends TreeNode { final String key; @@ -13,7 +16,7 @@ abstract base class INode extends TreeNode { final uInfo = UseState(null); final uIsSelected = UseState(false); - final propertyNodes = TreeList(); + final propertyNodes = TreeList(); INode({ required this.key, @@ -21,5 +24,44 @@ abstract base class INode extends TreeNode { required this.type, }); - Future loadDetails(); + Future loadDetails() async { + await loadDependencyRef(); + } + + Future loadDependencyRef() async { + final eval = await EvalService.devtoolsEval; + final isAlive = Disposable(); + final dependencyKey = uInfo.value?.dependencyRef; + + final dependencyRefValue = await eval.evalInstance( + 'RtDevTools._instance?.getDependencyRef("$dependencyKey")', + isAlive: isAlive, + ); + + PropertyAsyncNode? propertyNode; + + for (final node in propertyNodes) { + if (node.key == 'dependencyRef' && node is PropertyAsyncNode) { + propertyNode = node; + break; + } + } + + if (dependencyRefValue.kind == InstanceKind.kNull) { + propertyNode?.remove(); + return; + } + + if (propertyNode == null) { + propertyNodes.add( + PropertyAsyncNode( + key: 'dependencyRef', + valueRef: dependencyRefValue, + isExpanded: false, + ), + ); + } else { + propertyNode.updateValueRef(dependencyRefValue); + } + } } diff --git a/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart b/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart index fcfd96e4..65ef2546 100644 --- a/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart +++ b/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart @@ -1,6 +1,7 @@ import 'package:devtools_app_shared/ui.dart'; import 'package:flutter/material.dart' hide Split; import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/widgets/instance_title.dart'; import 'package:reactter_devtools_extension/src/widgets/nodes_list.dart'; import 'package:reactter_devtools_extension/src/widgets/properties_list.dart'; import 'controllers/nodes_controller.dart'; @@ -16,8 +17,8 @@ class RtDevToolsExtension extends StatelessWidget { return SplitPane( axis: SplitPane.axisFor(context, 0.8), initialFractions: const [0.40, 0.60], - children: const [ - RoundedOutlinedBorder( + children: [ + const RoundedOutlinedBorder( clip: true, child: Column( children: [ @@ -32,8 +33,34 @@ class RtDevToolsExtension extends StatelessWidget { clip: true, child: Column( children: [ - AreaPaneHeader(title: Text("Details")), - Expanded( + RtWatcher((context, watch) { + final nKey = watch(inst.uCurrentNodeKey).value; + final selectedNode = inst.currentNode; + + if (nKey == null) { + return const AreaPaneHeader(title: Text("Select a node")); + } + + final info = watch(selectedNode!.uInfo).value; + + return AreaPaneHeader( + title: Row( + children: [ + const Text( + "Details of ", + ), + InstanceTitle( + nKey: nKey, + type: selectedNode.type, + kind: selectedNode.kind, + label: selectedNode.label, + isDependency: info?.dependencyRef != null, + ), + ], + ), + ); + }), + const Expanded( child: PropertiesList(), ), ], diff --git a/packages/reactter_devtools_extension/lib/src/utils/extensions.dart b/packages/reactter_devtools_extension/lib/src/utils/extensions.dart index 79f185e6..09c4990e 100644 --- a/packages/reactter_devtools_extension/lib/src/utils/extensions.dart +++ b/packages/reactter_devtools_extension/lib/src/utils/extensions.dart @@ -3,7 +3,9 @@ import 'package:reactter_devtools_extension/src/services/eval_service.dart'; import 'package:vm_service/vm_service.dart'; extension InstanceExt on Instance { - Future evalValue([Disposable? isAlive]) async { + Future evalValue([Disposable? isAlive, int? level]) async { + if (level != null && level == 0) return this; + switch (kind) { case InstanceKind.kNull: return null; @@ -22,8 +24,10 @@ extension InstanceExt on Instance { final InstanceRef keyRef = entry.key; final InstanceRef valueRef = entry.value; - nodeInfo[await keyRef.evalValue(isAlive)] = - await valueRef.evalValue(isAlive); + nodeInfo[await keyRef.evalValue(isAlive)] = await valueRef.evalValue( + isAlive, + level == null ? null : level - 1, + ); } return nodeInfo; @@ -32,7 +36,10 @@ extension InstanceExt on Instance { final listValues = []; for (final e in list) { - final value = await e.evalValue(isAlive); + final value = await e.evalValue( + isAlive, + level == null ? null : level - 1, + ); listValues.add(value); } @@ -91,9 +98,11 @@ extension InstanceRefExt on InstanceRef { } } - Future evalValue([Disposable? isAlive]) async { + Future evalValue([Disposable? isAlive, int? level]) async { + if (level != null && level == 0) return this; + final instance = await safeGetInstance(isAlive); - return await instance?.evalValue(isAlive); + return await instance?.evalValue(isAlive, level); } Future evalValueFirstLevel([Disposable? isAlive]) async { diff --git a/packages/reactter_devtools_extension/lib/src/vm.dart b/packages/reactter_devtools_extension/lib/src/vm.dart deleted file mode 100644 index e69de29b..00000000 diff --git a/packages/reactter_devtools_extension/lib/src/widgets/instance_icon.dart b/packages/reactter_devtools_extension/lib/src/widgets/instance_icon.dart new file mode 100644 index 00000000..a12d77fc --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/widgets/instance_icon.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:reactter_devtools_extension/src/data/constants.dart'; + +class InstanceIcon extends StatelessWidget { + final String kind; + final bool isDependency; + + const InstanceIcon({ + super.key, + required this.kind, + this.isDependency = false, + }); + + @override + Widget build(BuildContext context) { + final nodeKind = NodeKind.getKind(kind); + + if (nodeKind == null) return const SizedBox(); + return SizedBox.square( + dimension: 24, + child: Padding( + padding: const EdgeInsets.all(1), + child: Badge( + backgroundColor: isDependency ? Colors.teal : Colors.transparent, + child: Padding( + padding: const EdgeInsets.all(2.0), + child: Center( + child: CircleAvatar( + backgroundColor: nodeKind.color, + child: Text( + nodeKind.abbr, + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/widgets/instance_title.dart b/packages/reactter_devtools_extension/lib/src/widgets/instance_title.dart index 9965023d..b596a3ab 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/instance_title.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/instance_title.dart @@ -1,53 +1,72 @@ import 'package:flutter/material.dart'; +import 'package:reactter_devtools_extension/src/widgets/instance_icon.dart'; class InstanceTitle extends StatelessWidget { + final String nKey; final String type; + final String? kind; final String? label; - final String nKey; + final bool isDependency; + final void Function()? onTapIcon; const InstanceTitle({ super.key, + required this.nKey, required this.type, + this.kind, this.label, - required this.nKey, + this.isDependency = false, + this.onTapIcon, }); @override Widget build(BuildContext context) { - return RichText( - selectionRegistrar: SelectionContainer.maybeOf(context), - selectionColor: Theme.of(context).highlightColor, - text: TextSpan( - style: Theme.of(context).textTheme.labelSmall, - children: [ - TextSpan( - text: type, - style: Theme.of(context) - .textTheme - .labelSmall - ?.copyWith(color: Colors.amber), + return Row( + children: [ + if (kind != null) + InkWell( + onTap: onTapIcon, + child: InstanceIcon( + kind: kind!, + isDependency: isDependency, + ), ), - if (label != null) - TextSpan( - children: [ - const TextSpan(text: "("), + RichText( + selectionRegistrar: SelectionContainer.maybeOf(context), + selectionColor: Theme.of(context).highlightColor, + text: TextSpan( + style: Theme.of(context).textTheme.labelSmall, + children: [ + TextSpan( + text: type, + style: Theme.of(context) + .textTheme + .labelSmall + ?.copyWith(color: Colors.amber), + ), + if (label != null) TextSpan( - text: label!, - style: Theme.of(context).textTheme.labelSmall?.copyWith( - color: Theme.of(context).colorScheme.primary, - ), - ), - const TextSpan(text: ")"), - ], - ), - TextSpan( - text: " #$nKey", - style: Theme.of(context).textTheme.labelSmall?.copyWith( - color: Theme.of(context).colorScheme.secondary, + children: [ + const TextSpan(text: "("), + TextSpan( + text: label!, + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: Theme.of(context).colorScheme.primary, + ), + ), + const TextSpan(text: ")"), + ], ), + TextSpan( + text: " #$nKey", + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ), + ], ), - ], - ), + ), + ], ); } } diff --git a/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart index 55688c09..67241554 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart @@ -30,41 +30,6 @@ class NodeTile extends StatelessWidget { } } -class NodeTileIcon extends StatelessWidget { - final String kind; - final bool isDependency; - - const NodeTileIcon({ - super.key, - required this.kind, - this.isDependency = false, - }); - - @override - Widget build(BuildContext context) { - final nodeKind = NodeKind.getKind(kind); - - return Badge( - backgroundColor: isDependency ? Colors.teal : Colors.transparent, - child: Padding( - padding: const EdgeInsets.all(2.0), - child: Center( - child: CircleAvatar( - backgroundColor: nodeKind.color, - child: Text( - nodeKind.abbr, - style: Theme.of(context).textTheme.labelSmall?.copyWith( - color: Colors.white, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - ), - ); - } -} - class NodeTileTitle extends StatelessWidget { final INode node; @@ -79,24 +44,12 @@ class NodeTileTitle extends StatelessWidget { final info = watch(node.uInfo).value; final dependencyRef = info?.dependencyRef; - return Row( - children: [ - SizedBox.square( - dimension: 24, - child: Padding( - padding: const EdgeInsets.all(1), - child: NodeTileIcon( - kind: node.kind, - isDependency: dependencyRef != null, - ), - ), - ), - InstanceTitle( - type: node.type, - label: node.label, - nKey: node.key, - ), - ], + return InstanceTitle( + nKey: node.key, + type: node.type, + kind: node.kind, + label: node.label, + isDependency: dependencyRef != null, ); }); } diff --git a/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart index a7ad8e2c..2f00e5bf 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; import 'package:reactter_devtools_extension/src/controllers/nodes_controller.dart'; +import 'package:reactter_devtools_extension/src/data/constants.dart'; import 'package:reactter_devtools_extension/src/data/property_node.dart'; import 'package:reactter_devtools_extension/src/widgets/instance_title.dart'; import 'package:reactter_devtools_extension/src/widgets/loading.dart'; @@ -12,7 +13,7 @@ class PropertyTile extends StatelessWidget { required this.propertyNode, }); - final PropertyNode propertyNode; + final IPropertyNode propertyNode; @override Widget build(BuildContext context) { @@ -28,12 +29,15 @@ class PropertyTile extends StatelessWidget { ?.copyWith(color: Theme.of(context).colorScheme.primary), ), RtWatcher((context, watch) { - final isLoading = watch(propertyNode.uIsLoading).value; + if (propertyNode is PropertyAsyncNode) { + final propertyAsynNode = propertyNode as PropertyAsyncNode; + final isLoading = watch(propertyAsynNode.uIsLoading).value; - if (isLoading) return const Loading(); + if (isLoading) return const Loading(); - watch(propertyNode.uValueFuture).value ?? - propertyNode.getValueAsync(); + watch(propertyAsynNode.uValueFuture).value ?? + propertyAsynNode.getValueAsync(); + } final value = watch(propertyNode.uValue).value; @@ -44,21 +48,29 @@ class PropertyTile extends StatelessWidget { final nodesController = context.use(); final node = nodesController.uNodes.value[nKey]; - return Row( - children: [ - if (node != null) - InkWell( - child: const Icon(Icons.reply), - onTap: () { - nodesController.selectNodeByKey(nKey); - }, - ), - InstanceTitle( - type: instanceInfo['type'], - label: instanceInfo['id'] ?? instanceInfo['debugLabel'], - nKey: nKey, - ), - ], + if (node != null) watch(node.uInfo); + + final type = node != null ? node.type : instanceInfo['type']; + final kind = node != null ? node.kind : instanceInfo['kind']; + final label = node != null + ? node.label + : instanceInfo['id'] ?? + instanceInfo['debugLabel'] ?? + instanceInfo['name']; + final dependencyRef = node != null + ? node.uInfo.value?.dependencyRef + : instanceInfo['dependencyRef']; + + return InstanceTitle( + nKey: nKey, + type: type, + kind: + node == null && kind == NodeKind.instance.key ? null : kind, + label: label, + isDependency: dependencyRef != null, + onTapIcon: node != null + ? () => nodesController.selectNodeByKey(nKey) + : null, ); } From 2fd9371a45ae2abd10c7e419a31276c1277e4f27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 15 Nov 2024 22:18:50 -0600 Subject: [PATCH 053/141] feat(devtools): Add devtools options configuration and update version to 1.0.0 --- packages/flutter_reactter/devtools_options.yaml | 4 ++++ packages/flutter_reactter/example/devtools_options.yaml | 3 ++- packages/reactter/devtools_options.yaml | 1 + packages/reactter/extension/devtools/.pubignore | 1 + packages/reactter/extension/devtools/config.yaml | 2 +- 5 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 packages/flutter_reactter/devtools_options.yaml create mode 100644 packages/reactter/extension/devtools/.pubignore diff --git a/packages/flutter_reactter/devtools_options.yaml b/packages/flutter_reactter/devtools_options.yaml new file mode 100644 index 00000000..995fadae --- /dev/null +++ b/packages/flutter_reactter/devtools_options.yaml @@ -0,0 +1,4 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: + - reactter: true \ No newline at end of file diff --git a/packages/flutter_reactter/example/devtools_options.yaml b/packages/flutter_reactter/example/devtools_options.yaml index 428f2ac0..995fadae 100644 --- a/packages/flutter_reactter/example/devtools_options.yaml +++ b/packages/flutter_reactter/example/devtools_options.yaml @@ -1,3 +1,4 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states extensions: - - provider: false - reactter: true \ No newline at end of file diff --git a/packages/reactter/devtools_options.yaml b/packages/reactter/devtools_options.yaml index fa0b357c..995fadae 100644 --- a/packages/reactter/devtools_options.yaml +++ b/packages/reactter/devtools_options.yaml @@ -1,3 +1,4 @@ description: This file stores settings for Dart & Flutter DevTools. documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states extensions: + - reactter: true \ No newline at end of file diff --git a/packages/reactter/extension/devtools/.pubignore b/packages/reactter/extension/devtools/.pubignore new file mode 100644 index 00000000..684ae5d8 --- /dev/null +++ b/packages/reactter/extension/devtools/.pubignore @@ -0,0 +1 @@ +!build \ No newline at end of file diff --git a/packages/reactter/extension/devtools/config.yaml b/packages/reactter/extension/devtools/config.yaml index 64da7229..6bb38e83 100644 --- a/packages/reactter/extension/devtools/config.yaml +++ b/packages/reactter/extension/devtools/config.yaml @@ -1,5 +1,5 @@ name: reactter -version: 0.0.1 +version: 1.0.0 issueTracker: https://github.com/2devs-team/reactter/issues materialIconCodePoint: "0xf0537" requiresConnection: false From d14ef476bcacd2f79a13775e600069f31b438e3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Sat, 16 Nov 2024 01:05:05 -0600 Subject: [PATCH 054/141] refactor: Remove code deprecated --- .../lib/src/framework/provider_impl.dart | 7 - .../lib/src/widgets/rt_component.dart | 7 - .../lib/src/widgets/rt_consumer.dart | 7 - .../lib/src/widgets/rt_provider.dart | 13 - .../lib/src/widgets/rt_providers.dart | 7 - .../lib/src/widgets/rt_scope.dart | 13 - .../lib/src/widgets/rt_selector.dart | 7 - .../lib/src/widgets/rt_signal_watcher.dart | 7 - .../test/widgets/rt_selector_test.dart | 5 +- packages/reactter/lib/reactter.dart | 4 +- .../lib/src/core/dependency_injection.dart | 31 +- .../lib/src/core/dependency_mode.dart | 6 - packages/reactter/lib/src/core/lifecycle.dart | 14 - .../lib/src/core/lifecycle_observer.dart | 12 - packages/reactter/lib/src/devtools.dart | 2 +- .../lib/src/framework/rt_context.dart | 7 - .../lib/src/framework/rt_dependency.dart | 14 - .../reactter/lib/src/framework/rt_hook.dart | 15 +- .../lib/src/framework/rt_interface.dart | 7 - .../lib/src/framework/rt_state_base.dart | 13 +- .../lib/src/hooks/use_dependency.dart | 6 - .../reactter/lib/src/hooks/use_reducer.dart | 14 - .../reactter/lib/src/interfaces/hook.dart | 2 +- .../reactter/lib/src/interfaces/state.dart | 16 +- .../interceptors/memo_multi_interceptor.dart | 7 - .../memo_safe_async_interceptor.dart | 7 - .../memo_temporary_cache_interceptor.dart | 7 - .../memo_wrapper_interceptor.dart | 7 - .../lib/src/obj/extensions/obj_bigint.dart | 686 ---------- .../lib/src/obj/extensions/obj_bool.dart | 19 - .../lib/src/obj/extensions/obj_date_time.dart | 664 ---------- .../lib/src/obj/extensions/obj_double.dart | 334 ----- .../lib/src/obj/extensions/obj_int.dart | 477 ------- .../lib/src/obj/extensions/obj_iterable.dart | 1070 ---------------- .../lib/src/obj/extensions/obj_list.dart | 1114 ----------------- .../lib/src/obj/extensions/obj_map.dart | 500 -------- .../lib/src/obj/extensions/obj_num.dart | 783 ------------ .../lib/src/obj/extensions/obj_set.dart | 422 ------- .../lib/src/obj/extensions/obj_string.dart | 1057 ---------------- packages/reactter/lib/src/obj/obj.dart | 14 - packages/reactter/lib/src/obj/obj_impl.dart | 97 -- .../{signal/signal_impl.dart => signal.dart} | 59 +- .../src/signal/extensions/signal_list.dart | 821 ------------ .../lib/src/signal/extensions/signal_map.dart | 327 ----- .../lib/src/signal/extensions/signal_set.dart | 236 ---- packages/reactter/lib/src/signal/signal.dart | 11 - .../reactter/test/hooks/use_state_test.dart | 34 +- packages/reactter/test/memo_test.dart | 8 +- packages/reactter/test/obj_test.dart | 134 -- packages/reactter/test/signal_test.dart | 23 +- .../lib/src/widgets/node_tile.dart | 1 - 51 files changed, 97 insertions(+), 9058 deletions(-) delete mode 100644 packages/reactter/lib/src/obj/extensions/obj_bigint.dart delete mode 100644 packages/reactter/lib/src/obj/extensions/obj_bool.dart delete mode 100644 packages/reactter/lib/src/obj/extensions/obj_date_time.dart delete mode 100644 packages/reactter/lib/src/obj/extensions/obj_double.dart delete mode 100644 packages/reactter/lib/src/obj/extensions/obj_int.dart delete mode 100644 packages/reactter/lib/src/obj/extensions/obj_iterable.dart delete mode 100644 packages/reactter/lib/src/obj/extensions/obj_list.dart delete mode 100644 packages/reactter/lib/src/obj/extensions/obj_map.dart delete mode 100644 packages/reactter/lib/src/obj/extensions/obj_num.dart delete mode 100644 packages/reactter/lib/src/obj/extensions/obj_set.dart delete mode 100644 packages/reactter/lib/src/obj/extensions/obj_string.dart delete mode 100644 packages/reactter/lib/src/obj/obj.dart delete mode 100644 packages/reactter/lib/src/obj/obj_impl.dart rename packages/reactter/lib/src/{signal/signal_impl.dart => signal.dart} (80%) delete mode 100644 packages/reactter/lib/src/signal/extensions/signal_list.dart delete mode 100644 packages/reactter/lib/src/signal/extensions/signal_map.dart delete mode 100644 packages/reactter/lib/src/signal/extensions/signal_set.dart delete mode 100644 packages/reactter/lib/src/signal/signal.dart delete mode 100644 packages/reactter/test/obj_test.dart diff --git a/packages/flutter_reactter/lib/src/framework/provider_impl.dart b/packages/flutter_reactter/lib/src/framework/provider_impl.dart index 701c9c28..3022bb98 100644 --- a/packages/flutter_reactter/lib/src/framework/provider_impl.dart +++ b/packages/flutter_reactter/lib/src/framework/provider_impl.dart @@ -356,10 +356,3 @@ https://stackoverflow.com/questions/tagged/flutter '''; } } - -/// {@macro flutter_reactter.provider_not_found_exception} -@Deprecated( - 'Use `RtDependencyNotFoundException` instead. ' - 'This feature was deprecated after v7.3.0.', -) -typedef ReactterDependencyNotFoundException = RtDependencyNotFoundException; diff --git a/packages/flutter_reactter/lib/src/widgets/rt_component.dart b/packages/flutter_reactter/lib/src/widgets/rt_component.dart index d3104edd..23be6aab 100644 --- a/packages/flutter_reactter/lib/src/widgets/rt_component.dart +++ b/packages/flutter_reactter/lib/src/widgets/rt_component.dart @@ -143,10 +143,3 @@ abstract class RtComponent extends StatelessWidget { ); } } - -/// {@macro flutter_reactter.rt_component} -@Deprecated( - 'Use `RtComponent` instead. ' - 'This feature was deprecated after v7.3.0.', -) -typedef ReactterComponent = RtComponent; diff --git a/packages/flutter_reactter/lib/src/widgets/rt_consumer.dart b/packages/flutter_reactter/lib/src/widgets/rt_consumer.dart index f70883b2..91d10662 100644 --- a/packages/flutter_reactter/lib/src/widgets/rt_consumer.dart +++ b/packages/flutter_reactter/lib/src/widgets/rt_consumer.dart @@ -105,10 +105,3 @@ class RtConsumer extends StatelessWidget { ); } } - -/// {@macro flutter_reactter.rt_consumer} -@Deprecated( - 'Use `RtConsumer` instead. ' - 'This feature was deprecated after v7.3.0.', -) -typedef ReactterConsumer = RtConsumer; diff --git a/packages/flutter_reactter/lib/src/widgets/rt_provider.dart b/packages/flutter_reactter/lib/src/widgets/rt_provider.dart index dab11782..25073864 100644 --- a/packages/flutter_reactter/lib/src/widgets/rt_provider.dart +++ b/packages/flutter_reactter/lib/src/widgets/rt_provider.dart @@ -131,11 +131,6 @@ class RtProvider extends ProviderBase Key? key, String? id, DependencyMode mode = DependencyMode.builder, - @Deprecated( - 'This feature not working anymore, use `RtProvider.init` instead. ' - 'It was deprecated after v7.2.0.', - ) - bool init = false, Widget? child, InstanceChildBuilder? builder, }) : super( @@ -143,7 +138,6 @@ class RtProvider extends ProviderBase key: key, id: id, mode: mode, - init: init, child: child, builder: builder, ); @@ -280,10 +274,3 @@ class RtProviderElement extends ComponentElement ); } } - -/// {@macro flutter_reactter.rt_provider} -@Deprecated( - 'Use `RtProvider` instead. ' - 'This feature was deprecated after v7.3.0.', -) -typedef ReactterProvider = RtProvider; diff --git a/packages/flutter_reactter/lib/src/widgets/rt_providers.dart b/packages/flutter_reactter/lib/src/widgets/rt_providers.dart index 586dd76b..6176e7c6 100644 --- a/packages/flutter_reactter/lib/src/widgets/rt_providers.dart +++ b/packages/flutter_reactter/lib/src/widgets/rt_providers.dart @@ -133,10 +133,3 @@ class RtMultiProviderElement extends StatelessElement return nextNode; } } - -/// {@macro flutter_reactter.rt_multi_provider} -@Deprecated( - 'Use `RtMultiProvider` instead. ' - 'This feature was deprecated after v7.3.0.', -) -typedef ReactterProviders = RtMultiProvider; diff --git a/packages/flutter_reactter/lib/src/widgets/rt_scope.dart b/packages/flutter_reactter/lib/src/widgets/rt_scope.dart index aee745f3..900b4935 100644 --- a/packages/flutter_reactter/lib/src/widgets/rt_scope.dart +++ b/packages/flutter_reactter/lib/src/widgets/rt_scope.dart @@ -91,16 +91,3 @@ https://stackoverflow.com/questions/tagged/flutter '''; } } - -/// {@macro flutter_reactter.rt_scope} -@Deprecated( - 'Use `RtScope` instead. ' - 'This feature was deprecated after v7.3.0.', -) -typedef ReactterScope = RtScope; - -@Deprecated( - 'Use `RtScopeNotFoundException` instead. ' - 'This feature was deprecated after v7.3.0.', -) -typedef ReactterScopeNotFoundException = RtScopeNotFoundException; diff --git a/packages/flutter_reactter/lib/src/widgets/rt_selector.dart b/packages/flutter_reactter/lib/src/widgets/rt_selector.dart index 1b7c3da3..b59085f9 100644 --- a/packages/flutter_reactter/lib/src/widgets/rt_selector.dart +++ b/packages/flutter_reactter/lib/src/widgets/rt_selector.dart @@ -176,10 +176,3 @@ class RtSelector extends StatelessWidget { return dependency.value; } } - -/// {@macro flutter_reactter.rt_selector} -@Deprecated( - 'Use `RtSelector` instead. ' - 'This feature was deprecated after v7.3.0.', -) -typedef ReactterSelector = RtSelector; diff --git a/packages/flutter_reactter/lib/src/widgets/rt_signal_watcher.dart b/packages/flutter_reactter/lib/src/widgets/rt_signal_watcher.dart index 45a7fb64..b6c51d3f 100644 --- a/packages/flutter_reactter/lib/src/widgets/rt_signal_watcher.dart +++ b/packages/flutter_reactter/lib/src/widgets/rt_signal_watcher.dart @@ -152,10 +152,3 @@ class _RtSignalWatcherState extends State { _states.clear(); } } - -/// {@macro flutter_reactter.rt_signal_watcher} -@Deprecated( - 'Use `RtSignalWatcher` instead. ' - 'This feature was deprecated after v7.3.0.', -) -typedef ReactterWatcher = RtSignalWatcher; diff --git a/packages/flutter_reactter/test/widgets/rt_selector_test.dart b/packages/flutter_reactter/test/widgets/rt_selector_test.dart index cd126a45..16859106 100644 --- a/packages/flutter_reactter/test/widgets/rt_selector_test.dart +++ b/packages/flutter_reactter/test/widgets/rt_selector_test.dart @@ -16,6 +16,7 @@ void main() { child: RtSelector( selector: (_, $) { return $(signalList) + .value .fold(0, (prev, elem) => prev + elem); }, builder: (context, inst, total, child) => Text("total: $total"), @@ -28,7 +29,7 @@ void main() { expect(find.text("total: 0"), findsOneWidget); - signalList.addAll([1, 1, 2]); + signalList.value.addAll([1, 1, 2]); await tester.pumpAndSettle(); expect(find.text("total: 4"), findsOneWidget); @@ -38,7 +39,7 @@ void main() { expect(find.text("total: 4"), findsOneWidget); - signalList.refresh(); + signalList.notify(); await tester.pumpAndSettle(); expect(find.text("total: 5"), findsOneWidget); diff --git a/packages/reactter/lib/reactter.dart b/packages/reactter/lib/reactter.dart index 4ebc98a4..6f4faf88 100644 --- a/packages/reactter/lib/reactter.dart +++ b/packages/reactter/lib/reactter.dart @@ -5,7 +5,7 @@ export 'src/devtools.dart' hide RtDevTools; export 'src/framework.dart'; export 'src/hooks/hooks.dart' hide UseAsyncStateBase; export 'src/memo/memo.dart'; -export 'src/obj/obj.dart'; -export 'src/signal/signal.dart'; + +export 'src/signal.dart'; export 'src/logger.dart' hide RtLogger; export 'src/types.dart'; diff --git a/packages/reactter/lib/src/core/dependency_injection.dart b/packages/reactter/lib/src/core/dependency_injection.dart index 3f56c86a..f4044fa5 100644 --- a/packages/reactter/lib/src/core/dependency_injection.dart +++ b/packages/reactter/lib/src/core/dependency_injection.dart @@ -430,15 +430,6 @@ abstract class DependencyInjection implements IContext { return _getDependencyRegister(id)?.instance != null; } - /// Checks if an instance is registered in Reactter - // coverage:ignore-start - @Deprecated( - 'Use `isActive` instead.' - 'This feature was deprecated after v7.2.0.', - ) - bool isRegistered(Object? instance) => isActive(instance); - // coverage:ignore-end - /// Checks if the [instance] is active in Reactter. bool isActive(Object? instance) { return _instances[instance] != null; @@ -450,15 +441,6 @@ abstract class DependencyInjection implements IContext { return _dependencyRegisters.lookup(dependencyRef) != null; } - // coverage:ignore-start - @Deprecated( - 'Use `getDependencyMode` instead. ' - 'This feature was deprecated after v7.1.0.', - ) - InstanceManageMode? getInstanceManageMode(Object? instance) => - getDependencyMode(instance); - // coverage:ignore-end - /// Returns [DependencyMode] of instance parameter. DependencyMode? getDependencyMode(Object? instance) { return _instances[instance]?.mode; @@ -508,12 +490,6 @@ abstract class DependencyInjection implements IContext { BindingZone.autoBinding(() => _createInstance(instanceRegister)); - // ignore: deprecated_member_use_from_same_package - eventHandler.emit( - instanceRegister, - Lifecycle.initialized, - instanceRegister.instance, - ); eventHandler.emit( instanceRegister, Lifecycle.created, @@ -537,16 +513,11 @@ abstract class DependencyInjection implements IContext { /// Removes an instance of a generic type from a [DependencyRegister]. void _removeInstance(DependencyRegister dependencyRegister) { final instance = dependencyRegister.instance; + dependencyRegister._instance = null; _instances.remove(instance); - - // ignore: deprecated_member_use_from_same_package - eventHandler.emit(instance, Lifecycle.destroyed, instance); eventHandler.emit(instance, Lifecycle.deleted, instance); - - // ignore: deprecated_member_use_from_same_package - eventHandler.emit(dependencyRegister, Lifecycle.destroyed, instance); eventHandler.emit(dependencyRegister, Lifecycle.deleted, instance); if (instance is IState && !(instance as IState).isDisposed) { diff --git a/packages/reactter/lib/src/core/dependency_mode.dart b/packages/reactter/lib/src/core/dependency_mode.dart index daff0efd..c0f48d5a 100644 --- a/packages/reactter/lib/src/core/dependency_mode.dart +++ b/packages/reactter/lib/src/core/dependency_mode.dart @@ -1,11 +1,5 @@ part of '../internals.dart'; -@Deprecated( - 'Use `DependencyMode` instead. ' - 'This feature was deprecated after v7.1.0.', -) -typedef InstanceManageMode = DependencyMode; - /// {@template reactter.dependency_mode} /// Represents different ways for managing instances. /// {@endtemplate} diff --git a/packages/reactter/lib/src/core/lifecycle.dart b/packages/reactter/lib/src/core/lifecycle.dart index 4dad14d1..9947614b 100644 --- a/packages/reactter/lib/src/core/lifecycle.dart +++ b/packages/reactter/lib/src/core/lifecycle.dart @@ -4,13 +4,6 @@ enum Lifecycle { /// This event is triggered when the [DependencyInjection] registers the dependency. registered, - /// This event is triggered when the [DependencyInjection] initializes the dependency. - @Deprecated( - 'Use `Lifecycle.created` instead. ' - 'This feature was deprecated after v7.2.0.', - ) - initialized, - /// This event is triggered when the [DependencyInjection] created the dependency instance. created, @@ -32,13 +25,6 @@ enum Lifecycle { /// This event(exclusive to `flutter_reactter`) happens when the dependency has been successfully unmounted from the widget tree. didUnmount, - /// This event is triggered when the [DependencyInjection] destroys the dependency. - @Deprecated( - 'Use `Lifecycle.deleted` instead. ' - 'This feature was deprecated after v7.2.0.', - ) - destroyed, - /// This event is triggered when the [DependencyInjection] destroys the dependency instance. deleted, diff --git a/packages/reactter/lib/src/core/lifecycle_observer.dart b/packages/reactter/lib/src/core/lifecycle_observer.dart index 5687862f..f92973d8 100644 --- a/packages/reactter/lib/src/core/lifecycle_observer.dart +++ b/packages/reactter/lib/src/core/lifecycle_observer.dart @@ -28,13 +28,6 @@ part of '../internals.dart'; /// {@endtemplate} abstract class LifecycleObserver { - /// This method is called when the dependency is initialized. - @Deprecated( - 'Use `onCreated` instead. ' - 'This feature was deprecated after v7.2.0.', - ) - void onInitialized() {} - /// This method is called when the dependency instance is created. void onCreated() {} @@ -69,11 +62,6 @@ void _executeLifecycleObserver( dynamic param, ]) { switch (lifecycle) { - // ignore: deprecated_member_use_from_same_package - case Lifecycle.initialized: - // ignore: deprecated_member_use_from_same_package - observer.onInitialized(); - break; case Lifecycle.created: observer.onCreated(); break; diff --git a/packages/reactter/lib/src/devtools.dart b/packages/reactter/lib/src/devtools.dart index 2fd78b02..5eeaa1d2 100644 --- a/packages/reactter/lib/src/devtools.dart +++ b/packages/reactter/lib/src/devtools.dart @@ -2,10 +2,10 @@ import 'dart:collection'; import 'dart:developer' as dev; import 'package:meta/meta.dart'; +import 'package:reactter/src/signal.dart'; import 'framework.dart'; import 'internals.dart'; -import 'signal/signal.dart'; extension RtDevToolsExt on RtInterface { void initializeDevTools() { diff --git a/packages/reactter/lib/src/framework/rt_context.dart b/packages/reactter/lib/src/framework/rt_context.dart index 3cc354bd..ab63fd09 100644 --- a/packages/reactter/lib/src/framework/rt_context.dart +++ b/packages/reactter/lib/src/framework/rt_context.dart @@ -21,10 +21,3 @@ mixin RtContext implements IContext { @internal EventHandler get eventHandler => Rt; } - -/// {@macro reactter.rt} -@Deprecated( - 'Use `Rt` instead. ' - 'This feature was deprecated after v7.3.0.', -) -final Reactter = Rt; diff --git a/packages/reactter/lib/src/framework/rt_dependency.dart b/packages/reactter/lib/src/framework/rt_dependency.dart index 68b69a62..821db05b 100644 --- a/packages/reactter/lib/src/framework/rt_dependency.dart +++ b/packages/reactter/lib/src/framework/rt_dependency.dart @@ -8,17 +8,3 @@ part of '../framework.dart'; class RtDependency extends DependencyRef { const RtDependency([String? id]) : super(id); } - -/// {@macro reactter.rt_dependency} -@Deprecated( - 'Use `RtDependency` instead.' - 'This feature was deprecated after v7.1.0.', -) -typedef ReactterInstance = RtDependency; - -/// {@macro reactter.rt_dependency} -@Deprecated( - 'Use `RtDependency` instead. ' - 'This feature was deprecated after v7.3.0.', -) -typedef ReactterDependency = RtDependency; diff --git a/packages/reactter/lib/src/framework/rt_hook.dart b/packages/reactter/lib/src/framework/rt_hook.dart index 76a46513..31ac73f7 100644 --- a/packages/reactter/lib/src/framework/rt_hook.dart +++ b/packages/reactter/lib/src/framework/rt_hook.dart @@ -62,20 +62,9 @@ abstract class RtHook with RtContext, RtStateBase implements IHook { $.bindInstanceToStates(this); } - /// Executes [callback], and notifies the listeners about the update. - /// - /// If [callback] is provided, it will be executed before notifying the listeners. - /// If [callback] is not provided, an empty function will be executed. @override @mustCallSuper - void update([Function? callback]) { - return super.update(callback ?? () {}); + void update([Function()? fnUpdate]) { + return super.update(fnUpdate ?? () {}); } } - -/// {@macro reactter.rt_hook} -@Deprecated( - 'Use `RtHook` instead. ' - 'This feature was deprecated after v7.3.0.', -) -typedef ReactterHook = RtHook; diff --git a/packages/reactter/lib/src/framework/rt_interface.dart b/packages/reactter/lib/src/framework/rt_interface.dart index 752babfb..06a099c4 100644 --- a/packages/reactter/lib/src/framework/rt_interface.dart +++ b/packages/reactter/lib/src/framework/rt_interface.dart @@ -43,10 +43,3 @@ class RtInterface } } } - -/// {@macro reactter.rt_interface} -@Deprecated( - 'Use `RtInterface` instead. ' - 'This feature was deprecated after v7.3.0.', -) -typedef ReactterInterface = RtInterface; diff --git a/packages/reactter/lib/src/framework/rt_state_base.dart b/packages/reactter/lib/src/framework/rt_state_base.dart index 78a568ce..dba293eb 100644 --- a/packages/reactter/lib/src/framework/rt_state_base.dart +++ b/packages/reactter/lib/src/framework/rt_state_base.dart @@ -101,8 +101,16 @@ abstract class RtStateBase> implements RtState { @override @mustCallSuper + + /// {@macro reactter.istate.update} + /// + /// The [fnUpdate] must be a function without arguments(Function()). void update(covariant Function? fnUpdate) { assert(!_isDisposed, "Can't update when it's been disposed"); + assert( + fnUpdate is Function(), + "The fnUpdate must be a function without arguments", + ); if (!_hasListeners || _isUpdating) { fnUpdate?.call(); @@ -118,11 +126,6 @@ abstract class RtStateBase> implements RtState { _isUpdating = false; } - @override - @mustCallSuper - @Deprecated("Use 'notify' instead.") - void refresh() => notify(); - @override @mustCallSuper void notify() { diff --git a/packages/reactter/lib/src/hooks/use_dependency.dart b/packages/reactter/lib/src/hooks/use_dependency.dart index 0be1a08f..70d219f8 100644 --- a/packages/reactter/lib/src/hooks/use_dependency.dart +++ b/packages/reactter/lib/src/hooks/use_dependency.dart @@ -1,11 +1,5 @@ part of 'hooks.dart'; -@Deprecated( - 'Use `UseDependency` instead. ' - 'This feature was deprecated after v7.1.0.', -) -typedef UseInstance = UseDependency; - /// {@template reactter.use_dependency} /// A [RtHook] that allows to manages a dependency of [T] with/without [id]. /// diff --git a/packages/reactter/lib/src/hooks/use_reducer.dart b/packages/reactter/lib/src/hooks/use_reducer.dart index 563eaca3..b8851fb2 100644 --- a/packages/reactter/lib/src/hooks/use_reducer.dart +++ b/packages/reactter/lib/src/hooks/use_reducer.dart @@ -178,17 +178,3 @@ abstract class RtActionCallable extends RtAction

{ /// the action has been applied). T call(T state); } - -/// {@macro reactter.rt_action} -@Deprecated( - 'Use `RtAction` instead. ' - 'This feature was deprecated after v7.3.0.', -) -typedef ReactterAction = RtAction; - -/// {@macro reactter.rt_action_callable} -@Deprecated( - 'Use `RtActionCallable` instead. ' - 'This feature was deprecated after v7.3.0.', -) -typedef ReactterActionCallable = RtActionCallable; diff --git a/packages/reactter/lib/src/interfaces/hook.dart b/packages/reactter/lib/src/interfaces/hook.dart index afd1903a..a849df8b 100644 --- a/packages/reactter/lib/src/interfaces/hook.dart +++ b/packages/reactter/lib/src/interfaces/hook.dart @@ -12,7 +12,7 @@ abstract class IHook { /// If [callback] is provided, it will be executed before notifying the listeners. /// If [callback] is not provided, an empty function will be executed. @mustCallSuper - void update([Function? callback]); + void update([Function()? callback]); } @internal diff --git a/packages/reactter/lib/src/interfaces/state.dart b/packages/reactter/lib/src/interfaces/state.dart index 09946f73..0ceaae29 100644 --- a/packages/reactter/lib/src/interfaces/state.dart +++ b/packages/reactter/lib/src/interfaces/state.dart @@ -29,20 +29,16 @@ abstract class IState implements IContext { @mustCallSuper void unbind(); + /// {@template reactter.istate.update} /// Executes [fnUpdate], and notify the listeners about to update. /// - /// This method triggers the `Lifecycle.didUpdate` event, - /// which allows listeners to react to the updated state. - @mustCallSuper - void update(covariant Function fnUpdate); - - /// Executes [fnUpdate], and notify the listeners about to update. + /// If [fnUpdate] is provided, it will be executed before notifying the listeners. + /// If [fnUpdate] is not provided, an empty function will be executed. /// - /// This method triggers the `Lifecycle.didUpdate` event, - /// which allows listeners to react to the updated state. + /// This method triggers the `Lifecycle.willUpdate` and `Lifecycle.didUpdate` events, which allows listeners to react to the updated state. + /// {@endtemplate} @mustCallSuper - @Deprecated("Use 'notify' instead.") - void refresh(); + void update(covariant Function? fnUpdate); /// It's used to notify listeners that the state has been updated. /// It is typically called after making changes to the state object. diff --git a/packages/reactter/lib/src/memo/interceptors/memo_multi_interceptor.dart b/packages/reactter/lib/src/memo/interceptors/memo_multi_interceptor.dart index 4a005b96..661d0a02 100644 --- a/packages/reactter/lib/src/memo/interceptors/memo_multi_interceptor.dart +++ b/packages/reactter/lib/src/memo/interceptors/memo_multi_interceptor.dart @@ -37,10 +37,3 @@ class MemoMultiInterceptor extends MemoInterceptor { } } } - -/// {@macro reactter.memo_multi_interceptor} -@Deprecated( - 'Use `MemoMultiInterceptor` instead. ' - 'This feature was deprecated after v7.3.0.', -) -typedef MemoInterceptors = MemoMultiInterceptor; diff --git a/packages/reactter/lib/src/memo/interceptors/memo_safe_async_interceptor.dart b/packages/reactter/lib/src/memo/interceptors/memo_safe_async_interceptor.dart index 1bd8cb98..9f7a1852 100644 --- a/packages/reactter/lib/src/memo/interceptors/memo_safe_async_interceptor.dart +++ b/packages/reactter/lib/src/memo/interceptors/memo_safe_async_interceptor.dart @@ -17,10 +17,3 @@ class MemoSafeAsyncInterceptor extends MemoInterceptor { } } } - -/// {@macro reactter.memo_safe_async_interceptor} -@Deprecated( - 'Use `MemoSafeAsyncInterceptor` instead. ' - 'This feature was deprecated after v7.3.0.', -) -typedef AsyncMemoSafe = MemoSafeAsyncInterceptor; diff --git a/packages/reactter/lib/src/memo/interceptors/memo_temporary_cache_interceptor.dart b/packages/reactter/lib/src/memo/interceptors/memo_temporary_cache_interceptor.dart index 9fe54187..fbed3c50 100644 --- a/packages/reactter/lib/src/memo/interceptors/memo_temporary_cache_interceptor.dart +++ b/packages/reactter/lib/src/memo/interceptors/memo_temporary_cache_interceptor.dart @@ -19,10 +19,3 @@ class MemoTemporaryCacheInterceptor extends MemoInterceptor { } } } - -/// {@macro reactter.memo_temporary_cache_interceptor} -@Deprecated( - 'Use `MemoTemporaryCacheInterceptor` instead. ' - 'This feature was deprecated after v7.3.0.', -) -typedef TemporaryCacheMemo = MemoTemporaryCacheInterceptor; diff --git a/packages/reactter/lib/src/memo/interceptors/memo_wrapper_interceptor.dart b/packages/reactter/lib/src/memo/interceptors/memo_wrapper_interceptor.dart index c5c948ee..d8f028b1 100644 --- a/packages/reactter/lib/src/memo/interceptors/memo_wrapper_interceptor.dart +++ b/packages/reactter/lib/src/memo/interceptors/memo_wrapper_interceptor.dart @@ -54,10 +54,3 @@ class MemoWrapperInterceptor extends MemoInterceptor { _onFinish?.call(memo, arg); } } - -/// {@macro reactter.memo_wrapper_interceptor} -@Deprecated( - 'Use `MemoWrapperInterceptor` instead. ' - 'This feature was deprecated after v7.3.0.', -) -typedef MemoInterceptorWrapper = MemoWrapperInterceptor; diff --git a/packages/reactter/lib/src/obj/extensions/obj_bigint.dart b/packages/reactter/lib/src/obj/extensions/obj_bigint.dart deleted file mode 100644 index d9da49d7..00000000 --- a/packages/reactter/lib/src/obj/extensions/obj_bigint.dart +++ /dev/null @@ -1,686 +0,0 @@ -// coverage:ignore-file -part of '../obj.dart'; - -extension ObjBigIntExt on Obj { - /// Return the negative value of this integer. - /// - /// The result of negating an integer always has the opposite sign, except - /// for zero, which is its own negation. - Obj operator -() => Obj(-value); - - /// Adds [other] to this big integer. - /// - /// The result is again a big integer. - Obj operator +(Obj other) => Obj(value + other.value); - - /// Subtracts [other] from this big integer. - /// - /// The result is again a big integer. - Obj operator -(Obj other) => Obj(value - other.value); - - /// Multiplies [other] by this big integer. - /// - /// The result is again a big integer. - Obj operator *(Obj other) => Obj(value * other.value); - - /// Double division operator. - /// - /// Matching the similar operator on [int], - /// this operation first performs [toDouble] on both this big integer - /// and [other], then does [double.operator/] on those values and - /// returns the result. - /// - /// **Note:** The initial [toDouble] conversion may lose precision. - /// - /// Example: - /// ```dart - /// print(Obj(BigInt.from(1)) / Obj(BigInt.from(2))); // Obj(0.5) - /// print(Obj(BigInt.from(1.99999)) / Obj(BigInt.from(2))); // Obj(0.5) - /// ``` - Obj operator /(Obj other) => Obj(value / other.value); - - /// Truncating integer division operator. - /// - /// Performs a truncating integer division, where the remainder is discarded. - /// - /// The remainder can be computed using the [remainder] method. - /// - /// Examples: - /// ```dart - /// var seven = Obj(BigInt.from(7)); - /// var three = Obj(BigInt.from(3)); - /// seven ~/ three; // => 2 - /// (-seven) ~/ three; // => -2 - /// seven ~/ -three; // => -2 - /// seven.remainder(three); // => 1 - /// (-seven).remainder(three); // => -1 - /// seven.remainder(-three); // => 1 - /// ``` - Obj operator ~/(Obj other) => Obj(value ~/ other.value); - - /// Euclidean modulo operator. - /// - /// Returns the remainder of the Euclidean division. The Euclidean division of - /// two integers `a` and `b` yields two integers `q` and `r` such that - /// `a == b * q + r` and `0 <= r < b.abs()`. - /// - /// The sign of the returned value `r` is always positive. - /// - /// See [remainder] for the remainder of the truncating division. - /// - /// Example: - /// ```dart - /// print(Obj(BigInt.from(5)) % Obj(BigInt.from(3))); // Obj(2) - /// print(Obj(BigInt.from(-)5) % Obj(BigInt.from(3))); // Obj(1) - /// print(Obj(BigInt.from(5)) % Obj(BigInt.from(-)3)); // Obj(2) - /// print(Obj(BigInt.from(-)5) % Obj(BigInt.from(-)3)); // Obj(1) - /// ``` - Obj operator %(Obj other) => Obj(value % other.value); - - /// Shift the bits of this integer to the left by [shiftAmount]. - /// - /// Shifting to the left makes the number larger, effectively multiplying - /// the number by `pow(2, shiftIndex)`. - /// - /// There is no limit on the size of the result. It may be relevant to - /// limit intermediate values by using the "and" operator with a suitable - /// mask. - /// - /// It is an error if [shiftAmount] is negative. - Obj operator <<(Obj shiftAmount) => - Obj(value << shiftAmount.value); - - /// Shift the bits of this integer to the right by [shiftAmount]. - /// - /// Shifting to the right makes the number smaller and drops the least - /// significant bits, effectively doing an integer division by - ///`pow(2, shiftIndex)`. - /// - /// It is an error if [shiftAmount] is negative. - Obj operator >>(Obj shiftAmount) => - Obj(value >> shiftAmount.value); - - /// Bit-wise and operator. - /// - /// Treating both `this` and [other] as sufficiently large two's component - /// integers, the result is a number with only the bits set that are set in - /// both `this` and [other] - /// - /// Of both operands are negative, the result is negative, otherwise - /// the result is non-negative. - Obj operator &(Obj other) => Obj(value & other.value); - - /// Bit-wise or operator. - /// - /// Treating both `this` and [other] as sufficiently large two's component - /// integers, the result is a number with the bits set that are set in either - /// of `this` and [other] - /// - /// If both operands are non-negative, the result is non-negative, - /// otherwise the result is negative. - Obj operator |(Obj other) => Obj(value | other.value); - - /// Bit-wise exclusive-or operator. - /// - /// Treating both `this` and [other] as sufficiently large two's component - /// integers, the result is a number with the bits set that are set in one, - /// but not both, of `this` and [other] - /// - /// If the operands have the same sign, the result is non-negative, - /// otherwise the result is negative. - Obj operator ^(Obj other) => Obj(value ^ other.value); - - /// The bit-wise negate operator. - /// - /// Treating `this` as a sufficiently large two's component integer, - /// the result is a number with the opposite bits set. - /// - /// This maps any integer `x` to `-x - 1`. - Obj operator ~() => Obj(~value); - - /// Whether this big integer is numerically smaller than [other]. - bool operator <(Obj other) => value < other.value; - - /// Whether [other] is numerically greater than this big integer. - bool operator <=(Obj other) => value <= other.value; - - /// Whether this big integer is numerically greater than [other]. - bool operator >(Obj other) => value > other.value; - - /// Whether [other] is numerically smaller than this big integer. - bool operator >=(Obj other) => value >= other.value; - - /// Returns the absolute value of this integer. - /// - /// For any integer `x`, the result is the same as `x < 0 ? -x : x`. - BigInt abs() => value.abs(); - - /// Returns the remainder of the truncating division of `this` by [other]. - /// - /// The result `r` of this operation satisfies: - /// `this == (this ~/ other) * other + r`. - /// As a consequence the remainder `r` has the same sign as the divider `this`. - /// - /// Example: - /// ```dart - /// print(Obj(BigInt.from(5)).remainder(BigInt.from(3))); // 2 - /// print(Obj(BigInt.from(-5)).remainder(BigInt.from(3))); // -2 - /// print(Obj(BigInt.from(5)).remainder(BigInt.from(-3))); // 2 - /// print(Obj(BigInt.from(-5)).remainder(BigInt.from(-3))); // -2 - /// ``` - BigInt remainder(BigInt other) => value.remainder(other); - - /// Compares this to `other`. - /// - /// Returns a negative number if `this` is less than `other`, zero if they are - /// equal, and a positive number if `this` is greater than `other`. - /// - /// Example: - /// ```dart - /// print(Obj(BigInt.from(1)).compareTo(BigInt.from(2))); // => -1 - /// print(Obj(BigInt.from(2)).compareTo(BigInt.from(1))); // => 1 - /// print(Obj(BigInt.from(1)).compareTo(BigInt.from(1))); // => 0 - /// ``` - int compareTo(BigInt other) => value.compareTo(other); - - /// Returns the minimum number of bits required to store this big integer. - /// - /// The number of bits excludes the sign bit, which gives the natural length - /// for non-negative (unsigned) values. Negative values are complemented to - /// return the bit position of the first bit that differs from the sign bit. - /// - /// To find the number of bits needed to store the value as a signed value, - /// add one, i.e. use `x.bitLength + 1`. - /// - /// ```dart - /// x.bitLength == (-x-1).bitLength; - /// - /// Obj(BigInt.from(3)).bitLength == 2; // 00000011 - /// Obj(BigInt.from(2)).bitLength == 2; // 00000010 - /// Obj(BigInt.from(1)).bitLength == 1; // 00000001 - /// Obj(BigInt.from(0)).bitLength == 0; // 00000000 - /// Obj(BigInt.from(-1)).bitLength == 0; // 11111111 - /// Obj(BigInt.from(-2)).bitLength == 1; // 11111110 - /// Obj(BigInt.from(-3)).bitLength == 2; // 11111101 - /// Obj(BigInt.from(-4)).bitLength == 2; // 11111100 - /// ``` - int get bitLength => value.bitLength; - - /// Returns the sign of this big integer. - /// - /// Returns 0 for zero, -1 for values less than zero and - /// +1 for values greater than zero. - int get sign => value.sign; - - /// Whether this big integer is even. - bool get isEven => value.isEven; - - /// Whether this big integer is odd. - bool get isOdd => value.isOdd; - - /// Whether this number is negative. - bool get isNegative => value.isNegative; - - /// Returns `this` to the power of [exponent]. - /// - /// Returns [one] if the [exponent] equals 0. - /// - /// The [exponent] must otherwise be positive. - /// - /// The result is always equal to the mathematical result of this to the power - /// [exponent], only limited by the available memory. - /// - /// Example: - /// ```dart - /// var value = Obj(BigInt.from(1000)); - /// print(value.pow(0)); // 1 - /// print(value.pow(1)); // 1000 - /// print(value.pow(2)); // 1000000 - /// print(value.pow(3)); // 1000000000 - /// print(value.pow(4)); // 1000000000000 - /// print(value.pow(5)); // 1000000000000000 - /// print(value.pow(6)); // 1000000000000000000 - /// print(value.pow(7)); // 1000000000000000000000 - /// print(value.pow(8)); // 1000000000000000000000000 - /// ``` - BigInt pow(int exponent) => value.pow(exponent); - - /// Returns this integer to the power of [exponent] modulo [modulus]. - /// - /// The [exponent] must be non-negative and [modulus] must be - /// positive. - BigInt modPow(BigInt exponent, BigInt modulus) => - value.modPow(exponent, modulus); - - /// Returns the modular multiplicative inverse of this big integer - /// modulo [modulus]. - /// - /// The [modulus] must be positive. - /// - /// It is an error if no modular inverse exists. - // Returns 1/this % modulus, with modulus > 0. - BigInt modInverse(BigInt modulus) => value.modInverse(modulus); - - /// Returns the greatest common divisor of this big integer and [other]. - /// - /// If either number is non-zero, the result is the numerically greatest - /// integer dividing both `this` and `other`. - /// - /// The greatest common divisor is independent of the order, - /// so `x.gcd(y)` is always the same as `y.gcd(x)`. - /// - /// For any integer `x`, `x.gcd(x)` is `x.abs()`. - /// - /// If both `this` and `other` is zero, the result is also zero. - /// - /// Example: - /// ```dart - /// print(Obj(BigInt.from(4)).gcd(BigInt.from(2))); // 2 - /// print(Obj(BigInt.from(8)).gcd(BigInt.from(4))); // 4 - /// print(Obj(BigInt.from(10)).gcd(BigInt.from(12))); // 2 - /// print(Obj(BigInt.from(10)).gcd(BigInt.from(10))); // 10 - /// print(Obj(BigInt.from(-2)).gcd(BigInt.from(-3))); // 1 - /// ``` - BigInt gcd(BigInt other) => value.gcd(other); - - /// Returns the least significant [width] bits of this big integer as a - /// non-negative number (i.e. unsigned representation). The returned value has - /// zeros in all bit positions higher than [width]. - /// - /// ```dart - /// Obj(BigInt.from(-1)).toUnsigned(5) == 31 // 11111111 -> 00011111 - /// ``` - /// - /// This operation can be used to simulate arithmetic from low level languages. - /// For example, to increment an 8 bit quantity: - /// - /// ```dart - /// q = (q + 1).toUnsigned(8); - /// ``` - /// - /// `q` will count from `0` up to `255` and then wrap around to `0`. - /// - /// If the input fits in [width] bits without truncation, the result is the - /// same as the input. The minimum width needed to avoid truncation of `x` is - /// given by `x.bitLength`, i.e. - /// - /// ```dart - /// x == x.toUnsigned(x.bitLength); - /// ``` - BigInt toUnsigned(int width) => value.toUnsigned(width); - - /// Returns the least significant [width] bits of this integer, extending the - /// highest retained bit to the sign. This is the same as truncating the value - /// to fit in [width] bits using an signed 2-s complement representation. The - /// returned value has the same bit value in all positions higher than [width]. - /// - /// ```dart - /// var big15 = Obj(BigInt.from(15)); - /// var big16 = Obj(BigInt.from(16)); - /// var big239 = Obj(BigInt.from(239)); - /// // V--sign bit-V - /// big16.toSigned(5) == -big16; // 00010000 -> 11110000 - /// big239.toSigned(5) == big15; // 11101111 -> 00001111 - /// // ^ ^ - /// ``` - /// - /// This operation can be used to simulate arithmetic from low level languages. - /// For example, to increment an 8 bit signed quantity: - /// - /// ```dart - /// q = (q + 1).toSigned(8); - /// ``` - /// - /// `q` will count from `0` up to `127`, wrap to `-128` and count back up to - /// `127`. - /// - /// If the input value fits in [width] bits without truncation, the result is - /// the same as the input. The minimum width needed to avoid truncation of `x` - /// is `x.bitLength + 1`, i.e. - /// - /// ```dart - /// x == x.toSigned(x.bitLength + 1); - /// ``` - BigInt toSigned(int width) => value.toSigned(width); - - /// Whether this big integer can be represented as an `int` without losing - /// precision. - /// - /// **Warning:** this function may give a different result on - /// dart2js, dev compiler, and the VM, due to the differences in - /// integer precision. - /// - /// Example: - /// ```dart - /// var bigNumber = BigInt.parse('100000000000000000000000'); - /// print(bigNumber.isValidInt); // false - /// - /// var value = BigInt.parse('0xFF'); // 255 - /// print(value.isValidInt); // true - /// ``` - bool get isValidInt => value.isValidInt; - - /// Returns this [BigInt] as an [int]. - /// - /// If the number does not fit, clamps to the max (or min) - /// integer. - /// - /// **Warning:** the clamping behaves differently between the web and - /// native platforms due to the differences in integer precision. - /// - /// Example: - /// ```dart - /// var bigNumber = BigInt.parse('100000000000000000000000'); - /// print(bigNumber.isValidInt); // false - /// print(bigNumber.toInt()); // 9223372036854775807 - /// ``` - int toInt() => value.toInt(); - - /// Returns this [BigInt] as a [double]. - /// - /// If the number is not representable as a [double], an - /// approximation is returned. For numerically large integers, the - /// approximation may be infinite. - /// - /// Example: - /// ```dart - /// var bigNumber = BigInt.parse('100000000000000000000000'); - /// print(bigNumber.toDouble()); // 1e+23 - /// ``` - double toDouble() => value.toDouble(); - - /// Converts [this] to a string representation in the given [radix]. - /// - /// In the string representation, lower-case letters are used for digits above - /// '9', with 'a' being 10 an 'z' being 35. - /// - /// The [radix] argument must be an integer in the range 2 to 36. - /// - /// Example: - /// ```dart - /// // Binary (base 2). - /// print(Obj(BigInt.from(12)).toRadixString(2)); // 1100 - /// print(Obj(BigInt.from(31)).toRadixString(2)); // 11111 - /// print(Obj(BigInt.from(2021)).toRadixString(2)); // 11111100101 - /// print(Obj(BigInt.from(-)12).toRadixString(2)); // -1100 - /// // Octal (base 8). - /// print(Obj(BigInt.from(12)).toRadixString(8)); // 14 - /// print(Obj(BigInt.from(31)).toRadixString(8)); // 37 - /// print(Obj(BigInt.from(2021)).toRadixString(8)); // 3745 - /// // Hexadecimal (base 16). - /// print(Obj(BigInt.from(12)).toRadixString(16)); // c - /// print(Obj(BigInt.from(31)).toRadixString(16)); // 1f - /// print(Obj(BigInt.from(2021)).toRadixString(16)); // 7e5 - /// // Base 36. - /// print(Obj(BigInt.from(35 * 36 + 1)).toRadixString(36)); // z1 - /// ``` - String toRadixString(int radix) => value.toRadixString(radix); -} - -extension ObjBigIntNullExt on Obj { - /// Returns the absolute value of this integer. - /// - /// For any integer `x`, the result is the same as `x < 0 ? -x : x`. - BigInt? abs() => value?.abs(); - - /// Returns the remainder of the truncating division of `this` by [other]. - /// - /// The result `r` of this operation satisfies: - /// `this == (this ~/ other) * other + r`. - /// As a consequence the remainder `r` has the same sign as the divider `this`. - /// - /// Example: - /// ```dart - /// print(Obj(BigInt.from(5)).remainder(BigInt.from(3))); // 2 - /// print(Obj(BigInt.from(-5)).remainder(BigInt.from(3))); // -2 - /// print(Obj(BigInt.from(5)).remainder(BigInt.from(-3))); // 2 - /// print(Obj(BigInt.from(-5)).remainder(BigInt.from(-3))); // -2 - /// ``` - BigInt? remainder(BigInt other) => value?.remainder(other); - - /// Compares this to `other`. - /// - /// Returns a negative number if `this` is less than `other`, zero if they are - /// equal, and a positive number if `this` is greater than `other`. - /// - /// Example: - /// ```dart - /// print(Obj(BigInt.from(1)).compareTo(BigInt.from(2))); // => -1 - /// print(Obj(BigInt.from(2)).compareTo(BigInt.from(1))); // => 1 - /// print(Obj(BigInt.from(1)).compareTo(BigInt.from(1))); // => 0 - /// ``` - int? compareTo(BigInt other) => value?.compareTo(other); - - /// Returns the minimum number of bits required to store this big integer. - /// - /// The number of bits excludes the sign bit, which gives the natural length - /// for non-negative (unsigned) values. Negative values are complemented to - /// return the bit position of the first bit that differs from the sign bit. - /// - /// To find the number of bits needed to store the value as a signed value, - /// add one, i.e. use `x.bitLength + 1`. - /// - /// ```dart - /// x.bitLength == (-x-1).bitLength; - /// - /// Obj(BigInt.from(3)).bitLength == 2; // 00000011 - /// Obj(BigInt.from(2)).bitLength == 2; // 00000010 - /// Obj(BigInt.from(1)).bitLength == 1; // 00000001 - /// Obj(BigInt.from(0)).bitLength == 0; // 00000000 - /// Obj(BigInt.from(-1)).bitLength == 0; // 11111111 - /// Obj(BigInt.from(-2)).bitLength == 1; // 11111110 - /// Obj(BigInt.from(-3)).bitLength == 2; // 11111101 - /// Obj(BigInt.from(-4)).bitLength == 2; // 11111100 - /// ``` - int? get bitLength => value?.bitLength; - - /// Returns the sign of this big integer. - /// - /// Returns 0 for zero, -1 for values less than zero and - /// +1 for values greater than zero. - int? get sign => value?.sign; - - /// Whether this big integer is even. - bool? get isEven => value?.isEven; - - /// Whether this big integer is odd. - bool? get isOdd => value?.isOdd; - - /// Whether this number is negative. - bool? get isNegative => value?.isNegative; - - /// Returns `this` to the power of [exponent]. - /// - /// Returns [one] if the [exponent] equals 0. - /// - /// The [exponent] must otherwise be positive. - /// - /// The result is always equal to the mathematical result of this to the power - /// [exponent], only limited by the available memory. - /// - /// Example: - /// ```dart - /// var value = Obj(BigInt.from(1000)); - /// print(value.pow(0)); // 1 - /// print(value.pow(1)); // 1000 - /// print(value.pow(2)); // 1000000 - /// print(value.pow(3)); // 1000000000 - /// print(value.pow(4)); // 1000000000000 - /// print(value.pow(5)); // 1000000000000000 - /// print(value.pow(6)); // 1000000000000000000 - /// print(value.pow(7)); // 1000000000000000000000 - /// print(value.pow(8)); // 1000000000000000000000000 - /// ``` - BigInt? pow(int exponent) => value?.pow(exponent); - - /// Returns this integer to the power of [exponent] modulo [modulus]. - /// - /// The [exponent] must be non-negative and [modulus] must be - /// positive. - BigInt? modPow(BigInt exponent, BigInt modulus) => - value?.modPow(exponent, modulus); - - /// Returns the modular multiplicative inverse of this big integer - /// modulo [modulus]. - /// - /// The [modulus] must be positive. - /// - /// It is an error if no modular inverse exists. - // Returns 1/this % modulus, with modulus > 0. - BigInt? modInverse(BigInt modulus) => value?.modInverse(modulus); - - /// Returns the greatest common divisor of this big integer and [other]. - /// - /// If either number is non-zero, the result is the numerically greatest - /// integer dividing both `this` and `other`. - /// - /// The greatest common divisor is independent of the order, - /// so `x.gcd(y)` is always the same as `y.gcd(x)`. - /// - /// For any integer `x`, `x.gcd(x)` is `x.abs()`. - /// - /// If both `this` and `other` is zero, the result is also zero. - /// - /// Example: - /// ```dart - /// print(Obj(BigInt.from(4)).gcd(BigInt.from(2))); // 2 - /// print(Obj(BigInt.from(8)).gcd(BigInt.from(4))); // 4 - /// print(Obj(BigInt.from(10)).gcd(BigInt.from(12))); // 2 - /// print(Obj(BigInt.from(10)).gcd(BigInt.from(10))); // 10 - /// print(Obj(BigInt.from(-2)).gcd(BigInt.from(-3))); // 1 - /// ``` - BigInt? gcd(BigInt other) => value?.gcd(other); - - /// Returns the least significant [width] bits of this big integer as a - /// non-negative number (i.e. unsigned representation). The returned value has - /// zeros in all bit positions higher than [width]. - /// - /// ```dart - /// Obj(BigInt.from(-1)).toUnsigned(5) == 31 // 11111111 -> 00011111 - /// ``` - /// - /// This operation can be used to simulate arithmetic from low level languages. - /// For example, to increment an 8 bit quantity: - /// - /// ```dart - /// q = (q + 1).toUnsigned(8); - /// ``` - /// - /// `q` will count from `0` up to `255` and then wrap around to `0`. - /// - /// If the input fits in [width] bits without truncation, the result is the - /// same as the input. The minimum width needed to avoid truncation of `x` is - /// given by `x.bitLength`, i.e. - /// - /// ```dart - /// x == x.toUnsigned(x.bitLength); - /// ``` - BigInt? toUnsigned(int width) => value?.toUnsigned(width); - - /// Returns the least significant [width] bits of this integer, extending the - /// highest retained bit to the sign. This is the same as truncating the value - /// to fit in [width] bits using an signed 2-s complement representation. The - /// returned value has the same bit value in all positions higher than [width]. - /// - /// ```dart - /// var big15 = Obj(BigInt.from(15)); - /// var big16 = Obj(BigInt.from(16)); - /// var big239 = Obj(BigInt.from(239)); - /// // V--sign bit-V - /// big16.toSigned(5) == -big16; // 00010000 -> 11110000 - /// big239.toSigned(5) == big15; // 11101111 -> 00001111 - /// // ^ ^ - /// ``` - /// - /// This operation can be used to simulate arithmetic from low level languages. - /// For example, to increment an 8 bit signed quantity: - /// - /// ```dart - /// q = (q + 1).toSigned(8); - /// ``` - /// - /// `q` will count from `0` up to `127`, wrap to `-128` and count back up to - /// `127`. - /// - /// If the input value fits in [width] bits without truncation, the result is - /// the same as the input. The minimum width needed to avoid truncation of `x` - /// is `x.bitLength + 1`, i.e. - /// - /// ```dart - /// x == x.toSigned(x.bitLength + 1); - /// ``` - BigInt? toSigned(int width) => value?.toSigned(width); - - /// Whether this big integer can be represented as an `int` without losing - /// precision. - /// - /// **Warning:** this function may give a different result on - /// dart2js, dev compiler, and the VM, due to the differences in - /// integer precision. - /// - /// Example: - /// ```dart - /// var bigNumber = BigInt.parse('100000000000000000000000'); - /// print(bigNumber.isValidInt); // false - /// - /// var value = BigInt.parse('0xFF'); // 255 - /// print(value.isValidInt); // true - /// ``` - bool? get isValidInt => value?.isValidInt; - - /// Returns this [BigInt] as an [int]. - /// - /// If the number does not fit, clamps to the max (or min) - /// integer. - /// - /// **Warning:** the clamping behaves differently between the web and - /// native platforms due to the differences in integer precision. - /// - /// Example: - /// ```dart - /// var bigNumber = BigInt.parse('100000000000000000000000'); - /// print(bigNumber.isValidInt); // false - /// print(bigNumber.toInt()); // 9223372036854775807 - /// ``` - int? toInt() => value?.toInt(); - - /// Returns this [BigInt] as a [double]. - /// - /// If the number is not representable as a [double], an - /// approximation is returned. For numerically large integers, the - /// approximation may be infinite. - /// - /// Example: - /// ```dart - /// var bigNumber = BigInt.parse('100000000000000000000000'); - /// print(bigNumber.toDouble()); // 1e+23 - /// ``` - double? toDouble() => value?.toDouble(); - - /// Converts [this] to a string representation in the given [radix]. - /// - /// In the string representation, lower-case letters are used for digits above - /// '9', with 'a' being 10 an 'z' being 35. - /// - /// The [radix] argument must be an integer in the range 2 to 36. - /// - /// Example: - /// ```dart - /// // Binary (base 2). - /// print(Obj(BigInt.from(12)).toRadixString(2)); // 1100 - /// print(Obj(BigInt.from(31)).toRadixString(2)); // 11111 - /// print(Obj(BigInt.from(2021)).toRadixString(2)); // 11111100101 - /// print(Obj(BigInt.from(-12)).toRadixString(2)); // -1100 - /// // Octal (base 8). - /// print(Obj(BigInt.from(12)).toRadixString(8)); // 14 - /// print(Obj(BigInt.from(31)).toRadixString(8)); // 37 - /// print(Obj(BigInt.from(2021)).toRadixString(8)); // 3745 - /// // Hexadecimal (base 16). - /// print(Obj(BigInt.from(12)).toRadixString(16)); // c - /// print(Obj(BigInt.from(31)).toRadixString(16)); // 1f - /// print(Obj(BigInt.from(2021)).toRadixString(16)); // 7e5 - /// // Base 36. - /// print(Obj(BigInt.from(35 * 36 + 1)).toRadixString(36)); // z1 - /// ``` - String? toRadixString(int radix) => value?.toRadixString(radix); -} diff --git a/packages/reactter/lib/src/obj/extensions/obj_bool.dart b/packages/reactter/lib/src/obj/extensions/obj_bool.dart deleted file mode 100644 index 03f2eafd..00000000 --- a/packages/reactter/lib/src/obj/extensions/obj_bool.dart +++ /dev/null @@ -1,19 +0,0 @@ -// coverage:ignore-file -part of '../obj.dart'; - -extension ObjBoolExt on Obj { - /// The logical conjunction ("and") of this and [other]. - /// - /// Returns `true` if both this and [other] are `true`, and `false` otherwise. - Obj operator &(Obj other) => Obj(value && other.value); - - /// The logical disjunction ("inclusive or") of this and [other]. - /// - /// Returns `true` if either this or [other] is `true`, and `false` otherwise. - Obj operator |(Obj other) => Obj(value || other.value); - - /// The logical exclusive disjunction ("exclusive or") of this and [other]. - /// - /// Returns whether this and [other] are neither both `true` nor both `false`. - Obj operator ^(Obj other) => Obj(value ^ other.value); -} diff --git a/packages/reactter/lib/src/obj/extensions/obj_date_time.dart b/packages/reactter/lib/src/obj/extensions/obj_date_time.dart deleted file mode 100644 index f6812b4f..00000000 --- a/packages/reactter/lib/src/obj/extensions/obj_date_time.dart +++ /dev/null @@ -1,664 +0,0 @@ -// coverage:ignore-file -part of '../obj.dart'; - -extension ObjDateTimeExt on Obj { - /// Returns true if [this] occurs before [other]. - /// - /// The comparison is independent - /// of whether the time is in UTC or in the local time zone. - /// - /// ```dart - /// final now = DateTime.now(); - /// final earlier = now.subtract(const Duration(seconds: 5)); - /// print(earlier.isBefore(now)); // true - /// print(!now.isBefore(now)); // true - /// - /// // This relation stays the same, even when changing timezones. - /// print(earlier.isBefore(now.toUtc())); // true - /// print(earlier.toUtc().isBefore(now)); // true - /// - /// print(!now.toUtc().isBefore(now)); // true - /// print(!now.isBefore(now.toUtc())); // true - /// ``` - bool isBefore(DateTime other) => value.isBefore(other); - - /// Returns true if [this] occurs after [other]. - /// - /// The comparison is independent - /// of whether the time is in UTC or in the local time zone. - /// - /// ```dart - /// final now = DateTime.now(); - /// final later = now.add(const Duration(seconds: 5)); - /// print(later.isAfter(now)); // true - /// print(!now.isBefore(now)); // true - /// - /// // This relation stays the same, even when changing timezones. - /// print(later.isAfter(now.toUtc())); // true - /// print(later.toUtc().isAfter(now)); // true - /// - /// print(!now.toUtc().isAfter(now)); // true - /// print(!now.isAfter(now.toUtc())); // true - /// ``` - bool isAfter(DateTime other) => value.isAfter(other); - - /// Returns true if [this] occurs at the same moment as [other]. - /// - /// The comparison is independent of whether the time is in UTC or in the local - /// time zone. - /// - /// ```dart - /// final now = DateTime.now(); - /// final later = now.add(const Duration(seconds: 5)); - /// print(!later.isAtSameMomentAs(now)); // true - /// print(now.isAtSameMomentAs(now)); // true - /// - /// // This relation stays the same, even when changing timezones. - /// print(!later.isAtSameMomentAs(now.toUtc())); // true - /// print(!later.toUtc().isAtSameMomentAs(now)); // true - /// - /// print(now.toUtc().isAtSameMomentAs(now)); // true - /// print(now.isAtSameMomentAs(now.toUtc())); // true - /// ``` - bool isAtSameMomentAs(DateTime other) => value.isAtSameMomentAs(other); - - /// Compares this DateTime object to [other], - /// returning zero if the values are equal. - /// - /// A [compareTo] function returns: - /// * a negative value if this DateTime [isBefore] [other]. - /// * `0` if this DateTime [isAtSameMomentAs] [other], and - /// * a positive value otherwise (when this DateTime [isAfter] [other]). - /// - /// ```dart - /// final now = DateTime.now(); - /// final future = now.add(const Duration(days: 2)); - /// final past = now.subtract(const Duration(days: 2)); - /// final newDate = now.toUtc(); - /// - /// print(now.compareTo(future)); // -1 - /// print(now.compareTo(past)); // 1 - /// print(now.compareTo(newDate)); // 0 - /// ``` - int compareTo(DateTime other) => value.compareTo(other); - - /// Returns this DateTime value in the local time zone. - /// - /// Returns [this] if it is already in the local time zone. - /// Otherwise this method is equivalent to: - /// - /// ```dart template:expression - /// DateTime.fromMicrosecondsSinceEpoch(microsecondsSinceEpoch, - /// isUtc: false) - /// ``` - DateTime toLocal() => value.toLocal(); - - /// Returns this DateTime value in the UTC time zone. - /// - /// Returns [this] if it is already in UTC. - /// Otherwise this method is equivalent to: - /// - /// ```dart template:expression - /// DateTime.fromMicrosecondsSinceEpoch(microsecondsSinceEpoch, - /// isUtc: true) - /// ``` - DateTime toUtc() => value.toUtc(); - - /// Returns an ISO-8601 full-precision extended format representation. - /// - /// The format is `yyyy-MM-ddTHH:mm:ss.mmmuuuZ` for UTC time, and - /// `yyyy-MM-ddTHH:mm:ss.mmmuuu` (no trailing "Z") for local/non-UTC time, - /// where: - /// - /// * `yyyy` is a, possibly negative, four digit representation of the year, - /// if the year is in the range -9999 to 9999, - /// otherwise it is a signed six digit representation of the year. - /// * `MM` is the month in the range 01 to 12, - /// * `dd` is the day of the month in the range 01 to 31, - /// * `HH` are hours in the range 00 to 23, - /// * `mm` are minutes in the range 00 to 59, - /// * `ss` are seconds in the range 00 to 59 (no leap seconds), - /// * `mmm` are milliseconds in the range 000 to 999, and - /// * `uuu` are microseconds in the range 001 to 999. If [microsecond] equals - /// 0, then this part is omitted. - /// - /// The resulting string can be parsed back using [parse]. - /// ```dart - /// final moonLanding = DateTime.utc(1969, 7, 20, 20, 18, 04); - /// final isoDate = moonLanding.toIso8601String(); - /// print(isoDate); // 1969-07-20T20:18:04.000Z - /// ``` - String toIso8601String() => value.toIso8601String(); - - /// Returns a new [DateTime] instance with [duration] added to [this]. - /// - /// ```dart - /// final today = DateTime.now(); - /// final fiftyDaysFromNow = today.add(const Duration(days: 50)); - /// ``` - /// - /// Notice that the duration being added is actually 50 * 24 * 60 * 60 - /// seconds. If the resulting `DateTime` has a different daylight saving offset - /// than `this`, then the result won't have the same time-of-day as `this`, and - /// may not even hit the calendar date 50 days later. - /// - /// Be careful when working with dates in local time. - DateTime add(Duration duration) => value.add(duration); - - /// Returns a new [DateTime] instance with [duration] subtracted from [this]. - /// - /// ```dart - /// final today = DateTime.now(); - /// final fiftyDaysAgo = today.subtract(const Duration(days: 50)); - /// ``` - /// - /// Notice that the duration being subtracted is actually 50 * 24 * 60 * 60 - /// seconds. If the resulting `DateTime` has a different daylight saving offset - /// than `this`, then the result won't have the same time-of-day as `this`, and - /// may not even hit the calendar date 50 days earlier. - /// - /// Be careful when working with dates in local time. - DateTime subtract(Duration duration) => value.subtract(duration); - - /// Returns a [Duration] with the difference when subtracting [other] from - /// [this]. - /// - /// The returned [Duration] will be negative if [other] occurs after [this]. - /// - /// ```dart - /// final berlinWallFell = DateTime.utc(1989, DateTime.november, 9); - /// final dDay = DateTime.utc(1944, DateTime.june, 6); - /// - /// final difference = berlinWallFell.difference(dDay); - /// print(difference.inDays); // 16592 - /// ``` - /// - /// The difference is measured in seconds and fractions of seconds. - /// The difference above counts the number of fractional seconds between - /// midnight at the beginning of those dates. - /// If the dates above had been in local time, not UTC, then the difference - /// between two midnights may not be a multiple of 24 hours due to daylight - /// saving differences. - /// - /// For example, in Australia, similar code using local time instead of UTC: - /// - /// ```dart - /// final berlinWallFell = DateTime(1989, DateTime.november, 9); - /// final dDay = DateTime(1944, DateTime.june, 6); - /// final difference = berlinWallFell.difference(dDay); - /// print(difference.inDays); // 16591 - /// assert(difference.inDays == 16592); - /// ``` - /// will fail because the difference is actually 16591 days and 23 hours, and - /// [Duration.inDays] only returns the number of whole days. - Duration difference(DateTime other) => value.difference(other); - - /// The number of milliseconds since - /// the "Unix epoch" 1970-01-01T00:00:00Z (UTC). - /// - /// This value is independent of the time zone. - /// - /// This value is at most - /// 8,640,000,000,000,000ms (100,000,000 days) from the Unix epoch. - /// In other words: `millisecondsSinceEpoch.abs() <= 8640000000000000`. - int get millisecondsSinceEpoch => value.millisecondsSinceEpoch; - - /// The number of microseconds since - /// the "Unix epoch" 1970-01-01T00:00:00Z (UTC). - /// - /// This value is independent of the time zone. - /// - /// This value is at most - /// 8,640,000,000,000,000,000us (100,000,000 days) from the Unix epoch. - /// In other words: `microsecondsSinceEpoch.abs() <= 8640000000000000000`. - /// - /// Note that this value does not fit into 53 bits (the size of a IEEE double). - /// A JavaScript number is not able to hold this value. - int get microsecondsSinceEpoch => value.microsecondsSinceEpoch; - - /// The time zone name. - /// - /// This value is provided by the operating system and may be an - /// abbreviation or a full name. - /// - /// In the browser or on Unix-like systems commonly returns abbreviations, - /// such as "CET" or "CEST". On Windows returns the full name, for example - /// "Pacific Standard Time". - String get timeZoneName => value.timeZoneName; - - /// The time zone offset, which - /// is the difference between local time and UTC. - /// - /// The offset is positive for time zones east of UTC. - /// - /// Note, that JavaScript, Python and C return the difference between UTC and - /// local time. Java, C# and Ruby return the difference between local time and - /// UTC. - /// - /// For example, using local time in San Francisco, United States: - /// ```dart - /// final dateUS = DateTime.parse('2021-11-01 20:18:04Z').toLocal(); - /// print(dateUS); // 2021-11-01 13:18:04.000 - /// print(dateUS.timeZoneName); // PDT ( Pacific Daylight Time ) - /// print(dateUS.timeZoneOffset.inHours); // -7 - /// print(dateUS.timeZoneOffset.inMinutes); // -420 - /// ``` - /// - /// For example, using local time in Canberra, Australia: - /// ```dart - /// final dateAus = DateTime.parse('2021-11-01 20:18:04Z').toLocal(); - /// print(dateAus); // 2021-11-02 07:18:04.000 - /// print(dateAus.timeZoneName); // AEDT ( Australian Eastern Daylight Time ) - /// print(dateAus.timeZoneOffset.inHours); // 11 - /// print(dateAus.timeZoneOffset.inMinutes); // 660 - /// ``` - Duration get timeZoneOffset => value.timeZoneOffset; - - /// The year. - /// - /// ```dart - /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); - /// print(moonLanding.year); // 1969 - /// ``` - int get year => value.year; - - /// The month `[1..12]`. - /// - /// ```dart - /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); - /// print(moonLanding.month); // 7 - /// assert(moonLanding.month == DateTime.july); - /// ``` - int get month => value.month; - - /// The day of the month `[1..31]`. - /// - /// ```dart - /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); - /// print(moonLanding.day); // 20 - /// ``` - int get day => value.day; - - /// The hour of the day, expressed as in a 24-hour clock `[0..23]`. - /// - /// ```dart - /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); - /// print(moonLanding.hour); // 20 - /// ``` - int get hour => value.hour; - - /// The minute `[0...59]`. - /// - /// ```dart - /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); - /// print(moonLanding.minute); // 18 - /// ``` - int get minute => value.minute; - - /// The second `[0...59]`. - /// - /// ```dart - /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); - /// print(moonLanding.second); // 4 - /// ``` - int get second => value.second; - - /// The millisecond `[0...999]`. - /// - /// ```dart - /// final date = DateTime.parse('1970-01-01 05:01:01.234567Z'); - /// print(date.millisecond); // 234 - /// ``` - int get millisecond => value.microsecond; - - /// The microsecond `[0...999]`. - /// - /// ```dart - /// final date = DateTime.parse('1970-01-01 05:01:01.234567Z'); - /// print(date.microsecond); // 567 - /// ``` - int get microsecond => value.microsecond; - - /// The day of the week [monday]..[sunday]. - /// - /// In accordance with ISO 8601 - /// a week starts with Monday, which has the value 1. - /// - /// ```dart - /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); - /// print(moonLanding.weekday); // 7 - /// assert(moonLanding.weekday == DateTime.sunday); - /// ``` - int get weekday => value.weekday; -} - -extension ObjDateTimeNullExt on Obj { - /// Returns true if [this] occurs before [other]. - /// - /// The comparison is independent - /// of whether the time is in UTC or in the local time zone. - /// - /// ```dart - /// final now = DateTime.now(); - /// final earlier = now.subtract(const Duration(seconds: 5)); - /// print(earlier.isBefore(now)); // true - /// print(!now.isBefore(now)); // true - /// - /// // This relation stays the same, even when changing timezones. - /// print(earlier.isBefore(now.toUtc())); // true - /// print(earlier.toUtc().isBefore(now)); // true - /// - /// print(!now.toUtc().isBefore(now)); // true - /// print(!now.isBefore(now.toUtc())); // true - /// ``` - bool? isBefore(DateTime other) => value?.isBefore(other); - - /// Returns true if [this] occurs after [other]. - /// - /// The comparison is independent - /// of whether the time is in UTC or in the local time zone. - /// - /// ```dart - /// final now = DateTime.now(); - /// final later = now.add(const Duration(seconds: 5)); - /// print(later.isAfter(now)); // true - /// print(!now.isBefore(now)); // true - /// - /// // This relation stays the same, even when changing timezones. - /// print(later.isAfter(now.toUtc())); // true - /// print(later.toUtc().isAfter(now)); // true - /// - /// print(!now.toUtc().isAfter(now)); // true - /// print(!now.isAfter(now.toUtc())); // true - /// ``` - bool? isAfter(DateTime other) => value?.isAfter(other); - - /// Returns true if [this] occurs at the same moment as [other]. - /// - /// The comparison is independent of whether the time is in UTC or in the local - /// time zone. - /// - /// ```dart - /// final now = DateTime.now(); - /// final later = now.add(const Duration(seconds: 5)); - /// print(!later.isAtSameMomentAs(now)); // true - /// print(now.isAtSameMomentAs(now)); // true - /// - /// // This relation stays the same, even when changing timezones. - /// print(!later.isAtSameMomentAs(now.toUtc())); // true - /// print(!later.toUtc().isAtSameMomentAs(now)); // true - /// - /// print(now.toUtc().isAtSameMomentAs(now)); // true - /// print(now.isAtSameMomentAs(now.toUtc())); // true - /// ``` - bool? isAtSameMomentAs(DateTime other) => value?.isAtSameMomentAs(other); - - /// Compares this DateTime object to [other], - /// returning zero if the values are equal. - /// - /// A [compareTo] function returns: - /// * a negative value if this DateTime [isBefore] [other]. - /// * `0` if this DateTime [isAtSameMomentAs] [other], and - /// * a positive value otherwise (when this DateTime [isAfter] [other]). - /// - /// ```dart - /// final now = DateTime.now(); - /// final future = now.add(const Duration(days: 2)); - /// final past = now.subtract(const Duration(days: 2)); - /// final newDate = now.toUtc(); - /// - /// print(now.compareTo(future)); // -1 - /// print(now.compareTo(past)); // 1 - /// print(now.compareTo(newDate)); // 0 - /// ``` - int? compareTo(DateTime other) => value?.compareTo(other); - - /// Returns this DateTime value in the local time zone. - /// - /// Returns [this] if it is already in the local time zone. - /// Otherwise this method is equivalent to: - /// - /// ```dart template:expression - /// DateTime.fromMicrosecondsSinceEpoch(microsecondsSinceEpoch, - /// isUtc: false) - /// ``` - DateTime? toLocal() => value?.toLocal(); - - /// Returns this DateTime value in the UTC time zone. - /// - /// Returns [this] if it is already in UTC. - /// Otherwise this method is equivalent to: - /// - /// ```dart template:expression - /// DateTime.fromMicrosecondsSinceEpoch(microsecondsSinceEpoch, - /// isUtc: true) - /// ``` - DateTime? toUtc() => value?.toUtc(); - - /// Returns an ISO-8601 full-precision extended format representation. - /// - /// The format is `yyyy-MM-ddTHH:mm:ss.mmmuuuZ` for UTC time, and - /// `yyyy-MM-ddTHH:mm:ss.mmmuuu` (no trailing "Z") for local/non-UTC time, - /// where: - /// - /// * `yyyy` is a, possibly negative, four digit representation of the year, - /// if the year is in the range -9999 to 9999, - /// otherwise it is a signed six digit representation of the year. - /// * `MM` is the month in the range 01 to 12, - /// * `dd` is the day of the month in the range 01 to 31, - /// * `HH` are hours in the range 00 to 23, - /// * `mm` are minutes in the range 00 to 59, - /// * `ss` are seconds in the range 00 to 59 (no leap seconds), - /// * `mmm` are milliseconds in the range 000 to 999, and - /// * `uuu` are microseconds in the range 001 to 999. If [microsecond] equals - /// 0, then this part is omitted. - /// - /// The resulting string can be parsed back using [parse]. - /// ```dart - /// final moonLanding = DateTime.utc(1969, 7, 20, 20, 18, 04); - /// final isoDate = moonLanding.toIso8601String(); - /// print(isoDate); // 1969-07-20T20:18:04.000Z - /// ``` - String? toIso8601String() => value?.toIso8601String(); - - /// Returns a new [DateTime] instance with [duration] added to [this]. - /// - /// ```dart - /// final today = DateTime.now(); - /// final fiftyDaysFromNow = today.add(const Duration(days: 50)); - /// ``` - /// - /// Notice that the duration being added is actually 50 * 24 * 60 * 60 - /// seconds. If the resulting `DateTime` has a different daylight saving offset - /// than `this`, then the result won't have the same time-of-day as `this`, and - /// may not even hit the calendar date 50 days later. - /// - /// Be careful when working with dates in local time. - DateTime? add(Duration duration) => value?.add(duration); - - /// Returns a new [DateTime] instance with [duration] subtracted from [this]. - /// - /// ```dart - /// final today = DateTime.now(); - /// final fiftyDaysAgo = today.subtract(const Duration(days: 50)); - /// ``` - /// - /// Notice that the duration being subtracted is actually 50 * 24 * 60 * 60 - /// seconds. If the resulting `DateTime` has a different daylight saving offset - /// than `this`, then the result won't have the same time-of-day as `this`, and - /// may not even hit the calendar date 50 days earlier. - /// - /// Be careful when working with dates in local time. - DateTime? subtract(Duration duration) => value?.subtract(duration); - - /// Returns a [Duration] with the difference when subtracting [other] from - /// [this]. - /// - /// The returned [Duration] will be negative if [other] occurs after [this]. - /// - /// ```dart - /// final berlinWallFell = DateTime.utc(1989, DateTime.november, 9); - /// final dDay = DateTime.utc(1944, DateTime.june, 6); - /// - /// final difference = berlinWallFell.difference(dDay); - /// print(difference.inDays); // 16592 - /// ``` - /// - /// The difference is measured in seconds and fractions of seconds. - /// The difference above counts the number of fractional seconds between - /// midnight at the beginning of those dates. - /// If the dates above had been in local time, not UTC, then the difference - /// between two midnights may not be a multiple of 24 hours due to daylight - /// saving differences. - /// - /// For example, in Australia, similar code using local time instead of UTC: - /// - /// ```dart - /// final berlinWallFell = DateTime(1989, DateTime.november, 9); - /// final dDay = DateTime(1944, DateTime.june, 6); - /// final difference = berlinWallFell.difference(dDay); - /// print(difference.inDays); // 16591 - /// assert(difference.inDays == 16592); - /// ``` - /// will fail because the difference is actually 16591 days and 23 hours, and - /// [Duration.inDays] only returns the number of whole days. - Duration? difference(DateTime other) => value?.difference(other); - - /// The number of milliseconds since - /// the "Unix epoch" 1970-01-01T00:00:00Z (UTC). - /// - /// This value is independent of the time zone. - /// - /// This value is at most - /// 8,640,000,000,000,000ms (100,000,000 days) from the Unix epoch. - /// In other words: `millisecondsSinceEpoch.abs() <= 8640000000000000`. - int? get millisecondsSinceEpoch => value?.millisecondsSinceEpoch; - - /// The number of microseconds since - /// the "Unix epoch" 1970-01-01T00:00:00Z (UTC). - /// - /// This value is independent of the time zone. - /// - /// This value is at most - /// 8,640,000,000,000,000,000us (100,000,000 days) from the Unix epoch. - /// In other words: `microsecondsSinceEpoch.abs() <= 8640000000000000000`. - /// - /// Note that this value does not fit into 53 bits (the size of a IEEE double). - /// A JavaScript number is not able to hold this value. - int? get microsecondsSinceEpoch => value?.microsecondsSinceEpoch; - - /// The time zone name. - /// - /// This value is provided by the operating system and may be an - /// abbreviation or a full name. - /// - /// In the browser or on Unix-like systems commonly returns abbreviations, - /// such as "CET" or "CEST". On Windows returns the full name, for example - /// "Pacific Standard Time". - String? get timeZoneName => value?.timeZoneName; - - /// The time zone offset, which - /// is the difference between local time and UTC. - /// - /// The offset is positive for time zones east of UTC. - /// - /// Note, that JavaScript, Python and C return the difference between UTC and - /// local time. Java, C# and Ruby return the difference between local time and - /// UTC. - /// - /// For example, using local time in San Francisco, United States: - /// ```dart - /// final dateUS = DateTime.parse('2021-11-01 20:18:04Z').toLocal(); - /// print(dateUS); // 2021-11-01 13:18:04.000 - /// print(dateUS.timeZoneName); // PDT ( Pacific Daylight Time ) - /// print(dateUS.timeZoneOffset.inHours); // -7 - /// print(dateUS.timeZoneOffset.inMinutes); // -420 - /// ``` - /// - /// For example, using local time in Canberra, Australia: - /// ```dart - /// final dateAus = DateTime.parse('2021-11-01 20:18:04Z').toLocal(); - /// print(dateAus); // 2021-11-02 07:18:04.000 - /// print(dateAus.timeZoneName); // AEDT ( Australian Eastern Daylight Time ) - /// print(dateAus.timeZoneOffset.inHours); // 11 - /// print(dateAus.timeZoneOffset.inMinutes); // 660 - /// ``` - Duration? get timeZoneOffset => value?.timeZoneOffset; - - /// The year. - /// - /// ```dart - /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); - /// print(moonLanding.year); // 1969 - /// ``` - int? get year => value?.year; - - /// The month `[1..12]`. - /// - /// ```dart - /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); - /// print(moonLanding.month); // 7 - /// assert(moonLanding.month == DateTime.july); - /// ``` - int? get month => value?.month; - - /// The day of the month `[1..31]`. - /// - /// ```dart - /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); - /// print(moonLanding.day); // 20 - /// ``` - int? get day => value?.day; - - /// The hour of the day, expressed as in a 24-hour clock `[0..23]`. - /// - /// ```dart - /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); - /// print(moonLanding.hour); // 20 - /// ``` - int? get hour => value?.hour; - - /// The minute `[0...59]`. - /// - /// ```dart - /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); - /// print(moonLanding.minute); // 18 - /// ``` - int? get minute => value?.minute; - - /// The second `[0...59]`. - /// - /// ```dart - /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); - /// print(moonLanding.second); // 4 - /// ``` - int? get second => value?.second; - - /// The millisecond `[0...999]`. - /// - /// ```dart - /// final date = DateTime.parse('1970-01-01 05:01:01.234567Z'); - /// print(date.millisecond); // 234 - /// ``` - int? get millisecond => value?.microsecond; - - /// The microsecond `[0...999]`. - /// - /// ```dart - /// final date = DateTime.parse('1970-01-01 05:01:01.234567Z'); - /// print(date.microsecond); // 567 - /// ``` - int? get microsecond => value?.microsecond; - - /// The day of the week [monday]..[sunday]. - /// - /// In accordance with ISO 8601 - /// a week starts with Monday, which has the value 1. - /// - /// ```dart - /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); - /// print(moonLanding.weekday); // 7 - /// assert(moonLanding.weekday == DateTime.sunday); - /// ``` - int? get weekday => value?.weekday; -} diff --git a/packages/reactter/lib/src/obj/extensions/obj_double.dart b/packages/reactter/lib/src/obj/extensions/obj_double.dart deleted file mode 100644 index 4a060856..00000000 --- a/packages/reactter/lib/src/obj/extensions/obj_double.dart +++ /dev/null @@ -1,334 +0,0 @@ -// coverage:ignore-file -part of '../obj.dart'; - -extension ObjDoubleExt on Obj { - Obj operator +(Obj other) => Obj(value + other.value); - - Obj operator -(Obj other) => Obj(value - other.value); - - Obj operator *(Obj other) => Obj(value * other.value); - - Obj operator %(Obj other) => Obj(value % other.value); - - Obj operator /(Obj other) => Obj(value / other.value); - - Obj operator ~/(Obj other) => Obj(value ~/ other.value); - - Obj operator -() => Obj(-value); - - double remainder(num other) => value.remainder(other); - - /// Returns the absolute value of this integer. - /// - /// For any integer `value`, - /// the result is the same as `value < 0 ? -value : value`. - /// - /// Integer overflow may cause the result of `-value` to stay negative. - double abs() => value.abs(); - - /// The sign of the double's numerical value. - /// - /// Returns -1.0 if the value is less than zero, - /// +1.0 if the value is greater than zero, - /// and the value itself if it is -0.0, 0.0 or NaN. - double get sign => value.sign; - - /// Returns the integer closest to this number. - /// - /// Rounds away from zero when there is no closest integer: - /// `Obj(3.5).round() == 4` and `Obj(-3.5).round() == -4`. - /// - /// Throws an [UnsupportedError] if this number is not finite - /// (NaN or an infinity). - /// ```dart - /// print(Obj(3.0).round()); // 3 - /// print(Obj(3.25).round()); // 3 - /// print(Obj(3.5).round()); // 4 - /// print(Obj(3.75).round()); // 4 - /// print(Obj(-3.5).round()); // -4 - /// ``` - int round() => value.round(); - - /// Returns the greatest integer no greater than this number. - /// - /// Rounds the number towards negative infinity. - /// - /// Throws an [UnsupportedError] if this number is not finite - /// (NaN or infinity). - /// ```dart - /// print(Obj(1.99999).floor()); // 1 - /// print(Obj(2.0).floor()); // 2 - /// print(Obj(2.99999).floor()); // 2 - /// print(Obj(-1.99999).floor()); // -2 - /// print(Obj(-2.0).floor()); // -2 - /// print(Obj(-2.00001).floor()); // -3 - /// ``` - int floor() => value.floor(); - - /// Returns the least integer that is not smaller than this number. - /// - /// Rounds the number towards infinity. - /// - /// Throws an [UnsupportedError] if this number is not finite - /// (NaN or an infinity). - /// ```dart - /// print(Obj(1.99999).ceil()); // 2 - /// print(Obj(2.0).ceil()); // 2 - /// print(Obj(2.00001).ceil()); // 3 - /// print(Obj(-1.99999).ceil()); // -1 - /// print(Obj(-2.0).ceil()); // -2 - /// print(Obj(-2.00001).ceil()); // -2 - /// ``` - int ceil() => value.ceil(); - - /// Returns the integer obtained by discarding any fractional - /// part of this number. - /// - /// Rounds the number towards zero. - /// - /// Throws an [UnsupportedError] if this number is not finite - /// (NaN or an infinity). - /// ```dart - /// print(Obj(2.00001).truncate()); // 2 - /// print(Obj(1.99999).truncate()); // 1 - /// print(Obj(0.5).truncate()); // 0 - /// print(Obj(-0.5).truncate()); // 0 - /// print(Obj(-1.5).truncate()); // -1 - /// print(Obj(-2.5).truncate()); // -2 - /// ``` - int truncate() => value.truncate(); - - /// Returns the integer double value closest to `this`. - /// - /// Rounds away from zero when there is no closest integer: - /// `Obj(3.5).roundToDouble() == 4` and `Obj(-3.5).roundToDouble() == -4`. - /// - /// If this is already an integer valued double, including `-0.0`, or it is not - /// a finite value, the value is returned unmodified. - /// - /// For the purpose of rounding, `-0.0` is considered to be below `0.0`, - /// and `-0.0` is therefore considered closer to negative numbers than `0.0`. - /// This means that for a value `d` in the range `-0.5 < d < 0.0`, - /// the result is `-0.0`. - /// ```dart - /// print(Obj(3.0).roundToDouble()); // 3.0 - /// print(Obj(3.25).roundToDouble()); // 3.0 - /// print(Obj(3.5).roundToDouble()); // 4.0 - /// print(Obj(3.75).roundToDouble()); // 4.0 - /// print(Obj(-3.5).roundToDouble()); // -4.0 - /// ``` - double roundToDouble() => value.roundToDouble(); - - /// Returns the greatest integer double value no greater than `this`. - /// - /// If this is already an integer valued double, including `-0.0`, or it is not - /// a finite value, the value is returned unmodified. - /// - /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. - /// A number `d` in the range `0.0 < d < 1.0` will return `0.0`. - /// ```dart - /// print(Obj(1.99999).floorToDouble()); // 1.0 - /// print(Obj(2.0).floorToDouble()); // 2.0 - /// print(Obj(2.99999).floorToDouble()); // 2.0 - /// print(Obj(-1.99999).floorToDouble()); // -2.0 - /// print(Obj(-2.0).floorToDouble()); // -2.0 - /// print(Obj(-2.00001).floorToDouble()); // -3.0 - /// ``` - double floorToDouble() => value.floorToDouble(); - - /// Returns the least integer double value no smaller than `this`. - /// - /// If this is already an integer valued double, including `-0.0`, or it is not - /// a finite value, the value is returned unmodified. - /// - /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. - /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`. - /// ```dart - /// print(Obj(1.99999).ceilToDouble()); // 2.0 - /// print(Obj(2.0).ceilToDouble()); // 2.0 - /// print(Obj(2.00001).ceilToDouble()); // 3.0 - /// print(Obj(-1.99999).ceilToDouble()); // -1.0 - /// print(Obj(-2.0).ceilToDouble()); // -2.0 - /// print(Obj(-2.00001).ceilToDouble()); // -2.0 - /// ``` - double ceilToDouble() => value.ceilToDouble(); - - /// Returns the integer double value obtained by discarding any fractional - /// digits from `this`. - /// - /// If this is already an integer valued double, including `-0.0`, or it is not - /// a finite value, the value is returned unmodified. - /// - /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. - /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`, and - /// in the range `0.0 < d < 1.0` it will return 0.0. - /// ```dart - /// print(Obj(2.5).truncateToDouble()); // 2.0 - /// print(Obj(2.00001).truncateToDouble()); // 2.0 - /// print(Obj(1.99999).truncateToDouble()); // 1.0 - /// print(Obj(0.5).truncateToDouble()); // 0.0 - /// print(Obj(-0.5).truncateToDouble()); // -0.0 - /// print(Obj(-1.5).truncateToDouble()); // -1.0 - /// print(Obj(-2.5).truncateToDouble()); // -2.0 - /// ``` - double truncateToDouble() => value.truncateToDouble(); -} - -extension ObjDoubleNullExt on Obj { - double? remainder(num other) => value?.remainder(other); - - /// Returns the absolute value of this integer. - /// - /// For any integer `value`, - /// the result is the same as `value < 0 ? -value : value`. - /// - /// Integer overflow may cause the result of `-value` to stay negative. - double? abs() => value?.abs(); - - /// The sign of the double's numerical value. - /// - /// Returns -1.0 if the value is less than zero, - /// +1.0 if the value is greater than zero, - /// and the value itself if it is -0.0, 0.0 or NaN. - double? get sign => value?.sign; - - /// Returns the integer closest to this number. - /// - /// Rounds away from zero when there is no closest integer: - /// `Obj(3.5).round() == 4` and `Obj(-3.5).round() == -4`. - /// - /// Throws an [UnsupportedError] if this number is not finite - /// (NaN or an infinity). - /// ```dart - /// print(Obj(3.0).round()); // 3 - /// print(Obj(3.25).round()); // 3 - /// print(Obj(3.5).round()); // 4 - /// print(Obj(3.75).round()); // 4 - /// print(Obj(-3.5).round()); // -4 - /// ``` - int? round() => value?.round(); - - /// Returns the greatest integer no greater than this number. - /// - /// Rounds the number towards negative infinity. - /// - /// Throws an [UnsupportedError] if this number is not finite - /// (NaN or infinity). - /// ```dart - /// print(Obj(1.99999).floor()); // 1 - /// print(Obj(2.0).floor()); // 2 - /// print(Obj(2.99999).floor()); // 2 - /// print(Obj(-1.99999).floor()); // -2 - /// print(Obj(-2.0).floor()); // -2 - /// print(Obj(-2.00001).floor()); // -3 - /// ``` - int? floor() => value?.floor(); - - /// Returns the least integer that is not smaller than this number. - /// - /// Rounds the number towards infinity. - /// - /// Throws an [UnsupportedError] if this number is not finite - /// (NaN or an infinity). - /// ```dart - /// print(Obj(1.99999).ceil()); // 2 - /// print(Obj(2.0).ceil()); // 2 - /// print(Obj(2.00001).ceil()); // 3 - /// print(Obj(-1.99999).ceil()); // -1 - /// print(Obj(-2.0).ceil()); // -2 - /// print(Obj(-2.00001).ceil()); // -2 - /// ``` - int? ceil() => value?.ceil(); - - /// Returns the integer obtained by discarding any fractional - /// part of this number. - /// - /// Rounds the number towards zero. - /// - /// Throws an [UnsupportedError] if this number is not finite - /// (NaN or an infinity). - /// ```dart - /// print(Obj(2.00001).truncate()); // 2 - /// print(Obj(1.99999).truncate()); // 1 - /// print(Obj(0.5).truncate()); // 0 - /// print(Obj(-0.5).truncate()); // 0 - /// print(Obj(-1.5).truncate()); // -1 - /// print(Obj(-2.5).truncate()); // -2 - /// ``` - int? truncate() => value?.truncate(); - - /// Returns the integer double value closest to `this`. - /// - /// Rounds away from zero when there is no closest integer: - /// `Obj(3.5).roundToDouble() == 4` and `Obj(-3.5).roundToDouble() == -4`. - /// - /// If this is already an integer valued double, including `-0.0`, or it is not - /// a finite value, the value is returned unmodified. - /// - /// For the purpose of rounding, `-0.0` is considered to be below `0.0`, - /// and `-0.0` is therefore considered closer to negative numbers than `0.0`. - /// This means that for a value `d` in the range `-0.5 < d < 0.0`, - /// the result is `-0.0`. - /// ```dart - /// print(Obj(3.0).roundToDouble()); // 3.0 - /// print(Obj(3.25).roundToDouble()); // 3.0 - /// print(Obj(3.5).roundToDouble()); // 4.0 - /// print(Obj(3.75).roundToDouble()); // 4.0 - /// print(Obj(-3.5).roundToDouble()); // -4.0 - /// ``` - double? roundToDouble() => value?.roundToDouble(); - - /// Returns the greatest integer double value no greater than `this`. - /// - /// If this is already an integer valued double, including `-0.0`, or it is not - /// a finite value, the value is returned unmodified. - /// - /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. - /// A number `d` in the range `0.0 < d < 1.0` will return `0.0`. - /// ```dart - /// print(Obj(1.99999).floorToDouble()); // 1.0 - /// print(Obj(2.0).floorToDouble()); // 2.0 - /// print(Obj(2.99999).floorToDouble()); // 2.0 - /// print(Obj(-1.99999).floorToDouble()); // -2.0 - /// print(Obj(-2.0).floorToDouble()); // -2.0 - /// print(Obj(-2.00001).floorToDouble()); // -3.0 - /// ``` - double? floorToDouble() => value?.floorToDouble(); - - /// Returns the least integer double value no smaller than `this`. - /// - /// If this is already an integer valued double, including `-0.0`, or it is not - /// a finite value, the value is returned unmodified. - /// - /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. - /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`. - /// ```dart - /// print(Obj(1.99999).ceilToDouble()); // 2.0 - /// print(Obj(2.0).ceilToDouble()); // 2.0 - /// print(Obj(2.00001).ceilToDouble()); // 3.0 - /// print(Obj(-1.99999).ceilToDouble()); // -1.0 - /// print(Obj(-2.0).ceilToDouble()); // -2.0 - /// print(Obj(-2.00001).ceilToDouble()); // -2.0 - /// ``` - double? ceilToDouble() => value?.ceilToDouble(); - - /// Returns the integer double value obtained by discarding any fractional - /// digits from `this`. - /// - /// If this is already an integer valued double, including `-0.0`, or it is not - /// a finite value, the value is returned unmodified. - /// - /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. - /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`, and - /// in the range `0.0 < d < 1.0` it will return 0.0. - /// ```dart - /// print(Obj(2.5).truncateToDouble()); // 2.0 - /// print(Obj(2.00001).truncateToDouble()); // 2.0 - /// print(Obj(1.99999).truncateToDouble()); // 1.0 - /// print(Obj(0.5).truncateToDouble()); // 0.0 - /// print(Obj(-0.5).truncateToDouble()); // -0.0 - /// print(Obj(-1.5).truncateToDouble()); // -1.0 - /// print(Obj(-2.5).truncateToDouble()); // -2.0 - /// ``` - double? truncateToDouble() => value?.truncateToDouble(); -} diff --git a/packages/reactter/lib/src/obj/extensions/obj_int.dart b/packages/reactter/lib/src/obj/extensions/obj_int.dart deleted file mode 100644 index 2312d23f..00000000 --- a/packages/reactter/lib/src/obj/extensions/obj_int.dart +++ /dev/null @@ -1,477 +0,0 @@ -// coverage:ignore-file -part of '../obj.dart'; - -extension ObjIntExt on Obj { - /// Bit-wise and operator. - /// - /// Treating both `this` and [other] as sufficiently large two's component - /// integers, the result is a number with only the bits set that are set in - /// both `this` and [other] - /// - /// If both operands are negative, the result is negative, otherwise - /// the result is non-negative. - /// ```dart - /// print((Obj(2) & Obj(1)).toRadixString(2)); // 0010 & 0001 -> 0000 - /// print((Obj(3) & Obj(1)).toRadixString(2)); // 0011 & 0001 -> 0001 - /// print((Obj(10) & Obj(2)).toRadixString(2)); // 1010 & 0010 -> 0010 - /// ``` - Obj operator &(Obj other) => Obj(value & other.value); - - /// Bit-wise or operator. - /// - /// Treating both `this` and [other] as sufficiently large two's component - /// integers, the result is a number with the bits set that are set in either - /// of `this` and [other] - /// - /// If both operands are non-negative, the result is non-negative, - /// otherwise the result is negative. - /// - /// Example: - /// ```dart - /// print((Obj(2) | Obj(1)).toRadixString(2)); // 0010 | 0001 -> 0011 - /// print((Obj(3) | Obj(1)).toRadixString(2)); // 0011 | 0001 -> 0011 - /// print((Obj(10) | Obj(2)).toRadixString(2)); // 1010 | 0010 -> 1010 - /// ``` - Obj operator |(Obj other) => Obj(value | other.value); - - /// Bit-wise exclusive-or operator. - /// - /// Treating both `this` and [other] as sufficiently large two's component - /// integers, the result is a number with the bits set that are set in one, - /// but not both, of `this` and [other] - /// - /// If the operands have the same sign, the result is non-negative, - /// otherwise the result is negative. - /// - /// Example: - /// ```dart - /// print((Obj(2) ^ Obj(1)).toRadixString(2)); // 0010 ^ 0001 -> 0011 - /// print((Obj(3) ^ Obj(1)).toRadixString(2)); // 0011 ^ 0001 -> 0010 - /// print((Obj(10) ^ Obj(2)).toRadixString(2)); // 1010 ^ 0010 -> 1000 - /// ``` - Obj operator ^(Obj other) => Obj(value ^ other.value); - - /// The bit-wise negate operator. - /// - /// Treating `this` as a sufficiently large two's component integer, - /// the result is a number with the opposite bits set. - /// - /// This maps any integer `x` to `-x - 1`. - Obj operator ~() => Obj(~value); - - /// Shift the bits of this integer to the left by [shiftAmount]. - /// - /// Shifting to the left makes the number larger, effectively multiplying - /// the number by `pow(2, shiftIndex)`. - /// - /// There is no limit on the size of the result. It may be relevant to - /// limit intermediate values by using the "and" operator with a suitable - /// mask. - /// - /// It is an error if [shiftAmount] is negative. - /// - /// Example: - /// ```dart - /// print((Obj(3) << Obj(1)).toRadixString(2)); // 0011 -> 0110 - /// print((Obj(9) << Obj(2)).toRadixString(2)); // 1001 -> 100100 - /// print((Obj(10) << Obj(3)).toRadixString(2)); // 1010 -> 1010000 - /// ``` - Obj operator <<(Obj shiftAmount) => Obj(value << shiftAmount.value); - - /// Shift the bits of this integer to the right by [shiftAmount]. - /// - /// Shifting to the right makes the number smaller and drops the least - /// significant bits, effectively doing an integer division by - /// `pow(2, shiftIndex)`. - /// - /// It is an error if [shiftAmount] is negative. - /// - /// Example: - /// ```dart - /// print((Obj(3) >> Obj(1)).toRadixString(2)); // 0011 -> 0001 - /// print((Obj(9) >> Obj(2)).toRadixString(2)); // 1001 -> 0010 - /// print((Obj(10) >> Obj(3)).toRadixString(2)); // 1010 -> 0001 - /// print((Obj(-6) >> Obj(2)).toRadixString); // 111...1010 -> 111...1110 == -2 - /// print((Obj(-85) >> Obj(3)).toRadixString); // 111...10101011 -> 111...11110101 == -11 - /// ``` - Obj operator >>(Obj shiftAmount) => Obj(value >> shiftAmount.value); - - /// Bitwise unsigned right shift by [shiftAmount] bits. - /// - /// The least significant [shiftAmount] bits are dropped, - /// the remaining bits (if any) are shifted down, - /// and zero-bits are shifted in as the new most significant bits. - /// - /// The [shiftAmount] must be non-negative. - /// - /// Example: - /// ```dart - /// print((Obj(3) >>> Obj(1)).toRadixString(2)); // 0011 -> 0001 - /// print((Obj(9) >>> Obj(2)).toRadixString(2)); // 1001 -> 0010 - /// print((Obj(-9) >>> Obj(2)).toRadixString(2)); // 111...1011 -> 001...1110 (> 0) - /// ``` - Obj operator >>>(Obj shiftAmount) => - Obj(value >>> shiftAmount.value); - - /// Return the negative value of this integer. - /// - /// The result of negating an integer always has the opposite sign, except - /// for zero, which is its own negation. - Obj operator -() => Obj(-value); - - /// Returns this integer to the power of [exponent] modulo [modulus]. - /// - /// The [exponent] must be non-negative and [modulus] must be - /// positive. - int modPow(int exponent, int modulus) => value.modPow(exponent, modulus); - - /// Returns the modular multiplicative inverse of this integer - /// modulo [modulus]. - /// - /// The [modulus] must be positive. - /// - /// It is an error if no modular inverse exists. - int modInverse(int modulus) => value.modInverse(modulus); - - /// Returns the greatest common divisor of this integer and [other]. - /// - /// If either number is non-zero, the result is the numerically greatest - /// integer dividing both `this` and `other`. - /// - /// The greatest common divisor is independent of the order, - /// so `x.gcd(y)` is always the same as `y.gcd(x)`. - /// - /// For any integer `x`, `x.gcd(x)` is `x.abs()`. - /// - /// If both `this` and `other` is zero, the result is also zero. - /// - /// Example: - /// ```dart - /// print(Obj(4).gcd(2)); // 2 - /// print(Obj(8).gcd(4)); // 4 - /// print(Obj(10).gcd(12)); // 2 - /// print(Obj(10).gcd(0)); // 10 - /// print(Obj(-2).gcd(-3)); // 1 - /// ``` - int gcd(int other) => value.gcd(other); - - /// Returns true if and only if this integer is even. - bool get isEven => value.isEven; - - /// Returns true if and only if this integer is odd. - bool get isOdd => value.isOdd; - - /// Returns the minimum number of bits required to store this integer. - /// - /// The number of bits excludes the sign bit, which gives the natural length - /// for non-negative (unsigned) values. Negative values are complemented to - /// return the bit position of the first bit that differs from the sign bit. - /// - /// To find the number of bits needed to store the value as a signed value, - /// add one, i.e. use `x.bitLength + 1`. - /// ```dart - /// x.bitLength == (-x-1).bitLength; - /// - /// Obj(3).bitLength == 2; // 00000011 - /// Obj(2).bitLength == 2; // 00000010 - /// Obj(1).bitLength == 1; // 00000001 - /// Obj(0).bitLength == 0; // 00000000 - /// Obj(-1).bitLength == 0; // 11111111 - /// Obj(-2).bitLength == 1; // 11111110 - /// Obj(-3).bitLength == 2; // 11111101 - /// Obj(-4).bitLength == 2; // 11111100 - /// ``` - int get bitLength => value.bitLength; - - /// Returns the least significant [width] bits of this integer as a - /// non-negative number (i.e. unsigned representation). The returned value has - /// zeros in all bit positions higher than [width]. - /// ```dart - /// Obj(-1).toUnsigned(5) == 31 // 11111111 -> 00011111 - /// ``` - /// This operation can be used to simulate arithmetic from low level languages. - /// For example, to increment an 8 bit quantity: - /// ```dart - /// q = (q + 1).toUnsigned(8); - /// ``` - /// `q` will count from `0` up to `255` and then wrap around to `0`. - /// - /// If the input fits in [width] bits without truncation, the result is the - /// same as the input. The minimum width needed to avoid truncation of `x` is - /// given by `x.bitLength`, i.e. - /// ```dart - /// x == x.toUnsigned(x.bitLength); - /// ``` - int toUnsigned(int width) => value.toUnsigned(width); - - /// Returns the least significant [width] bits of this integer, extending the - /// highest retained bit to the sign. This is the same as truncating the value - /// to fit in [width] bits using an signed 2-s complement representation. The - /// returned value has the same bit value in all positions higher than [width]. - /// - /// ```dart - /// // V--sign bit-V - /// Obj(16).toSigned(5) == -16; // 00010000 -> 11110000 - /// Obj(239).toSigned(5) == 15; // 11101111 -> 00001111 - /// // ^ ^ - /// ``` - /// This operation can be used to simulate arithmetic from low level languages. - /// For example, to increment an 8 bit signed quantity: - /// ```dart - /// q = (q + 1).toSigned(8); - /// ``` - /// `q` will count from `0` up to `127`, wrap to `-128` and count back up to - /// `127`. - /// - /// If the input value fits in [width] bits without truncation, the result is - /// the same as the input. The minimum width needed to avoid truncation of `x` - /// is `x.bitLength + 1`, i.e. - /// ```dart - /// x == x.toSigned(x.bitLength + 1); - /// ``` - int toSigned(int width) => value.toSigned(width); - - /// Returns the absolute value of this integer. - /// - /// For any integer `value`, - /// the result is the same as `value < 0 ? -value : value`. - /// - /// Integer overflow may cause the result of `-value` to stay negative. - int abs() => value.abs(); - - /// Returns the sign of this integer. - /// - /// Returns 0 for zero, -1 for values less than zero and - /// +1 for values greater than zero. - int get sign => value.sign; - - /// Returns `this`. - int round() => value.round(); - - /// Returns `this`. - int floor() => value.floor(); - - /// Returns `this`. - int ceil() => value.ceil(); - - /// Returns `this`. - int truncate() => value.truncate(); - - /// Returns `this.toDouble()`. - double roundToDouble() => value.roundToDouble(); - - /// Returns `this.toDouble()`. - double floorToDouble() => value.floorToDouble(); - - /// Returns `this.toDouble()`. - double ceilToDouble() => value.ceilToDouble(); - - /// Returns `this.toDouble()`. - double truncateToDouble() => value.truncateToDouble(); - - /// Converts [this] to a string representation in the given [radix]. - /// - /// In the string representation, lower-case letters are used for digits above - /// '9', with 'a' being 10 and 'z' being 35. - /// - /// The [radix] argument must be an integer in the range 2 to 36. - /// - /// Example: - /// ```dart - /// // Binary (base 2). - /// print(Obj(12).toRadixString(2)); // 1100 - /// print(Obj(31).toRadixString(2)); // 11111 - /// print(Obj(2021).toRadixString(2)); // 11111100101 - /// print(Obj(-12).toRadixString(2)); // -1100 - /// // Octal (base 8). - /// print(Obj(12).toRadixString(8)); // 14 - /// print(Obj(31).toRadixString(8)); // 37 - /// print(Obj(2021).toRadixString(8)); // 3745 - /// // Hexadecimal (base 16). - /// print(Obj(12).toRadixString(16)); // c - /// print(Obj(31).toRadixString(16)); // 1f - /// print(Obj(2021).toRadixString(16)); // 7e5 - /// // Base 36. - /// print(Obj(35 * 36 + 1).toRadixString(36)); // z1 - /// ``` - String toRadixString(int radix) => value.toRadixString(radix); -} - -extension ObjIntNullExt on Obj { - /// Returns this integer to the power of [exponent] modulo [modulus]. - /// - /// The [exponent] must be non-negative and [modulus] must be - /// positive. - int? modPow(int exponent, int modulus) => value?.modPow(exponent, modulus); - - /// Returns the modular multiplicative inverse of this integer - /// modulo [modulus]. - /// - /// The [modulus] must be positive. - /// - /// It is an error if no modular inverse exists. - int? modInverse(int modulus) => value?.modInverse(modulus); - - /// Returns the greatest common divisor of this integer and [other]. - /// - /// If either number is non-zero, the result is the numerically greatest - /// integer dividing both `this` and `other`. - /// - /// The greatest common divisor is independent of the order, - /// so `x.gcd(y)` is always the same as `y.gcd(x)`. - /// - /// For any integer `x`, `x.gcd(x)` is `x.abs()`. - /// - /// If both `this` and `other` is zero, the result is also zero. - /// - /// Example: - /// ```dart - /// print(Obj(4).gcd(2)); // 2 - /// print(Obj(8).gcd(4)); // 4 - /// print(Obj(10).gcd(12)); // 2 - /// print(Obj(10).gcd(0)); // 10 - /// print(Obj(-2).gcd(-3)); // 1 - /// ``` - int? gcd(int other) => value?.gcd(other); - - /// Returns true if and only if this integer is even. - bool? get isEven => value?.isEven; - - /// Returns true if and only if this integer is odd. - bool? get isOdd => value?.isOdd; - - /// Returns the minimum number of bits required to store this integer. - /// - /// The number of bits excludes the sign bit, which gives the natural length - /// for non-negative (unsigned) values. Negative values are complemented to - /// return the bit position of the first bit that differs from the sign bit. - /// - /// To find the number of bits needed to store the value as a signed value, - /// add one, i.e. use `x.bitLength + 1`. - /// ```dart - /// x.bitLength == (-x-1).bitLength; - /// - /// Obj(3).bitLength == 2; // 00000011 - /// Obj(2).bitLength == 2; // 00000010 - /// Obj(1).bitLength == 1; // 00000001 - /// Obj(0).bitLength == 0; // 00000000 - /// Obj(-1).bitLength == 0; // 11111111 - /// Obj(-2).bitLength == 1; // 11111110 - /// Obj(-3).bitLength == 2; // 11111101 - /// Obj(-4).bitLength == 2; // 11111100 - /// ``` - int? get bitLength => value?.bitLength; - - /// Returns the least significant [width] bits of this integer as a - /// non-negative number (i.e. unsigned representation). The returned value has - /// zeros in all bit positions higher than [width]. - /// ```dart - /// Obj(-1).toUnsigned(5) == 31 // 11111111 -> 00011111 - /// ``` - /// This operation can be used to simulate arithmetic from low level languages. - /// For example, to increment an 8 bit quantity: - /// ```dart - /// q = (q + 1).toUnsigned(8); - /// ``` - /// `q` will count from `0` up to `255` and then wrap around to `0`. - /// - /// If the input fits in [width] bits without truncation, the result is the - /// same as the input. The minimum width needed to avoid truncation of `x` is - /// given by `x.bitLength`, i.e. - /// ```dart - /// x == x.toUnsigned(x.bitLength); - /// ``` - int? toUnsigned(int width) => value?.toUnsigned(width); - - /// Returns the least significant [width] bits of this integer, extending the - /// highest retained bit to the sign. This is the same as truncating the value - /// to fit in [width] bits using an signed 2-s complement representation. The - /// returned value has the same bit value in all positions higher than [width]. - /// - /// ```dart - /// // V--sign bit-V - /// Obj(16).toSigned(5) == -16; // 00010000 -> 11110000 - /// Obj(239).toSigned(5) == 15; // 11101111 -> 00001111 - /// // ^ ^ - /// ``` - /// This operation can be used to simulate arithmetic from low level languages. - /// For example, to increment an 8 bit signed quantity: - /// ```dart - /// q = (q + 1).toSigned(8); - /// ``` - /// `q` will count from `0` up to `127`, wrap to `-128` and count back up to - /// `127`. - /// - /// If the input value fits in [width] bits without truncation, the result is - /// the same as the input. The minimum width needed to avoid truncation of `x` - /// is `x.bitLength + 1`, i.e. - /// ```dart - /// x == x.toSigned(x.bitLength + 1); - /// ``` - int? toSigned(int width) => value?.toSigned(width); - - /// Returns the absolute value of this integer. - /// - /// For any integer `value`, - /// the result is the same as `value < 0 ? -value : value`. - /// - /// Integer overflow may cause the result of `-value` to stay negative. - int? abs() => value?.abs(); - - /// Returns the sign of this integer. - /// - /// Returns 0 for zero, -1 for values less than zero and - /// +1 for values greater than zero. - int? get sign => value?.sign; - - /// Returns `this`. - int? round() => value?.round(); - - /// Returns `this`. - int? floor() => value?.floor(); - - /// Returns `this`. - int? ceil() => value?.ceil(); - - /// Returns `this`. - int? truncate() => value?.truncate(); - - /// Returns `this.toDouble()`. - double? roundToDouble() => value?.roundToDouble(); - - /// Returns `this.toDouble()`. - double? floorToDouble() => value?.floorToDouble(); - - /// Returns `this.toDouble()`. - double? ceilToDouble() => value?.ceilToDouble(); - - /// Returns `this.toDouble()`. - double? truncateToDouble() => value?.truncateToDouble(); - - /// Converts [this] to a string representation in the given [radix]. - /// - /// In the string representation, lower-case letters are used for digits above - /// '9', with 'a' being 10 and 'z' being 35. - /// - /// The [radix] argument must be an integer in the range 2 to 36. - /// - /// Example: - /// ```dart - /// // Binary (base 2). - /// print(Obj(12).toRadixString(2)); // 1100 - /// print(Obj(31).toRadixString(2)); // 11111 - /// print(Obj(2021).toRadixString(2)); // 11111100101 - /// print(Obj(-12).toRadixString(2)); // -1100 - /// // Octal (base 8). - /// print(Obj(12).toRadixString(8)); // 14 - /// print(Obj(31).toRadixString(8)); // 37 - /// print(Obj(2021).toRadixString(8)); // 3745 - /// // Hexadecimal (base 16). - /// print(Obj(12).toRadixString(16)); // c - /// print(Obj(31).toRadixString(16)); // 1f - /// print(Obj(2021).toRadixString(16)); // 7e5 - /// // Base 36. - /// print(Obj(35 * 36 + 1).toRadixString(36)); // z1 - /// ``` - String? toRadixString(int radix) => value?.toRadixString(radix); -} diff --git a/packages/reactter/lib/src/obj/extensions/obj_iterable.dart b/packages/reactter/lib/src/obj/extensions/obj_iterable.dart deleted file mode 100644 index d538e2bb..00000000 --- a/packages/reactter/lib/src/obj/extensions/obj_iterable.dart +++ /dev/null @@ -1,1070 +0,0 @@ -// coverage:ignore-file -part of '../obj.dart'; - -extension ObjIterableExt on Obj> { - /// Returns a new `Iterator` that allows iterating the elements of this - /// `Iterable`. - /// - /// Iterable classes may specify the iteration order of their elements - /// (for example [List] always iterate in index order), - /// or they may leave it unspecified (for example a hash-based [Set] - /// may iterate in any order). - /// - /// Each time `iterator` is read, it returns a new iterator, - /// which can be used to iterate through all the elements again. - /// The iterators of the same iterable can be stepped through independently, - /// but should return the same elements in the same order, - /// as long as the underlying collection isn't changed. - /// - /// Modifying the collection may cause new iterators to produce - /// different elements, and may change the order of existing elements. - /// A [List] specifies its iteration order precisely, - /// so modifying the list changes the iteration order predictably. - /// A hash-based [Set] may change its iteration order completely - /// when adding a new element to the set. - /// - /// Modifying the underlying collection after creating the new iterator - /// may cause an error the next time [Iterator.moveNext] is called - /// on that iterator. - /// Any *modifiable* iterable class should specify which operations will - /// break iteration. - Iterator get iterator => value.iterator; - - /// Provides a view of this iterable as an iterable of [R] instances. - /// - /// If this iterable only contains instances of [R], all operations - /// will work correctly. If any operation tries to access an element - /// that is not an instance of [R], the access will throw instead. - /// - /// When the returned iterable creates a new object that depends on - /// the type [R], e.g., from [toList], it will have exactly the type [R]. - Iterable cast() => value.cast(); - - /// Returns the lazy concatenation of this iterable and [other]. - /// - /// The returned iterable will provide the same elements as this iterable, - /// and, after that, the elements of [other], in the same order as in the - /// original iterables. - /// - /// Example: - /// ```dart - /// var planets = ['Earth', 'Jupiter']; - /// var updated = planets.followedBy(['Mars', 'Venus']); - /// print(updated); // (Earth, Jupiter, Mars, Venus) - /// ``` - Iterable followedBy(Iterable other) => value.followedBy(other); - - /// The current elements of this iterable modified by [toElement]. - /// - /// Returns a new lazy [Iterable] with elements that are created by - /// calling `toElement` on each element of this `Iterable` in - /// iteration order. - /// - /// The returned iterable is lazy, so it won't iterate the elements of - /// this iterable until it is itself iterated, and then it will apply - /// [toElement] to create one element at a time. - /// The converted elements are not cached. - /// Iterating multiple times over the returned [Iterable] - /// will invoke the supplied [toElement] function once per element - /// for on each iteration. - /// - /// Methods on the returned iterable are allowed to omit calling `toElement` - /// on any element where the result isn't needed. - /// For example, [elementAt] may call `toElement` only once. - /// - /// Equivalent to: - /// ``` - /// Iterable map(T toElement(E e)) sync* { - /// for (var value in this) { - /// yield toElement(value); - /// } - /// } - /// ``` - /// Example: - /// ```dart import:convert - /// var products = jsonDecode(''' - /// [ - /// {"name": "Screwdriver", "price": 42.00}, - /// {"name": "Wingnut", "price": 0.50} - /// ] - /// '''); - /// var values = products.map((product) => product['price'] as double); - /// var totalPrice = values.fold(0.0, (a, b) => a + b); // 42.5. - /// ``` - Iterable map(T Function(E e) toElement) => value.map(toElement); - - /// Returns a new lazy [Iterable] with all elements that satisfy the - /// predicate [test]. - /// - /// The matching elements have the same order in the returned iterable - /// as they have in [iterator]. - /// - /// This method returns a view of the mapped elements. - /// As long as the returned [Iterable] is not iterated over, - /// the supplied function [test] will not be invoked. - /// Iterating will not cache results, and thus iterating multiple times over - /// the returned [Iterable] may invoke the supplied - /// function [test] multiple times on the same element. - /// - /// Example: - /// ```dart - /// final numbers = [1, 2, 3, 5, 6, 7]; - /// var result = numbers.where((x) => x < 5); // (1, 2, 3) - /// result = numbers.where((x) => x > 5); // (6, 7) - /// result = numbers.where((x) => x.isEven); // (2, 6) - /// ``` - Iterable where(bool Function(E element) test) => value.where(test); - - /// Returns a new lazy [Iterable] with all elements that have type [T]. - /// - /// The matching elements have the same order in the returned iterable - /// as they have in [iterator]. - /// - /// This method returns a view of the mapped elements. - /// Iterating will not cache results, and thus iterating multiple times over - /// the returned [Iterable] may yield different results, - /// if the underlying elements change between iterations. - Iterable whereType() => value.whereType(); - - /// Expands each element of this [Iterable] into zero or more elements. - /// - /// The resulting Iterable runs through the elements returned - /// by [toElements] for each element of this, in iteration order. - /// - /// The returned [Iterable] is lazy, and calls [toElements] for each element - /// of this iterable every time the returned iterable is iterated. - /// - /// Example: - /// ```dart - /// Iterable count(int n) sync* { - /// for (var i = 1; i <= n; i++) { - /// yield i; - /// } - /// } - /// - /// var numbers = [1, 3, 0, 2]; - /// print(numbers.expand(count)); // (1, 1, 2, 3, 1, 2) - /// ``` - /// - /// Equivalent to: - /// ``` - /// Iterable expand(Iterable toElements(E e)) sync* { - /// for (var value in this) { - /// yield* toElements(value); - /// } - /// } - /// ``` - Iterable expand(Iterable Function(E element) toElements) => - value.expand(toElements); - - /// Whether the collection contains an element equal to [element]. - /// - /// This operation will check each element in order for being equal to - /// [element], unless it has a more efficient way to find an element - /// equal to [element]. - /// - /// The equality used to determine whether [element] is equal to an element of - /// the iterable defaults to the [Object.==] of the element. - /// - /// Some types of iterable may have a different equality used for its elements. - /// For example, a [Set] may have a custom equality - /// (see [Set.identity]) that its `contains` uses. - /// Likewise the `Iterable` returned by a [Map.keys] call - /// should use the same equality that the `Map` uses for keys. - /// - /// Example: - /// ```dart - /// final gasPlanets = {1: 'Jupiter', 2: 'Saturn'}; - /// final containsOne = gasPlanets.keys.contains(1); // true - /// final containsFive = gasPlanets.keys.contains(5); // false - /// final containsJupiter = gasPlanets.values.contains('Jupiter'); // true - /// final containsMercury = gasPlanets.values.contains('Mercury'); // false - /// ``` - bool contains(Object? element) => value.contains(element); - - /// Invokes [action] on each element of this iterable in iteration order. - /// - /// Example: - /// ```dart - /// final numbers = [1, 2, 6, 7]; - /// numbers.forEach(print); - /// // 1 - /// // 2 - /// // 6 - /// // 7 - /// ``` - void forEach(void Function(E element) action) => value.forEach(action); - - /// Reduces a collection to a single value by iteratively combining elements - /// of the collection using the provided function. - /// - /// The iterable must have at least one element. - /// If it has only one element, that element is returned. - /// - /// Otherwise this method starts with the first element from the iterator, - /// and then combines it with the remaining elements in iteration order, - /// as if by: - /// ``` - /// E value = iterable.first; - /// iterable.skip(1).forEach((element) { - /// value = combine(value, element); - /// }); - /// return value; - /// ``` - /// Example of calculating the sum of an iterable: - /// ```dart - /// final numbers = [10, 2, 5, 0.5]; - /// final result = numbers.reduce((value, element) => value + element); - /// print(result); // 17.5 - /// ``` - E reduce(E Function(E value, E element) combine) => value.reduce(combine); - - /// Reduces a collection to a single value by iteratively combining each - /// element of the collection with an existing value - /// - /// Uses [initialValue] as the initial value, - /// then iterates through the elements and updates the value with - /// each element using the [combine] function, as if by: - /// ``` - /// var value = initialValue; - /// for (E element in this) { - /// value = combine(value, element); - /// } - /// return value; - /// ``` - /// Example of calculating the sum of an iterable: - /// ```dart - /// final numbers = [10, 2, 5, 0.5]; - /// const initialValue = 100.0; - /// final result = numbers.fold( - /// initialValue, (previousValue, element) => previousValue + element); - /// print(result); // 117.5 - /// ``` - T fold(T initialValue, T Function(T previousValue, E element) combine) => - value.fold(initialValue, combine); - - /// Checks whether every element of this iterable satisfies [test]. - /// - /// Checks every element in iteration order, and returns `false` if - /// any of them make [test] return `false`, otherwise returns `true`. - /// - /// Example: - /// ```dart - /// final planetsByMass = {0.06: 'Mercury', 0.81: 'Venus', - /// 0.11: 'Mars'}; - /// // Checks whether all keys are smaller than 1. - /// final every = planetsByMass.keys.every((key) => key < 1.0); // true - /// ``` - bool every(bool Function(E element) test) => value.every(test); - - /// Converts each element to a [String] and concatenates the strings. - /// - /// Iterates through elements of this iterable, - /// converts each one to a [String] by calling [Object.toString], - /// and then concatenates the strings, with the - /// [separator] string interleaved between the elements. - /// - /// Example: - /// ```dart - /// final planetsByMass = {0.06: 'Mercury', 0.81: 'Venus', - /// 0.11: 'Mars'}; - /// final joinedNames = planetsByMass.values.join('-'); // Mercury-Venus-Mars - /// ``` - String join([String separator = ""]) => value.join(separator); - - /// Checks whether any element of this iterable satisfies [test]. - /// - /// Checks every element in iteration order, and returns `true` if - /// any of them make [test] return `true`, otherwise returns false. - /// - /// Example: - /// ```dart - /// final numbers = [1, 2, 3, 5, 6, 7]; - /// var result = numbers.any((element) => element >= 5); // true; - /// result = numbers.any((element) => element >= 10); // false; - /// ``` - bool any(bool Function(E element) test) => value.any(test); - - /// Creates a [List] containing the elements of this [Iterable]. - /// - /// The elements are in iteration order. - /// The list is fixed-length if [growable] is false. - /// - /// Example: - /// ```dart - /// final planets = {1: 'Mercury', 2: 'Venus', 3: 'Mars'}; - /// final keysList = planets.keys.toList(growable: false); // [1, 2, 3] - /// final valuesList = - /// planets.values.toList(growable: false); // [Mercury, Venus, Mars] - /// ``` - List toList({bool growable = true}) => value.toList(growable: growable); - - /// Creates a [Set] containing the same elements as this iterable. - /// - /// The set may contain fewer elements than the iterable, - /// if the iterable contains an element more than once, - /// or it contains one or more elements that are equal. - /// The order of the elements in the set is not guaranteed to be the same - /// as for the iterable. - /// - /// Example: - /// ```dart - /// final planets = {1: 'Mercury', 2: 'Venus', 3: 'Mars'}; - /// final valueSet = planets.values.toSet(); // {Mercury, Venus, Mars} - /// ``` - Set toSet() => value.toSet(); - - /// Returns the number of elements in [this]. - /// - /// Counting all elements may involve iterating through all elements and can - /// therefore be slow. - /// Some iterables have a more efficient way to find the number of elements. - int get length => value.length; - - /// Whether this collection has no elements. - /// - /// May be computed by checking if `iterator.moveNext()` returns `false`. - /// - /// Example: - /// ```dart - /// final emptyList = []; - /// print(emptyList.isEmpty); // true; - /// print(emptyList.iterator.moveNext()); // false - /// ``` - bool get isEmpty => value.isEmpty; - - /// Whether this collection has at least one element. - /// - /// May be computed by checking if `iterator.moveNext()` returns `true`. - /// - /// Example: - /// ```dart - /// final numbers = {1, 2, 3}; - /// print(numbers.isNotEmpty); // true; - /// print(numbers.iterator.moveNext()); // true - /// ``` - bool get isNotEmpty => value.isNotEmpty; - - /// Returns a lazy iterable of the [count] first elements of this iterable. - /// - /// The returned `Iterable` may contain fewer than `count` elements, if `this` - /// contains fewer than `count` elements. - /// - /// The elements can be computed by stepping through [iterator] until [count] - /// elements have been seen. - /// - /// The `count` must not be negative. - /// - /// Example: - /// ```dart - /// final numbers = [1, 2, 3, 5, 6, 7]; - /// final result = numbers.take(4); // (1, 2, 3, 5) - /// final takeAll = numbers.take(100); // (1, 2, 3, 5, 6, 7) - /// ``` - Iterable take(int count) => value.take(count); - - /// Returns a lazy iterable of the leading elements satisfying [test]. - /// - /// The filtering happens lazily. Every new iterator of the returned - /// iterable starts iterating over the elements of `this`. - /// - /// The elements can be computed by stepping through [iterator] until an - /// element is found where `test(element)` is false. At that point, - /// the returned iterable stops (its `moveNext()` returns false). - /// - /// Example: - /// ```dart - /// final numbers = [1, 2, 3, 5, 6, 7]; - /// var result = numbers.takeWhile((x) => x < 5); // (1, 2, 3) - /// result = numbers.takeWhile((x) => x != 3); // (1, 2) - /// result = numbers.takeWhile((x) => x != 4); // (1, 2, 3, 5, 6, 7) - /// result = numbers.takeWhile((x) => x.isOdd); // (1) - /// ``` - Iterable takeWhile(bool Function(E value) test) => value.takeWhile(test); - - /// Returns an [Iterable] that provides all but the first [count] elements. - /// - /// When the returned iterable is iterated, it starts iterating over `this`, - /// first skipping past the initial [count] elements. - /// If `this` has fewer than `count` elements, then the resulting Iterable is - /// empty. - /// After that, the remaining elements are iterated in the same order as - /// in this iterable. - /// - /// Some iterables may be able to find later elements without first iterating - /// through earlier elements, for example when iterating a [List]. - /// Such iterables are allowed to ignore the initial skipped elements. - /// - /// Example: - /// ```dart - /// final numbers = [1, 2, 3, 5, 6, 7]; - /// final result = numbers.skip(4); // (6, 7) - /// final skipAll = numbers.skip(100); // () - no elements. - /// ``` - /// - /// The [count] must not be negative. - Iterable skip(int count) => value.skip(count); - - /// Returns an `Iterable` that skips leading elements while [test] is satisfied. - /// - /// The filtering happens lazily. Every new [Iterator] of the returned - /// iterable iterates over all elements of `this`. - /// - /// The returned iterable provides elements by iterating this iterable, - /// but skipping over all initial elements where `test(element)` returns - /// true. If all elements satisfy `test` the resulting iterable is empty, - /// otherwise it iterates the remaining elements in their original order, - /// starting with the first element for which `test(element)` returns `false`. - /// - /// Example: - /// ```dart - /// final numbers = [1, 2, 3, 5, 6, 7]; - /// var result = numbers.skipWhile((x) => x < 5); // (5, 6, 7) - /// result = numbers.skipWhile((x) => x != 3); // (3, 5, 6, 7) - /// result = numbers.skipWhile((x) => x != 4); // () - /// result = numbers.skipWhile((x) => x.isOdd); // (2, 3, 5, 6, 7) - /// ``` - Iterable skipWhile(bool Function(E value) test) => value.skipWhile(test); - - /// Returns the first element. - /// - /// Throws a [StateError] if `this` is empty. - /// Otherwise returns the first element in the iteration order, - /// equivalent to `this.elementAt(0)`. - E get first => value.first; - - /// Returns the last element. - /// - /// Throws a [StateError] if `this` is empty. - /// Otherwise may iterate through the elements and returns the last one - /// seen. - /// Some iterables may have more efficient ways to find the last element - /// (for example a list can directly access the last element, - /// without iterating through the previous ones). - E get last => value.last; - - /// Checks that this iterable has only one element, and returns that element. - /// - /// Throws a [StateError] if `this` is empty or has more than one element. - E get single => value.single; - - /// Returns the first element that satisfies the given predicate [test]. - /// - /// Iterates through elements and returns the first to satisfy [test]. - /// - /// Example: - /// ```dart - /// final numbers = [1, 2, 3, 5, 6, 7]; - /// var result = numbers.firstWhere((element) => element < 5); // 1 - /// result = numbers.firstWhere((element) => element > 5); // 6 - /// result = - /// numbers.firstWhere((element) => element > 10, orElse: () => -1); // -1 - /// ``` - /// - /// If no element satisfies [test], the result of invoking the [orElse] - /// function is returned. - /// If [orElse] is omitted, it defaults to throwing a [StateError]. - E firstWhere(bool Function(E element) test, {E Function()? orElse}) => - value.firstWhere(test, orElse: orElse); - - /// Returns the last element that satisfies the given predicate [test]. - /// - /// An iterable that can access its elements directly may check its - /// elements in any order (for example a list starts by checking the - /// last element and then moves towards the start of the list). - /// The default implementation iterates elements in iteration order, - /// checks `test(element)` for each, - /// and finally returns that last one that matched. - /// - /// Example: - /// ```dart - /// final numbers = [1, 2, 3, 5, 6, 7]; - /// var result = numbers.lastWhere((element) => element < 5); // 3 - /// result = numbers.lastWhere((element) => element > 5); // 7 - /// result = numbers.lastWhere((element) => element > 10, - /// orElse: () => -1); // -1 - /// ``` - /// - /// If no element satisfies [test], the result of invoking the [orElse] - /// function is returned. - /// If [orElse] is omitted, it defaults to throwing a [StateError]. - E lastWhere(bool Function(E element) test, {E Function()? orElse}) => - value.lastWhere(test, orElse: orElse); - - /// Returns the single element that satisfies [test]. - /// - /// Checks elements to see if `test(element)` returns true. - /// If exactly one element satisfies [test], that element is returned. - /// If more than one matching element is found, throws [StateError]. - /// If no matching element is found, returns the result of [orElse]. - /// If [orElse] is omitted, it defaults to throwing a [StateError]. - /// - /// Example: - /// ```dart - /// final numbers = [2, 2, 10]; - /// var result = numbers.singleWhere((element) => element > 5); // 10 - /// ``` - /// When no matching element is found, the result of calling [orElse] is - /// returned instead. - /// ```dart continued - /// result = numbers.singleWhere((element) => element == 1, - /// orElse: () => -1); // -1 - /// ``` - /// There must not be more than one matching element. - /// ```dart continued - /// result = numbers.singleWhere((element) => element == 2); // Throws Error. - /// ``` - E singleWhere(bool Function(E element) test, {E Function()? orElse}) => - value.singleWhere(test, orElse: orElse); - - /// Returns the [index]th element. - /// - /// The [index] must be non-negative and less than [length]. - /// Index zero represents the first element (so `iterable.elementAt(0)` is - /// equivalent to `iterable.first`). - /// - /// May iterate through the elements in iteration order, ignoring the - /// first [index] elements and then returning the next. - /// Some iterables may have a more efficient way to find the element. - /// - /// Example: - /// ```dart - /// final numbers = [1, 2, 3, 5, 6, 7]; - /// final elementAt = numbers.elementAt(4); // 6 - /// ``` - E elementAt(int index) => value.elementAt(index); -} - -extension ObjIterableNullExt on Obj?> { - /// Returns a new `Iterator` that allows iterating the elements of this - /// `Iterable`. - /// - /// Iterable classes may specify the iteration order of their elements - /// (for example [List] always iterate in index order), - /// or they may leave it unspecified (for example a hash-based [Set] - /// may iterate in any order). - /// - /// Each time `iterator` is read, it returns a new iterator, - /// which can be used to iterate through all the elements again. - /// The iterators of the same iterable can be stepped through independently, - /// but should return the same elements in the same order, - /// as long as the underlying collection isn't changed. - /// - /// Modifying the collection may cause new iterators to produce - /// different elements, and may change the order of existing elements. - /// A [List] specifies its iteration order precisely, - /// so modifying the list changes the iteration order predictably. - /// A hash-based [Set] may change its iteration order completely - /// when adding a new element to the set. - /// - /// Modifying the underlying collection after creating the new iterator - /// may cause an error the next time [Iterator.moveNext] is called - /// on that iterator. - /// Any *modifiable* iterable class should specify which operations will - /// break iteration. - Iterator? get iterator => value?.iterator; - - /// Provides a view of this iterable as an iterable of [R] instances. - /// - /// If this iterable only contains instances of [R], all operations - /// will work correctly. If any operation tries to access an element - /// that is not an instance of [R], the access will throw instead. - /// - /// When the returned iterable creates a new object that depends on - /// the type [R], e.g., from [toList], it will have exactly the type [R]. - Iterable? cast() => value?.cast(); - - /// Returns the lazy concatenation of this iterable and [other]. - /// - /// The returned iterable will provide the same elements as this iterable, - /// and, after that, the elements of [other], in the same order as in the - /// original iterables. - /// - /// Example: - /// ```dart - /// var planets = ['Earth', 'Jupiter']; - /// var updated = planets.followedBy(['Mars', 'Venus']); - /// print(updated); // (Earth, Jupiter, Mars, Venus) - /// ``` - Iterable? followedBy(Iterable other) => value?.followedBy(other); - - /// The current elements of this iterable modified by [toElement]. - /// - /// Returns a new lazy [Iterable] with elements that are created by - /// calling `toElement` on each element of this `Iterable` in - /// iteration order. - /// - /// The returned iterable is lazy, so it won't iterate the elements of - /// this iterable until it is itself iterated, and then it will apply - /// [toElement] to create one element at a time. - /// The converted elements are not cached. - /// Iterating multiple times over the returned [Iterable] - /// will invoke the supplied [toElement] function once per element - /// for on each iteration. - /// - /// Methods on the returned iterable are allowed to omit calling `toElement` - /// on any element where the result isn't needed. - /// For example, [elementAt] may call `toElement` only once. - /// - /// Equivalent to: - /// ``` - /// Iterable map(T toElement(E e)) sync* { - /// for (var value in this) { - /// yield toElement(value); - /// } - /// } - /// ``` - /// Example: - /// ```dart import:convert - /// var products = jsonDecode(''' - /// [ - /// {"name": "Screwdriver", "price": 42.00}, - /// {"name": "Wingnut", "price": 0.50} - /// ] - /// '''); - /// var values = products.map((product) => product['price'] as double); - /// var totalPrice = values.fold(0.0, (a, b) => a + b); // 42.5. - /// ``` - Iterable? map(T Function(E e) toElement) => value?.map(toElement); - - /// Returns a new lazy [Iterable] with all elements that satisfy the - /// predicate [test]. - /// - /// The matching elements have the same order in the returned iterable - /// as they have in [iterator]. - /// - /// This method returns a view of the mapped elements. - /// As long as the returned [Iterable] is not iterated over, - /// the supplied function [test] will not be invoked. - /// Iterating will not cache results, and thus iterating multiple times over - /// the returned [Iterable] may invoke the supplied - /// function [test] multiple times on the same element. - /// - /// Example: - /// ```dart - /// final numbers = [1, 2, 3, 5, 6, 7]; - /// var result = numbers.where((x) => x < 5); // (1, 2, 3) - /// result = numbers.where((x) => x > 5); // (6, 7) - /// result = numbers.where((x) => x.isEven); // (2, 6) - /// ``` - Iterable? where(bool Function(E element) test) => value?.where(test); - - /// Returns a new lazy [Iterable] with all elements that have type [T]. - /// - /// The matching elements have the same order in the returned iterable - /// as they have in [iterator]. - /// - /// This method returns a view of the mapped elements. - /// Iterating will not cache results, and thus iterating multiple times over - /// the returned [Iterable] may yield different results, - /// if the underlying elements change between iterations. - Iterable? whereType() => value?.whereType(); - - /// Expands each element of this [Iterable] into zero or more elements. - /// - /// The resulting Iterable runs through the elements returned - /// by [toElements] for each element of this, in iteration order. - /// - /// The returned [Iterable] is lazy, and calls [toElements] for each element - /// of this iterable every time the returned iterable is iterated. - /// - /// Example: - /// ```dart - /// Iterable count(int n) sync* { - /// for (var i = 1; i <= n; i++) { - /// yield i; - /// } - /// } - /// - /// var numbers = [1, 3, 0, 2]; - /// print(numbers.expand(count)); // (1, 1, 2, 3, 1, 2) - /// ``` - /// - /// Equivalent to: - /// ``` - /// Iterable expand(Iterable toElements(E e)) sync* { - /// for (var value in this) { - /// yield* toElements(value); - /// } - /// } - /// ``` - Iterable? expand(Iterable Function(E element) toElements) => - value?.expand(toElements); - - /// Whether the collection contains an element equal to [element]. - /// - /// This operation will check each element in order for being equal to - /// [element], unless it has a more efficient way to find an element - /// equal to [element]. - /// - /// The equality used to determine whether [element] is equal to an element of - /// the iterable defaults to the [Object.==] of the element. - /// - /// Some types of iterable may have a different equality used for its elements. - /// For example, a [Set] may have a custom equality - /// (see [Set.identity]) that its `contains` uses. - /// Likewise the `Iterable` returned by a [Map.keys] call - /// should use the same equality that the `Map` uses for keys. - /// - /// Example: - /// ```dart - /// final gasPlanets = {1: 'Jupiter', 2: 'Saturn'}; - /// final containsOne = gasPlanets.keys.contains(1); // true - /// final containsFive = gasPlanets.keys.contains(5); // false - /// final containsJupiter = gasPlanets.values.contains('Jupiter'); // true - /// final containsMercury = gasPlanets.values.contains('Mercury'); // false - /// ``` - bool? contains(Object? element) => value?.contains(element); - - /// Invokes [action] on each element of this iterable in iteration order. - /// - /// Example: - /// ```dart - /// final numbers = [1, 2, 6, 7]; - /// numbers.forEach(print); - /// // 1 - /// // 2 - /// // 6 - /// // 7 - /// ``` - void forEach(void Function(E element) action) => value?.forEach(action); - - /// Reduces a collection to a single value by iteratively combining elements - /// of the collection using the provided function. - /// - /// The iterable must have at least one element. - /// If it has only one element, that element is returned. - /// - /// Otherwise this method starts with the first element from the iterator, - /// and then combines it with the remaining elements in iteration order, - /// as if by: - /// ``` - /// E value = iterable.first; - /// iterable.skip(1).forEach((element) { - /// value = combine(value, element); - /// }); - /// return value; - /// ``` - /// Example of calculating the sum of an iterable: - /// ```dart - /// final numbers = [10, 2, 5, 0.5]; - /// final result = numbers.reduce((value, element) => value + element); - /// print(result); // 17.5 - /// ``` - E? reduce(E Function(E value, E element) combine) => value?.reduce(combine); - - /// Reduces a collection to a single value by iteratively combining each - /// element of the collection with an existing value - /// - /// Uses [initialValue] as the initial value, - /// then iterates through the elements and updates the value with - /// each element using the [combine] function, as if by: - /// ``` - /// var value = initialValue; - /// for (E element in this) { - /// value = combine(value, element); - /// } - /// return value; - /// ``` - /// Example of calculating the sum of an iterable: - /// ```dart - /// final numbers = [10, 2, 5, 0.5]; - /// const initialValue = 100.0; - /// final result = numbers.fold( - /// initialValue, (previousValue, element) => previousValue + element); - /// print(result); // 117.5 - /// ``` - T? fold(T initialValue, T Function(T previousValue, E element) combine) => - value?.fold(initialValue, combine); - - /// Checks whether every element of this iterable satisfies [test]. - /// - /// Checks every element in iteration order, and returns `false` if - /// any of them make [test] return `false`, otherwise returns `true`. - /// - /// Example: - /// ```dart - /// final planetsByMass = {0.06: 'Mercury', 0.81: 'Venus', - /// 0.11: 'Mars'}; - /// // Checks whether all keys are smaller than 1. - /// final every = planetsByMass.keys.every((key) => key < 1.0); // true - /// ``` - bool? every(bool Function(E element) test) => value?.every(test); - - /// Converts each element to a [String] and concatenates the strings. - /// - /// Iterates through elements of this iterable, - /// converts each one to a [String] by calling [Object.toString], - /// and then concatenates the strings, with the - /// [separator] string interleaved between the elements. - /// - /// Example: - /// ```dart - /// final planetsByMass = {0.06: 'Mercury', 0.81: 'Venus', - /// 0.11: 'Mars'}; - /// final joinedNames = planetsByMass.values.join('-'); // Mercury-Venus-Mars - /// ``` - String? join([String separator = ""]) => value?.join(separator); - - /// Checks whether any element of this iterable satisfies [test]. - /// - /// Checks every element in iteration order, and returns `true` if - /// any of them make [test] return `true`, otherwise returns false. - /// - /// Example: - /// ```dart - /// final numbers = [1, 2, 3, 5, 6, 7]; - /// var result = numbers.any((element) => element >= 5); // true; - /// result = numbers.any((element) => element >= 10); // false; - /// ``` - bool? any(bool Function(E element) test) => value?.any(test); - - /// Creates a [List] containing the elements of this [Iterable]. - /// - /// The elements are in iteration order. - /// The list is fixed-length if [growable] is false. - /// - /// Example: - /// ```dart - /// final planets = {1: 'Mercury', 2: 'Venus', 3: 'Mars'}; - /// final keysList = planets.keys.toList(growable: false); // [1, 2, 3] - /// final valuesList = - /// planets.values.toList(growable: false); // [Mercury, Venus, Mars] - /// ``` - List? toList({bool growable = true}) => value?.toList(growable: growable); - - /// Creates a [Set] containing the same elements as this iterable. - /// - /// The set may contain fewer elements than the iterable, - /// if the iterable contains an element more than once, - /// or it contains one or more elements that are equal. - /// The order of the elements in the set is not guaranteed to be the same - /// as for the iterable. - /// - /// Example: - /// ```dart - /// final planets = {1: 'Mercury', 2: 'Venus', 3: 'Mars'}; - /// final valueSet = planets.values.toSet(); // {Mercury, Venus, Mars} - /// ``` - Set? toSet() => value?.toSet(); - - /// Returns the number of elements in [this]. - /// - /// Counting all elements may involve iterating through all elements and can - /// therefore be slow. - /// Some iterables have a more efficient way to find the number of elements. - int? get length => value?.length; - - /// Whether this collection has no elements. - /// - /// May be computed by checking if `iterator.moveNext()` returns `false`. - /// - /// Example: - /// ```dart - /// final emptyList = []; - /// print(emptyList.isEmpty); // true; - /// print(emptyList.iterator.moveNext()); // false - /// ``` - bool? get isEmpty => value?.isEmpty; - - /// Whether this collection has at least one element. - /// - /// May be computed by checking if `iterator.moveNext()` returns `true`. - /// - /// Example: - /// ```dart - /// final numbers = {1, 2, 3}; - /// print(numbers.isNotEmpty); // true; - /// print(numbers.iterator.moveNext()); // true - /// ``` - bool? get isNotEmpty => value?.isNotEmpty; - - /// Returns a lazy iterable of the [count] first elements of this iterable. - /// - /// The returned `Iterable` may contain fewer than `count` elements, if `this` - /// contains fewer than `count` elements. - /// - /// The elements can be computed by stepping through [iterator] until [count] - /// elements have been seen. - /// - /// The `count` must not be negative. - /// - /// Example: - /// ```dart - /// final numbers = [1, 2, 3, 5, 6, 7]; - /// final result = numbers.take(4); // (1, 2, 3, 5) - /// final takeAll = numbers.take(100); // (1, 2, 3, 5, 6, 7) - /// ``` - Iterable? take(int count) => value?.take(count); - - /// Returns a lazy iterable of the leading elements satisfying [test]. - /// - /// The filtering happens lazily. Every new iterator of the returned - /// iterable starts iterating over the elements of `this`. - /// - /// The elements can be computed by stepping through [iterator] until an - /// element is found where `test(element)` is false. At that point, - /// the returned iterable stops (its `moveNext()` returns false). - /// - /// Example: - /// ```dart - /// final numbers = [1, 2, 3, 5, 6, 7]; - /// var result = numbers.takeWhile((x) => x < 5); // (1, 2, 3) - /// result = numbers.takeWhile((x) => x != 3); // (1, 2) - /// result = numbers.takeWhile((x) => x != 4); // (1, 2, 3, 5, 6, 7) - /// result = numbers.takeWhile((x) => x.isOdd); // (1) - /// ``` - Iterable? takeWhile(bool Function(E value) test) => value?.takeWhile(test); - - /// Returns an [Iterable] that provides all but the first [count] elements. - /// - /// When the returned iterable is iterated, it starts iterating over `this`, - /// first skipping past the initial [count] elements. - /// If `this` has fewer than `count` elements, then the resulting Iterable is - /// empty. - /// After that, the remaining elements are iterated in the same order as - /// in this iterable. - /// - /// Some iterables may be able to find later elements without first iterating - /// through earlier elements, for example when iterating a [List]. - /// Such iterables are allowed to ignore the initial skipped elements. - /// - /// Example: - /// ```dart - /// final numbers = [1, 2, 3, 5, 6, 7]; - /// final result = numbers.skip(4); // (6, 7) - /// final skipAll = numbers.skip(100); // () - no elements. - /// ``` - /// - /// The [count] must not be negative. - Iterable? skip(int count) => value?.skip(count); - - /// Returns an `Iterable` that skips leading elements while [test] is satisfied. - /// - /// The filtering happens lazily. Every new [Iterator] of the returned - /// iterable iterates over all elements of `this`. - /// - /// The returned iterable provides elements by iterating this iterable, - /// but skipping over all initial elements where `test(element)` returns - /// true. If all elements satisfy `test` the resulting iterable is empty, - /// otherwise it iterates the remaining elements in their original order, - /// starting with the first element for which `test(element)` returns `false`. - /// - /// Example: - /// ```dart - /// final numbers = [1, 2, 3, 5, 6, 7]; - /// var result = numbers.skipWhile((x) => x < 5); // (5, 6, 7) - /// result = numbers.skipWhile((x) => x != 3); // (3, 5, 6, 7) - /// result = numbers.skipWhile((x) => x != 4); // () - /// result = numbers.skipWhile((x) => x.isOdd); // (2, 3, 5, 6, 7) - /// ``` - Iterable? skipWhile(bool Function(E value) test) => value?.skipWhile(test); - - /// Returns the first element. - /// - /// Throws a [StateError] if `this` is empty. - /// Otherwise returns the first element in the iteration order, - /// equivalent to `this.elementAt(0)`. - E? get first => value?.first; - - /// Returns the last element. - /// - /// Throws a [StateError] if `this` is empty. - /// Otherwise may iterate through the elements and returns the last one - /// seen. - /// Some iterables may have more efficient ways to find the last element - /// (for example a list can directly access the last element, - /// without iterating through the previous ones). - E? get last => value?.last; - - /// Checks that this iterable has only one element, and returns that element. - /// - /// Throws a [StateError] if `this` is empty or has more than one element. - E? get single => value?.single; - - /// Returns the first element that satisfies the given predicate [test]. - /// - /// Iterates through elements and returns the first to satisfy [test]. - /// - /// Example: - /// ```dart - /// final numbers = [1, 2, 3, 5, 6, 7]; - /// var result = numbers.firstWhere((element) => element < 5); // 1 - /// result = numbers.firstWhere((element) => element > 5); // 6 - /// result = - /// numbers.firstWhere((element) => element > 10, orElse: () => -1); // -1 - /// ``` - /// - /// If no element satisfies [test], the result of invoking the [orElse] - /// function is returned. - /// If [orElse] is omitted, it defaults to throwing a [StateError]. - E? firstWhere(bool Function(E element) test, {E Function()? orElse}) => - value?.firstWhere(test, orElse: orElse); - - /// Returns the last element that satisfies the given predicate [test]. - /// - /// An iterable that can access its elements directly may check its - /// elements in any order (for example a list starts by checking the - /// last element and then moves towards the start of the list). - /// The default implementation iterates elements in iteration order, - /// checks `test(element)` for each, - /// and finally returns that last one that matched. - /// - /// Example: - /// ```dart - /// final numbers = [1, 2, 3, 5, 6, 7]; - /// var result = numbers.lastWhere((element) => element < 5); // 3 - /// result = numbers.lastWhere((element) => element > 5); // 7 - /// result = numbers.lastWhere((element) => element > 10, - /// orElse: () => -1); // -1 - /// ``` - /// - /// If no element satisfies [test], the result of invoking the [orElse] - /// function is returned. - /// If [orElse] is omitted, it defaults to throwing a [StateError]. - E? lastWhere(bool Function(E element) test, {E Function()? orElse}) => - value?.lastWhere(test, orElse: orElse); - - /// Returns the single element that satisfies [test]. - /// - /// Checks elements to see if `test(element)` returns true. - /// If exactly one element satisfies [test], that element is returned. - /// If more than one matching element is found, throws [StateError]. - /// If no matching element is found, returns the result of [orElse]. - /// If [orElse] is omitted, it defaults to throwing a [StateError]. - /// - /// Example: - /// ```dart - /// final numbers = [2, 2, 10]; - /// var result = numbers.singleWhere((element) => element > 5); // 10 - /// ``` - /// When no matching element is found, the result of calling [orElse] is - /// returned instead. - /// ```dart continued - /// result = numbers.singleWhere((element) => element == 1, - /// orElse: () => -1); // -1 - /// ``` - /// There must not be more than one matching element. - /// ```dart continued - /// result = numbers.singleWhere((element) => element == 2); // Throws Error. - /// ``` - E? singleWhere(bool Function(E element) test, {E Function()? orElse}) => - value?.singleWhere(test, orElse: orElse); - - /// Returns the [index]th element. - /// - /// The [index] must be non-negative and less than [length]. - /// Index zero represents the first element (so `iterable.elementAt(0)` is - /// equivalent to `iterable.first`). - /// - /// May iterate through the elements in iteration order, ignoring the - /// first [index] elements and then returning the next. - /// Some iterables may have a more efficient way to find the element. - /// - /// Example: - /// ```dart - /// final numbers = [1, 2, 3, 5, 6, 7]; - /// final elementAt = numbers.elementAt(4); // 6 - /// ``` - E? elementAt(int index) => value?.elementAt(index); -} diff --git a/packages/reactter/lib/src/obj/extensions/obj_list.dart b/packages/reactter/lib/src/obj/extensions/obj_list.dart deleted file mode 100644 index ddcdb509..00000000 --- a/packages/reactter/lib/src/obj/extensions/obj_list.dart +++ /dev/null @@ -1,1114 +0,0 @@ -// coverage:ignore-file -part of '../obj.dart'; - -extension ObjListExt on Obj> { - /// The object at the given [index] in the list. - /// - /// The [index] must be a valid index of this list, - /// which means that `index` must be non-negative and - /// less than [length]. - E operator [](int index) => value[index]; - - /// Sets the value at the given [index] in the list to [value]. - /// - /// The [index] must be a valid index of this list, - /// which means that `index` must be non-negative and - /// less than [length]. - void operator []=(int index, E valueToSet) => value[index] = valueToSet; - - /// Returns the concatenation of this list and [other]. - /// - /// Returns a new list containing the elements of this list followed by - /// the elements of [other]. - /// - /// The default behavior is to return a normal growable list. - /// Some list types may choose to return a list of the same type as themselves - /// (see [Uint8List.+]); - Obj> operator +(Obj> other) => Obj(value + other.value); - - /// Returns a view of this list as a list of [R] instances. - /// - /// If this list contains only instances of [R], all read operations - /// will work correctly. If any operation tries to read an element - /// that is not an instance of [R], the access will throw instead. - /// - /// Elements added to the list (e.g., by using [add] or [addAll]) - /// must be instances of [R] to be valid arguments to the adding function, - /// and they must also be instances of [E] to be accepted by - /// this list as well. - /// - /// Methods which accept `Object?` as argument, like [contains] and [remove], - /// will pass the argument directly to the this list's method - /// without any checks. - /// That means that you can do `listOfStrings.cast().remove("a")` - /// successfully, even if it looks like it shouldn't have any effect. - /// - /// Typically implemented as `List.castFrom(this)`. - List cast() => value.cast(); - - /// The first element of the list. - /// - /// The list must be non-empty when accessing its first element. - /// - /// The first element of a list can be modified, unlike an [Iterable]. - /// A `list.first` is equivalent to `list[0]`, - /// both for getting and setting the value. - /// - /// ```dart - /// final numbers = [1, 2, 3]; - /// print(numbers.first); // 1 - /// numbers.first = 10; - /// print(numbers.first); // 10 - /// numbers.clear(); - /// numbers.first; // Throws. - /// ``` - set first(E valueToSet) => value.first = valueToSet; - - /// The last element of the list. - /// - /// The list must be non-empty when accessing its last element. - /// - /// The last element of a list can be modified, unlike an [Iterable]. - /// A `list.last` is equivalent to `theList[theList.length - 1]`, - /// both for getting and setting the value. - /// - /// ```dart - /// final numbers = [1, 2, 3]; - /// print(numbers.last); // 3 - /// numbers.last = 10; - /// print(numbers.last); // 10 - /// numbers.clear(); - /// numbers.last; // Throws. - /// ``` - set last(E valueToSet) => value.last = valueToSet; - - /// The number of objects in this list. - /// - /// The valid indices for a list are `0` through `length - 1`. - /// ```dart - /// final numbers = [1, 2, 3]; - /// print(numbers.length); // 3 - /// ``` - int get length => value.length; - - /// Setting the `length` changes the number of elements in the list. - /// - /// The list must be growable. - /// If [newLength] is greater than current length, - /// new entries are initialized to `null`, - /// so [newLength] must not be greater than the current length - /// if the element type [E] is non-nullable. - /// - /// ```dart - /// final maybeNumbers = [1, null, 3]; - /// maybeNumbers.length = 5; - /// print(maybeNumbers); // [1, null, 3, null, null] - /// maybeNumbers.length = 2; - /// print(maybeNumbers); // [1, null] - /// - /// final numbers = [1, 2, 3]; - /// numbers.length = 1; - /// print(numbers); // [1] - /// numbers.length = 5; // Throws, cannot add `null`s. - /// ``` - set length(int newLength) => value.length = newLength; - - /// Adds [value] to the end of this list, - /// extending the length by one. - /// - /// The list must be growable. - /// - /// ```dart - /// final numbers = [1, 2, 3]; - /// numbers.add(4); - /// print(numbers); // [1, 2, 3, 4] - /// ``` - void add(E valueToAdd) => value.add(valueToAdd); - - /// Appends all objects of [iterable] to the end of this list. - /// - /// Extends the length of the list by the number of objects in [iterable]. - /// The list must be growable. - /// - /// ```dart - /// final numbers = [1, 2, 3]; - /// numbers.addAll([4, 5, 6]); - /// print(numbers); // [1, 2, 3, 4, 5, 6] - /// ``` - void addAll(Iterable iterable) => value.addAll(iterable); - - /// An [Iterable] of the objects in this list in reverse order. - /// ```dart - /// final numbers = ['two', 'three', 'four']; - /// final reverseOrder = numbers.reversed; - /// print(reverseOrder.toList()); // [four, three, two] - /// ``` - Iterable get reversed => value.reversed; - - /// Sorts this list according to the order specified by the [compare] function. - /// - /// The [compare] function must act as a [Comparator]. - /// ```dart - /// final numbers = ['two', 'three', 'four']; - /// // Sort from shortest to longest. - /// numbers.sort((a, b) => a.length.compareTo(b.length)); - /// print(numbers); // [two, four, three] - /// ``` - /// The default [List] implementations use [Comparable.compare] if - /// [compare] is omitted. - /// ```dart - /// final numbers = [13, 2, -11, 0]; - /// numbers.sort(); - /// print(numbers); // [-11, 0, 2, 13] - /// ``` - /// In that case, the elements of the list must be [Comparable] to - /// each other. - /// - /// A [Comparator] may compare objects as equal (return zero), even if they - /// are distinct objects. - /// The sort function is not guaranteed to be stable, so distinct objects - /// that compare as equal may occur in any order in the result: - /// ```dart - /// final numbers = ['one', 'two', 'three', 'four']; - /// numbers.sort((a, b) => a.length.compareTo(b.length)); - /// print(numbers); // [one, two, four, three] OR [two, one, four, three] - /// ``` - void sort([int Function(E a, E b)? compare]) => value.sort(compare); - - /// Shuffles the elements of this list randomly. - /// ```dart - /// final numbers = [1, 2, 3, 4, 5]; - /// numbers.shuffle(); - /// print(numbers); // [1, 3, 4, 5, 2] OR some other random result. - /// ``` - void shuffle([Random? random]) => value.shuffle(random); - - /// The first index of [element] in this list. - /// - /// Searches the list from index [start] to the end of the list. - /// The first time an object `o` is encountered so that `o == element`, - /// the index of `o` is returned. - /// ```dart - /// final notes = ['do', 're', 'mi', 're']; - /// print(notes.indexOf('re')); // 1 - /// - /// final indexWithStart = notes.indexOf('re', 2); // 3 - /// ``` - /// Returns -1 if [element] is not found. - /// ```dart - /// final notes = ['do', 're', 'mi', 're']; - /// final index = notes.indexOf('fa'); // -1 - /// ``` - int indexOf(E element, [int start = 0]) => value.indexOf(element, start); - - /// The first index in the list that satisfies the provided [test]. - /// - /// Searches the list from index [start] to the end of the list. - /// The first time an object `o` is encountered so that `test(o)` is true, - /// the index of `o` is returned. - /// - /// ```dart - /// final notes = ['do', 're', 'mi', 're']; - /// final first = notes.indexWhere((note) => note.startsWith('r')); // 1 - /// final second = notes.indexWhere((note) => note.startsWith('r'), 2); // 3 - /// ``` - /// - /// Returns -1 if [element] is not found. - /// ```dart - /// final notes = ['do', 're', 'mi', 're']; - /// final index = notes.indexWhere((note) => note.startsWith('k')); // -1 - /// ``` - int indexWhere(bool Function(E element) test, [int start = 0]) => - value.indexWhere(test); - - /// The last index in the list that satisfies the provided [test]. - /// - /// Searches the list from index [start] to 0. - /// The first time an object `o` is encountered so that `test(o)` is true, - /// the index of `o` is returned. - /// If [start] is omitted, it defaults to the [length] of the list. - /// - /// ```dart - /// final notes = ['do', 're', 'mi', 're']; - /// final first = notes.lastIndexWhere((note) => note.startsWith('r')); // 3 - /// final second = notes.lastIndexWhere((note) => note.startsWith('r'), - /// 2); // 1 - /// ``` - /// - /// Returns -1 if [element] is not found. - /// ```dart - /// final notes = ['do', 're', 'mi', 're']; - /// final index = notes.lastIndexWhere((note) => note.startsWith('k')); - /// print(index); // -1 - /// ``` - int lastIndexWhere(bool Function(E element) test, [int? start]) => - value.lastIndexWhere(test, start); - - /// The last index of [element] in this list. - /// - /// Searches the list backwards from index [start] to 0. - /// - /// The first time an object `o` is encountered so that `o == element`, - /// the index of `o` is returned. - /// ```dart - /// final notes = ['do', 're', 'mi', 're']; - /// const startIndex = 2; - /// final index = notes.lastIndexOf('re', startIndex); // 1 - /// ``` - /// If [start] is not provided, this method searches from the end of the - /// list. - /// ```dart - /// final notes = ['do', 're', 'mi', 're']; - /// final index = notes.lastIndexOf('re'); // 3 - /// ``` - /// Returns -1 if [element] is not found. - /// ```dart - /// final notes = ['do', 're', 'mi', 're']; - /// final index = notes.lastIndexOf('fa'); // -1 - /// ``` - int lastIndexOf(E element, [int? start]) => value.lastIndexOf(element, start); - - /// Removes all objects from this list; the length of the list becomes zero. - /// - /// The list must be growable. - /// - /// ```dart - /// final numbers = [1, 2, 3]; - /// numbers.clear(); - /// print(numbers.length); // 0 - /// print(numbers); // [] - /// ``` - void clear() => value.clear(); - - /// Inserts [element] at position [index] in this list. - /// - /// This increases the length of the list by one and shifts all objects - /// at or after the index towards the end of the list. - /// - /// The list must be growable. - /// The [index] value must be non-negative and no greater than [length]. - /// - /// ```dart - /// final numbers = [1, 2, 3, 4]; - /// const index = 2; - /// numbers.insert(index, 10); - /// print(numbers); // [1, 2, 10, 3, 4] - /// ``` - void insert(int index, E element) => value.insert(index, element); - - /// Inserts all objects of [iterable] at position [index] in this list. - /// - /// This increases the length of the list by the length of [iterable] and - /// shifts all later objects towards the end of the list. - /// - /// The list must be growable. - /// The [index] value must be non-negative and no greater than [length]. - /// ```dart - /// final numbers = [1, 2, 3, 4]; - /// final insertItems = [10, 11]; - /// numbers.insertAll(2, insertItems); - /// print(numbers); // [1, 2, 10, 11, 3, 4] - /// ``` - void insertAll(int index, Iterable iterable) => - value.insertAll(index, iterable); - - /// Overwrites elements with the objects of [iterable]. - /// - /// The elements of [iterable] are written into this list, - /// starting at position [index]. - /// This operation does not increase the length of the list. - /// - /// The [index] must be non-negative and no greater than [length]. - /// - /// The [iterable] must not have more elements than what can fit from [index] - /// to [length]. - /// - /// If `iterable` is based on this list, its values may change _during_ the - /// `setAll` operation. - /// ```dart - /// final list = ['a', 'b', 'c', 'd']; - /// list.setAll(1, ['bee', 'sea']); - /// print(list); // [a, bee, sea, d] - /// ``` - void setAll(int index, Iterable iterable) => value.setAll(index, iterable); - - /// Removes the first occurrence of [value] from this list. - /// - /// Returns true if [value] was in the list, false otherwise. - /// The list must be growable. - /// - /// ```dart - /// final parts = ['head', 'shoulders', 'knees', 'toes']; - /// final retVal = parts.remove('head'); // true - /// print(parts); // [shoulders, knees, toes] - /// ``` - /// The method has no effect if [value] was not in the list. - /// ```dart - /// final parts = ['shoulders', 'knees', 'toes']; - /// // Note: 'head' has already been removed. - /// final retVal = parts.remove('head'); // false - /// print(parts); // [shoulders, knees, toes] - /// ``` - bool remove(Object? valueToRemove) => value.remove(valueToRemove); - - /// Removes the object at position [index] from this list. - /// - /// This method reduces the length of `this` by one and moves all later objects - /// down by one position. - /// - /// Returns the removed value. - /// - /// The [index] must be in the range `0 ≤ index < length`. - /// The list must be growable. - /// ```dart - /// final parts = ['head', 'shoulder', 'knees', 'toes']; - /// final retVal = parts.removeAt(2); // knees - /// print(parts); // [head, shoulder, toes] - /// ``` - E removeAt(int index) => value.removeAt(index); - - /// Removes and returns the last object in this list. - /// - /// The list must be growable and non-empty. - /// ```dart - /// final parts = ['head', 'shoulder', 'knees', 'toes']; - /// final retVal = parts.removeLast(); // toes - /// print(parts); // [head, shoulder, knees] - /// ``` - E removeLast() => value.removeLast(); - - /// Removes all objects from this list that satisfy [test]. - /// - /// An object `o` satisfies [test] if `test(o)` is true. - /// ```dart - /// final numbers = ['one', 'two', 'three', 'four']; - /// numbers.removeWhere((item) => item.length == 3); - /// print(numbers); // [three, four] - /// ``` - /// The list must be growable. - void removeWhere(bool Function(E element) test) => value.removeWhere(test); - - /// Removes all objects from this list that fail to satisfy [test]. - /// - /// An object `o` satisfies [test] if `test(o)` is true. - /// ```dart - /// final numbers = ['one', 'two', 'three', 'four']; - /// numbers.retainWhere((item) => item.length == 3); - /// print(numbers); // [one, two] - /// ``` - /// The list must be growable. - void retainWhere(bool Function(E element) test) => value.retainWhere(test); - - /// Returns a new list containing the elements between [start] and [end]. - /// - /// The new list is a `List` containing the elements of this list at - /// positions greater than or equal to [start] and less than [end] in the same - /// order as they occur in this list. - /// - /// ```dart - /// final colors = ['red', 'green', 'blue', 'orange', 'pink']; - /// print(colors.sublist(1, 3)); // [green, blue] - /// ``` - /// - /// If [end] is omitted, it defaults to the [length] of this list. - /// - /// ```dart - /// final colors = ['red', 'green', 'blue', 'orange', 'pink']; - /// print(colors.sublist(3)); // [orange, pink] - /// ``` - /// - /// The `start` and `end` positions must satisfy the relations - /// 0 ≤ `start` ≤ `end` ≤ [length]. - /// If `end` is equal to `start`, then the returned list is empty. - List sublist(int start, [int? end]) => value.sublist(start, end); - - /// Creates an [Iterable] that iterates over a range of elements. - /// - /// The returned iterable iterates over the elements of this list - /// with positions greater than or equal to [start] and less than [end]. - /// - /// The provided range, [start] and [end], must be valid at the time - /// of the call. - /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. - /// An empty range (with `end == start`) is valid. - /// - /// The returned [Iterable] behaves like `skip(start).take(end - start)`. - /// That is, it does *not* break if this list changes size, it just - /// ends early if it reaches the end of the list early - /// (if `end`, or even `start`, becomes greater than [length]). - /// ```dart - /// final colors = ['red', 'green', 'blue', 'orange', 'pink']; - /// final firstRange = colors.getRange(0, 3); - /// print(firstRange.join(', ')); // red, green, blue - /// - /// final secondRange = colors.getRange(2, 5); - /// print(secondRange.join(', ')); // blue, orange, pink - /// ``` - Iterable getRange(int start, int end) => value.getRange(start, end); - - /// Writes some elements of [iterable] into a range of this list. - /// - /// Copies the objects of [iterable], skipping [skipCount] objects first, - /// into the range from [start], inclusive, to [end], exclusive, of this list. - /// ```dart - /// final list1 = [1, 2, 3, 4]; - /// final list2 = [5, 6, 7, 8, 9]; - /// // Copies the 4th and 5th items in list2 as the 2nd and 3rd items - /// // of list1. - /// const skipCount = 3; - /// list1.setRange(1, 3, list2, skipCount); - /// print(list1); // [1, 8, 9, 4] - /// ``` - /// The provided range, given by [start] and [end], must be valid. - /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. - /// An empty range (with `end == start`) is valid. - /// - /// The [iterable] must have enough objects to fill the range from `start` - /// to `end` after skipping [skipCount] objects. - /// - /// If `iterable` is this list, the operation correctly copies the elements - /// originally in the range from `skipCount` to `skipCount + (end - start)` to - /// the range `start` to `end`, even if the two ranges overlap. - /// - /// If `iterable` depends on this list in some other way, no guarantees are - /// made. - void setRange(int start, int end, Iterable iterable, - [int skipCount = 0]) => - value.setRange(start, end, iterable, skipCount); - - /// Removes a range of elements from the list. - /// - /// Removes the elements with positions greater than or equal to [start] - /// and less than [end], from the list. - /// This reduces the list's length by `end - start`. - /// - /// The provided range, given by [start] and [end], must be valid. - /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. - /// An empty range (with `end == start`) is valid. - /// - /// The list must be growable. - /// ```dart - /// final numbers = [1, 2, 3, 4, 5]; - /// numbers.removeRange(1, 4); - /// print(numbers); // [1, 5] - /// ``` - void removeRange(int start, int end) => value.removeRange(start, end); - - /// Overwrites a range of elements with [fillValue]. - /// - /// Sets the positions greater than or equal to [start] and less than [end], - /// to [fillValue]. - /// - /// The provided range, given by [start] and [end], must be valid. - /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. - /// An empty range (with `end == start`) is valid. - /// - /// If the element type is not nullable, the [fillValue] must be provided and - /// must be non-`null`. - /// - /// Example: - /// ```dart - /// final words = List.filled(5, 'old'); - /// print(words); // [old, old, old, old, old] - /// words.fillRange(1, 3, 'new'); - /// print(words); // [old, new, new, old, old] - /// ``` - void fillRange(int start, int end, [E? fillValue]) => - value.fillRange(start, end, fillValue); - - /// Replaces a range of elements with the elements of [replacements]. - /// - /// Removes the objects in the range from [start] to [end], - /// then inserts the elements of [replacements] at [start]. - /// ```dart - /// final numbers = [1, 2, 3, 4, 5]; - /// final replacements = [6, 7]; - /// numbers.replaceRange(1, 4, replacements); - /// print(numbers); // [1, 6, 7, 5] - /// ``` - /// The provided range, given by [start] and [end], must be valid. - /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. - /// An empty range (with `end == start`) is valid. - /// - /// The operation `list.replaceRange(start, end, replacements)` - /// is roughly equivalent to: - /// ```dart - /// final numbers = [1, 2, 3, 4, 5]; - /// numbers.removeRange(1, 4); - /// final replacements = [6, 7]; - /// numbers.insertAll(1, replacements); - /// print(numbers); // [1, 6, 7, 5] - /// ``` - /// but may be more efficient. - /// - /// The list must be growable. - /// This method does not work on fixed-length lists, even when [replacements] - /// has the same number of elements as the replaced range. In that case use - /// [setRange] instead. - void replaceRange(int start, int end, Iterable replacements) => - value.replaceRange(start, end, replacements); - - /// An unmodifiable [Map] view of this list. - /// - /// The map uses the indices of this list as keys and the corresponding objects - /// as values. The `Map.keys` [Iterable] iterates the indices of this list - /// in numerical order. - /// ```dart - /// var words = ['fee', 'fi', 'fo', 'fum']; - /// var map = words.asMap(); // {0: fee, 1: fi, 2: fo, 3: fum} - /// map.keys.toList(); // [0, 1, 2, 3] - /// ``` - Map asMap() => value.asMap(); -} - -extension ObjListNullExt on Obj?> { - /// The object at the given [index] in the list. - /// - /// The [index] must be a valid index of this list, - /// which means that `index` must be non-negative and - /// less than [length]. - E? operator [](int index) => value?[index]; - - /// Sets the value at the given [index] in the list to [value]. - /// - /// The [index] must be a valid index of this list, - /// which means that `index` must be non-negative and - /// less than [length]. - void operator []=(int index, E valueToSet) => value?[index] = valueToSet; - - /// Returns a view of this list as a list of [R] instances. - /// - /// If this list contains only instances of [R], all read operations - /// will work correctly. If any operation tries to read an element - /// that is not an instance of [R], the access will throw instead. - /// - /// Elements added to the list (e.g., by using [add] or [addAll]) - /// must be instances of [R] to be valid arguments to the adding function, - /// and they must also be instances of [E] to be accepted by - /// this list as well. - /// - /// Methods which accept `Object?` as argument, like [contains] and [remove], - /// will pass the argument directly to the this list's method - /// without any checks. - /// That means that you can do `listOfStrings.cast().remove("a")` - /// successfully, even if it looks like it shouldn't have any effect. - /// - /// Typically implemented as `List.castFrom(this)`. - List? cast() => value?.cast(); - - /// The first element of the list. - /// - /// The list must be non-empty when accessing its first element. - /// - /// The first element of a list can be modified, unlike an [Iterable]. - /// A `list.first` is equivalent to `list[0]`, - /// both for getting and setting the value. - /// - /// ```dart - /// final numbers = [1, 2, 3]; - /// print(numbers.first); // 1 - /// numbers.first = 10; - /// print(numbers.first); // 10 - /// numbers.clear(); - /// numbers.first; // Throws. - /// ``` - set first(E valueToSet) => value?.first = valueToSet; - - /// The last element of the list. - /// - /// The list must be non-empty when accessing its last element. - /// - /// The last element of a list can be modified, unlike an [Iterable]. - /// A `list.last` is equivalent to `theList[theList.length - 1]`, - /// both for getting and setting the value. - /// - /// ```dart - /// final numbers = [1, 2, 3]; - /// print(numbers.last); // 3 - /// numbers.last = 10; - /// print(numbers.last); // 10 - /// numbers.clear(); - /// numbers.last; // Throws. - /// ``` - set last(E valueToSet) => value?.last = valueToSet; - - /// The number of objects in this list. - /// - /// The valid indices for a list are `0` through `length - 1`. - /// ```dart - /// final numbers = [1, 2, 3]; - /// print(numbers.length); // 3 - /// ``` - int get length => value?.length ?? 0; - - /// Setting the `length` changes the number of elements in the list. - /// - /// The list must be growable. - /// If [newLength] is greater than current length, - /// new entries are initialized to `null`, - /// so [newLength] must not be greater than the current length - /// if the element type [E] is non-nullable. - /// - /// ```dart - /// final maybeNumbers = [1, null, 3]; - /// maybeNumbers.length = 5; - /// print(maybeNumbers); // [1, null, 3, null, null] - /// maybeNumbers.length = 2; - /// print(maybeNumbers); // [1, null] - /// - /// final numbers = [1, 2, 3]; - /// numbers.length = 1; - /// print(numbers); // [1] - /// numbers.length = 5; // Throws, cannot add `null`s. - /// ``` - set length(int newLength) => value?.length = newLength; - - /// Adds [value] to the end of this list, - /// extending the length by one. - /// - /// The list must be growable. - /// - /// ```dart - /// final numbers = [1, 2, 3]; - /// numbers.add(4); - /// print(numbers); // [1, 2, 3, 4] - /// ``` - void add(E valueToAdd) => value?.add(valueToAdd); - - /// Appends all objects of [iterable] to the end of this list. - /// - /// Extends the length of the list by the number of objects in [iterable]. - /// The list must be growable. - /// - /// ```dart - /// final numbers = [1, 2, 3]; - /// numbers.addAll([4, 5, 6]); - /// print(numbers); // [1, 2, 3, 4, 5, 6] - /// ``` - void addAll(Iterable iterable) => value?.addAll(iterable); - - /// An [Iterable] of the objects in this list in reverse order. - /// ```dart - /// final numbers = ['two', 'three', 'four']; - /// final reverseOrder = numbers.reversed; - /// print(reverseOrder.toList()); // [four, three, two] - /// ``` - Iterable? get reversed => value?.reversed; - - /// Sorts this list according to the order specified by the [compare] function. - /// - /// The [compare] function must act as a [Comparator]. - /// ```dart - /// final numbers = ['two', 'three', 'four']; - /// // Sort from shortest to longest. - /// numbers.sort((a, b) => a.length.compareTo(b.length)); - /// print(numbers); // [two, four, three] - /// ``` - /// The default [List] implementations use [Comparable.compare] if - /// [compare] is omitted. - /// ```dart - /// final numbers = [13, 2, -11, 0]; - /// numbers.sort(); - /// print(numbers); // [-11, 0, 2, 13] - /// ``` - /// In that case, the elements of the list must be [Comparable] to - /// each other. - /// - /// A [Comparator] may compare objects as equal (return zero), even if they - /// are distinct objects. - /// The sort function is not guaranteed to be stable, so distinct objects - /// that compare as equal may occur in any order in the result: - /// ```dart - /// final numbers = ['one', 'two', 'three', 'four']; - /// numbers.sort((a, b) => a.length.compareTo(b.length)); - /// print(numbers); // [one, two, four, three] OR [two, one, four, three] - /// ``` - void sort([int Function(E a, E b)? compare]) => value?.sort(compare); - - /// Shuffles the elements of this list randomly. - /// ```dart - /// final numbers = [1, 2, 3, 4, 5]; - /// numbers.shuffle(); - /// print(numbers); // [1, 3, 4, 5, 2] OR some other random result. - /// ``` - void shuffle([Random? random]) => value?.shuffle(random); - - /// The first index of [element] in this list. - /// - /// Searches the list from index [start] to the end of the list. - /// The first time an object `o` is encountered so that `o == element`, - /// the index of `o` is returned. - /// ```dart - /// final notes = ['do', 're', 'mi', 're']; - /// print(notes.indexOf('re')); // 1 - /// - /// final indexWithStart = notes.indexOf('re', 2); // 3 - /// ``` - /// Returns -1 if [element] is not found. - /// ```dart - /// final notes = ['do', 're', 'mi', 're']; - /// final index = notes.indexOf('fa'); // -1 - /// ``` - int? indexOf(E element, [int start = 0]) => value?.indexOf(element, start); - - /// The first index in the list that satisfies the provided [test]. - /// - /// Searches the list from index [start] to the end of the list. - /// The first time an object `o` is encountered so that `test(o)` is true, - /// the index of `o` is returned. - /// - /// ```dart - /// final notes = ['do', 're', 'mi', 're']; - /// final first = notes.indexWhere((note) => note.startsWith('r')); // 1 - /// final second = notes.indexWhere((note) => note.startsWith('r'), 2); // 3 - /// ``` - /// - /// Returns -1 if [element] is not found. - /// ```dart - /// final notes = ['do', 're', 'mi', 're']; - /// final index = notes.indexWhere((note) => note.startsWith('k')); // -1 - /// ``` - int? indexWhere(bool Function(E element) test, [int start = 0]) => - value?.indexWhere(test); - - /// The last index in the list that satisfies the provided [test]. - /// - /// Searches the list from index [start] to 0. - /// The first time an object `o` is encountered so that `test(o)` is true, - /// the index of `o` is returned. - /// If [start] is omitted, it defaults to the [length] of the list. - /// - /// ```dart - /// final notes = ['do', 're', 'mi', 're']; - /// final first = notes.lastIndexWhere((note) => note.startsWith('r')); // 3 - /// final second = notes.lastIndexWhere((note) => note.startsWith('r'), - /// 2); // 1 - /// ``` - /// - /// Returns -1 if [element] is not found. - /// ```dart - /// final notes = ['do', 're', 'mi', 're']; - /// final index = notes.lastIndexWhere((note) => note.startsWith('k')); - /// print(index); // -1 - /// ``` - int? lastIndexWhere(bool Function(E element) test, [int? start]) => - value?.lastIndexWhere(test, start); - - /// The last index of [element] in this list. - /// - /// Searches the list backwards from index [start] to 0. - /// - /// The first time an object `o` is encountered so that `o == element`, - /// the index of `o` is returned. - /// ```dart - /// final notes = ['do', 're', 'mi', 're']; - /// const startIndex = 2; - /// final index = notes.lastIndexOf('re', startIndex); // 1 - /// ``` - /// If [start] is not provided, this method searches from the end of the - /// list. - /// ```dart - /// final notes = ['do', 're', 'mi', 're']; - /// final index = notes.lastIndexOf('re'); // 3 - /// ``` - /// Returns -1 if [element] is not found. - /// ```dart - /// final notes = ['do', 're', 'mi', 're']; - /// final index = notes.lastIndexOf('fa'); // -1 - /// ``` - int? lastIndexOf(E element, [int? start]) => - value?.lastIndexOf(element, start); - - /// Removes all objects from this list; the length of the list becomes zero. - /// - /// The list must be growable. - /// - /// ```dart - /// final numbers = [1, 2, 3]; - /// numbers.clear(); - /// print(numbers.length); // 0 - /// print(numbers); // [] - /// ``` - void clear() => value?.clear(); - - /// Inserts [element] at position [index] in this list. - /// - /// This increases the length of the list by one and shifts all objects - /// at or after the index towards the end of the list. - /// - /// The list must be growable. - /// The [index] value must be non-negative and no greater than [length]. - /// - /// ```dart - /// final numbers = [1, 2, 3, 4]; - /// const index = 2; - /// numbers.insert(index, 10); - /// print(numbers); // [1, 2, 10, 3, 4] - /// ``` - void insert(int index, E element) => value?.insert(index, element); - - /// Inserts all objects of [iterable] at position [index] in this list. - /// - /// This increases the length of the list by the length of [iterable] and - /// shifts all later objects towards the end of the list. - /// - /// The list must be growable. - /// The [index] value must be non-negative and no greater than [length]. - /// ```dart - /// final numbers = [1, 2, 3, 4]; - /// final insertItems = [10, 11]; - /// numbers.insertAll(2, insertItems); - /// print(numbers); // [1, 2, 10, 11, 3, 4] - /// ``` - void insertAll(int index, Iterable iterable) => - value?.insertAll(index, iterable); - - /// Overwrites elements with the objects of [iterable]. - /// - /// The elements of [iterable] are written into this list, - /// starting at position [index]. - /// This operation does not increase the length of the list. - /// - /// The [index] must be non-negative and no greater than [length]. - /// - /// The [iterable] must not have more elements than what can fit from [index] - /// to [length]. - /// - /// If `iterable` is based on this list, its values may change _during_ the - /// `setAll` operation. - /// ```dart - /// final list = ['a', 'b', 'c', 'd']; - /// list.setAll(1, ['bee', 'sea']); - /// print(list); // [a, bee, sea, d] - /// ``` - void setAll(int index, Iterable iterable) => - value?.setAll(index, iterable); - - /// Removes the first occurrence of [value] from this list. - /// - /// Returns true if [value] was in the list, false otherwise. - /// The list must be growable. - /// - /// ```dart - /// final parts = ['head', 'shoulders', 'knees', 'toes']; - /// final retVal = parts.remove('head'); // true - /// print(parts); // [shoulders, knees, toes] - /// ``` - /// The method has no effect if [value] was not in the list. - /// ```dart - /// final parts = ['shoulders', 'knees', 'toes']; - /// // Note: 'head' has already been removed. - /// final retVal = parts.remove('head'); // false - /// print(parts); // [shoulders, knees, toes] - /// ``` - bool? remove(Object? valueToRemove) => value?.remove(valueToRemove); - - /// Removes the object at position [index] from this list. - /// - /// This method reduces the length of `this` by one and moves all later objects - /// down by one position. - /// - /// Returns the removed value. - /// - /// The [index] must be in the range `0 ≤ index < length`. - /// The list must be growable. - /// ```dart - /// final parts = ['head', 'shoulder', 'knees', 'toes']; - /// final retVal = parts.removeAt(2); // knees - /// print(parts); // [head, shoulder, toes] - /// ``` - E? removeAt(int index) => value?.removeAt(index); - - /// Removes and returns the last object in this list. - /// - /// The list must be growable and non-empty. - /// ```dart - /// final parts = ['head', 'shoulder', 'knees', 'toes']; - /// final retVal = parts.removeLast(); // toes - /// print(parts); // [head, shoulder, knees] - /// ``` - E? removeLast() => value?.removeLast(); - - /// Removes all objects from this list that satisfy [test]. - /// - /// An object `o` satisfies [test] if `test(o)` is true. - /// ```dart - /// final numbers = ['one', 'two', 'three', 'four']; - /// numbers.removeWhere((item) => item.length == 3); - /// print(numbers); // [three, four] - /// ``` - /// The list must be growable. - void removeWhere(bool Function(E element) test) => value?.removeWhere(test); - - /// Removes all objects from this list that fail to satisfy [test]. - /// - /// An object `o` satisfies [test] if `test(o)` is true. - /// ```dart - /// final numbers = ['one', 'two', 'three', 'four']; - /// numbers.retainWhere((item) => item.length == 3); - /// print(numbers); // [one, two] - /// ``` - /// The list must be growable. - void retainWhere(bool Function(E element) test) => value?.retainWhere(test); - - /// Returns a new list containing the elements between [start] and [end]. - /// - /// The new list is a `List` containing the elements of this list at - /// positions greater than or equal to [start] and less than [end] in the same - /// order as they occur in this list. - /// - /// ```dart - /// final colors = ['red', 'green', 'blue', 'orange', 'pink']; - /// print(colors.sublist(1, 3)); // [green, blue] - /// ``` - /// - /// If [end] is omitted, it defaults to the [length] of this list. - /// - /// ```dart - /// final colors = ['red', 'green', 'blue', 'orange', 'pink']; - /// print(colors.sublist(3)); // [orange, pink] - /// ``` - /// - /// The `start` and `end` positions must satisfy the relations - /// 0 ≤ `start` ≤ `end` ≤ [length]. - /// If `end` is equal to `start`, then the returned list is empty. - List? sublist(int start, [int? end]) => value?.sublist(start, end); - - /// Creates an [Iterable] that iterates over a range of elements. - /// - /// The returned iterable iterates over the elements of this list - /// with positions greater than or equal to [start] and less than [end]. - /// - /// The provided range, [start] and [end], must be valid at the time - /// of the call. - /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. - /// An empty range (with `end == start`) is valid. - /// - /// The returned [Iterable] behaves like `skip(start).take(end - start)`. - /// That is, it does *not* break if this list changes size, it just - /// ends early if it reaches the end of the list early - /// (if `end`, or even `start`, becomes greater than [length]). - /// ```dart - /// final colors = ['red', 'green', 'blue', 'orange', 'pink']; - /// final firstRange = colors.getRange(0, 3); - /// print(firstRange.join(', ')); // red, green, blue - /// - /// final secondRange = colors.getRange(2, 5); - /// print(secondRange.join(', ')); // blue, orange, pink - /// ``` - Iterable? getRange(int start, int end) => value?.getRange(start, end); - - /// Writes some elements of [iterable] into a range of this list. - /// - /// Copies the objects of [iterable], skipping [skipCount] objects first, - /// into the range from [start], inclusive, to [end], exclusive, of this list. - /// ```dart - /// final list1 = [1, 2, 3, 4]; - /// final list2 = [5, 6, 7, 8, 9]; - /// // Copies the 4th and 5th items in list2 as the 2nd and 3rd items - /// // of list1. - /// const skipCount = 3; - /// list1.setRange(1, 3, list2, skipCount); - /// print(list1); // [1, 8, 9, 4] - /// ``` - /// The provided range, given by [start] and [end], must be valid. - /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. - /// An empty range (with `end == start`) is valid. - /// - /// The [iterable] must have enough objects to fill the range from `start` - /// to `end` after skipping [skipCount] objects. - /// - /// If `iterable` is this list, the operation correctly copies the elements - /// originally in the range from `skipCount` to `skipCount + (end - start)` to - /// the range `start` to `end`, even if the two ranges overlap. - /// - /// If `iterable` depends on this list in some other way, no guarantees are - /// made. - void setRange(int start, int end, Iterable iterable, - [int skipCount = 0]) => - value?.setRange(start, end, iterable, skipCount); - - /// Removes a range of elements from the list. - /// - /// Removes the elements with positions greater than or equal to [start] - /// and less than [end], from the list. - /// This reduces the list's length by `end - start`. - /// - /// The provided range, given by [start] and [end], must be valid. - /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. - /// An empty range (with `end == start`) is valid. - /// - /// The list must be growable. - /// ```dart - /// final numbers = [1, 2, 3, 4, 5]; - /// numbers.removeRange(1, 4); - /// print(numbers); // [1, 5] - /// ``` - void removeRange(int start, int end) => value?.removeRange(start, end); - - /// Overwrites a range of elements with [fillValue]. - /// - /// Sets the positions greater than or equal to [start] and less than [end], - /// to [fillValue]. - /// - /// The provided range, given by [start] and [end], must be valid. - /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. - /// An empty range (with `end == start`) is valid. - /// - /// If the element type is not nullable, the [fillValue] must be provided and - /// must be non-`null`. - /// - /// Example: - /// ```dart - /// final words = List.filled(5, 'old'); - /// print(words); // [old, old, old, old, old] - /// words.fillRange(1, 3, 'new'); - /// print(words); // [old, new, new, old, old] - /// ``` - void fillRange(int start, int end, [E? fillValue]) => - value?.fillRange(start, end, fillValue); - - /// Replaces a range of elements with the elements of [replacements]. - /// - /// Removes the objects in the range from [start] to [end], - /// then inserts the elements of [replacements] at [start]. - /// ```dart - /// final numbers = [1, 2, 3, 4, 5]; - /// final replacements = [6, 7]; - /// numbers.replaceRange(1, 4, replacements); - /// print(numbers); // [1, 6, 7, 5] - /// ``` - /// The provided range, given by [start] and [end], must be valid. - /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. - /// An empty range (with `end == start`) is valid. - /// - /// The operation `list.replaceRange(start, end, replacements)` - /// is roughly equivalent to: - /// ```dart - /// final numbers = [1, 2, 3, 4, 5]; - /// numbers.removeRange(1, 4); - /// final replacements = [6, 7]; - /// numbers.insertAll(1, replacements); - /// print(numbers); // [1, 6, 7, 5] - /// ``` - /// but may be more efficient. - /// - /// The list must be growable. - /// This method does not work on fixed-length lists, even when [replacements] - /// has the same number of elements as the replaced range. In that case use - /// [setRange] instead. - void replaceRange(int start, int end, Iterable replacements) => - value?.replaceRange(start, end, replacements); - - /// An unmodifiable [Map] view of this list. - /// - /// The map uses the indices of this list as keys and the corresponding objects - /// as values. The `Map.keys` [Iterable] iterates the indices of this list - /// in numerical order. - /// ```dart - /// var words = ['fee', 'fi', 'fo', 'fum']; - /// var map = words.asMap(); // {0: fee, 1: fi, 2: fo, 3: fum} - /// map.keys.toList(); // [0, 1, 2, 3] - /// ``` - Map? asMap() => value?.asMap(); -} diff --git a/packages/reactter/lib/src/obj/extensions/obj_map.dart b/packages/reactter/lib/src/obj/extensions/obj_map.dart deleted file mode 100644 index fa3b931f..00000000 --- a/packages/reactter/lib/src/obj/extensions/obj_map.dart +++ /dev/null @@ -1,500 +0,0 @@ -// coverage:ignore-file -part of '../obj.dart'; - -extension ObjMapExt on Obj> { - /// Provides a view of this map as having [RK] keys and [RV] instances, - /// if necessary. - /// - /// If this map is already a `Map`, it is returned unchanged. - /// - /// If this set contains only keys of type [RK] and values of type [RV], - /// all read operations will work correctly. - /// If any operation exposes a non-[RK] key or non-[RV] value, - /// the operation will throw instead. - /// - /// Entries added to the map must be valid for both a `Map` and a - /// `Map`. - /// - /// Methods which accept `Object?` as argument, - /// like [containsKey], [remove] and [operator []], - /// will pass the argument directly to the this map's method - /// without any checks. - /// That means that you can do `mapWithStringKeys.cast().remove("a")` - /// successfully, even if it looks like it shouldn't have any effect. - Map cast() => value.cast(); - - /// Whether this map contains the given [value]. - /// - /// Returns true if any of the values in the map are equal to `value` - /// according to the `==` operator. - /// ```dart - /// final moonCount = {'Mercury': 0, 'Venus': 0, 'Earth': 1, - /// 'Mars': 2, 'Jupiter': 79, 'Saturn': 82, 'Uranus': 27, 'Neptune': 14 }; - /// final moons3 = moonCount.containsValue(3); // false - /// final moons82 = moonCount.containsValue(82); // true - /// ``` - bool containsValue(Object? valueToEvaluate) => - value.containsValue(valueToEvaluate); - - /// Whether this map contains the given [key]. - /// - /// Returns true if any of the keys in the map are equal to `key` - /// according to the equality used by the map. - /// ```dart - /// final moonCount = {'Mercury': 0, 'Venus': 0, 'Earth': 1, - /// 'Mars': 2, 'Jupiter': 79, 'Saturn': 82, 'Uranus': 27, 'Neptune': 14 }; - /// final containsUranus = moonCount.containsKey('Uranus'); // true - /// final containsPluto = moonCount.containsKey('Pluto'); // false - /// ``` - bool containsKey(Object? key) => value.containsKey(key); - - /// The value for the given [key], or `null` if [key] is not in the map. - /// - /// Some maps allow `null` as a value. - /// For those maps, a lookup using this operator cannot distinguish between a - /// key not being in the map, and the key being there with a `null` value. - /// Methods like [containsKey] or [putIfAbsent] can be used if the distinction - /// is important. - V? operator [](Object? key) => value[key]; - - /// Associates the [key] with the given [value]. - /// - /// If the key was already in the map, its associated value is changed. - /// Otherwise the key/value pair is added to the map. - void operator []=(K key, V valueToSet) => value[key] = valueToSet; - - /// The map entries of [this]. - Iterable> get entries => value.entries; - - /// Returns a new map where all entries of this map are transformed by - /// the given [convert] function. - Map map(MapEntry Function(K key, V value) convert) => - value.map(convert); - - /// Adds all key/value pairs of [newEntries] to this map. - /// - /// If a key of [newEntries] is already in this map, - /// the corresponding value is overwritten. - /// - /// The operation is equivalent to doing `this[entry.key] = entry.value` - /// for each [MapEntry] of the iterable. - /// ```dart - /// final planets = {1: 'Mercury', 2: 'Venus', - /// 3: 'Earth', 4: 'Mars'}; - /// final gasGiants = {5: 'Jupiter', 6: 'Saturn'}; - /// final iceGiants = {7: 'Uranus', 8: 'Neptune'}; - /// planets.addEntries(gasGiants.entries); - /// planets.addEntries(iceGiants.entries); - /// print(planets); - /// // {1: Mercury, 2: Venus, 3: Earth, 4: Mars, 5: Jupiter, 6: Saturn, - /// // 7: Uranus, 8: Neptune} - /// ``` - void addEntries(Iterable> newEntries) => - value.addEntries(newEntries); - - /// Updates the value for the provided [key]. - /// - /// Returns the new value associated with the key. - /// - /// If the key is present, invokes [update] with the current value and stores - /// the new value in the map. - /// - /// If the key is not present and [ifAbsent] is provided, calls [ifAbsent] - /// and adds the key with the returned value to the map. - /// - /// If the key is not present, [ifAbsent] must be provided. - /// ```dart - /// final planetsFromSun = {1: 'Mercury', 2: 'unknown', - /// 3: 'Earth'}; - /// // Update value for known key value 2. - /// planetsFromSun.update(2, (value) => 'Venus'); - /// print(planetsFromSun); // {1: Mercury, 2: Venus, 3: Earth} - /// - /// final largestPlanets = {1: 'Jupiter', 2: 'Saturn', - /// 3: 'Neptune'}; - /// // Key value 8 is missing from list, add it using [ifAbsent]. - /// largestPlanets.update(8, (value) => 'New', ifAbsent: () => 'Mercury'); - /// print(largestPlanets); // {1: Jupiter, 2: Saturn, 3: Neptune, 8: Mercury} - /// ``` - V updateMap(K key, V Function(V value) update, {V Function()? ifAbsent}) => - value.update(key, update, ifAbsent: ifAbsent); - - /// Updates all values. - /// - /// Iterates over all entries in the map and updates them with the result - /// of invoking [update]. - /// ```dart - /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; - /// terrestrial.updateAll((key, value) => value.toUpperCase()); - /// print(terrestrial); // {1: MERCURY, 2: VENUS, 3: EARTH} - /// ``` - void updateAll(V Function(K key, V value) update) => value.updateAll(update); - - /// Removes all entries of this map that satisfy the given [test]. - /// ```dart - /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; - /// terrestrial.removeWhere((key, value) => value.startsWith('E')); - /// print(terrestrial); // {1: Mercury, 2: Venus} - /// ``` - void removeWhere(bool Function(K key, V value) test) => - value.removeWhere(test); - - /// Look up the value of [key], or add a new entry if it isn't there. - /// - /// Returns the value associated to [key], if there is one. - /// Otherwise calls [ifAbsent] to get a new value, associates [key] to - /// that value, and then returns the new value. - /// ```dart - /// final diameters = {1.0: 'Earth'}; - /// final otherDiameters = {0.383: 'Mercury', 0.949: 'Venus'}; - /// - /// for (final item in otherDiameters.entries) { - /// diameters.putIfAbsent(item.key, () => item.value); - /// } - /// print(diameters); // {1.0: Earth, 0.383: Mercury, 0.949: Venus} - /// - /// // If the key already exists, the current value is returned. - /// final result = diameters.putIfAbsent(0.383, () => 'Random'); - /// print(result); // Mercury - /// print(diameters); // {1.0: Earth, 0.383: Mercury, 0.949: Venus} - /// ``` - /// Calling [ifAbsent] must not add or remove keys from the map. - V putIfAbsent(K key, V Function() ifAbsent) => - value.putIfAbsent(key, ifAbsent); - - /// Adds all key/value pairs of [other] to this map. - /// - /// If a key of [other] is already in this map, its value is overwritten. - /// - /// The operation is equivalent to doing `this[key] = value` for each key - /// and associated value in other. It iterates over [other], which must - /// therefore not change during the iteration. - /// ```dart - /// final planets = {1: 'Mercury', 2: 'Earth'}; - /// planets.addAll({5: 'Jupiter', 6: 'Saturn'}); - /// print(planets); // {1: Mercury, 2: Earth, 5: Jupiter, 6: Saturn} - /// ``` - void addAll(Map other) => value.addAll(other); - - /// Removes [key] and its associated value, if present, from the map. - /// - /// Returns the value associated with `key` before it was removed. - /// Returns `null` if `key` was not in the map. - /// - /// Note that some maps allow `null` as a value, - /// so a returned `null` value doesn't always mean that the key was absent. - /// ```dart - /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; - /// final removedValue = terrestrial.remove(2); // Venus - /// print(terrestrial); // {1: Mercury, 3: Earth} - /// ``` - V? remove(Object? key) => value.remove(key); - - /// Removes all entries from the map. - /// - /// After this, the map is empty. - /// ```dart - /// final planets = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; - /// planets.clear(); // {} - /// ``` - void clear() => value.clear(); - - /// Applies [action] to each key/value pair of the map. - /// - /// Calling `action` must not add or remove keys from the map. - /// ```dart - /// final planetsByMass = {0.81: 'Venus', 1: 'Earth', - /// 0.11: 'Mars', 17.15: 'Neptune'}; - /// - /// planetsByMass.forEach((key, value) { - /// print('$key: $value'); - /// // 0.81: Venus - /// // 1: Earth - /// // 0.11: Mars - /// // 17.15: Neptune - /// }); - /// ``` - void forEach(void Function(K key, V value) action) => value.forEach(action); - - /// The keys of [this]. - /// - /// The returned iterable has efficient `length` and `contains` operations, - /// based on [length] and [containsKey] of the map. - /// - /// The order of iteration is defined by the individual `Map` implementation, - /// but must be consistent between changes to the map. - /// - /// Modifying the map while iterating the keys may break the iteration. - Iterable get keys => value.keys; - - /// The values of [this]. - /// - /// The values are iterated in the order of their corresponding keys. - /// This means that iterating [keys] and [values] in parallel will - /// provide matching pairs of keys and values. - /// - /// The returned iterable has an efficient `length` method based on the - /// [length] of the map. Its [Iterable.contains] method is based on - /// `==` comparison. - /// - /// Modifying the map while iterating the values may break the iteration. - Iterable get values => value.values; - - /// The number of key/value pairs in the map. - int get length => value.length; - - /// Whether there is no key/value pair in the map. - bool get isEmpty => value.isEmpty; - - /// Whether there is at least one key/value pair in the map. - bool get isNotEmpty => value.isNotEmpty; -} - -extension ObjMapNullExt on Obj?> { - /// Provides a view of this map as having [RK] keys and [RV] instances, - /// if necessary. - /// - /// If this map is already a `Map`, it is returned unchanged. - /// - /// If this set contains only keys of type [RK] and values of type [RV], - /// all read operations will work correctly. - /// If any operation exposes a non-[RK] key or non-[RV] value, - /// the operation will throw instead. - /// - /// Entries added to the map must be valid for both a `Map` and a - /// `Map`. - /// - /// Methods which accept `Object?` as argument, - /// like [containsKey], [remove] and [operator []], - /// will pass the argument directly to the this map's method - /// without any checks. - /// That means that you can do `mapWithStringKeys.cast().remove("a")` - /// successfully, even if it looks like it shouldn't have any effect. - Map? cast() => value?.cast(); - - /// Whether this map contains the given [value]. - /// - /// Returns true if any of the values in the map are equal to `value` - /// according to the `==` operator. - /// ```dart - /// final moonCount = {'Mercury': 0, 'Venus': 0, 'Earth': 1, - /// 'Mars': 2, 'Jupiter': 79, 'Saturn': 82, 'Uranus': 27, 'Neptune': 14 }; - /// final moons3 = moonCount.containsValue(3); // false - /// final moons82 = moonCount.containsValue(82); // true - /// ``` - bool? containsValue(Object? valueToEvaluate) => - value?.containsValue(valueToEvaluate); - - /// Whether this map contains the given [key]. - /// - /// Returns true if any of the keys in the map are equal to `key` - /// according to the equality used by the map. - /// ```dart - /// final moonCount = {'Mercury': 0, 'Venus': 0, 'Earth': 1, - /// 'Mars': 2, 'Jupiter': 79, 'Saturn': 82, 'Uranus': 27, 'Neptune': 14 }; - /// final containsUranus = moonCount.containsKey('Uranus'); // true - /// final containsPluto = moonCount.containsKey('Pluto'); // false - /// ``` - bool? containsKey(Object? key) => value?.containsKey(key); - - /// The value for the given [key], or `null` if [key] is not in the map. - /// - /// Some maps allow `null` as a value. - /// For those maps, a lookup using this operator cannot distinguish between a - /// key not being in the map, and the key being there with a `null` value. - /// Methods like [containsKey] or [putIfAbsent] can be used if the distinction - /// is important. - V? operator [](Object? key) => value?[key]; - - /// Associates the [key] with the given [value]. - /// - /// If the key was already in the map, its associated value is changed. - /// Otherwise the key/value pair is added to the map. - void operator []=(K key, V valueToSet) => value?[key] = valueToSet; - - /// The map entries of [this]. - Iterable>? get entries => value?.entries; - - /// Returns a new map where all entries of this map are transformed by - /// the given [convert] function. - Map? map(MapEntry Function(K key, V value) convert) => - value?.map(convert); - - /// Adds all key/value pairs of [newEntries] to this map. - /// - /// If a key of [newEntries] is already in this map, - /// the corresponding value is overwritten. - /// - /// The operation is equivalent to doing `this[entry.key] = entry.value` - /// for each [MapEntry] of the iterable. - /// ```dart - /// final planets = {1: 'Mercury', 2: 'Venus', - /// 3: 'Earth', 4: 'Mars'}; - /// final gasGiants = {5: 'Jupiter', 6: 'Saturn'}; - /// final iceGiants = {7: 'Uranus', 8: 'Neptune'}; - /// planets.addEntries(gasGiants.entries); - /// planets.addEntries(iceGiants.entries); - /// print(planets); - /// // {1: Mercury, 2: Venus, 3: Earth, 4: Mars, 5: Jupiter, 6: Saturn, - /// // 7: Uranus, 8: Neptune} - /// ``` - void addEntries(Iterable> newEntries) => - value?.addEntries(newEntries); - - /// Updates the value for the provided [key]. - /// - /// Returns the new value associated with the key. - /// - /// If the key is present, invokes [update] with the current value and stores - /// the new value in the map. - /// - /// If the key is not present and [ifAbsent] is provided, calls [ifAbsent] - /// and adds the key with the returned value to the map. - /// - /// If the key is not present, [ifAbsent] must be provided. - /// ```dart - /// final planetsFromSun = {1: 'Mercury', 2: 'unknown', - /// 3: 'Earth'}; - /// // Update value for known key value 2. - /// planetsFromSun.update(2, (value) => 'Venus'); - /// print(planetsFromSun); // {1: Mercury, 2: Venus, 3: Earth} - /// - /// final largestPlanets = {1: 'Jupiter', 2: 'Saturn', - /// 3: 'Neptune'}; - /// // Key value 8 is missing from list, add it using [ifAbsent]. - /// largestPlanets.update(8, (value) => 'New', ifAbsent: () => 'Mercury'); - /// print(largestPlanets); // {1: Jupiter, 2: Saturn, 3: Neptune, 8: Mercury} - /// ``` - V? updateMap(K key, V Function(V value) update, {V Function()? ifAbsent}) => - value?.update(key, update, ifAbsent: ifAbsent); - - /// Updates all values. - /// - /// Iterates over all entries in the map and updates them with the result - /// of invoking [update]. - /// ```dart - /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; - /// terrestrial.updateAll((key, value) => value.toUpperCase()); - /// print(terrestrial); // {1: MERCURY, 2: VENUS, 3: EARTH} - /// ``` - void updateAll(V Function(K key, V value) update) => value?.updateAll(update); - - /// Removes all entries of this map that satisfy the given [test]. - /// ```dart - /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; - /// terrestrial.removeWhere((key, value) => value.startsWith('E')); - /// print(terrestrial); // {1: Mercury, 2: Venus} - /// ``` - void removeWhere(bool Function(K key, V value) test) => - value?.removeWhere(test); - - /// Look up the value of [key], or add a new entry if it isn't there. - /// - /// Returns the value associated to [key], if there is one. - /// Otherwise calls [ifAbsent] to get a new value, associates [key] to - /// that value, and then returns the new value. - /// ```dart - /// final diameters = {1.0: 'Earth'}; - /// final otherDiameters = {0.383: 'Mercury', 0.949: 'Venus'}; - /// - /// for (final item in otherDiameters.entries) { - /// diameters.putIfAbsent(item.key, () => item.value); - /// } - /// print(diameters); // {1.0: Earth, 0.383: Mercury, 0.949: Venus} - /// - /// // If the key already exists, the current value is returned. - /// final result = diameters.putIfAbsent(0.383, () => 'Random'); - /// print(result); // Mercury - /// print(diameters); // {1.0: Earth, 0.383: Mercury, 0.949: Venus} - /// ``` - /// Calling [ifAbsent] must not add or remove keys from the map. - V? putIfAbsent(K key, V Function() ifAbsent) => - value?.putIfAbsent(key, ifAbsent); - - /// Adds all key/value pairs of [other] to this map. - /// - /// If a key of [other] is already in this map, its value is overwritten. - /// - /// The operation is equivalent to doing `this[key] = value` for each key - /// and associated value in other. It iterates over [other], which must - /// therefore not change during the iteration. - /// ```dart - /// final planets = {1: 'Mercury', 2: 'Earth'}; - /// planets.addAll({5: 'Jupiter', 6: 'Saturn'}); - /// print(planets); // {1: Mercury, 2: Earth, 5: Jupiter, 6: Saturn} - /// ``` - void addAll(Map other) => value?.addAll(other); - - /// Removes [key] and its associated value, if present, from the map. - /// - /// Returns the value associated with `key` before it was removed. - /// Returns `null` if `key` was not in the map. - /// - /// Note that some maps allow `null` as a value, - /// so a returned `null` value doesn't always mean that the key was absent. - /// ```dart - /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; - /// final removedValue = terrestrial.remove(2); // Venus - /// print(terrestrial); // {1: Mercury, 3: Earth} - /// ``` - V? remove(Object? key) => value?.remove(key); - - /// Removes all entries from the map. - /// - /// After this, the map is empty. - /// ```dart - /// final planets = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; - /// planets.clear(); // {} - /// ``` - void clear() => value?.clear(); - - /// Applies [action] to each key/value pair of the map. - /// - /// Calling `action` must not add or remove keys from the map. - /// ```dart - /// final planetsByMass = {0.81: 'Venus', 1: 'Earth', - /// 0.11: 'Mars', 17.15: 'Neptune'}; - /// - /// planetsByMass.forEach((key, value) { - /// print('$key: $value'); - /// // 0.81: Venus - /// // 1: Earth - /// // 0.11: Mars - /// // 17.15: Neptune - /// }); - /// ``` - void forEach(void Function(K key, V value) action) => value?.forEach(action); - - /// The keys of [this]. - /// - /// The returned iterable has efficient `length` and `contains` operations, - /// based on [length] and [containsKey] of the map. - /// - /// The order of iteration is defined by the individual `Map` implementation, - /// but must be consistent between changes to the map. - /// - /// Modifying the map while iterating the keys may break the iteration. - Iterable? get keys => value?.keys; - - /// The values of [this]. - /// - /// The values are iterated in the order of their corresponding keys. - /// This means that iterating [keys] and [values] in parallel will - /// provide matching pairs of keys and values. - /// - /// The returned iterable has an efficient `length` method based on the - /// [length] of the map. Its [Iterable.contains] method is based on - /// `==` comparison. - /// - /// Modifying the map while iterating the values may break the iteration. - Iterable? get values => value?.values; - - /// The number of key/value pairs in the map. - int? get length => value?.length; - - /// Whether there is no key/value pair in the map. - bool? get isEmpty => value?.isEmpty; - - /// Whether there is at least one key/value pair in the map. - bool? get isNotEmpty => value?.isNotEmpty; -} diff --git a/packages/reactter/lib/src/obj/extensions/obj_num.dart b/packages/reactter/lib/src/obj/extensions/obj_num.dart deleted file mode 100644 index c4432137..00000000 --- a/packages/reactter/lib/src/obj/extensions/obj_num.dart +++ /dev/null @@ -1,783 +0,0 @@ -// coverage:ignore-file -part of '../obj.dart'; - -extension ObjNumExt on Obj { - /// Adds [other] to this number. - /// - /// The result is an [Obj] of [double], as described by [double.+], - /// if both `this` and [other] is an [Obj] of [double], - /// otherwise the result is a [Obj] of [int]. - Obj operator +(Obj other) => Obj(value + other.value); - - /// Subtracts [other] from this number. - /// - /// The result is an [Obj] of [double], as described by [double.-], - /// if both `this` and [other] is an [Obj] of [double], - /// otherwise the result is a [Obj] of [int]. - Obj operator -(Obj other) => Obj(value - other.value); - - /// Multiplies this number by [other]. - /// - /// The result is an [Obj] of [double], as described by [double.*], - /// if both `this` and [other] is an [Obj] of [double], - /// otherwise the result is a [Obj] of [int]. - Obj operator *(Obj other) => Obj(value * other.value); - - /// Euclidean modulo of this number by [other]. - /// - /// Returns the remainder of the Euclidean division. - /// The Euclidean division of two integers `a` and `b` - /// yields two integers `q` and `r` such that - /// `a == b * q + r` and `0 <= r < b.abs()`. - /// - /// The Euclidean division is only defined for integers, but can be easily - /// extended to work with doubles. In that case, `q` is still an integer, - /// but `r` may have a non-integer value that still satisfies `0 <= r < |b|`. - /// - /// The sign of the returned value `r` is always positive. - /// - /// See [remainder] for the remainder of the truncating division. - /// - /// The result is an [Obj] of [double], as described by [double.%], - /// if both `this` and [other] are [Obj] of [double], - /// otherwise the result is a [Obj] of [int]. - /// - /// Example: - /// ```dart - /// print(Obj(5) % Obj(3)); // Obj(2) - /// print(Obj(-5) % Obj(3)); // Obj(1) - /// print(Obj(5) % Obj(-3)); // Obj(2) - /// print(Obj(-5) % Obj(-3)); // Obj(1) - /// ``` - Obj operator %(Obj other) => Obj(value % other.value); - - /// Divides this number by [other]. - Obj operator /(Obj other) => Obj(value / other.value); - - /// Truncating division operator. - /// - /// Performs truncating division of this number by [other]. - /// Truncating division is division where a fractional result - /// is converted to an integer by rounding towards zero. - /// - /// If both operands are [int]s, then [other] must not be zero. - /// Then `a ~/ b` corresponds to `a.remainder(b)` - /// such that `a == (a ~/ b) * b + a.remainder(b)`. - /// - /// If either operand is a [double], then the other operand is converted - /// to a double before performing the division and truncation of the result. - /// Then `a ~/ b` is equivalent to `(a / b).truncate()`. - /// This means that the intermediate result of the double division - /// must be a finite integer (not an infinity or [double.nan]). - Obj operator ~/(Obj other) => Obj(value ~/ other.value); - - /// The negation of this value. - /// - /// The negation of a number is a number of the same kind - /// (`int` or `double`) representing the negation of the - /// numbers numerical value (the result of subtracting the - /// number from zero), if that value *exists*. - /// - /// Negating a double gives a number with the same magnitude - /// as the original value (`number.abs() == (-number).abs()`), - /// and the opposite sign (`-(number.sign) == (-number).sign`). - /// - /// Negating an integer, `-number`, is equivalent to subtracting - /// it from zero, `0 - number`. - /// - /// (Both properties generally also hold for the other type, - /// but with a few edge case exceptions). - Obj operator -() => Obj(-value); - - /// Whether this number is numerically smaller than [other]. - /// - /// Returns `true` if this number is smaller than [other]. - /// Returns `false` if this number is greater than or equal to [other] - /// or if either value is a NaN value like [double.nan]. - bool operator <(Obj other) => value < other.value; - - /// Whether this number is numerically smaller than or equal to [other]. - /// - /// Returns `true` if this number is smaller than or equal to [other]. - /// Returns `false` if this number is greater than [other] - /// or if either value is a NaN value like [double.nan]. - bool operator <=(Obj other) => value <= other.value; - - /// Whether this number is numerically greater than [other]. - /// - /// Returns `true` if this number is greater than [other]. - /// Returns `false` if this number is smaller than or equal to [other] - /// or if either value is a NaN value like [double.nan]. - bool operator >(Obj other) => value > other.value; - - /// Whether this number is numerically greater than or equal to [other]. - /// - /// Returns `true` if this number is greater than or equal to [other]. - /// Returns `false` if this number is smaller than [other] - /// or if either value is a NaN value like [double.nan]. - bool operator >=(Obj other) => value >= other.value; - - /// Compares this to `other`. - /// - /// Returns a negative number if `this` is less than `other`, zero if they are - /// equal, and a positive number if `this` is greater than `other`. - /// - /// The ordering represented by this method is a total ordering of [num] - /// values. All distinct doubles are non-equal, as are all distinct integers, - /// but integers are equal to doubles if they have the same numerical - /// value. - /// - /// For doubles, the `compareTo` operation is different from the partial - /// ordering given by [operator==], [operator<] and [operator>]. For example, - /// IEEE doubles impose that `0.0 == -0.0` and all comparison operations on - /// NaN return false. - /// - /// This function imposes a complete ordering for doubles. When using - /// `compareTo`, the following properties hold: - /// - /// - All NaN values are considered equal, and greater than any numeric value. - /// - -0.0 is less than 0.0 (and the integer 0), but greater than any non-zero - /// negative value. - /// - Negative infinity is less than all other values and positive infinity is - /// greater than all non-NaN values. - /// - All other values are compared using their numeric value. - /// - /// Examples: - /// ```dart - /// print(Obj(1).compareTo(2)); // => -1 - /// print(Obj(2).compareTo(1)); // => 1 - /// print(Obj(1).compareTo(1)); // => 0 - /// - /// // The following comparisons yield different results than the - /// // corresponding comparison operators. - /// print(Obj(-0.0).compareTo(0.0)); // => -1 - /// print(double.nan.compareTo(double.nan)); // => 0 - /// print(double.infinity.compareTo(double.nan)); // => -1 - /// - /// // -0.0, and NaN comparison operators have rules imposed by the IEEE - /// // standard. - /// print(-0.0 == 0.0); // => true - /// print(double.nan == double.nan); // => false - /// print(double.infinity < double.nan); // => false - /// print(double.nan < double.infinity); // => false - /// print(double.nan == double.infinity); // => false - /// ``` - int compareTo(num other) => value.compareTo(other); - - /// The remainder of the truncating division of `this` by [other]. - /// - /// The result `r` of this operation satisfies: - /// `this == (this ~/ other) * other + r`. - /// As a consequence, the remainder `r` has the same sign as the divider - /// `this`. - /// - /// The result is an [int], as described by [int.remainder], - /// if both `this` and [other] are integers, - /// otherwise the result is a [double]. - /// - /// Example: - /// ```dart - /// print(Obj(5).remainder(3)); // 2 - /// print(Obj(-5).remainder(3)); // -2 - /// print(Obj(5).remainder(-3)); // 2 - /// print(Obj(-5).remainder(-3)); // -2 - /// ``` - num remainder(num other) => value.remainder(other); - - /// Whether this number is a Not-a-Number value. - /// - /// Is `true` if this number is the [double.nan] value - /// or any other of the possible [double] NaN values. - /// Is `false` if this number is an integer, - /// a finite double or an infinite double ([double.infinity] - /// or [double.negativeInfinity]). - /// - /// All numbers satisfy exactly one of [isInfinite], [isFinite] - /// and `isNaN`. - bool get isNaN => value.isNaN; - - /// Whether this number is negative. - /// - /// A number is negative if it's smaller than zero, - /// or if it is the double `-0.0`. - /// This precludes a NaN value like [double.nan] from being negative. - bool get isNegative => value.isNegative; - - /// Whether this number is positive infinity or negative infinity. - /// - /// Only satisfied by [double.infinity] and [double.negativeInfinity]. - /// - /// All numbers satisfy exactly one of `isInfinite`, [isFinite] - /// and [isNaN]. - bool get isInfinite => value.isInfinite; - - /// Whether this number is finite. - /// - /// The only non-finite numbers are NaN values, positive infinity, and - /// negative infinity. All integers are finite. - /// - /// All numbers satisfy exactly one of [isInfinite], `isFinite` - /// and [isNaN]. - bool get isFinite => value.isFinite; - - /// The absolute value of this number. - /// - /// The absolute value is the value itself, if the value is non-negative, - /// and `-value` if the value is negative. - /// - /// Integer overflow may cause the result of `-value` to stay negative. - /// - /// ```dart - /// print(Obj(2).abs()); // 2 - /// print(Obj(-2.5).abs()); // 2.5 - /// ``` - num abs() => value.abs(); - - /// Negative one, zero or positive one depending on the sign and - /// numerical value of this number. - /// - /// The value minus one if this number is less than zero, - /// plus one if this number is greater than zero, - /// and zero if this number is equal to zero. - /// - /// Returns NaN if this number is a [double] NaN value. - /// - /// Returns a number of the same type as this number. - /// For doubles, `Obj(-0.0).sign` is `-0.0`. - /// - /// The result satisfies: - /// ```dart - /// n == n.sign * n.abs() - /// ``` - /// for all numbers `n` (except NaN, because NaN isn't `==` to itself). - num get sign => value.sign; - - /// The integer closest to this number. - /// - /// Rounds away from zero when there is no closest integer: - /// `Obj(3.5).round() == 4` and `Obj(-3.5).round() == -4`. - /// - /// The number must be finite (see [isFinite]). - /// - /// If the value is greater than the highest representable positive integer, - /// the result is that highest positive integer. - /// If the value is smaller than the highest representable negative integer, - /// the result is that highest negative integer. - int round() => value.round(); - - /// The greatest integer no greater than this number. - /// - /// Rounds fractional values towards negative infinity. - /// - /// The number must be finite (see [isFinite]). - /// - /// If the value is greater than the highest representable positive integer, - /// the result is that highest positive integer. - /// If the value is smaller than the highest representable negative integer, - /// the result is that highest negative integer. - int floor() => value.floor(); - - /// The least integer no smaller than `this`. - /// - /// Rounds fractional values towards positive infinity. - /// - /// The number must be finite (see [isFinite]). - /// - /// If the value is greater than the highest representable positive integer, - /// the result is that highest positive integer. - /// If the value is smaller than the highest representable negative integer, - /// the result is that highest negative integer. - int ceil() => value.ceil(); - - /// The integer obtained by discarding any fractional digits from `this`. - /// - /// Rounds fractional values towards zero. - /// - /// The number must be finite (see [isFinite]). - /// - /// If the value is greater than the highest representable positive integer, - /// the result is that highest positive integer. - /// If the value is smaller than the highest representable negative integer, - /// the result is that highest negative integer. - int truncate() => value.truncate(); - - /// The double integer value closest to this value. - /// - /// Rounds away from zero when there is no closest integer: - /// `Obj(3.5).roundToDouble() == 4` and `Obj(-3.5).roundToDouble() == -4`. - /// - /// If this is already an integer valued double, including `-0.0`, or it is a - /// non-finite double value, the value is returned unmodified. - /// - /// For the purpose of rounding, `-0.0` is considered to be below `0.0`, - /// and `-0.0` is therefore considered closer to negative numbers than `0.0`. - /// This means that for a value `d` in the range `-0.5 < d < 0.0`, - /// the result is `-0.0`. - double roundToDouble() => value.roundToDouble(); - - /// Returns the greatest double integer value no greater than `this`. - /// - /// If this is already an integer valued double, including `-0.0`, or it is a - /// non-finite double value, the value is returned unmodified. - /// - /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. - /// A number `d` in the range `0.0 < d < 1.0` will return `0.0`. - double floorToDouble() => value.floorToDouble(); - - /// Returns the least double integer value no smaller than `this`. - /// - /// If this is already an integer valued double, including `-0.0`, or it is a - /// non-finite double value, the value is returned unmodified. - /// - /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. - /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`. - double ceilToDouble() => value.ceilToDouble(); - - /// Returns the double integer value obtained by discarding any fractional - /// digits from the double value of `this`. - /// - /// If this is already an integer valued double, including `-0.0`, or it is a - /// non-finite double value, the value is returned unmodified. - /// - /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. - /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`, and - /// in the range `0.0 < d < 1.0` it will return 0.0. - double truncateToDouble() => value.truncateToDouble(); - - /// Returns this [num] clamped to be in the range [lowerLimit]-[upperLimit]. - /// - /// The comparison is done using [compareTo] and therefore takes `-0.0` into - /// account. This also implies that [double.nan] is treated as the maximal - /// double value. - /// - /// The arguments [lowerLimit] and [upperLimit] must form a valid range where - /// `lowerLimit.compareTo(upperLimit) <= 0`. - /// - /// Example: - /// ```dart - /// var result = Obj(10.5).clamp(5, 10.0); // 10.0 - /// result = Obj(0.75).clamp(5, 10.0); // 5 - /// result = Obj(-10).clamp(-5, 5.0); // -5 - /// result = Obj(-0.0).clamp(-5, 5.0); // -0.0 - /// ``` - num clamp(num lowerLimit, num upperLimit) => - value.clamp(lowerLimit, upperLimit); - - /// Truncates this [num] to an integer and returns the result as an [int]. - /// - /// Equivalent to [truncate]. - int toInt() => value.toInt(); - - /// This number as a [double]. - /// - /// If an integer number is not precisely representable as a [double], - /// an approximation is returned. - double toDouble() => value.toDouble(); - - /// A decimal-point string-representation of this number. - /// - /// Converts this number to a [double] - /// before computing the string representation, - /// as by [toDouble]. - /// - /// If the absolute value of `this` is greater than or equal to `10^21`, then - /// this methods returns an exponential representation computed by - /// `this.toStringAsExponential()`. Otherwise the result - /// is the closest string representation with exactly [fractionDigits] digits - /// after the decimal point. If [fractionDigits] equals 0, then the decimal - /// point is omitted. - /// - /// The parameter [fractionDigits] must be an integer satisfying: - /// `0 <= fractionDigits <= 20`. - /// - /// Examples: - /// ```dart - /// Obj(1).toStringAsFixed(3); // 1.000 - /// Obj(4321.12345678).toStringAsFixed(3); // 4321.123 - /// Obj(4321.12345678).toStringAsFixed(5); // 4321.12346 - /// Obj(123456789012345).toStringAsFixed(3); // 123456789012345.000 - /// Obj(10000000000000000).toStringAsFixed(4); // 10000000000000000.0000 - /// Obj(5.25).toStringAsFixed(0); // 5 - /// ``` - String toStringAsFixed(int fractionDigits) => - value.toStringAsFixed(fractionDigits); - - /// An exponential string-representation of this number. - /// - /// Converts this number to a [double] - /// before computing the string representation. - /// - /// If [fractionDigits] is given, then it must be an integer satisfying: - /// `0 <= fractionDigits <= 20`. In this case the string contains exactly - /// [fractionDigits] after the decimal point. Otherwise, without the parameter, - /// the returned string uses the shortest number of digits that accurately - /// represent this number. - /// - /// If [fractionDigits] equals 0, then the decimal point is omitted. - /// Examples: - /// ```dart - /// Obj(1).toStringAsExponential(); // 1e+0 - /// Signa(1).toStringAsExponential(3); // 1.000e+0 - /// Obj(123456).toStringAsExponential(); // 1.23456e+5 - /// Obj(123456).toStringAsExponential(3); // 1.235e+5 - /// Obj(123).toStringAsExponential(0); // 1e+2 - /// ``` - String toStringAsExponential([int? fractionDigits]) => - value.toStringAsExponential(fractionDigits); - - /// A string representation with [precision] significant digits. - /// - /// Converts this number to a [double] - /// and returns a string representation of that value - /// with exactly [precision] significant digits. - /// - /// The parameter [precision] must be an integer satisfying: - /// `1 <= precision <= 21`. - /// - /// Examples: - /// ```dart - /// Obj(1).toStringAsPrecision(2); // 1.0 - /// Obj(1e15).toStringAsPrecision(3); // 1.00e+15 - /// Obj(1234567).toStringAsPrecision(3); // 1.23e+6 - /// Obj(1234567).toStringAsPrecision(9); // 1234567.00 - /// Obj(12345678901234567890).toStringAsPrecision(20); // 12345678901234567168 - /// Obj(12345678901234567890).toStringAsPrecision(14); // 1.2345678901235e+19 - /// Obj(0.00000012345).toStringAsPrecision(15); // 1.23450000000000e-7 - /// Obj(0.0000012345).toStringAsPrecision(15); // 0.00000123450000000000 - /// ``` - String toStringAsPrecision(int precision) => - value.toStringAsPrecision(precision); -} - -extension ObjNumNullExt on Obj { - /// Compares this to `other`. - /// - /// Returns a negative number if `this` is less than `other`, zero if they are - /// equal, and a positive number if `this` is greater than `other`. - /// - /// The ordering represented by this method is a total ordering of [num] - /// values. All distinct doubles are non-equal, as are all distinct integers, - /// but integers are equal to doubles if they have the same numerical - /// value. - /// - /// For doubles, the `compareTo` operation is different from the partial - /// ordering given by [operator==], [operator<] and [operator>]. For example, - /// IEEE doubles impose that `0.0 == -0.0` and all comparison operations on - /// NaN return false. - /// - /// This function imposes a complete ordering for doubles. When using - /// `compareTo`, the following properties hold: - /// - /// - All NaN values are considered equal, and greater than any numeric value. - /// - -0.0 is less than 0.0 (and the integer 0), but greater than any non-zero - /// negative value. - /// - Negative infinity is less than all other values and positive infinity is - /// greater than all non-NaN values. - /// - All other values are compared using their numeric value. - /// - /// Examples: - /// ```dart - /// print(Obj(1).compareTo(2)); // => -1 - /// print(Obj(2).compareTo(1)); // => 1 - /// print(Obj(1).compareTo(1)); // => 0 - /// - /// // The following comparisons yield different results than the - /// // corresponding comparison operators. - /// print(Obj(-0.0).compareTo(0.0)); // => -1 - /// print(double.nan.compareTo(double.nan)); // => 0 - /// print(double.infinity.compareTo(double.nan)); // => -1 - /// - /// // -0.0, and NaN comparison operators have rules imposed by the IEEE - /// // standard. - /// print(-0.0 == 0.0); // => true - /// print(double.nan == double.nan); // => false - /// print(double.infinity < double.nan); // => false - /// print(double.nan < double.infinity); // => false - /// print(double.nan == double.infinity); // => false - /// ``` - int? compareTo(num other) => value?.compareTo(other); - - /// The remainder of the truncating division of `this` by [other]. - /// - /// The result `r` of this operation satisfies: - /// `this == (this ~/ other) * other + r`. - /// As a consequence, the remainder `r` has the same sign as the divider - /// `this`. - /// - /// The result is an [int], as described by [int.remainder], - /// if both `this` and [other] are integers, - /// otherwise the result is a [double]. - /// - /// Example: - /// ```dart - /// print(Obj(5).remainder(3)); // 2 - /// print(Obj(-5).remainder(3)); // -2 - /// print(Obj(5).remainder(-3)); // 2 - /// print(Obj(-5).remainder(-3)); // -2 - /// ``` - num? remainder(num other) => value?.remainder(other); - - /// Whether this number is a Not-a-Number value. - /// - /// Is `true` if this number is the [double.nan] value - /// or any other of the possible [double] NaN values. - /// Is `false` if this number is an integer, - /// a finite double or an infinite double ([double.infinity] - /// or [double.negativeInfinity]). - /// - /// All numbers satisfy exactly one of [isInfinite], [isFinite] - /// and `isNaN`. - bool? get isNaN => value?.isNaN; - - /// Whether this number is negative. - /// - /// A number is negative if it's smaller than zero, - /// or if it is the double `-0.0`. - /// This precludes a NaN value like [double.nan] from being negative. - bool? get isNegative => value?.isNegative; - - /// Whether this number is positive infinity or negative infinity. - /// - /// Only satisfied by [double.infinity] and [double.negativeInfinity]. - /// - /// All numbers satisfy exactly one of `isInfinite`, [isFinite] - /// and [isNaN]. - bool? get isInfinite => value?.isInfinite; - - /// Whether this number is finite. - /// - /// The only non-finite numbers are NaN values, positive infinity, and - /// negative infinity. All integers are finite. - /// - /// All numbers satisfy exactly one of [isInfinite], `isFinite` - /// and [isNaN]. - bool? get isFinite => value?.isFinite; - - /// The absolute value of this number. - /// - /// The absolute value is the value itself, if the value is non-negative, - /// and `-value` if the value is negative. - /// - /// Integer overflow may cause the result of `-value` to stay negative. - /// - /// ```dart - /// print(Obj(2).abs()); // 2 - /// print(Obj(-2.5).abs()); // 2.5 - /// ``` - num? abs() => value?.abs(); - - /// Negative one, zero or positive one depending on the sign and - /// numerical value of this number. - /// - /// The value minus one if this number is less than zero, - /// plus one if this number is greater than zero, - /// and zero if this number is equal to zero. - /// - /// Returns NaN if this number is a [double] NaN value. - /// - /// Returns a number of the same type as this number. - /// For doubles, `Obj(-0.0).sign` is `-0.0`. - /// - /// The result satisfies: - /// ```dart - /// n == n.sign * n.abs() - /// ``` - /// for all numbers `n` (except NaN, because NaN isn't `==` to itself). - num? get sign => value?.sign; - - /// The integer closest to this number. - /// - /// Rounds away from zero when there is no closest integer: - /// `Obj(3.5).round() == 4` and `Obj(-3.5).round() == -4`. - /// - /// The number must be finite (see [isFinite]). - /// - /// If the value is greater than the highest representable positive integer, - /// the result is that highest positive integer. - /// If the value is smaller than the highest representable negative integer, - /// the result is that highest negative integer. - int? round() => value?.round(); - - /// The greatest integer no greater than this number. - /// - /// Rounds fractional values towards negative infinity. - /// - /// The number must be finite (see [isFinite]). - /// - /// If the value is greater than the highest representable positive integer, - /// the result is that highest positive integer. - /// If the value is smaller than the highest representable negative integer, - /// the result is that highest negative integer. - int? floor() => value?.floor(); - - /// The least integer no smaller than `this`. - /// - /// Rounds fractional values towards positive infinity. - /// - /// The number must be finite (see [isFinite]). - /// - /// If the value is greater than the highest representable positive integer, - /// the result is that highest positive integer. - /// If the value is smaller than the highest representable negative integer, - /// the result is that highest negative integer. - int? ceil() => value?.ceil(); - - /// The integer obtained by discarding any fractional digits from `this`. - /// - /// Rounds fractional values towards zero. - /// - /// The number must be finite (see [isFinite]). - /// - /// If the value is greater than the highest representable positive integer, - /// the result is that highest positive integer. - /// If the value is smaller than the highest representable negative integer, - /// the result is that highest negative integer. - int? truncate() => value?.truncate(); - - /// The double integer value closest to this value. - /// - /// Rounds away from zero when there is no closest integer: - /// `Obj(3.5).roundToDouble() == 4` and `Obj(-3.5).roundToDouble() == -4`. - /// - /// If this is already an integer valued double, including `-0.0`, or it is a - /// non-finite double value, the value is returned unmodified. - /// - /// For the purpose of rounding, `-0.0` is considered to be below `0.0`, - /// and `-0.0` is therefore considered closer to negative numbers than `0.0`. - /// This means that for a value `d` in the range `-0.5 < d < 0.0`, - /// the result is `-0.0`. - double? roundToDouble() => value?.roundToDouble(); - - /// Returns the greatest double integer value no greater than `this`. - /// - /// If this is already an integer valued double, including `-0.0`, or it is a - /// non-finite double value, the value is returned unmodified. - /// - /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. - /// A number `d` in the range `0.0 < d < 1.0` will return `0.0`. - double? floorToDouble() => value?.floorToDouble(); - - /// Returns the least double integer value no smaller than `this`. - /// - /// If this is already an integer valued double, including `-0.0`, or it is a - /// non-finite double value, the value is returned unmodified. - /// - /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. - /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`. - double? ceilToDouble() => value?.ceilToDouble(); - - /// Returns the double integer value obtained by discarding any fractional - /// digits from the double value of `this`. - /// - /// If this is already an integer valued double, including `-0.0`, or it is a - /// non-finite double value, the value is returned unmodified. - /// - /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. - /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`, and - /// in the range `0.0 < d < 1.0` it will return 0.0. - double? truncateToDouble() => value?.truncateToDouble(); - - /// Returns this [num] clamped to be in the range [lowerLimit]-[upperLimit]. - /// - /// The comparison is done using [compareTo] and therefore takes `-0.0` into - /// account. This also implies that [double.nan] is treated as the maximal - /// double value. - /// - /// The arguments [lowerLimit] and [upperLimit] must form a valid range where - /// `lowerLimit.compareTo(upperLimit) <= 0`. - /// - /// Example: - /// ```dart - /// var result = Obj(10.5).clamp(5, 10.0); // 10.0 - /// result = Obj(0.75).clamp(5, 10.0); // 5 - /// result = Obj(-10).clamp(-5, 5.0); // -5 - /// result = Obj(-0.0).clamp(-5, 5.0); // -0.0 - /// ``` - num? clamp(num lowerLimit, num upperLimit) => - value?.clamp(lowerLimit, upperLimit); - - /// Truncates this [num] to an integer and returns the result as an [int]. - /// - /// Equivalent to [truncate]. - int? toInt() => value?.toInt(); - - /// This number as a [double]. - /// - /// If an integer number is not precisely representable as a [double], - /// an approximation is returned. - double? toDouble() => value?.toDouble(); - - /// A decimal-point string-representation of this number. - /// - /// Converts this number to a [double] - /// before computing the string representation, - /// as by [toDouble]. - /// - /// If the absolute value of `this` is greater than or equal to `10^21`, then - /// this methods returns an exponential representation computed by - /// `this.toStringAsExponential()`. Otherwise the result - /// is the closest string representation with exactly [fractionDigits] digits - /// after the decimal point. If [fractionDigits] equals 0, then the decimal - /// point is omitted. - /// - /// The parameter [fractionDigits] must be an integer satisfying: - /// `0 <= fractionDigits <= 20`. - /// - /// Examples: - /// ```dart - /// Obj(1).toStringAsFixed(3); // 1.000 - /// Obj(4321.12345678).toStringAsFixed(3); // 4321.123 - /// (4321.12345678).toStringAsFixed(5); // 4321.12346 - /// Obj(123456789012345).toStringAsFixed(3); // 123456789012345.000 - /// Obj(10000000000000000).toStringAsFixed(4); // 10000000000000000.0000 - /// Obj(5.25).toStringAsFixed(0); // 5 - /// ``` - String? toStringAsFixed(int fractionDigits) => - value?.toStringAsFixed(fractionDigits); - - /// An exponential string-representation of this number. - /// - /// Converts this number to a [double] - /// before computing the string representation. - /// - /// If [fractionDigits] is given, then it must be an integer satisfying: - /// `0 <= fractionDigits <= 20`. In this case the string contains exactly - /// [fractionDigits] after the decimal point. Otherwise, without the parameter, - /// the returned string uses the shortest number of digits that accurately - /// represent this number. - /// - /// If [fractionDigits] equals 0, then the decimal point is omitted. - /// Examples: - /// ```dart - /// Obj(1).toStringAsExponential(); // 1e+0 - /// Obj(1).toStringAsExponential(3); // 1.000e+0 - /// Obj(123456).toStringAsExponential(); // 1.23456e+5 - /// Obj(123456).toStringAsExponential(3); // 1.235e+5 - /// Obj(123).toStringAsExponential(0); // 1e+2 - /// ``` - String? toStringAsExponential([int? fractionDigits]) => - value?.toStringAsExponential(fractionDigits); - - /// A string representation with [precision] significant digits. - /// - /// Converts this number to a [double] - /// and returns a string representation of that value - /// with exactly [precision] significant digits. - /// - /// The parameter [precision] must be an integer satisfying: - /// `1 <= precision <= 21`. - /// - /// Examples: - /// ```dart - /// Obj(1).toStringAsPrecision(2); // 1.0 - /// Obj(1e15).toStringAsPrecision(3); // 1.00e+15 - /// Obj(1234567).toStringAsPrecision(3); // 1.23e+6 - /// Obj(1234567).toStringAsPrecision(9); // 1234567.00 - /// Obj(12345678901234567890).toStringAsPrecision(20); // 12345678901234567168 - /// Obj(12345678901234567890).toStringAsPrecision(14); // 1.2345678901235e+19 - /// Obj(0.00000012345).toStringAsPrecision(15); // 1.23450000000000e-7 - /// Obj(0.0000012345).toStringAsPrecision(15); // 0.00000123450000000000 - /// ``` - String? toStringAsPrecision(int precision) => - value?.toStringAsPrecision(precision); -} diff --git a/packages/reactter/lib/src/obj/extensions/obj_set.dart b/packages/reactter/lib/src/obj/extensions/obj_set.dart deleted file mode 100644 index 0c02d3b0..00000000 --- a/packages/reactter/lib/src/obj/extensions/obj_set.dart +++ /dev/null @@ -1,422 +0,0 @@ -// coverage:ignore-file -part of '../obj.dart'; - -extension ObjSetExt on Obj> { - /// Provides a view of this set as a set of [R] instances. - /// - /// If this set contains only instances of [R], all read operations - /// will work correctly. If any operation tries to access an element - /// that is not an instance of [R], the access will throw instead. - /// - /// Elements added to the set (e.g., by using [add] or [addAll]) - /// must be instances of [R] to be valid arguments to the adding function, - /// and they must be instances of [E] as well to be accepted by - /// this set as well. - /// - /// Methods which accept one or more `Object?` as argument, - /// like [contains], [remove] and [removeAll], - /// will pass the argument directly to the this set's method - /// without any checks. - /// That means that you can do `setOfStrings.cast().remove("a")` - /// successfully, even if it looks like it shouldn't have any effect. - Set cast() => value.cast(); - - /// An iterator that iterates over the elements of this set. - /// - /// The order of iteration is defined by the individual `Set` implementation, - /// but must be consistent between changes to the set. - Iterator get iterator => value.iterator; - - /// Whether [value] is in the set. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// final containsB = characters.contains('B'); // true - /// final containsD = characters.contains('D'); // false - /// ``` - bool contains(Object? valueToEvaluate) => value.contains(valueToEvaluate); - - /// Adds [value] to the set. - /// - /// Returns `true` if [value] (or an equal value) was not yet in the set. - /// Otherwise returns `false` and the set is not changed. - /// - /// Example: - /// ```dart - /// final dateTimes = {}; - /// final time1 = DateTime.fromMillisecondsSinceEpoch(0); - /// final time2 = DateTime.fromMillisecondsSinceEpoch(0); - /// // time1 and time2 are equal, but not identical. - /// assert(time1 == time2); - /// assert(!identical(time1, time2)); - /// final time1Added = dateTimes.add(time1); - /// print(time1Added); // true - /// // A value equal to time2 exists already in the set, and the call to - /// // add doesn't change the set. - /// final time2Added = dateTimes.add(time2); - /// print(time2Added); // false - /// - /// print(dateTimes); // {1970-01-01 02:00:00.000} - /// assert(dateTimes.length == 1); - /// assert(identical(time1, dateTimes.first)); - /// print(dateTimes.length); - /// ``` - bool add(E valueToAdd) => value.add(valueToAdd); - - /// Adds all [elements] to this set. - /// - /// Equivalent to adding each element in [elements] using [add], - /// but some collections may be able to optimize it. - /// ```dart - /// final characters = {'A', 'B'}; - /// characters.addAll({'A', 'B', 'C'}); - /// print(characters); // {A, B, C} - /// ``` - void addAll(Iterable elements) => value.addAll(elements); - - /// Removes [value] from the set. - /// - /// Returns `true` if [value] was in the set, and `false` if not. - /// The method has no effect if [value] was not in the set. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// final didRemoveB = characters.remove('B'); // true - /// final didRemoveD = characters.remove('D'); // false - /// print(characters); // {A, C} - /// ``` - bool remove(Object? valueToRemove) => value.remove(valueToRemove); - - /// If an object equal to [object] is in the set, return it. - /// - /// Checks whether [object] is in the set, like [contains], and if so, - /// returns the object in the set, otherwise returns `null`. - /// - /// If the equality relation used by the set is not identity, - /// then the returned object may not be *identical* to [object]. - /// Some set implementations may not be able to implement this method. - /// If the [contains] method is computed, - /// rather than being based on an actual object instance, - /// then there may not be a specific object instance representing the - /// set element. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// final containsB = characters.lookup('B'); - /// print(containsB); // B - /// final containsD = characters.lookup('D'); - /// print(containsD); // null - /// ``` - E? lookup(Object? object) => value.lookup(object); - - /// Removes each element of [elements] from this set. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// characters.removeAll({'A', 'B', 'X'}); - /// print(characters); // {C} - /// ``` - void removeAll(Iterable elements) => value.removeAll(elements); - - /// Removes all elements of this set that are not elements in [elements]. - /// - /// Checks for each element of [elements] whether there is an element in this - /// set that is equal to it (according to `this.contains`), and if so, the - /// equal element in this set is retained, and elements that are not equal - /// to any element in [elements] are removed. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// characters.retainAll({'A', 'B', 'X'}); - /// print(characters); // {A, B} - /// ``` - void retainAll(Iterable elements) => value.retainAll(elements); - - /// Removes all elements of this set that satisfy [test]. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// characters.removeWhere((element) => element.startsWith('B')); - /// print(characters); // {A, C} - /// ``` - void removeWhere(bool Function(E element) test) => value.removeWhere(test); - - /// Removes all elements of this set that fail to satisfy [test]. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// characters.retainWhere( - /// (element) => element.startsWith('B') || element.startsWith('C')); - /// print(characters); // {B, C} - /// ``` - void retainWhere(bool Function(E element) test) => value.removeWhere(test); - - /// Whether this set contains all the elements of [other]. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// final containsAB = characters.containsAll({'A', 'B'}); - /// print(containsAB); // true - /// final containsAD = characters.containsAll({'A', 'D'}); - /// print(containsAD); // false - /// ``` - bool containsAll(Iterable other) => value.containsAll(other); - - /// Creates a new set which is the intersection between this set and [other]. - /// - /// That is, the returned set contains all the elements of this [Set] that - /// are also elements of [other] according to `other.contains`. - /// ```dart - /// final characters1 = {'A', 'B', 'C'}; - /// final characters2 = {'A', 'E', 'F'}; - /// final unionSet = characters1.intersection(characters2); - /// print(unionSet); // {A} - /// ``` - Set intersection(Set other) => value.intersection(other); - - /// Creates a new set which contains all the elements of this set and [other]. - /// - /// That is, the returned set contains all the elements of this [Set] and - /// all the elements of [other]. - /// ```dart - /// final characters1 = {'A', 'B', 'C'}; - /// final characters2 = {'A', 'E', 'F'}; - /// final unionSet1 = characters1.union(characters2); - /// print(unionSet1); // {A, B, C, E, F} - /// final unionSet2 = characters2.union(characters1); - /// print(unionSet2); // {A, E, F, B, C} - /// ``` - Set union(Set other) => value.union(other); - - /// Creates a new set with the elements of this that are not in [other]. - /// - /// That is, the returned set contains all the elements of this [Set] that - /// are not elements of [other] according to `other.contains`. - /// ```dart - /// final characters1 = {'A', 'B', 'C'}; - /// final characters2 = {'A', 'E', 'F'}; - /// final differenceSet1 = characters1.difference(characters2); - /// print(differenceSet1); // {B, C} - /// final differenceSet2 = characters2.difference(characters1); - /// print(differenceSet2); // {E, F} - /// ``` - Set difference(Set other) => value.difference(other); - - /// Removes all elements from the set. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// characters.clear(); // {} - /// ``` - void clear() => value.clear(); - - /// Creates a [Set] with the same elements and behavior as this `Set`. - /// - /// The returned set behaves the same as this set - /// with regard to adding and removing elements. - /// It initially contains the same elements. - /// If this set specifies an ordering of the elements, - /// the returned set will have the same order. - Set toSet() => value.toSet(); -} - -extension ObjSetNullExt on Obj?> { - /// Provides a view of this set as a set of [R] instances. - /// - /// If this set contains only instances of [R], all read operations - /// will work correctly. If any operation tries to access an element - /// that is not an instance of [R], the access will throw instead. - /// - /// Elements added to the set (e.g., by using [add] or [addAll]) - /// must be instances of [R] to be valid arguments to the adding function, - /// and they must be instances of [E] as well to be accepted by - /// this set as well. - /// - /// Methods which accept one or more `Object?` as argument, - /// like [contains], [remove] and [removeAll], - /// will pass the argument directly to the this set's method - /// without any checks. - /// That means that you can do `setOfStrings.cast().remove("a")` - /// successfully, even if it looks like it shouldn't have any effect. - Set? cast() => value?.cast(); - - /// An iterator that iterates over the elements of this set. - /// - /// The order of iteration is defined by the individual `Set` implementation, - /// but must be consistent between changes to the set. - Iterator? get iterator => value?.iterator; - - /// Whether [value] is in the set. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// final containsB = characters.contains('B'); // true - /// final containsD = characters.contains('D'); // false - /// ``` - bool? contains(Object? valueToEvaluate) => value?.contains(valueToEvaluate); - - /// Adds [value] to the set. - /// - /// Returns `true` if [value] (or an equal value) was not yet in the set. - /// Otherwise returns `false` and the set is not changed. - /// - /// Example: - /// ```dart - /// final dateTimes = {}; - /// final time1 = DateTime.fromMillisecondsSinceEpoch(0); - /// final time2 = DateTime.fromMillisecondsSinceEpoch(0); - /// // time1 and time2 are equal, but not identical. - /// assert(time1 == time2); - /// assert(!identical(time1, time2)); - /// final time1Added = dateTimes.add(time1); - /// print(time1Added); // true - /// // A value equal to time2 exists already in the set, and the call to - /// // add doesn't change the set. - /// final time2Added = dateTimes.add(time2); - /// print(time2Added); // false - /// - /// print(dateTimes); // {1970-01-01 02:00:00.000} - /// assert(dateTimes.length == 1); - /// assert(identical(time1, dateTimes.first)); - /// print(dateTimes.length); - /// ``` - bool? add(E valueToAdd) => value?.add(valueToAdd); - - /// Adds all [elements] to this set. - /// - /// Equivalent to adding each element in [elements] using [add], - /// but some collections may be able to optimize it. - /// ```dart - /// final characters = {'A', 'B'}; - /// characters.addAll({'A', 'B', 'C'}); - /// print(characters); // {A, B, C} - /// ``` - void addAll(Iterable elements) => value?.addAll(elements); - - /// Removes [value] from the set. - /// - /// Returns `true` if [value] was in the set, and `false` if not. - /// The method has no effect if [value] was not in the set. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// final didRemoveB = characters.remove('B'); // true - /// final didRemoveD = characters.remove('D'); // false - /// print(characters); // {A, C} - /// ``` - bool? remove(Object? valueToRemove) => value?.remove(valueToRemove); - - /// If an object equal to [object] is in the set, return it. - /// - /// Checks whether [object] is in the set, like [contains], and if so, - /// returns the object in the set, otherwise returns `null`. - /// - /// If the equality relation used by the set is not identity, - /// then the returned object may not be *identical* to [object]. - /// Some set implementations may not be able to implement this method. - /// If the [contains] method is computed, - /// rather than being based on an actual object instance, - /// then there may not be a specific object instance representing the - /// set element. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// final containsB = characters.lookup('B'); - /// print(containsB); // B - /// final containsD = characters.lookup('D'); - /// print(containsD); // null - /// ``` - E? lookup(Object? object) => value?.lookup(object); - - /// Removes each element of [elements] from this set. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// characters.removeAll({'A', 'B', 'X'}); - /// print(characters); // {C} - /// ``` - void removeAll(Iterable elements) => value?.removeAll(elements); - - /// Removes all elements of this set that are not elements in [elements]. - /// - /// Checks for each element of [elements] whether there is an element in this - /// set that is equal to it (according to `this.contains`), and if so, the - /// equal element in this set is retained, and elements that are not equal - /// to any element in [elements] are removed. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// characters.retainAll({'A', 'B', 'X'}); - /// print(characters); // {A, B} - /// ``` - void retainAll(Iterable elements) => value?.retainAll(elements); - - /// Removes all elements of this set that satisfy [test]. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// characters.removeWhere((element) => element.startsWith('B')); - /// print(characters); // {A, C} - /// ``` - void removeWhere(bool Function(E element) test) => value?.removeWhere(test); - - /// Removes all elements of this set that fail to satisfy [test]. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// characters.retainWhere( - /// (element) => element.startsWith('B') || element.startsWith('C')); - /// print(characters); // {B, C} - /// ``` - void retainWhere(bool Function(E element) test) => value?.removeWhere(test); - - /// Whether this set contains all the elements of [other]. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// final containsAB = characters.containsAll({'A', 'B'}); - /// print(containsAB); // true - /// final containsAD = characters.containsAll({'A', 'D'}); - /// print(containsAD); // false - /// ``` - bool? containsAll(Iterable other) => value?.containsAll(other); - - /// Creates a new set which is the intersection between this set and [other]. - /// - /// That is, the returned set contains all the elements of this [Set] that - /// are also elements of [other] according to `other.contains`. - /// ```dart - /// final characters1 = {'A', 'B', 'C'}; - /// final characters2 = {'A', 'E', 'F'}; - /// final unionSet = characters1.intersection(characters2); - /// print(unionSet); // {A} - /// ``` - Set? intersection(Set other) => value?.intersection(other); - - /// Creates a new set which contains all the elements of this set and [other]. - /// - /// That is, the returned set contains all the elements of this [Set] and - /// all the elements of [other]. - /// ```dart - /// final characters1 = {'A', 'B', 'C'}; - /// final characters2 = {'A', 'E', 'F'}; - /// final unionSet1 = characters1.union(characters2); - /// print(unionSet1); // {A, B, C, E, F} - /// final unionSet2 = characters2.union(characters1); - /// print(unionSet2); // {A, E, F, B, C} - /// ``` - Set? union(Set other) => value?.union(other); - - /// Creates a new set with the elements of this that are not in [other]. - /// - /// That is, the returned set contains all the elements of this [Set] that - /// are not elements of [other] according to `other.contains`. - /// ```dart - /// final characters1 = {'A', 'B', 'C'}; - /// final characters2 = {'A', 'E', 'F'}; - /// final differenceSet1 = characters1.difference(characters2); - /// print(differenceSet1); // {B, C} - /// final differenceSet2 = characters2.difference(characters1); - /// print(differenceSet2); // {E, F} - /// ``` - Set? difference(Set other) => value?.difference(other); - - /// Removes all elements from the set. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// characters.clear(); // {} - /// ``` - void clear() => value?.clear(); - - /// Creates a [Set] with the same elements and behavior as this `Set`. - /// - /// The returned set behaves the same as this set - /// with regard to adding and removing elements. - /// It initially contains the same elements. - /// If this set specifies an ordering of the elements, - /// the returned set will have the same order. - Set? toSet() => value?.toSet(); -} diff --git a/packages/reactter/lib/src/obj/extensions/obj_string.dart b/packages/reactter/lib/src/obj/extensions/obj_string.dart deleted file mode 100644 index cfbe3bf4..00000000 --- a/packages/reactter/lib/src/obj/extensions/obj_string.dart +++ /dev/null @@ -1,1057 +0,0 @@ -// coverage:ignore-file -part of '../obj.dart'; - -extension ObjStringExt on Obj { - /// The character (as a single-code-unit [String]) at the given [index]. - /// - /// The returned string represents exactly one UTF-16 code unit, which may be - /// half of a surrogate pair. A single member of a surrogate pair is an - /// invalid UTF-16 string: - /// ```dart - /// var clef = '\u{1D11E}'; - /// // These represent invalid UTF-16 strings. - /// clef[0].codeUnits; // [0xD834] - /// clef[1].codeUnits; // [0xDD1E] - /// ``` - /// This method is equivalent to - /// `String.fromCharCode(this.codeUnitAt(index))`. - String operator [](int index) => value[index]; - - /// Creates a new string by concatenating this string with [other]. - /// - /// Example: - /// ```dart - /// const string = 'dart' + 'lang'; // 'dartlang' - /// ``` - String operator +(Obj other) => value + other.value; - - /// Creates a new string by concatenating this string with itself a number - /// of times. - /// - /// The result of `str * n` is equivalent to - /// `str + str + ...`(n times)`... + str`. - /// - /// ```dart - /// const string = 'Dart'; - /// final multiplied = string * 3; - /// print(multiplied); // 'DartDartDart' - /// ``` - /// Returns an empty string if [times] is zero or negative. - String operator *(int times) => value * times; - - /// Returns the 16-bit UTF-16 code unit at the given [index]. - int codeUnitAt(int index) => value.codeUnitAt(index); - - /// The length of the string. - /// - /// Returns the number of UTF-16 code units in this string. The number - /// of [runes] might be fewer if the string contains characters outside - /// the Basic Multilingual Plane (plane 0): - /// ```dart - /// 'Dart'.length; // 4 - /// 'Dart'.runes.length; // 4 - /// - /// var clef = '\u{1D11E}'; - /// clef.length; // 2 - /// clef.runes.length; // 1 - /// ``` - int get length => value.length; - - /// Compares this string to [other]. - /// - /// Returns a negative value if `this` is ordered before `other`, - /// a positive value if `this` is ordered after `other`, - /// or zero if `this` and `other` are equivalent. - /// - /// The ordering is the same as the ordering of the code units at the first - /// position where the two strings differ. - /// If one string is a prefix of the other, - /// then the shorter string is ordered before the longer string. - /// If the strings have exactly the same content, they are equivalent with - /// regard to the ordering. - /// Ordering does not check for Unicode equivalence. - /// The comparison is case sensitive. - /// ```dart - /// var relation = 'Dart'.compareTo('Go'); - /// print(relation); // < 0 - /// relation = 'Go'.compareTo('Forward'); - /// print(relation); // > 0 - /// relation = 'Forward'.compareTo('Forward'); - /// print(relation); // 0 - /// ``` - int compareTo(String other) => value.compareTo(other); - - /// Whether this string ends with [other]. - /// - /// For example: - /// ```dart - /// const string = 'Dart is open source'; - /// print(string.endsWith('urce')); // true - /// ``` - bool endsWith(String other) => value.endsWith(other); - - /// Whether this string starts with a match of [pattern]. - /// - /// ```dart - /// const string = 'Dart is open source'; - /// print(string.startsWith('Dar')); // true - /// print(string.startsWith(RegExp(r'[A-Z][a-z]'))); // true - /// ``` - /// If [index] is provided, this method checks if the substring starting - /// at that index starts with a match of [pattern]: - /// ```dart - /// const string = 'Dart'; - /// print(string.startsWith('art', 0)); // false - /// print(string.startsWith('art', 1)); // true - /// print(string.startsWith(RegExp(r'\w{3}'), 2)); // false - /// ``` - /// [index] must not be negative or greater than [length]. - /// - /// A [RegExp] containing '^' does not match if the [index] is greater than - /// zero and the regexp is not multi-line. - /// The pattern works on the string as a whole, and does not extract - /// a substring starting at [index] first: - /// ```dart - /// const string = 'Dart'; - /// print(string.startsWith(RegExp(r'^art'), 1)); // false - /// print(string.startsWith(RegExp(r'art'), 1)); // true - /// ``` - bool startsWith(Pattern pattern, [int index = 0]) => - value.startsWith(pattern, index); - - /// Returns the position of the first match of [pattern] in this string, - /// starting at [start], inclusive: - /// ```dart - /// const string = 'Dartisans'; - /// print(string.indexOf('art')); // 1 - /// print(string.indexOf(RegExp(r'[A-Z][a-z]'))); // 0 - /// ``` - /// Returns -1 if no match is found: - /// ```dart - /// const string = 'Dartisans'; - /// string.indexOf(RegExp(r'dart')); // -1 - /// ``` - /// The [start] must be non-negative and not greater than [length]. - int indexOf(Pattern pattern, [int start = 0]) => - value.indexOf(pattern, start); - - /// The starting position of the last match [pattern] in this string. - /// - /// Finds a match of pattern by searching backward starting at [start]: - /// ```dart - /// const string = 'Dartisans'; - /// print(string.lastIndexOf('a')); // 6 - /// print(string.lastIndexOf(RegExp(r'a(r|n)'))); // 6 - /// ``` - /// Returns -1 if [pattern] could not be found in this string. - /// ```dart - /// const string = 'Dartisans'; - /// print(string.lastIndexOf(RegExp(r'DART'))); // -1 - /// ``` - /// If [start] is omitted, search starts from the end of the string. - /// If supplied, [start] must be non-negative and not greater than [length]. - int lastIndexOf(Pattern pattern, [int? start]) => - value.lastIndexOf(pattern, start); - - /// Whether this string is empty. - bool get isEmpty => value.isEmpty; - - /// Whether this string is not empty. - bool get isNotEmpty => value.isNotEmpty; - - /// The substring of this string from [start], inclusive, to [end], exclusive. - /// - /// Example: - /// ```dart - /// const string = 'dartlang'; - /// var result = string.substring(1); // 'artlang' - /// result = string.substring(1, 4); // 'art' - /// ``` - /// - /// Both [start] and [end] must be non-negative and no greater than [length]; - /// [end], if provided, must be greater than or equal to [start]. - String substring(int start, [int? end]) => value.substring(start, end); - - /// The string without any leading and trailing whitespace. - /// - /// If the string contains leading or trailing whitespace, a new string with no - /// leading and no trailing whitespace is returned: - /// ```dart - /// final trimmed = '\tDart is fun\n'.trim(); - /// print(trimmed); // 'Dart is fun' - /// ``` - /// Otherwise, the original string itself is returned: - /// ```dart - /// const string1 = 'Dart'; - /// final string2 = string1.trim(); // 'Dart' - /// print(identical(string1, string2)); // true - /// ``` - /// Whitespace is defined by the Unicode White_Space property (as defined in - /// version 6.2 or later) and the BOM character, 0xFEFF. - /// - /// Here is the list of trimmed characters according to Unicode version 6.3: - /// ```plaintext - /// 0009..000D ; White_Space # Cc .. - /// 0020 ; White_Space # Zs SPACE - /// 0085 ; White_Space # Cc - /// 00A0 ; White_Space # Zs NO-BREAK SPACE - /// 1680 ; White_Space # Zs OGHAM SPACE MARK - /// 2000..200A ; White_Space # Zs EN QUAD..HAIR SPACE - /// 2028 ; White_Space # Zl LINE SEPARATOR - /// 2029 ; White_Space # Zp PARAGRAPH SEPARATOR - /// 202F ; White_Space # Zs NARROW NO-BREAK SPACE - /// 205F ; White_Space # Zs MEDIUM MATHEMATICAL SPACE - /// 3000 ; White_Space # Zs IDEOGRAPHIC SPACE - /// - /// FEFF ; BOM ZERO WIDTH NO_BREAK SPACE - /// ``` - /// Some later versions of Unicode do not include U+0085 as a whitespace - /// character. Whether it is trimmed depends on the Unicode version - /// used by the system. - String trim() => value.trim(); - - /// The string without any leading whitespace. - /// - /// As [trim], but only removes leading whitespace. - /// ```dart - /// final string = ' Dart '.trimLeft(); - /// print(string); // 'Dart ' - /// ``` - String trimLeft() => value.trimLeft(); - - /// The string without any trailing whitespace. - /// - /// As [trim], but only removes trailing whitespace. - /// ```dart - /// final string = ' Dart '.trimRight(); - /// print(string); // ' Dart' - /// ``` - String trimRight() => value.trimRight(); - - /// Pads this string on the left if it is shorter than [width]. - /// - /// Returns a new string that prepends [padding] onto this string - /// one time for each position the length is less than [width]. - /// - /// ```dart - /// const string = 'D'; - /// print(string.padLeft(4)); // ' D' - /// print(string.padLeft(2, 'x')); // 'xD' - /// print(string.padLeft(4, 'y')); // 'yyyD' - /// print(string.padLeft(4, '>>')); // '>>>>>>D' - /// ``` - /// - /// If [width] is already smaller than or equal to `this.length`, - /// no padding is added. A negative `width` is treated as zero. - /// - /// If [padding] has length different from 1, the result will not - /// have length `width`. This may be useful for cases where the - /// padding is a longer string representing a single character, like - /// `" "` or `"\u{10002}`". - /// In that case, the user should make sure that `this.length` is - /// the correct measure of the string's length. - String padLeft(int width, [String padding = ' ']) => - value.padLeft(width, padding); - - /// Pads this string on the right if it is shorter than [width]. - /// - /// Returns a new string that appends [padding] after this string - /// one time for each position the length is less than [width]. - /// - /// ```dart - /// const string = 'D'; - /// print(string.padRight(4)); // 'D ' - /// print(string.padRight(2, 'x')); // 'Dx' - /// print(string.padRight(4, 'y')); // 'Dyyy' - /// print(string.padRight(4, '>>')); // 'D>>>>>>' - /// ``` - /// - /// If [width] is already smaller than or equal to `this.length`, - /// no padding is added. A negative `width` is treated as zero. - /// - /// If [padding] has length different from 1, the result will not - /// have length `width`. This may be useful for cases where the - /// padding is a longer string representing a single character, like - /// `" "` or `"\u{10002}`". - /// In that case, the user should make sure that `this.length` is - /// the correct measure of the string's length. - String padRight(int width, [String padding = ' ']) => - value.padRight(width, padding); - - /// Whether this string contains a match of [other]. - /// - /// Example: - /// ```dart - /// const string = 'Dart strings'; - /// final containsD = string.contains('D'); // true - /// final containsUpperCase = string.contains(RegExp(r'[A-Z]')); // true - /// ``` - /// If [startIndex] is provided, this method matches only at or after that - /// index: - /// ```dart - /// const string = 'Dart strings'; - /// final containsD = string.contains(RegExp('D'), 0); // true - /// final caseSensitive = string.contains(RegExp(r'[A-Z]'), 1); // false - /// ``` - /// The [startIndex] must not be negative or greater than [length]. - bool contains(Pattern other, [int startIndex = 0]) => - value.contains(other, startIndex); - - /// Creates a new string with the first occurrence of [from] replaced by [to]. - /// - /// Finds the first match of [from] in this string, starting from [startIndex], - /// and creates a new string where that match is replaced with the [to] string. - /// - /// Example: - /// ```dart - /// '0.0001'.replaceFirst(RegExp(r'0'), ''); // '.0001' - /// '0.0001'.replaceFirst(RegExp(r'0'), '7', 1); // '0.7001' - /// ``` - String replaceFirst(Pattern from, String to, [int startIndex = 0]) => - value.replaceFirst(from, to, startIndex); - - /// Replace the first occurrence of [from] in this string. - /// - /// ```dart - /// const string = 'Dart is fun'; - /// print(string.replaceFirstMapped( - /// 'fun', (m) => 'open source')); // Dart is open source - /// - /// print(string.replaceFirstMapped( - /// RegExp(r'\w(\w*)'), (m) => '<${m[0]}-${m[1]}>')); // is fun - /// ``` - /// - /// Returns a new string, which is this string - /// except that the first match of [from], starting from [startIndex], - /// is replaced by the result of calling [replace] with the match object. - /// - /// The [startIndex] must be non-negative and no greater than [length]. - String replaceFirstMapped(Pattern from, String Function(Match match) replace, - [int startIndex = 0]) => - value.replaceFirstMapped(from, replace, startIndex); - - /// Replaces all substrings that match [from] with [replace]. - /// - /// Creates a new string in which the non-overlapping substrings matching - /// [from] (the ones iterated by `from.allMatches(thisString)`) are replaced - /// by the literal string [replace]. - /// ```dart - /// 'resume'.replaceAll(RegExp(r'e'), 'é'); // 'résumé' - /// ``` - /// Notice that the [replace] string is not interpreted. If the replacement - /// depends on the match (for example, on a [RegExp]'s capture groups), use - /// the [replaceAllMapped] method instead. - String replaceAll(Pattern from, String replace) => - value.replaceAll(from, replace); - - /// Replace all substrings that match [from] by a computed string. - /// - /// Creates a new string in which the non-overlapping substrings that match - /// [from] (the ones iterated by `from.allMatches(thisString)`) are replaced - /// by the result of calling [replace] on the corresponding [Match] object. - /// - /// This can be used to replace matches with new content that depends on the - /// match, unlike [replaceAll] where the replacement string is always the same. - /// - /// The [replace] function is called with the [Match] generated - /// by the pattern, and its result is used as replacement. - /// - /// The function defined below converts each word in a string to simplified - /// 'pig latin' using [replaceAllMapped]: - /// ```dart - /// String pigLatin(String words) => words.replaceAllMapped( - /// RegExp(r'\b(\w*?)([aeiou]\w*)', caseSensitive: false), - /// (Match m) => "${m[2]}${m[1]}${m[1]!.isEmpty ? 'way' : 'ay'}"); - /// - /// final result = pigLatin('I have a secret now!'); - /// print(result); // 'Iway avehay away ecretsay ownay!' - /// ``` - String replaceAllMapped(Pattern from, String Function(Match match) replace) => - value.replaceAllMapped(from, replace); - - /// Replaces the substring from [start] to [end] with [replacement]. - /// - /// Creates a new string equivalent to: - /// ```dart - /// this.substring(0, start) + replacement + this.substring(end) - /// ``` - /// Example: - /// ```dart - /// const string = 'Dart is fun'; - /// final result = string.replaceRange(8, null, 'open source'); - /// print(result); // Dart is open source - /// ``` - /// The [start] and [end] indices must specify a valid range of this string. - /// That is `0 <= start <= end <= this.length`. - /// If [end] is `null`, it defaults to [length]. - String replaceRange(int start, int? end, String replacement) => - value.replaceRange(start, end, replacement); - - /// Splits the string at matches of [pattern] and returns a list of substrings. - /// - /// Finds all the matches of `pattern` in this string, - /// as by using [Pattern.allMatches], - /// and returns the list of the substrings between the matches, - /// before the first match, and after the last match. - /// ```dart - /// const string = 'Hello world!'; - /// final splitted = string.split(' '); - /// print(splitted); // [Hello, world!]; - /// ``` - /// If the pattern doesn't match this string at all, - /// the result is always a list containing only the original string. - /// - /// If the [pattern] is a [String], then it's always the case that: - /// ```dart - /// string.split(pattern).join(pattern) == string - /// ``` - /// - /// If the first match is an empty match at the start of the string, - /// the empty substring before it is not included in the result. - /// If the last match is an empty match at the end of the string, - /// the empty substring after it is not included in the result. - /// If a match is empty, and it immediately follows a previous - /// match (it starts at the position where the previous match ended), - /// then the empty substring between the two matches is not - /// included in the result. - /// ```dart - /// const string = 'abba'; - /// final re = RegExp(r'b*'); - /// // re.allMatches(string) will find four matches: - /// // * empty match before first "a". - /// // * match of "bb" - /// // * empty match after "bb", before second "a" - /// // * empty match after second "a". - /// print(string.split(re)); // [a, a] - /// ``` - /// - /// A non-empty match at the start or end of the string, or after another - /// match, is not treated specially, and will introduce empty substrings - /// in the result: - /// ```dart - /// const string = 'abbaa'; - /// final splitted = string.split('a'); // ['', 'bb', '', ''] - /// ``` - /// - /// If this string is the empty string, the result is an empty list - /// if `pattern` matches the empty string, since the empty string - /// before and after the first-and-last empty match are not included. - /// (It is still a list containing the original empty string `[""]` - /// if the pattern doesn't match). - /// ```dart - /// const string = ''; - /// print(string.split('')); // [] - /// print(string.split('a')); // [] - /// ``` - /// - /// Splitting with an empty pattern splits the string into single-code unit - /// strings. - /// ```dart - /// const string = 'Pub'; - /// print(string.split('')); // [P, u, b] - /// - /// // Same as: - /// var codeUnitStrings = [ - /// for (final unit in string.codeUnits) String.fromCharCode(unit) - /// ]; - /// print(codeUnitStrings); // [P, u, b] - /// ``` - /// - /// Splitting happens at UTF-16 code unit boundaries, - /// and not at rune (Unicode code point) boundaries: - /// ```dart - /// // String made up of two code units, but one rune. - /// const string = '\u{1D11E}'; - /// final splitted = string.split(''); - /// print(splitted); // ['\ud834', '\udd1e'] - 2 unpaired surrogate values - /// ``` - /// To get a list of strings containing the individual runes of a string, - /// you should not use split. - /// You can instead get a string for each rune as follows: - /// ```dart - /// const string = '\u{1F642}'; - /// for (final rune in string.runes) { - /// print(String.fromCharCode(rune)); - /// } - /// ``` - List split(Pattern pattern) => value.split(pattern); - - /// Splits the string, converts its parts, and combines them into a new - /// string. - /// - /// The [pattern] is used to split the string - /// into parts and separating matches. - /// Each match of [Pattern.allMatches] of [pattern] on this string is - /// used as a match, and the substrings between the end of one match - /// (or the start of the string) and the start of the next match (or the - /// end of the string) is treated as a non-matched part. - /// (There is no omission of leading or trailing empty matchs, like - /// in [split], all matches and parts between the are included.) - /// - /// Each match is converted to a string by calling [onMatch]. If [onMatch] - /// is omitted, the matched substring is used. - /// - /// Each non-matched part is converted to a string by a call to [onNonMatch]. - /// If [onNonMatch] is omitted, the non-matching substring itself is used. - /// - /// Then all the converted parts are concatenated into the resulting string. - /// ```dart - /// final result = 'Eats shoots leaves'.splitMapJoin(RegExp(r'shoots'), - /// onMatch: (m) => '${m[0]}', // (or no onMatch at all) - /// onNonMatch: (n) => '*'); - /// print(result); // *shoots* - /// ``` - String splitMapJoin(Pattern pattern, - {String Function(Match)? onMatch, - String Function(String)? onNonMatch}) => - value.splitMapJoin(pattern); - - /// An unmodifiable list of the UTF-16 code units of this string. - List get codeUnits => value.codeUnits; - - /// An [Iterable] of Unicode code-points of this string. - /// - /// If the string contains surrogate pairs, they are combined and returned - /// as one integer by this iterator. Unmatched surrogate halves are treated - /// like valid 16-bit code-units. - Runes get runes => value.runes; - - /// Converts all characters in this string to lower case. - /// - /// If the string is already in all lower case, this method returns `this`. - /// ```dart - /// 'ALPHABET'.toLowerCase(); // 'alphabet' - /// 'abc'.toLowerCase(); // 'abc' - /// ``` - /// This function uses the language independent Unicode mapping and thus only - /// works in some languages. - String toLowerCase() => value.toLowerCase(); - - /// Converts all characters in this string to upper case. - /// - /// If the string is already in all upper case, this method returns `this`. - /// ```dart - /// 'alphabet'.toUpperCase(); // 'ALPHABET' - /// 'ABC'.toUpperCase(); // 'ABC' - /// ``` - /// This function uses the language independent Unicode mapping and thus only - /// works in some languages. - String toUpperCase() => value.toUpperCase(); -} - -extension ObjStringNullExt on Obj { - /// The character (as a single-code-unit [String]) at the given [index]. - /// - /// The returned string represents exactly one UTF-16 code unit, which may be - /// half of a surrogate pair. A single member of a surrogate pair is an - /// invalid UTF-16 string: - /// ```dart - /// var clef = '\u{1D11E}'; - /// // These represent invalid UTF-16 strings. - /// clef[0].codeUnits; // [0xD834] - /// clef[1].codeUnits; // [0xDD1E] - /// ``` - /// This method is equivalent to - /// `String.fromCharCode(this.codeUnitAt(index))`. - String? operator [](int index) => value?[index]; - - /// Returns the 16-bit UTF-16 code unit at the given [index]. - int? codeUnitAt(int index) => value?.codeUnitAt(index); - - /// The length of the string. - /// - /// Returns the number of UTF-16 code units in this string. The number - /// of [runes] might be fewer if the string contains characters outside - /// the Basic Multilingual Plane (plane 0): - /// ```dart - /// 'Dart'.length; // 4 - /// 'Dart'.runes.length; // 4 - /// - /// var clef = '\u{1D11E}'; - /// clef.length; // 2 - /// clef.runes.length; // 1 - /// ``` - int? get length => value?.length; - - /// Compares this string to [other]. - /// - /// Returns a negative value if `this` is ordered before `other`, - /// a positive value if `this` is ordered after `other`, - /// or zero if `this` and `other` are equivalent. - /// - /// The ordering is the same as the ordering of the code units at the first - /// position where the two strings differ. - /// If one string is a prefix of the other, - /// then the shorter string is ordered before the longer string. - /// If the strings have exactly the same content, they are equivalent with - /// regard to the ordering. - /// Ordering does not check for Unicode equivalence. - /// The comparison is case sensitive. - /// ```dart - /// var relation = 'Dart'.compareTo('Go'); - /// print(relation); // < 0 - /// relation = 'Go'.compareTo('Forward'); - /// print(relation); // > 0 - /// relation = 'Forward'.compareTo('Forward'); - /// print(relation); // 0 - /// ``` - int? compareTo(String other) => value?.compareTo(other); - - /// Whether this string ends with [other]. - /// - /// For example: - /// ```dart - /// const string = 'Dart is open source'; - /// print(string.endsWith('urce')); // true - /// ``` - bool? endsWith(String other) => value?.endsWith(other); - - /// Whether this string starts with a match of [pattern]. - /// - /// ```dart - /// const string = 'Dart is open source'; - /// print(string.startsWith('Dar')); // true - /// print(string.startsWith(RegExp(r'[A-Z][a-z]'))); // true - /// ``` - /// If [index] is provided, this method checks if the substring starting - /// at that index starts with a match of [pattern]: - /// ```dart - /// const string = 'Dart'; - /// print(string.startsWith('art', 0)); // false - /// print(string.startsWith('art', 1)); // true - /// print(string.startsWith(RegExp(r'\w{3}'), 2)); // false - /// ``` - /// [index] must not be negative or greater than [length]. - /// - /// A [RegExp] containing '^' does not match if the [index] is greater than - /// zero and the regexp is not multi-line. - /// The pattern works on the string as a whole, and does not extract - /// a substring starting at [index] first: - /// ```dart - /// const string = 'Dart'; - /// print(string.startsWith(RegExp(r'^art'), 1)); // false - /// print(string.startsWith(RegExp(r'art'), 1)); // true - /// ``` - bool? startsWith(Pattern pattern, [int index = 0]) => - value?.startsWith(pattern, index); - - /// Returns the position of the first match of [pattern] in this string, - /// starting at [start], inclusive: - /// ```dart - /// const string = 'Dartisans'; - /// print(string.indexOf('art')); // 1 - /// print(string.indexOf(RegExp(r'[A-Z][a-z]'))); // 0 - /// ``` - /// Returns -1 if no match is found: - /// ```dart - /// const string = 'Dartisans'; - /// string.indexOf(RegExp(r'dart')); // -1 - /// ``` - /// The [start] must be non-negative and not greater than [length]. - int? indexOf(Pattern pattern, [int start = 0]) => - value?.indexOf(pattern, start); - - /// The starting position of the last match [pattern] in this string. - /// - /// Finds a match of pattern by searching backward starting at [start]: - /// ```dart - /// const string = 'Dartisans'; - /// print(string.lastIndexOf('a')); // 6 - /// print(string.lastIndexOf(RegExp(r'a(r|n)'))); // 6 - /// ``` - /// Returns -1 if [pattern] could not be found in this string. - /// ```dart - /// const string = 'Dartisans'; - /// print(string.lastIndexOf(RegExp(r'DART'))); // -1 - /// ``` - /// If [start] is omitted, search starts from the end of the string. - /// If supplied, [start] must be non-negative and not greater than [length]. - int? lastIndexOf(Pattern pattern, [int? start]) => - value?.lastIndexOf(pattern, start); - - /// Whether this string is empty. - bool? get isEmpty => value?.isEmpty; - - /// Whether this string is not empty. - bool? get isNotEmpty => value?.isNotEmpty; - - /// The substring of this string from [start], inclusive, to [end], exclusive. - /// - /// Example: - /// ```dart - /// const string = 'dartlang'; - /// var result = string.substring(1); // 'artlang' - /// result = string.substring(1, 4); // 'art' - /// ``` - /// - /// Both [start] and [end] must be non-negative and no greater than [length]; - /// [end], if provided, must be greater than or equal to [start]. - String? substring(int start, [int? end]) => value?.substring(start, end); - - /// The string without any leading and trailing whitespace. - /// - /// If the string contains leading or trailing whitespace, a new string with no - /// leading and no trailing whitespace is returned: - /// ```dart - /// final trimmed = '\tDart is fun\n'.trim(); - /// print(trimmed); // 'Dart is fun' - /// ``` - /// Otherwise, the original string itself is returned: - /// ```dart - /// const string1 = 'Dart'; - /// final string2 = string1.trim(); // 'Dart' - /// print(identical(string1, string2)); // true - /// ``` - /// Whitespace is defined by the Unicode White_Space property (as defined in - /// version 6.2 or later) and the BOM character, 0xFEFF. - /// - /// Here is the list of trimmed characters according to Unicode version 6.3: - /// ```plaintext - /// 0009..000D ; White_Space # Cc .. - /// 0020 ; White_Space # Zs SPACE - /// 0085 ; White_Space # Cc - /// 00A0 ; White_Space # Zs NO-BREAK SPACE - /// 1680 ; White_Space # Zs OGHAM SPACE MARK - /// 2000..200A ; White_Space # Zs EN QUAD..HAIR SPACE - /// 2028 ; White_Space # Zl LINE SEPARATOR - /// 2029 ; White_Space # Zp PARAGRAPH SEPARATOR - /// 202F ; White_Space # Zs NARROW NO-BREAK SPACE - /// 205F ; White_Space # Zs MEDIUM MATHEMATICAL SPACE - /// 3000 ; White_Space # Zs IDEOGRAPHIC SPACE - /// - /// FEFF ; BOM ZERO WIDTH NO_BREAK SPACE - /// ``` - /// Some later versions of Unicode do not include U+0085 as a whitespace - /// character. Whether it is trimmed depends on the Unicode version - /// used by the system. - String? trim() => value?.trim(); - - /// The string without any leading whitespace. - /// - /// As [trim], but only removes leading whitespace. - /// ```dart - /// final string = ' Dart '.trimLeft(); - /// print(string); // 'Dart ' - /// ``` - String? trimLeft() => value?.trimLeft(); - - /// The string without any trailing whitespace. - /// - /// As [trim], but only removes trailing whitespace. - /// ```dart - /// final string = ' Dart '.trimRight(); - /// print(string); // ' Dart' - /// ``` - String? trimRight() => value?.trimRight(); - - /// Pads this string on the left if it is shorter than [width]. - /// - /// Returns a new string that prepends [padding] onto this string - /// one time for each position the length is less than [width]. - /// - /// ```dart - /// const string = 'D'; - /// print(string.padLeft(4)); // ' D' - /// print(string.padLeft(2, 'x')); // 'xD' - /// print(string.padLeft(4, 'y')); // 'yyyD' - /// print(string.padLeft(4, '>>')); // '>>>>>>D' - /// ``` - /// - /// If [width] is already smaller than or equal to `this.length`, - /// no padding is added. A negative `width` is treated as zero. - /// - /// If [padding] has length different from 1, the result will not - /// have length `width`. This may be useful for cases where the - /// padding is a longer string representing a single character, like - /// `" "` or `"\u{10002}`". - /// In that case, the user should make sure that `this.length` is - /// the correct measure of the string's length. - String? padLeft(int width, [String padding = ' ']) => - value?.padLeft(width, padding); - - /// Pads this string on the right if it is shorter than [width]. - /// - /// Returns a new string that appends [padding] after this string - /// one time for each position the length is less than [width]. - /// - /// ```dart - /// const string = 'D'; - /// print(string.padRight(4)); // 'D ' - /// print(string.padRight(2, 'x')); // 'Dx' - /// print(string.padRight(4, 'y')); // 'Dyyy' - /// print(string.padRight(4, '>>')); // 'D>>>>>>' - /// ``` - /// - /// If [width] is already smaller than or equal to `this.length`, - /// no padding is added. A negative `width` is treated as zero. - /// - /// If [padding] has length different from 1, the result will not - /// have length `width`. This may be useful for cases where the - /// padding is a longer string representing a single character, like - /// `" "` or `"\u{10002}`". - /// In that case, the user should make sure that `this.length` is - /// the correct measure of the string's length. - String? padRight(int width, [String padding = ' ']) => - value?.padRight(width, padding); - - /// Whether this string contains a match of [other]. - /// - /// Example: - /// ```dart - /// const string = 'Dart strings'; - /// final containsD = string.contains('D'); // true - /// final containsUpperCase = string.contains(RegExp(r'[A-Z]')); // true - /// ``` - /// If [startIndex] is provided, this method matches only at or after that - /// index: - /// ```dart - /// const string = 'Dart strings'; - /// final containsD = string.contains(RegExp('D'), 0); // true - /// final caseSensitive = string.contains(RegExp(r'[A-Z]'), 1); // false - /// ``` - /// The [startIndex] must not be negative or greater than [length]. - bool? contains(Pattern other, [int startIndex = 0]) => - value?.contains(other, startIndex); - - /// Creates a new string with the first occurrence of [from] replaced by [to]. - /// - /// Finds the first match of [from] in this string, starting from [startIndex], - /// and creates a new string where that match is replaced with the [to] string. - /// - /// Example: - /// ```dart - /// '0.0001'.replaceFirst(RegExp(r'0'), ''); // '.0001' - /// '0.0001'.replaceFirst(RegExp(r'0'), '7', 1); // '0.7001' - /// ``` - String? replaceFirst(Pattern from, String to, [int startIndex = 0]) => - value?.replaceFirst(from, to, startIndex); - - /// Replace the first occurrence of [from] in this string. - /// - /// ```dart - /// const string = 'Dart is fun'; - /// print(string.replaceFirstMapped( - /// 'fun', (m) => 'open source')); // Dart is open source - /// - /// print(string.replaceFirstMapped( - /// RegExp(r'\w(\w*)'), (m) => '<${m[0]}-${m[1]}>')); // is fun - /// ``` - /// - /// Returns a new string, which is this string - /// except that the first match of [from], starting from [startIndex], - /// is replaced by the result of calling [replace] with the match object. - /// - /// The [startIndex] must be non-negative and no greater than [length]. - String? replaceFirstMapped(Pattern from, String Function(Match match) replace, - [int startIndex = 0]) => - value?.replaceFirstMapped(from, replace, startIndex); - - /// Replaces all substrings that match [from] with [replace]. - /// - /// Creates a new string in which the non-overlapping substrings matching - /// [from] (the ones iterated by `from.allMatches(thisString)`) are replaced - /// by the literal string [replace]. - /// ```dart - /// 'resume'.replaceAll(RegExp(r'e'), 'é'); // 'résumé' - /// ``` - /// Notice that the [replace] string is not interpreted. If the replacement - /// depends on the match (for example, on a [RegExp]'s capture groups), use - /// the [replaceAllMapped] method instead. - String? replaceAll(Pattern from, String replace) => - value?.replaceAll(from, replace); - - /// Replace all substrings that match [from] by a computed string. - /// - /// Creates a new string in which the non-overlapping substrings that match - /// [from] (the ones iterated by `from.allMatches(thisString)`) are replaced - /// by the result of calling [replace] on the corresponding [Match] object. - /// - /// This can be used to replace matches with new content that depends on the - /// match, unlike [replaceAll] where the replacement string is always the same. - /// - /// The [replace] function is called with the [Match] generated - /// by the pattern, and its result is used as replacement. - /// - /// The function defined below converts each word in a string to simplified - /// 'pig latin' using [replaceAllMapped]: - /// ```dart - /// String pigLatin(String words) => words.replaceAllMapped( - /// RegExp(r'\b(\w*?)([aeiou]\w*)', caseSensitive: false), - /// (Match m) => "${m[2]}${m[1]}${m[1]!.isEmpty ? 'way' : 'ay'}"); - /// - /// final result = pigLatin('I have a secret now!'); - /// print(result); // 'Iway avehay away ecretsay ownay!' - /// ``` - String? replaceAllMapped( - Pattern from, String Function(Match match) replace) => - value?.replaceAllMapped(from, replace); - - /// Replaces the substring from [start] to [end] with [replacement]. - /// - /// Creates a new string equivalent to: - /// ```dart - /// this.substring(0, start) + replacement + this.substring(end) - /// ``` - /// Example: - /// ```dart - /// const string = 'Dart is fun'; - /// final result = string.replaceRange(8, null, 'open source'); - /// print(result); // Dart is open source - /// ``` - /// The [start] and [end] indices must specify a valid range of this string. - /// That is `0 <= start <= end <= this.length`. - /// If [end] is `null`, it defaults to [length]. - String? replaceRange(int start, int? end, String replacement) => - value?.replaceRange(start, end, replacement); - - /// Splits the string at matches of [pattern] and returns a list of substrings. - /// - /// Finds all the matches of `pattern` in this string, - /// as by using [Pattern.allMatches], - /// and returns the list of the substrings between the matches, - /// before the first match, and after the last match. - /// ```dart - /// const string = 'Hello world!'; - /// final splitted = string.split(' '); - /// print(splitted); // [Hello, world!]; - /// ``` - /// If the pattern doesn't match this string at all, - /// the result is always a list containing only the original string. - /// - /// If the [pattern] is a [String], then it's always the case that: - /// ```dart - /// string.split(pattern).join(pattern) == string - /// ``` - /// - /// If the first match is an empty match at the start of the string, - /// the empty substring before it is not included in the result. - /// If the last match is an empty match at the end of the string, - /// the empty substring after it is not included in the result. - /// If a match is empty, and it immediately follows a previous - /// match (it starts at the position where the previous match ended), - /// then the empty substring between the two matches is not - /// included in the result. - /// ```dart - /// const string = 'abba'; - /// final re = RegExp(r'b*'); - /// // re.allMatches(string) will find four matches: - /// // * empty match before first "a". - /// // * match of "bb" - /// // * empty match after "bb", before second "a" - /// // * empty match after second "a". - /// print(string.split(re)); // [a, a] - /// ``` - /// - /// A non-empty match at the start or end of the string, or after another - /// match, is not treated specially, and will introduce empty substrings - /// in the result: - /// ```dart - /// const string = 'abbaa'; - /// final splitted = string.split('a'); // ['', 'bb', '', ''] - /// ``` - /// - /// If this string is the empty string, the result is an empty list - /// if `pattern` matches the empty string, since the empty string - /// before and after the first-and-last empty match are not included. - /// (It is still a list containing the original empty string `[""]` - /// if the pattern doesn't match). - /// ```dart - /// const string = ''; - /// print(string.split('')); // [] - /// print(string.split('a')); // [] - /// ``` - /// - /// Splitting with an empty pattern splits the string into single-code unit - /// strings. - /// ```dart - /// const string = 'Pub'; - /// print(string.split('')); // [P, u, b] - /// - /// // Same as: - /// var codeUnitStrings = [ - /// for (final unit in string.codeUnits) String.fromCharCode(unit) - /// ]; - /// print(codeUnitStrings); // [P, u, b] - /// ``` - /// - /// Splitting happens at UTF-16 code unit boundaries, - /// and not at rune (Unicode code point) boundaries: - /// ```dart - /// // String made up of two code units, but one rune. - /// const string = '\u{1D11E}'; - /// final splitted = string.split(''); - /// print(splitted); // ['\ud834', '\udd1e'] - 2 unpaired surrogate values - /// ``` - /// To get a list of strings containing the individual runes of a string, - /// you should not use split. - /// You can instead get a string for each rune as follows: - /// ```dart - /// const string = '\u{1F642}'; - /// for (final rune in string.runes) { - /// print(String.fromCharCode(rune)); - /// } - /// ``` - List? split(Pattern pattern) => value?.split(pattern); - - /// Splits the string, converts its parts, and combines them into a new - /// string. - /// - /// The [pattern] is used to split the string - /// into parts and separating matches. - /// Each match of [Pattern.allMatches] of [pattern] on this string is - /// used as a match, and the substrings between the end of one match - /// (or the start of the string) and the start of the next match (or the - /// end of the string) is treated as a non-matched part. - /// (There is no omission of leading or trailing empty matchs, like - /// in [split], all matches and parts between the are included.) - /// - /// Each match is converted to a string by calling [onMatch]. If [onMatch] - /// is omitted, the matched substring is used. - /// - /// Each non-matched part is converted to a string by a call to [onNonMatch]. - /// If [onNonMatch] is omitted, the non-matching substring itself is used. - /// - /// Then all the converted parts are concatenated into the resulting string. - /// ```dart - /// final result = 'Eats shoots leaves'.splitMapJoin(RegExp(r'shoots'), - /// onMatch: (m) => '${m[0]}', // (or no onMatch at all) - /// onNonMatch: (n) => '*'); - /// print(result); // *shoots* - /// ``` - String? splitMapJoin(Pattern pattern, - {String Function(Match)? onMatch, - String Function(String)? onNonMatch}) => - value?.splitMapJoin(pattern); - - /// An unmodifiable list of the UTF-16 code units of this string. - List? get codeUnits => value?.codeUnits; - - /// An [Iterable] of Unicode code-points of this string. - /// - /// If the string contains surrogate pairs, they are combined and returned - /// as one integer by this iterator. Unmatched surrogate halves are treated - /// like valid 16-bit code-units. - Runes? get runes => value?.runes; - - /// Converts all characters in this string to lower case. - /// - /// If the string is already in all lower case, this method returns `this`. - /// ```dart - /// 'ALPHABET'.toLowerCase(); // 'alphabet' - /// 'abc'.toLowerCase(); // 'abc' - /// ``` - /// This function uses the language independent Unicode mapping and thus only - /// works in some languages. - String? toLowerCase() => value?.toLowerCase(); - - /// Converts all characters in this string to upper case. - /// - /// If the string is already in all upper case, this method returns `this`. - /// ```dart - /// 'alphabet'.toUpperCase(); // 'ALPHABET' - /// 'ABC'.toUpperCase(); // 'ABC' - /// ``` - /// This function uses the language independent Unicode mapping and thus only - /// works in some languages. - String? toUpperCase() => value?.toUpperCase(); -} diff --git a/packages/reactter/lib/src/obj/obj.dart b/packages/reactter/lib/src/obj/obj.dart deleted file mode 100644 index dc74a963..00000000 --- a/packages/reactter/lib/src/obj/obj.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'dart:math'; - -part 'obj_impl.dart'; -part 'extensions/obj_bigint.dart'; -part 'extensions/obj_bool.dart'; -part 'extensions/obj_date_time.dart'; -part 'extensions/obj_double.dart'; -part 'extensions/obj_int.dart'; -part 'extensions/obj_iterable.dart'; -part 'extensions/obj_list.dart'; -part 'extensions/obj_map.dart'; -part 'extensions/obj_num.dart'; -part 'extensions/obj_set.dart'; -part 'extensions/obj_string.dart'; diff --git a/packages/reactter/lib/src/obj/obj_impl.dart b/packages/reactter/lib/src/obj/obj_impl.dart deleted file mode 100644 index 2570a2ac..00000000 --- a/packages/reactter/lib/src/obj/obj_impl.dart +++ /dev/null @@ -1,97 +0,0 @@ -part of 'obj.dart'; - -/// {@template reactter.obj} -/// A base-class that store a value of [T]. -/// -/// You can create a new [Obj]: -/// -/// ```dart -/// // usign the `Obj` class -/// final strObj = Obj("initial value"); -/// final intObj = Obj(0); -/// final userObj = Obj(User()); -/// ``` -/// -/// You can get the [value]: -/// -/// ```dart -/// // usign a `value` getter -/// print(strObj.value); -/// // or using the callable -/// print(strObj()); -/// // or using `toString` implicit -/// print("$intObj"); -/// ``` -/// -/// You can set a new [value]: -/// -/// ```dart -/// // usign a `value` setter -/// strObj.value = "change value"; -/// // or using the callable -/// strObj("change value"); -/// ``` -/// {@endtemplate} -@Deprecated( - 'This feature was deprecated after v7.2.0 and will be removed in v8.0.0.', -) -class Obj with ObjBase { - @override - T value; - - /// {@macro reactter.obj} - Obj(this.value); -} - -abstract class ObjBase { - set value(T value); - T get value; - - /// The equality operator. - /// - /// It's checking if the [other] object is of the same type as the [Obj], if it is, it compares the - /// [value], else it compares like [Object] using [hashCode]. - /// - /// Examples: - /// ```dart - /// final obj = Obj(0); - /// final obj2 = Obj(1); - /// final obj3 = obj(0); - /// final objCopy = obj; - /// - /// print(obj == 0); // true - /// print(obj == 1); // false - /// print(obj == obj2); // false - /// print(obj == obj3); // true - /// print(obj == objCopy); // true - /// ``` - /// - @override - bool operator ==(Object other) => - other is Obj ? identical(this, other) : value == other; - - @override - // ignore: unnecessary_overrides - int get hashCode => super.hashCode; - - /// Gets and/or sets to [value] like a function - T call([T? val]) { - if (null is T || val != null) { - value = val as T; - } - - return value; - } - - @override - String toString() => value.toString(); -} - -extension ObjNullExt on Obj { - /// Returns a new `Obj` with the value of the current `Obj` - /// if it's not null. - Obj get notNull { - assert(value != null); - return Obj(value as T); - } -} diff --git a/packages/reactter/lib/src/signal/signal_impl.dart b/packages/reactter/lib/src/signal.dart similarity index 80% rename from packages/reactter/lib/src/signal/signal_impl.dart rename to packages/reactter/lib/src/signal.dart index 1d39e03f..f5cbf444 100644 --- a/packages/reactter/lib/src/signal/signal_impl.dart +++ b/packages/reactter/lib/src/signal.dart @@ -1,4 +1,4 @@ -part of 'signal.dart'; +import 'package:reactter/src/framework.dart'; /// This enumeration is used to represent different events that can occur when /// getting or setting the value of a `Signal` object. @@ -72,13 +72,8 @@ enum SignalEvent { onGetValue, onSetValue } /// If you use flutter, add [`flutter_reactter`](https://pub.dev/packages/flutter_reactter) /// package on your dependencies and use its Widgets. /// -/// See also: -/// -/// * [Obj], a base-class that can be used to store a value of [T]. /// {@endtemplate} -class Signal - with RtContext, RtStateBase>, ObjBase - implements Obj { +class Signal with RtContext, RtStateBase> { /// {@macro reactter.signal} Signal._( T value, { @@ -105,7 +100,6 @@ class Signal Map get debugInfo => {'value': value}; /// Returns the [value] of the signal. - @override T get value { _notifyGetValue(); @@ -114,7 +108,6 @@ class Signal /// Updates the [value] of the signal /// and notifies the observers when changes occur. - @override set value(T val) { if (_value == val) return; @@ -127,7 +120,6 @@ class Signal /// Gets and/or sets to [value] like a function /// This method doesn't allow setting its value to null. /// If you need to set null as value, use `.value = null`. - @override T call([T? val]) { assert(!isDisposed, "You can call when it's been disposed"); @@ -136,11 +128,42 @@ class Signal return value; } + /// Executes [callback], and notifies the listeners about the update. @override void update(void Function(T value) fnUpdate) { super.update(() => fnUpdate(value)); } + @override + String toString() => value.toString(); + + @override + // ignore: unnecessary_overrides + int get hashCode => super.hashCode; + + /// The equality operator. + /// + /// It's checking if the [other] object is of the same type as the [Signal], + /// if it is, it compares the [value], else it compares like [Object] using [hashCode]. + /// + /// Examples: + /// ```dart + /// final s = Signal(0); + /// final s2 = Signal(1); + /// final s3 = s(0); + /// final sCopy = s; + /// + /// print(s == 0); // true + /// print(s == 1); // false + /// print(s == s2); // false + /// print(s == s3); // true + /// print(s == sCopy); // true + /// ``` + /// + @override + bool operator ==(Object other) => + other is Signal ? identical(this, other) : value == other; + void _notifyGetValue() { if (!_shouldGetValueNotify) return; @@ -166,19 +189,3 @@ extension SignalNullExt on Signal { return Signal(value as T); } } - -extension ObjToSignalExt on Obj { - /// Returns a new Signal with the value of the current Obj. - @Deprecated( - 'This feature was deprecated after v7.2.0 and will be removed in v8.0.0.', - ) - Signal get toSignal => Signal(value); -} - -extension SignalToObjExt on Signal { - /// Returns a new Obj with the value of the current Signal. - @Deprecated( - 'This feature was deprecated after v7.2.0 and will be removed in v8.0.0.', - ) - Obj get toObj => Obj(value); -} diff --git a/packages/reactter/lib/src/signal/extensions/signal_list.dart b/packages/reactter/lib/src/signal/extensions/signal_list.dart deleted file mode 100644 index 285f7a2c..00000000 --- a/packages/reactter/lib/src/signal/extensions/signal_list.dart +++ /dev/null @@ -1,821 +0,0 @@ -// coverage:ignore-file -part of '../signal.dart'; - -extension SignalListExt on Signal> { - /// The object at the given [index] in the list. - /// - /// The [index] must be a valid index of this list, - /// which means that `index` must be non-negative and - /// less than [length]. - E operator [](int index) => value[index]; - - /// Sets the value at the given [index] in the list to [value]. - /// - /// The [index] must be a valid index of this list, - /// which means that `index` must be non-negative and - /// less than [length]. - void operator []=(int index, E valueToSet) => - update((_) => value[index] = valueToSet); - - /// Returns the first element. - /// - /// Throws a [StateError] if `this` is empty. - /// Otherwise returns the first element in the iteration order, - /// equivalent to `this.elementAt(0)`. - E get first => value.first; - - /// The first element of the list. - /// - /// The list must be non-empty when accessing its first element. - /// - /// The first element of a list can be modified, unlike an [Iterable]. - /// A `list.first` is equivalent to `list[0]`, - /// both for getting and setting the value. - /// - /// ```dart - /// final numbers = [1, 2, 3]; - /// print(numbers.first); // 1 - /// numbers.first = 10; - /// print(numbers.first); // 10 - /// numbers.clear(); - /// numbers.first; // Throws. - /// ``` - set first(E valueToSet) => update((_) => value.first = valueToSet); - - /// Returns the last element. - /// - /// Throws a [StateError] if `this` is empty. - /// Otherwise may iterate through the elements and returns the last one - /// seen. - /// Some iterables may have more efficient ways to find the last element - /// (for example a list can directly access the last element, - /// without iterating through the previous ones). - E get last => value.last; - - /// The last element of the list. - /// - /// The list must be non-empty when accessing its last element. - /// - /// The last element of a list can be modified, unlike an [Iterable]. - /// A `list.last` is equivalent to `theList[theList.length - 1]`, - /// both for getting and setting the value. - /// - /// ```dart - /// final numbers = [1, 2, 3]; - /// print(numbers.last); // 3 - /// numbers.last = 10; - /// print(numbers.last); // 10 - /// numbers.clear(); - /// numbers.last; // Throws. - /// ``` - set last(E valueToSet) => update((_) => value.last = valueToSet); - - /// The number of objects in this list. - /// - /// The valid indices for a list are `0` through `length - 1`. - /// ```dart - /// final numbers = [1, 2, 3]; - /// print(numbers.length); // 3 - /// ``` - int get length => value.length; - - /// Setting the `length` changes the number of elements in the list. - /// - /// The list must be growable. - /// If [newLength] is greater than current length, - /// new entries are initialized to `null`, - /// so [newLength] must not be greater than the current length - /// if the element type [E] is non-nullable. - /// - /// ```dart - /// final maybeNumbers = [1, null, 3]; - /// maybeNumbers.length = 5; - /// print(maybeNumbers); // [1, null, 3, null, null] - /// maybeNumbers.length = 2; - /// print(maybeNumbers); // [1, null] - /// - /// final numbers = [1, 2, 3]; - /// numbers.length = 1; - /// print(numbers); // [1] - /// numbers.length = 5; // Throws, cannot add `null`s. - /// ``` - set length(int newLength) => update((_) => value.length = newLength); - - /// Adds [value] to the end of this list, - /// extending the length by one. - /// - /// The list must be growable. - /// - /// ```dart - /// final numbers = [1, 2, 3]; - /// numbers.add(4); - /// print(numbers); // [1, 2, 3, 4] - /// ``` - void add(E valueToAdd) => update((_) => value.add(valueToAdd)); - - /// Appends all objects of [iterable] to the end of this list. - /// - /// Extends the length of the list by the number of objects in [iterable]. - /// The list must be growable. - /// - /// ```dart - /// final numbers = [1, 2, 3]; - /// numbers.addAll([4, 5, 6]); - /// print(numbers); // [1, 2, 3, 4, 5, 6] - /// ``` - void addAll(Iterable iterable) => update((_) => value.addAll(iterable)); - - /// Sorts this list according to the order specified by the [compare] function. - /// - /// The [compare] function must act as a [Comparator]. - /// ```dart - /// final numbers = ['two', 'three', 'four']; - /// // Sort from shortest to longest. - /// numbers.sort((a, b) => a.length.compareTo(b.length)); - /// print(numbers); // [two, four, three] - /// ``` - /// The default [List] implementations use [Comparable.compare] if - /// [compare] is omitted. - /// ```dart - /// final numbers = [13, 2, -11, 0]; - /// numbers.sort(); - /// print(numbers); // [-11, 0, 2, 13] - /// ``` - /// In that case, the elements of the list must be [Comparable] to - /// each other. - /// - /// A [Comparator] may compare objects as equal (return zero), even if they - /// are distinct objects. - /// The sort function is not guaranteed to be stable, so distinct objects - /// that compare as equal may occur in any order in the result: - /// ```dart - /// final numbers = ['one', 'two', 'three', 'four']; - /// numbers.sort((a, b) => a.length.compareTo(b.length)); - /// print(numbers); // [one, two, four, three] OR [two, one, four, three] - /// ``` - void sort([int Function(E a, E b)? compare]) => - update((_) => value.sort(compare)); - - /// Shuffles the elements of this list randomly. - /// ```dart - /// final numbers = [1, 2, 3, 4, 5]; - /// numbers.shuffle(); - /// print(numbers); // [1, 3, 4, 5, 2] OR some other random result. - /// ``` - void shuffle([Random? random]) => update((_) => value.shuffle(random)); - - /// Removes all objects from this list; the length of the list becomes zero. - /// - /// The list must be growable. - /// - /// ```dart - /// final numbers = [1, 2, 3]; - /// numbers.clear(); - /// print(numbers.length); // 0 - /// print(numbers); // [] - /// ``` - void clear() => update((_) => value.clear()); - - /// Inserts [element] at position [index] in this list. - /// - /// This increases the length of the list by one and shifts all objects - /// at or after the index towards the end of the list. - /// - /// The list must be growable. - /// The [index] value must be non-negative and no greater than [length]. - /// - /// ```dart - /// final numbers = [1, 2, 3, 4]; - /// const index = 2; - /// numbers.insert(index, 10); - /// print(numbers); // [1, 2, 10, 3, 4] - /// ``` - void insert(int index, E element) => - update((_) => value.insert(index, element)); - - /// Inserts all objects of [iterable] at position [index] in this list. - /// - /// This increases the length of the list by the length of [iterable] and - /// shifts all later objects towards the end of the list. - /// - /// The list must be growable. - /// The [index] value must be non-negative and no greater than [length]. - /// ```dart - /// final numbers = [1, 2, 3, 4]; - /// final insertItems = [10, 11]; - /// numbers.insertAll(2, insertItems); - /// print(numbers); // [1, 2, 10, 11, 3, 4] - /// ``` - void insertAll(int index, Iterable iterable) => - update((_) => value.insertAll(index, iterable)); - - /// Overwrites elements with the objects of [iterable]. - /// - /// The elements of [iterable] are written into this list, - /// starting at position [index]. - /// This operation does not increase the length of the list. - /// - /// The [index] must be non-negative and no greater than [length]. - /// - /// The [iterable] must not have more elements than what can fit from [index] - /// to [length]. - /// - /// If `iterable` is based on this list, its values may change _during_ the - /// `setAll` operation. - /// ```dart - /// final list = ['a', 'b', 'c', 'd']; - /// list.setAll(1, ['bee', 'sea']); - /// print(list); // [a, bee, sea, d] - /// ``` - void setAll(int index, Iterable iterable) => - update((_) => value.setAll(index, iterable)); - - /// Removes the first occurrence of [value] from this list. - /// - /// Returns true if [value] was in the list, false otherwise. - /// The list must be growable. - /// - /// ```dart - /// final parts = ['head', 'shoulders', 'knees', 'toes']; - /// final retVal = parts.remove('head'); // true - /// print(parts); // [shoulders, knees, toes] - /// ``` - /// The method has no effect if [value] was not in the list. - /// ```dart - /// final parts = ['shoulders', 'knees', 'toes']; - /// // Note: 'head' has already been removed. - /// final retVal = parts.remove('head'); // false - /// print(parts); // [shoulders, knees, toes] - /// ``` - bool remove(Object? valueToRemove) { - late bool result; - update((_) => result = value.remove(valueToRemove)); - return result; - } - - /// Removes the object at position [index] from this list. - /// - /// This method reduces the length of `this` by one and moves all later objects - /// down by one position. - /// - /// Returns the removed value. - /// - /// The [index] must be in the range `0 ≤ index < length`. - /// The list must be growable. - /// ```dart - /// final parts = ['head', 'shoulder', 'knees', 'toes']; - /// final retVal = parts.removeAt(2); // knees - /// print(parts); // [head, shoulder, toes] - /// ``` - E removeAt(int index) { - late E result; - update((_) => result = value.removeAt(index)); - return result; - } - - /// Removes and returns the last object in this list. - /// - /// The list must be growable and non-empty. - /// ```dart - /// final parts = ['head', 'shoulder', 'knees', 'toes']; - /// final retVal = parts.removeLast(); // toes - /// print(parts); // [head, shoulder, knees] - /// ``` - E removeLast() { - late E result; - update((_) => result = value.removeLast()); - return result; - } - - /// Removes all objects from this list that satisfy [test]. - /// - /// An object `o` satisfies [test] if `test(o)` is true. - /// ```dart - /// final numbers = ['one', 'two', 'three', 'four']; - /// numbers.removeWhere((item) => item.length == 3); - /// print(numbers); // [three, four] - /// ``` - /// The list must be growable. - void removeWhere(bool Function(E element) test) => - update((_) => value.removeWhere(test)); - - /// Removes all objects from this list that fail to satisfy [test]. - /// - /// An object `o` satisfies [test] if `test(o)` is true. - /// ```dart - /// final numbers = ['one', 'two', 'three', 'four']; - /// numbers.retainWhere((item) => item.length == 3); - /// print(numbers); // [one, two] - /// ``` - /// The list must be growable. - void retainWhere(bool Function(E element) test) => - update((_) => value.retainWhere(test)); - - /// Writes some elements of [iterable] into a range of this list. - /// - /// Copies the objects of [iterable], skipping [skipCount] objects first, - /// into the range from [start], inclusive, to [end], exclusive, of this list. - /// ```dart - /// final list1 = [1, 2, 3, 4]; - /// final list2 = [5, 6, 7, 8, 9]; - /// // Copies the 4th and 5th items in list2 as the 2nd and 3rd items - /// // of list1. - /// const skipCount = 3; - /// list1.setRange(1, 3, list2, skipCount); - /// print(list1); // [1, 8, 9, 4] - /// ``` - /// The provided range, given by [start] and [end], must be valid. - /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. - /// An empty range (with `end == start`) is valid. - /// - /// The [iterable] must have enough objects to fill the range from `start` - /// to `end` after skipping [skipCount] objects. - /// - /// If `iterable` is this list, the operation correctly copies the elements - /// originally in the range from `skipCount` to `skipCount + (end - start)` to - /// the range `start` to `end`, even if the two ranges overlap. - /// - /// If `iterable` depends on this list in some other way, no guarantees are - /// made. - void setRange(int start, int end, Iterable iterable, - [int skipCount = 0]) => - update((_) => value.setRange(start, end, iterable, skipCount)); - - /// Removes a range of elements from the list. - /// - /// Removes the elements with positions greater than or equal to [start] - /// and less than [end], from the list. - /// This reduces the list's length by `end - start`. - /// - /// The provided range, given by [start] and [end], must be valid. - /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. - /// An empty range (with `end == start`) is valid. - /// - /// The list must be growable. - /// ```dart - /// final numbers = [1, 2, 3, 4, 5]; - /// numbers.removeRange(1, 4); - /// print(numbers); // [1, 5] - /// ``` - void removeRange(int start, int end) => - update((_) => value.removeRange(start, end)); - - /// Overwrites a range of elements with [fillValue]. - /// - /// Sets the positions greater than or equal to [start] and less than [end], - /// to [fillValue]. - /// - /// The provided range, given by [start] and [end], must be valid. - /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. - /// An empty range (with `end == start`) is valid. - /// - /// If the element type is not nullable, the [fillValue] must be provided and - /// must be non-`null`. - /// - /// Example: - /// ```dart - /// final words = List.filled(5, 'old'); - /// print(words); // [old, old, old, old, old] - /// words.fillRange(1, 3, 'new'); - /// print(words); // [old, new, new, old, old] - /// ``` - void fillRange(int start, int end, [E? fillValue]) => - update((_) => value.fillRange(start, end, fillValue)); - - /// Replaces a range of elements with the elements of [replacements]. - /// - /// Removes the objects in the range from [start] to [end], - /// then inserts the elements of [replacements] at [start]. - /// ```dart - /// final numbers = [1, 2, 3, 4, 5]; - /// final replacements = [6, 7]; - /// numbers.replaceRange(1, 4, replacements); - /// print(numbers); // [1, 6, 7, 5] - /// ``` - /// The provided range, given by [start] and [end], must be valid. - /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. - /// An empty range (with `end == start`) is valid. - /// - /// The operation `list.replaceRange(start, end, replacements)` - /// is roughly equivalent to: - /// ```dart - /// final numbers = [1, 2, 3, 4, 5]; - /// numbers.removeRange(1, 4); - /// final replacements = [6, 7]; - /// numbers.insertAll(1, replacements); - /// print(numbers); // [1, 6, 7, 5] - /// ``` - /// but may be more efficient. - /// - /// The list must be growable. - /// This method does not work on fixed-length lists, even when [replacements] - /// has the same number of elements as the replaced range. In that case use - /// [setRange] instead. - void replaceRange(int start, int end, Iterable replacements) => - update((_) => value.replaceRange(start, end, replacements)); -} - -extension SignalListNullExt on Signal?> { - /// Sets the value at the given [index] in the list to [value]. - /// - /// The [index] must be a valid index of this list, - /// which means that `index` must be non-negative and - /// less than [length]. - void operator []=(int index, E valueToSet) { - if (value == null) return; - update((_) => value?[index] = valueToSet); - } - - /// The last element of the list. - /// - /// The list must be non-empty when accessing its last element. - /// - /// The last element of a list can be modified, unlike an [Iterable]. - /// A `list.last` is equivalent to `theList[theList.length - 1]`, - /// both for getting and setting the value. - /// - /// ```dart - /// final numbers = [1, 2, 3]; - /// print(numbers.last); // 3 - /// numbers.last = 10; - /// print(numbers.last); // 10 - /// numbers.clear(); - /// numbers.last; // Throws. - /// ``` - set last(E valueToSet) { - if (value == null) return; - update((_) => value?.last = valueToSet); - } - - /// Setting the `length` changes the number of elements in the list. - /// - /// The list must be growable. - /// If [newLength] is greater than current length, - /// new entries are initialized to `null`, - /// so [newLength] must not be greater than the current length - /// if the element type [E] is non-nullable. - /// - /// ```dart - /// final maybeNumbers = [1, null, 3]; - /// maybeNumbers.length = 5; - /// print(maybeNumbers); // [1, null, 3, null, null] - /// maybeNumbers.length = 2; - /// print(maybeNumbers); // [1, null] - /// - /// final numbers = [1, 2, 3]; - /// numbers.length = 1; - /// print(numbers); // [1] - /// numbers.length = 5; // Throws, cannot add `null`s. - /// ``` - set length(int newLength) { - if (value == null) return; - update((_) => value?.length = newLength); - } - - /// Adds [value] to the end of this list, - /// extending the length by one. - /// - /// The list must be growable. - /// - /// ```dart - /// final numbers = [1, 2, 3]; - /// numbers.add(4); - /// print(numbers); // [1, 2, 3, 4] - /// ``` - void add(E valueToAdd) { - if (value == null) return; - update((_) => value?.add(valueToAdd)); - } - - /// Appends all objects of [iterable] to the end of this list. - /// - /// Extends the length of the list by the number of objects in [iterable]. - /// The list must be growable. - /// - /// ```dart - /// final numbers = [1, 2, 3]; - /// numbers.addAll([4, 5, 6]); - /// print(numbers); // [1, 2, 3, 4, 5, 6] - /// ``` - void addAll(Iterable iterable) { - if (value == null) return; - update((_) => value?.addAll(iterable)); - } - - /// Sorts this list according to the order specified by the [compare] function. - /// - /// The [compare] function must act as a [Comparator]. - /// ```dart - /// final numbers = ['two', 'three', 'four']; - /// // Sort from shortest to longest. - /// numbers.sort((a, b) => a.length.compareTo(b.length)); - /// print(numbers); // [two, four, three] - /// ``` - /// The default [List] implementations use [Comparable.compare] if - /// [compare] is omitted. - /// ```dart - /// final numbers = [13, 2, -11, 0]; - /// numbers.sort(); - /// print(numbers); // [-11, 0, 2, 13] - /// ``` - /// In that case, the elements of the list must be [Comparable] to - /// each other. - /// - /// A [Comparator] may compare objects as equal (return zero), even if they - /// are distinct objects. - /// The sort function is not guaranteed to be stable, so distinct objects - /// that compare as equal may occur in any order in the result: - /// ```dart - /// final numbers = ['one', 'two', 'three', 'four']; - /// numbers.sort((a, b) => a.length.compareTo(b.length)); - /// print(numbers); // [one, two, four, three] OR [two, one, four, three] - /// ``` - void sort([int Function(E a, E b)? compare]) { - if (value == null) return; - update((_) => value?.sort(compare)); - } - - /// Shuffles the elements of this list randomly. - /// ```dart - /// final numbers = [1, 2, 3, 4, 5]; - /// numbers.shuffle(); - /// print(numbers); // [1, 3, 4, 5, 2] OR some other random result. - /// ``` - void shuffle([Random? random]) { - if (value == null) return; - update((_) => value?.shuffle(random)); - } - - /// Removes all objects from this list; the length of the list becomes zero. - /// - /// The list must be growable. - /// - /// ```dart - /// final numbers = [1, 2, 3]; - /// numbers.clear(); - /// print(numbers.length); // 0 - /// print(numbers); // [] - /// ``` - void clear() { - if (value == null) return; - update((_) => value?.clear()); - } - - /// Inserts [element] at position [index] in this list. - /// - /// This increases the length of the list by one and shifts all objects - /// at or after the index towards the end of the list. - /// - /// The list must be growable. - /// The [index] value must be non-negative and no greater than [length]. - /// - /// ```dart - /// final numbers = [1, 2, 3, 4]; - /// const index = 2; - /// numbers.insert(index, 10); - /// print(numbers); // [1, 2, 10, 3, 4] - /// ``` - void insert(int index, E element) { - if (value == null) return; - update((_) => value?.insert(index, element)); - } - - /// Inserts all objects of [iterable] at position [index] in this list. - /// - /// This increases the length of the list by the length of [iterable] and - /// shifts all later objects towards the end of the list. - /// - /// The list must be growable. - /// The [index] value must be non-negative and no greater than [length]. - /// ```dart - /// final numbers = [1, 2, 3, 4]; - /// final insertItems = [10, 11]; - /// numbers.insertAll(2, insertItems); - /// print(numbers); // [1, 2, 10, 11, 3, 4] - /// ``` - void insertAll(int index, Iterable iterable) { - if (value == null) return; - update((_) => value?.insertAll(index, iterable)); - } - - /// Overwrites elements with the objects of [iterable]. - /// - /// The elements of [iterable] are written into this list, - /// starting at position [index]. - /// This operation does not increase the length of the list. - /// - /// The [index] must be non-negative and no greater than [length]. - /// - /// The [iterable] must not have more elements than what can fit from [index] - /// to [length]. - /// - /// If `iterable` is based on this list, its values may change _during_ the - /// `setAll` operation. - /// ```dart - /// final list = ['a', 'b', 'c', 'd']; - /// list.setAll(1, ['bee', 'sea']); - /// print(list); // [a, bee, sea, d] - /// ``` - void setAll(int index, Iterable iterable) { - if (value == null) return; - update((_) => value?.setAll(index, iterable)); - } - - /// Removes the first occurrence of [value] from this list. - /// - /// Returns true if [value] was in the list, false otherwise. - /// The list must be growable. - /// - /// ```dart - /// final parts = ['head', 'shoulders', 'knees', 'toes']; - /// final retVal = parts.remove('head'); // true - /// print(parts); // [shoulders, knees, toes] - /// ``` - /// The method has no effect if [value] was not in the list. - /// ```dart - /// final parts = ['shoulders', 'knees', 'toes']; - /// // Note: 'head' has already been removed. - /// final retVal = parts.remove('head'); // false - /// print(parts); // [shoulders, knees, toes] - /// ``` - bool? remove(Object? valueToRemove) { - if (value == null) return null; - late bool result; - update((_) => result = value!.remove(valueToRemove)); - return result; - } - - /// Removes the object at position [index] from this list. - /// - /// This method reduces the length of `this` by one and moves all later objects - /// down by one position. - /// - /// Returns the removed value. - /// - /// The [index] must be in the range `0 ≤ index < length`. - /// The list must be growable. - /// ```dart - /// final parts = ['head', 'shoulder', 'knees', 'toes']; - /// final retVal = parts.removeAt(2); // knees - /// print(parts); // [head, shoulder, toes] - /// ``` - E? removeAt(int index) { - if (value == null) return null; - late E result; - update((_) => result = value!.removeAt(index)); - return result; - } - - /// Removes and returns the last object in this list. - /// - /// The list must be growable and non-empty. - /// ```dart - /// final parts = ['head', 'shoulder', 'knees', 'toes']; - /// final retVal = parts.removeLast(); // toes - /// print(parts); // [head, shoulder, knees] - /// ``` - E? removeLast() { - if (value == null) return null; - late E? result; - update((_) => result = value!.removeLast()); - return result; - } - - /// Removes all objects from this list that satisfy [test]. - /// - /// An object `o` satisfies [test] if `test(o)` is true. - /// ```dart - /// final numbers = ['one', 'two', 'three', 'four']; - /// numbers.removeWhere((item) => item.length == 3); - /// print(numbers); // [three, four] - /// ``` - /// The list must be growable. - void removeWhere(bool Function(E element) test) { - if (value == null) return; - update((_) => value?.removeWhere(test)); - } - - /// Removes all objects from this list that fail to satisfy [test]. - /// - /// An object `o` satisfies [test] if `test(o)` is true. - /// ```dart - /// final numbers = ['one', 'two', 'three', 'four']; - /// numbers.retainWhere((item) => item.length == 3); - /// print(numbers); // [one, two] - /// ``` - /// The list must be growable. - void retainWhere(bool Function(E element) test) { - if (value == null) return; - update((_) => value?.retainWhere(test)); - } - - /// Writes some elements of [iterable] into a range of this list. - /// - /// Copies the objects of [iterable], skipping [skipCount] objects first, - /// into the range from [start], inclusive, to [end], exclusive, of this list. - /// ```dart - /// final list1 = [1, 2, 3, 4]; - /// final list2 = [5, 6, 7, 8, 9]; - /// // Copies the 4th and 5th items in list2 as the 2nd and 3rd items - /// // of list1. - /// const skipCount = 3; - /// list1.setRange(1, 3, list2, skipCount); - /// print(list1); // [1, 8, 9, 4] - /// ``` - /// The provided range, given by [start] and [end], must be valid. - /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. - /// An empty range (with `end == start`) is valid. - /// - /// The [iterable] must have enough objects to fill the range from `start` - /// to `end` after skipping [skipCount] objects. - /// - /// If `iterable` is this list, the operation correctly copies the elements - /// originally in the range from `skipCount` to `skipCount + (end - start)` to - /// the range `start` to `end`, even if the two ranges overlap. - /// - /// If `iterable` depends on this list in some other way, no guarantees are - /// made. - void setRange(int start, int end, Iterable iterable, [int skipCount = 0]) { - if (value == null) return; - update((_) => value?.setRange(start, end, iterable, skipCount)); - } - - /// Removes a range of elements from the list. - /// - /// Removes the elements with positions greater than or equal to [start] - /// and less than [end], from the list. - /// This reduces the list's length by `end - start`. - /// - /// The provided range, given by [start] and [end], must be valid. - /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. - /// An empty range (with `end == start`) is valid. - /// - /// The list must be growable. - /// ```dart - /// final numbers = [1, 2, 3, 4, 5]; - /// numbers.removeRange(1, 4); - /// print(numbers); // [1, 5] - /// ``` - void removeRange(int start, int end) { - if (value == null) return; - update((_) => value?.removeRange(start, end)); - } - - /// Overwrites a range of elements with [fillValue]. - /// - /// Sets the positions greater than or equal to [start] and less than [end], - /// to [fillValue]. - /// - /// The provided range, given by [start] and [end], must be valid. - /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. - /// An empty range (with `end == start`) is valid. - /// - /// If the element type is not nullable, the [fillValue] must be provided and - /// must be non-`null`. - /// - /// Example: - /// ```dart - /// final words = List.filled(5, 'old'); - /// print(words); // [old, old, old, old, old] - /// words.fillRange(1, 3, 'new'); - /// print(words); // [old, new, new, old, old] - /// ``` - void fillRange(int start, int end, [E? fillValue]) { - if (value == null) return; - update((_) => value?.fillRange(start, end, fillValue)); - } - - /// Replaces a range of elements with the elements of [replacements]. - /// - /// Removes the objects in the range from [start] to [end], - /// then inserts the elements of [replacements] at [start]. - /// ```dart - /// final numbers = [1, 2, 3, 4, 5]; - /// final replacements = [6, 7]; - /// numbers.replaceRange(1, 4, replacements); - /// print(numbers); // [1, 6, 7, 5] - /// ``` - /// The provided range, given by [start] and [end], must be valid. - /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. - /// An empty range (with `end == start`) is valid. - /// - /// The operation `list.replaceRange(start, end, replacements)` - /// is roughly equivalent to: - /// ```dart - /// final numbers = [1, 2, 3, 4, 5]; - /// numbers.removeRange(1, 4); - /// final replacements = [6, 7]; - /// numbers.insertAll(1, replacements); - /// print(numbers); // [1, 6, 7, 5] - /// ``` - /// but may be more efficient. - /// - /// The list must be growable. - /// This method does not work on fixed-length lists, even when [replacements] - /// has the same number of elements as the replaced range. In that case use - /// [setRange] instead. - void replaceRange(int start, int end, Iterable replacements) { - if (value == null) return; - update((_) => value?.replaceRange(start, end, replacements)); - } -} diff --git a/packages/reactter/lib/src/signal/extensions/signal_map.dart b/packages/reactter/lib/src/signal/extensions/signal_map.dart deleted file mode 100644 index 2259b97d..00000000 --- a/packages/reactter/lib/src/signal/extensions/signal_map.dart +++ /dev/null @@ -1,327 +0,0 @@ -// coverage:ignore-file -part of '../signal.dart'; - -extension SignalMapExt on Signal> { - /// Associates the [key] with the given [value]. - /// - /// If the key was already in the map, its associated value is changed. - /// Otherwise the key/value pair is added to the map. - void operator []=(K key, V valueToSet) => - update((_) => value[key] = valueToSet); - - /// Adds all key/value pairs of [newEntries] to this map. - /// - /// If a key of [newEntries] is already in this map, - /// the corresponding value is overwritten. - /// - /// The operation is equivalent to doing `this[entry.key] = entry.value` - /// for each [MapEntry] of the iterable. - /// ```dart - /// final planets = {1: 'Mercury', 2: 'Venus', - /// 3: 'Earth', 4: 'Mars'}; - /// final gasGiants = {5: 'Jupiter', 6: 'Saturn'}; - /// final iceGiants = {7: 'Uranus', 8: 'Neptune'}; - /// planets.addEntries(gasGiants.entries); - /// planets.addEntries(iceGiants.entries); - /// print(planets); - /// // {1: Mercury, 2: Venus, 3: Earth, 4: Mars, 5: Jupiter, 6: Saturn, - /// // 7: Uranus, 8: Neptune} - /// ``` - void addEntries(Iterable> newEntries) => - update((_) => value.addEntries(newEntries)); - - /// Updates the value for the provided [key]. - /// - /// Returns the new value associated with the key. - /// - /// If the key is present, invokes [update] with the current value and stores - /// the new value in the map. - /// - /// If the key is not present and [ifAbsent] is provided, calls [ifAbsent] - /// and adds the key with the returned value to the map. - /// - /// If the key is not present, [ifAbsent] must be provided. - /// ```dart - /// final planetsFromSun = {1: 'Mercury', 2: 'unknown', - /// 3: 'Earth'}; - /// // Update value for known key value 2. - /// planetsFromSun.update(2, (value) => 'Venus'); - /// print(planetsFromSun); // {1: Mercury, 2: Venus, 3: Earth} - /// - /// final largestPlanets = {1: 'Jupiter', 2: 'Saturn', - /// 3: 'Neptune'}; - /// // Key value 8 is missing from list, add it using [ifAbsent]. - /// largestPlanets.update(8, (value) => 'New', ifAbsent: () => 'Mercury'); - /// print(largestPlanets); // {1: Jupiter, 2: Saturn, 3: Neptune, 8: Mercury} - /// ``` - V updateMap(K key, V Function(V value) fnUpdate, {V Function()? ifAbsent}) { - late V result; - update( - (_) => result = value.update( - key, - fnUpdate, - ifAbsent: ifAbsent, - ), - ); - return result; - } - - /// Updates all values. - /// - /// Iterates over all entries in the map and updates them with the result - /// of invoking [update]. - /// ```dart - /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; - /// terrestrial.updateAll((key, value) => value.toUpperCase()); - /// print(terrestrial); // {1: MERCURY, 2: VENUS, 3: EARTH} - /// ``` - void updateAll(V Function(K key, V value) fnUpdate) => - update((_) => value.updateAll(fnUpdate)); - - /// Removes all entries of this map that satisfy the given [test]. - /// ```dart - /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; - /// terrestrial.removeWhere((key, value) => value.startsWith('E')); - /// print(terrestrial); // {1: Mercury, 2: Venus} - /// ``` - void removeWhere(bool Function(K key, V value) test) => - update((_) => value.removeWhere(test)); - - /// Look up the value of [key], or add a new entry if it isn't there. - /// - /// Returns the value associated to [key], if there is one. - /// Otherwise calls [ifAbsent] to get a new value, associates [key] to - /// that value, and then returns the new value. - /// ```dart - /// final diameters = {1.0: 'Earth'}; - /// final otherDiameters = {0.383: 'Mercury', 0.949: 'Venus'}; - /// - /// for (final item in otherDiameters.entries) { - /// diameters.putIfAbsent(item.key, () => item.value); - /// } - /// print(diameters); // {1.0: Earth, 0.383: Mercury, 0.949: Venus} - /// - /// // If the key already exists, the current value is returned. - /// final result = diameters.putIfAbsent(0.383, () => 'Random'); - /// print(result); // Mercury - /// print(diameters); // {1.0: Earth, 0.383: Mercury, 0.949: Venus} - /// ``` - /// Calling [ifAbsent] must not add or remove keys from the map. - V putIfAbsent(K key, V Function() ifAbsent) { - late V result; - update((_) => result = value.putIfAbsent(key, ifAbsent)); - return result; - } - - /// Adds all key/value pairs of [other] to this map. - /// - /// If a key of [other] is already in this map, its value is overwritten. - /// - /// The operation is equivalent to doing `this[key] = value` for each key - /// and associated value in other. It iterates over [other], which must - /// therefore not change during the iteration. - /// ```dart - /// final planets = {1: 'Mercury', 2: 'Earth'}; - /// planets.addAll({5: 'Jupiter', 6: 'Saturn'}); - /// print(planets); // {1: Mercury, 2: Earth, 5: Jupiter, 6: Saturn} - /// ``` - void addAll(Map other) => update((_) => value.addAll(other)); - - /// Removes [key] and its associated value, if present, from the map. - /// - /// Returns the value associated with `key` before it was removed. - /// Returns `null` if `key` was not in the map. - /// - /// Note that some maps allow `null` as a value, - /// so a returned `null` value doesn't always mean that the key was absent. - /// ```dart - /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; - /// final removedValue = terrestrial.remove(2); // Venus - /// print(terrestrial); // {1: Mercury, 3: Earth} - /// ``` - V? remove(Object? key) { - late V? result; - update((_) => result = value.remove(key)); - return result; - } - - /// Removes all entries from the map. - /// - /// After this, the map is empty. - /// ```dart - /// final planets = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; - /// planets.clear(); // {} - /// ``` - void clear() => update((_) => value.clear()); -} - -extension SignalMapNullExt on Signal?> { - /// Associates the [key] with the given [value]. - /// - /// If the key was already in the map, its associated value is changed. - /// Otherwise the key/value pair is added to the map. - void operator []=(K key, V valueToSet) { - if (value == null) return; - update((_) => value?[key] = valueToSet); - } - - /// Adds all key/value pairs of [newEntries] to this map. - /// - /// If a key of [newEntries] is already in this map, - /// the corresponding value is overwritten. - /// - /// The operation is equivalent to doing `this[entry.key] = entry.value` - /// for each [MapEntry] of the iterable. - /// ```dart - /// final planets = {1: 'Mercury', 2: 'Venus', - /// 3: 'Earth', 4: 'Mars'}; - /// final gasGiants = {5: 'Jupiter', 6: 'Saturn'}; - /// final iceGiants = {7: 'Uranus', 8: 'Neptune'}; - /// planets.addEntries(gasGiants.entries); - /// planets.addEntries(iceGiants.entries); - /// print(planets); - /// // {1: Mercury, 2: Venus, 3: Earth, 4: Mars, 5: Jupiter, 6: Saturn, - /// // 7: Uranus, 8: Neptune} - /// ``` - void addEntries(Iterable> newEntries) { - if (value == null) return; - update((_) => value!.addEntries(newEntries)); - } - - /// Updates the value for the provided [key]. - /// - /// Returns the new value associated with the key. - /// - /// If the key is present, invokes [update] with the current value and stores - /// the new value in the map. - /// - /// If the key is not present and [ifAbsent] is provided, calls [ifAbsent] - /// and adds the key with the returned value to the map. - /// - /// If the key is not present, [ifAbsent] must be provided. - /// ```dart - /// final planetsFromSun = {1: 'Mercury', 2: 'unknown', - /// 3: 'Earth'}; - /// // Update value for known key value 2. - /// planetsFromSun.update(2, (value) => 'Venus'); - /// print(planetsFromSun); // {1: Mercury, 2: Venus, 3: Earth} - /// - /// final largestPlanets = {1: 'Jupiter', 2: 'Saturn', - /// 3: 'Neptune'}; - /// // Key value 8 is missing from list, add it using [ifAbsent]. - /// largestPlanets.update(8, (value) => 'New', ifAbsent: () => 'Mercury'); - /// print(largestPlanets); // {1: Jupiter, 2: Saturn, 3: Neptune, 8: Mercury} - /// ``` - V? updateMap(K key, V Function(V value) fnUpdate, {V Function()? ifAbsent}) { - if (value == null) return null; - late V result; - update( - (_) => result = value!.update( - key, - fnUpdate, - ifAbsent: ifAbsent, - ), - ); - return result; - } - - /// Updates all values. - /// - /// Iterates over all entries in the map and updates them with the result - /// of invoking [update]. - /// ```dart - /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; - /// terrestrial.updateAll((key, value) => value.toUpperCase()); - /// print(terrestrial); // {1: MERCURY, 2: VENUS, 3: EARTH} - /// ``` - void updateAll(V Function(K key, V value) fnUpdate) { - if (value == null) return; - update((_) => value!.updateAll(fnUpdate)); - } - - /// Removes all entries of this map that satisfy the given [test]. - /// ```dart - /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; - /// terrestrial.removeWhere((key, value) => value.startsWith('E')); - /// print(terrestrial); // {1: Mercury, 2: Venus} - /// ``` - void removeWhere(bool Function(K key, V value) test) { - if (value == null) return; - update((_) => value!.removeWhere(test)); - } - - /// Look up the value of [key], or add a new entry if it isn't there. - /// - /// Returns the value associated to [key], if there is one. - /// Otherwise calls [ifAbsent] to get a new value, associates [key] to - /// that value, and then returns the new value. - /// ```dart - /// final diameters = {1.0: 'Earth'}; - /// final otherDiameters = {0.383: 'Mercury', 0.949: 'Venus'}; - /// - /// for (final item in otherDiameters.entries) { - /// diameters.putIfAbsent(item.key, () => item.value); - /// } - /// print(diameters); // {1.0: Earth, 0.383: Mercury, 0.949: Venus} - /// - /// // If the key already exists, the current value is returned. - /// final result = diameters.putIfAbsent(0.383, () => 'Random'); - /// print(result); // Mercury - /// print(diameters); // {1.0: Earth, 0.383: Mercury, 0.949: Venus} - /// ``` - /// Calling [ifAbsent] must not add or remove keys from the map. - V? putIfAbsent(K key, V Function() ifAbsent) { - if (value == null) return null; - late V result; - update((_) => result = value!.putIfAbsent(key, ifAbsent)); - return result; - } - - /// Adds all key/value pairs of [other] to this map. - /// - /// If a key of [other] is already in this map, its value is overwritten. - /// - /// The operation is equivalent to doing `this[key] = value` for each key - /// and associated value in other. It iterates over [other], which must - /// therefore not change during the iteration. - /// ```dart - /// final planets = {1: 'Mercury', 2: 'Earth'}; - /// planets.addAll({5: 'Jupiter', 6: 'Saturn'}); - /// print(planets); // {1: Mercury, 2: Earth, 5: Jupiter, 6: Saturn} - /// ``` - void addAll(Map other) { - if (value == null) return; - update((_) => value!.addAll(other)); - } - - /// Removes [key] and its associated value, if present, from the map. - /// - /// Returns the value associated with `key` before it was removed. - /// Returns `null` if `key` was not in the map. - /// - /// Note that some maps allow `null` as a value, - /// so a returned `null` value doesn't always mean that the key was absent. - /// ```dart - /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; - /// final removedValue = terrestrial.remove(2); // Venus - /// print(terrestrial); // {1: Mercury, 3: Earth} - /// ``` - V? remove(Object? key) { - if (value == null) return null; - late V? result; - update((_) => result = value!.remove(key)); - return result; - } - - /// Removes all entries from the map. - /// - /// After this, the map is empty. - /// ```dart - /// final planets = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; - /// planets.clear(); // {} - /// ``` - void clear() { - if (value == null) return; - update((_) => value!.clear()); - } -} diff --git a/packages/reactter/lib/src/signal/extensions/signal_set.dart b/packages/reactter/lib/src/signal/extensions/signal_set.dart deleted file mode 100644 index 11d279bf..00000000 --- a/packages/reactter/lib/src/signal/extensions/signal_set.dart +++ /dev/null @@ -1,236 +0,0 @@ -// coverage:ignore-file -part of '../signal.dart'; - -extension SignalSetExt on Signal> { - /// Adds [value] to the set. - /// - /// Returns `true` if [value] (or an equal value) was not yet in the set. - /// Otherwise returns `false` and the set is not changed. - /// - /// Example: - /// ```dart - /// final dateTimes = {}; - /// final time1 = DateTime.fromMillisecondsSinceEpoch(0); - /// final time2 = DateTime.fromMillisecondsSinceEpoch(0); - /// // time1 and time2 are equal, but not identical. - /// assert(time1 == time2); - /// assert(!identical(time1, time2)); - /// final time1Added = dateTimes.add(time1); - /// print(time1Added); // true - /// // A value equal to time2 exists already in the set, and the call to - /// // add doesn't change the set. - /// final time2Added = dateTimes.add(time2); - /// print(time2Added); // false - /// - /// print(dateTimes); // {1970-01-01 02:00:00.000} - /// assert(dateTimes.length == 1); - /// assert(identical(time1, dateTimes.first)); - /// print(dateTimes.length); - /// ``` - bool add(E valueToAdd) { - late bool result; - update((_) => result = value.add(valueToAdd)); - return result; - } - - /// Adds all [elements] to this set. - /// - /// Equivalent to adding each element in [elements] using [add], - /// but some collections may be able to optimize it. - /// ```dart - /// final characters = {'A', 'B'}; - /// characters.addAll({'A', 'B', 'C'}); - /// print(characters); // {A, B, C} - /// ``` - void addAll(Iterable elements) => update((_) => value.addAll(elements)); - - /// Removes [value] from the set. - /// - /// Returns `true` if [value] was in the set, and `false` if not. - /// The method has no effect if [value] was not in the set. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// final didRemoveB = characters.remove('B'); // true - /// final didRemoveD = characters.remove('D'); // false - /// print(characters); // {A, C} - /// ``` - bool remove(Object? valueToRemove) { - late bool result; - update((_) => result = value.remove(valueToRemove)); - return result; - } - - /// Removes each element of [elements] from this set. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// characters.removeAll({'A', 'B', 'X'}); - /// print(characters); // {C} - /// ``` - void removeAll(Iterable elements) => - update((_) => value.removeAll(elements)); - - /// Removes all elements of this set that are not elements in [elements]. - /// - /// Checks for each element of [elements] whether there is an element in this - /// set that is equal to it (according to `this.contains`), and if so, the - /// equal element in this set is retained, and elements that are not equal - /// to any element in [elements] are removed. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// characters.retainAll({'A', 'B', 'X'}); - /// print(characters); // {A, B} - /// ``` - void retainAll(Iterable elements) => - update((_) => value.retainAll(elements)); - - /// Removes all elements of this set that satisfy [test]. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// characters.removeWhere((element) => element.startsWith('B')); - /// print(characters); // {A, C} - /// ``` - void removeWhere(bool Function(E element) test) => - update((_) => value.removeWhere(test)); - - /// Removes all elements of this set that fail to satisfy [test]. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// characters.retainWhere( - /// (element) => element.startsWith('B') || element.startsWith('C')); - /// print(characters); // {B, C} - /// ``` - void retainWhere(bool Function(E element) test) => - update((_) => value.removeWhere(test)); - - /// Removes all elements from the set. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// characters.clear(); // {} - /// ``` - void clear() => update((_) => value.clear()); -} - -extension SignalSetNullExt on Signal?> { - /// Adds [value] to the set. - /// - /// Returns `true` if [value] (or an equal value) was not yet in the set. - /// Otherwise returns `false` and the set is not changed. - /// - /// Example: - /// ```dart - /// final dateTimes = {}; - /// final time1 = DateTime.fromMillisecondsSinceEpoch(0); - /// final time2 = DateTime.fromMillisecondsSinceEpoch(0); - /// // time1 and time2 are equal, but not identical. - /// assert(time1 == time2); - /// assert(!identical(time1, time2)); - /// final time1Added = dateTimes.add(time1); - /// print(time1Added); // true - /// // A value equal to time2 exists already in the set, and the call to - /// // add doesn't change the set. - /// final time2Added = dateTimes.add(time2); - /// print(time2Added); // false - /// - /// print(dateTimes); // {1970-01-01 02:00:00.000} - /// assert(dateTimes.length == 1); - /// assert(identical(time1, dateTimes.first)); - /// print(dateTimes.length); - /// ``` - bool? add(E valueToAdd) { - if (value == null) return null; - late bool result; - update((_) => result = value!.add(valueToAdd)); - return result; - } - - /// Adds all [elements] to this set. - /// - /// Equivalent to adding each element in [elements] using [add], - /// but some collections may be able to optimize it. - /// ```dart - /// final characters = {'A', 'B'}; - /// characters.addAll({'A', 'B', 'C'}); - /// print(characters); // {A, B, C} - /// ``` - void addAll(Iterable elements) { - if (value == null) return; - update((_) => value!.addAll(elements)); - } - - /// Removes [value] from the set. - /// - /// Returns `true` if [value] was in the set, and `false` if not. - /// The method has no effect if [value] was not in the set. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// final didRemoveB = characters.remove('B'); // true - /// final didRemoveD = characters.remove('D'); // false - /// print(characters); // {A, C} - /// ``` - bool? remove(Object? valueToRemove) { - if (value == null) return null; - late bool result; - update((_) => result = value!.remove(valueToRemove)); - return result; - } - - /// Removes each element of [elements] from this set. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// characters.removeAll({'A', 'B', 'X'}); - /// print(characters); // {C} - /// ``` - void removeAll(Iterable elements) { - if (value == null) return; - update((_) => value!.removeAll(elements)); - } - - /// Removes all elements of this set that are not elements in [elements]. - /// - /// Checks for each element of [elements] whether there is an element in this - /// set that is equal to it (according to `this.contains`), and if so, the - /// equal element in this set is retained, and elements that are not equal - /// to any element in [elements] are removed. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// characters.retainAll({'A', 'B', 'X'}); - /// print(characters); // {A, B} - /// ``` - void retainAll(Iterable elements) { - if (value == null) return; - update((_) => value!.retainAll(elements)); - } - - /// Removes all elements of this set that satisfy [test]. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// characters.removeWhere((element) => element.startsWith('B')); - /// print(characters); // {A, C} - /// ``` - void removeWhere(bool Function(E element) test) { - if (value == null) return; - update((_) => value!.removeWhere(test)); - } - - /// Removes all elements of this set that fail to satisfy [test]. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// characters.retainWhere( - /// (element) => element.startsWith('B') || element.startsWith('C')); - /// print(characters); // {B, C} - /// ``` - void retainWhere(bool Function(E element) test) { - if (value == null) return; - update((_) => value!.removeWhere(test)); - } - - /// Removes all elements from the set. - /// ```dart - /// final characters = {'A', 'B', 'C'}; - /// characters.clear(); // {} - /// ``` - void clear() { - if (value == null) return; - update((_) => value!.clear()); - } -} diff --git a/packages/reactter/lib/src/signal/signal.dart b/packages/reactter/lib/src/signal/signal.dart deleted file mode 100644 index 56e05da0..00000000 --- a/packages/reactter/lib/src/signal/signal.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'dart:math'; - -import 'package:reactter/src/internals.dart'; - -import '../framework.dart'; -import '../obj/obj.dart'; - -part 'signal_impl.dart'; -part 'extensions/signal_list.dart'; -part 'extensions/signal_map.dart'; -part 'extensions/signal_set.dart'; diff --git a/packages/reactter/test/hooks/use_state_test.dart b/packages/reactter/test/hooks/use_state_test.dart index 3549de2c..df0c5e1d 100644 --- a/packages/reactter/test/hooks/use_state_test.dart +++ b/packages/reactter/test/hooks/use_state_test.dart @@ -96,11 +96,41 @@ void main() { }), ); - await Future.microtask(() {}); - expectLater(testController.stateInt.value, 3); Rt.delete(); }); + + test("should update manually", () { + final testController = Rt.create(() => TestController())!; + late bool didUpdate; + + testController.stateInt.value = 1; + + Rt.one(testController.stateInt, Lifecycle.didUpdate, (_, __) { + expect(testController.stateInt.value, 1); + didUpdate = true; + }); + + testController.stateInt.update(); + + expectLater(didUpdate, true); + + Rt.delete(); + }); + + test("should update manually with callback", () { + final testController = Rt.create(() => TestController())!; + + testController.stateInt.value = 1; + + testController.stateInt.update(() { + testController.stateInt.value += 1; + }); + + expect(testController.stateInt.value, 2); + + Rt.delete(); + }); }); } diff --git a/packages/reactter/test/memo_test.dart b/packages/reactter/test/memo_test.dart index 89799a46..f03ce24f 100644 --- a/packages/reactter/test/memo_test.dart +++ b/packages/reactter/test/memo_test.dart @@ -1,6 +1,6 @@ import 'package:fake_async/fake_async.dart'; import 'package:reactter/reactter.dart'; -import 'package:reactter/src/memo/memo.dart'; + import 'package:test/test.dart'; import 'shareds/test_controllers.dart'; @@ -131,7 +131,7 @@ void main() { fakeAsync((async) { final memo = Memo( (Args1 args) => args.arg1, - TemporaryCacheMemo( + MemoTemporaryCacheInterceptor( Duration(minutes: 1), ), ); @@ -165,11 +165,11 @@ void main() { final nInterceptors = 2; - final memoInterceptors = MemoInterceptors([ + final memoInterceptors = MemoMultiInterceptor([ FakeInterceptorForCoverage(), ...List.generate( nInterceptors, - (_) => MemoInterceptorWrapper( + (_) => MemoWrapperInterceptor( onInit: (memo, args) { nCallOnInit += 1; }, diff --git a/packages/reactter/test/obj_test.dart b/packages/reactter/test/obj_test.dart deleted file mode 100644 index bcd48e22..00000000 --- a/packages/reactter/test/obj_test.dart +++ /dev/null @@ -1,134 +0,0 @@ -// ignore_for_file: unnecessary_null_comparison, unrelated_type_equality_checks - -import 'package:reactter/reactter.dart'; -import 'package:test/test.dart'; - -import 'shareds/test_controllers.dart'; - -void main() { - group("Obj", () { - test("should be created by any type value", () { - final objBool = Obj(true); - expect(objBool, isA>()); - expect(objBool.value, true); - expect("$objBool", true.toString()); - - final objInt = Obj(1); - expect(objInt, isA>()); - expect(objInt.value, 1); - expect("$objInt", 1.toString()); - - final objDouble = Obj(1.2); - expect(objDouble, isA>()); - expect(objDouble.value, 1.2); - expect("$objDouble", 1.2.toString()); - - final objString = Obj("test"); - expect(objString, isA>()); - expect(objString.value, "test"); - - final objList = Obj(["test"]); - expect(objList, isA>>()); - expect(objList.value.first, "test"); - expect("$objList", ["test"].toString()); - - final objMap = Obj({"test": 1}); - expect(objMap, isA>>()); - expect(objMap["test"], 1); - expect("$objMap", {"test": 1}.toString()); - - final objAnother = Obj(TestClass("test")); - expect(objAnother, isA>()); - expect(objAnother.value.prop, "test"); - expect("$objAnother", TestClass("test").toString()); - - final objNull = Obj(null); - expect(objNull, isA>()); - expect(objNull.value, null); - expect("$objNull", null.toString()); - }); - - test("should set a new value", () { - final objBool = Obj(true); - objBool.value = false; - expect(objBool.value, false); - expect(objBool(true), true); - - final objInt = Obj(1); - objInt.value = 2; - expect(objInt.value, 2); - expect(objInt(3), 3); - - final objDouble = Obj(1.2); - objDouble.value = 2.3; - expect(objDouble.value, 2.3); - expect(objDouble(3.4), 3.4); - - final objString = Obj("test"); - objString.value = "other value"; - expect(objString.value, "other value"); - expect(objString("other value by call"), "other value by call"); - - final objList = Obj(["test"]); - objList.value = ["other value"]; - expect(objList.value.first, "other value"); - expect(objList(["other value by call"]).first, "other value by call"); - - final objMap = Obj({"test": 1}); - objMap.value = {"other": 2}; - expect(objMap["other"], 2); - expect(objMap({"other by call": 3})["other by call"], 3); - - final objAnother = Obj(TestClass("test")); - objAnother.value = TestClass("other instance"); - expect(objAnother.value.prop, "other instance"); - expect(objAnother(TestClass("other instance by call")).prop, - "other instance by call"); - - final objNull = Obj(null); - objNull.value = TestClass("not null"); - expect(objNull.value?.prop, "not null"); - expect(objNull(TestClass("not null by call"))?.prop, "not null by call"); - }); - - test("should be compared to another for its value", () { - final objBool = Obj(true); - expect(objBool == true, true); - expect(objBool == Obj(true), false); - - final objInt = Obj(1); - expect(objInt == 1, true); - expect(objInt == Obj(1), false); - - final objDouble = Obj(1.2); - expect(objDouble == 1.2, true); - expect(objDouble == Obj(1.2), false); - - final objString = Obj("test"); - expect(objString == "test", true); - expect(objString == Obj("test"), false); - - final objList = Obj(["test"]); - expect(objList == ["test"], false); - expect(objList == Obj(["test"]), false); - - final objMap = Obj({"test": 1}); - expect(objMap == {"test": 1}, false); - expect(objMap == Obj({"test": 1}), false); - - final objAnother = Obj(TestClass("test")); - expect(objAnother == TestClass("test"), false); - expect(objAnother == Obj(TestClass("test")), false); - - final objNull = Obj(null); - expect(objNull == null, false); - expect(objNull == Obj(null), false); - }); - - test("should be cast away nullability", () { - final objNull = Obj(true); - expect(objNull.value, true); - expect(objNull.notNull, isA>()); - }); - }); -} diff --git a/packages/reactter/test/signal_test.dart b/packages/reactter/test/signal_test.dart index 42373659..1a782e39 100644 --- a/packages/reactter/test/signal_test.dart +++ b/packages/reactter/test/signal_test.dart @@ -5,12 +5,6 @@ import 'shareds/test_controllers.dart'; void main() { group("Signal", () { - test("should be created by Obj", () { - final obj = Obj(true); - - expect(obj.toSignal, isA>()); - }); - test("should be cast away nullability", () { final signalNull = Signal(false); @@ -18,12 +12,6 @@ void main() { expect(signalNull.notNull, isA>()); }); - test("should be casted to Obj", () { - final signal = Signal(true); - - expect(signal.toObj, isA>()); - }); - test("should notifies when its value is changed", () { final signal = Signal("initial"); @@ -61,7 +49,7 @@ void main() { expectLater(didUpdateChecked, true); }); - test("should be refreshed", () { + test("should be notified manually", () { final signal = Signal("initial"); int count = 0; @@ -69,11 +57,11 @@ void main() { count += 1; }); - signal.refresh(); + signal.notify(); expectLater(count, 1); - signal.refresh(); + signal.notify(); expectLater(count, 1); }); @@ -110,10 +98,7 @@ void main() { test("should be able to used on instance with nested way", () { late final bool willUpdateChecked; late final bool didUpdateChecked; - - final test2Controller = Rt.create(() => Test2Controller())!; - final testController = test2Controller.testController.instance!; - + final testController = Rt.create(() => TestController())!; final signalString = testController.signalString; expect(signalString(), "initial"); diff --git a/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart index 67241554..4ca4ba0d 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:reactter_devtools_extension/src/data/constants.dart'; import 'package:reactter_devtools_extension/src/interfaces/node.dart'; import 'package:reactter_devtools_extension/src/widgets/instance_title.dart'; import 'package:reactter_devtools_extension/src/widgets/tile_builder.dart'; From b97174700f8fdf5ae5b0fa74289edaf8c1e1b442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Tue, 19 Nov 2024 23:29:48 -0600 Subject: [PATCH 055/141] refactor: Update dependency handling and improve widget lifecycle management --- .../lib/src/framework/dependency.dart | 7 ++- .../lib/src/framework/nested.dart | 19 ++++---- .../lib/src/framework/provider_impl.dart | 24 +++------- .../lib/src/framework/wrapper.dart | 10 +++++ .../lib/src/widgets/rt_provider.dart | 28 +++++++----- .../lib/src/widgets/rt_providers.dart | 44 ++++++++++++------- .../test/widgets/rt_selector_test.dart | 2 +- .../lib/src/core/dependency_register.dart | 2 +- 8 files changed, 75 insertions(+), 61 deletions(-) diff --git a/packages/flutter_reactter/lib/src/framework/dependency.dart b/packages/flutter_reactter/lib/src/framework/dependency.dart index 82e071a7..fd6aade0 100644 --- a/packages/flutter_reactter/lib/src/framework/dependency.dart +++ b/packages/flutter_reactter/lib/src/framework/dependency.dart @@ -70,13 +70,12 @@ class MasterDependency extends Dependency { super.dispose(); } + // coverage:ignore-start @override void commitToMaster(MasterDependency masterDependency) { - if (_instance != null) masterDependency._instance = _instance; - - masterDependency._states.addAll(_states); - masterDependency._selects.addAll(_selects); + assert(true, 'This method should not be called'); } + // coverage:ignore-end @override void listen(void Function() callback) { diff --git a/packages/flutter_reactter/lib/src/framework/nested.dart b/packages/flutter_reactter/lib/src/framework/nested.dart index 8bac3a63..eb1eb02c 100644 --- a/packages/flutter_reactter/lib/src/framework/nested.dart +++ b/packages/flutter_reactter/lib/src/framework/nested.dart @@ -48,31 +48,32 @@ class NestedElement extends StatelessElement { } } - WrapperWidget? _wrappedChild; - WrapperWidget? get wrappedChild => _wrappedChild; - set wrappedChild(WrapperWidget? value) { - if (_wrappedChild != value) { - _wrappedChild = value; + WrapperWidget? _wrappedWidget; + WrapperWidget? get wrappedWidget => _wrappedWidget; + set wrappedWidget(WrapperWidget? value) { + if (_wrappedWidget != value) { + _wrappedWidget?.dispose(); + _wrappedWidget = value; markNeedsBuild(); } } @override void mount(Element? parent, dynamic newSlot) { - widget.owner.nodes.add(this); - _wrappedChild = widget.wrappedWidget; + widget.owner.addNode(this); + _wrappedWidget = widget.wrappedWidget; _injectedChild = widget.injectedChild; super.mount(parent, newSlot); } @override void unmount() { - widget.owner.nodes.remove(this); + widget.owner.removeNode(this); super.unmount(); } @override Widget build() { - return wrappedChild!; + return wrappedWidget!; } } diff --git a/packages/flutter_reactter/lib/src/framework/provider_impl.dart b/packages/flutter_reactter/lib/src/framework/provider_impl.dart index 3022bb98..1b0b9762 100644 --- a/packages/flutter_reactter/lib/src/framework/provider_impl.dart +++ b/packages/flutter_reactter/lib/src/framework/provider_impl.dart @@ -148,10 +148,6 @@ class ProviderElement extends InheritedElement HashMap>? _inheritedElementsWithId; bool _isLazyInstanceObtained = false; - bool get isRoot { - return Rt.getRefAt(0, widget.id) == widget; - } - @override ProvideImpl get widget { return super.widget as ProvideImpl; @@ -174,17 +170,7 @@ class ProviderElement extends InheritedElement required ProvideImpl widget, String? id, }) : super(widget) { - if (widget.init && !widget.isLazy) { - // TODO: Remove this when the `init` property is removed - Rt.create( - widget.instanceBuilder, - id: widget.id, - mode: widget.mode, - ref: widget, - ); - - return; - } + if (widget.init && !widget.isLazy) return; Rt.register( widget.instanceBuilder, @@ -201,7 +187,7 @@ class ProviderElement extends InheritedElement final shouldNotifyMount = count == 1; if (!widget.init && !widget.isLazy) { - Rt.get(widget.id, widget); + Rt.get(widget.id, widget.ref); } _updateInheritedElementWithId(parent); @@ -230,8 +216,7 @@ class ProviderElement extends InheritedElement @override void unmount() { - final id = widget.id; - final ref = widget; + final ref = widget.ref; final dependency = RtDependency(widget.id); final count = (_instanceMountCount[dependency] ?? 0) - 1; final shouldNotifyUnmount = count < 1; @@ -253,7 +238,8 @@ class ProviderElement extends InheritedElement Rt.emit(dependency, Lifecycle.didUnmount); } - Rt.delete(id, ref); + if (ref is RtProvider) ref.disposeInstance(); + _inheritedElementsWithId = null; _prevChild = null; } diff --git a/packages/flutter_reactter/lib/src/framework/wrapper.dart b/packages/flutter_reactter/lib/src/framework/wrapper.dart index 6dd8bb74..96b63b5a 100644 --- a/packages/flutter_reactter/lib/src/framework/wrapper.dart +++ b/packages/flutter_reactter/lib/src/framework/wrapper.dart @@ -5,6 +5,8 @@ part of '../framework.dart'; abstract class WrapperWidget implements Widget { @override WrapperElementMixin createElement(); + + void dispose(); } /// Mixin to [WrapperWidget]'s Element @@ -35,4 +37,12 @@ mixin WrapperElementMixin on Element { return false; }); } + + void addNode(NestedElement node) { + nodes.add(node); + } + + void removeNode(NestedElement node) { + nodes.remove(node); + } } diff --git a/packages/flutter_reactter/lib/src/widgets/rt_provider.dart b/packages/flutter_reactter/lib/src/widgets/rt_provider.dart index 25073864..b82bb45d 100644 --- a/packages/flutter_reactter/lib/src/widgets/rt_provider.dart +++ b/packages/flutter_reactter/lib/src/widgets/rt_provider.dart @@ -158,12 +158,7 @@ class RtProvider extends ProviderBase child: child, builder: builder, ) { - Rt.create( - instanceBuilder, - id: id, - mode: mode, - ref: this, - ); + createInstance(); } /// Creates a lazy instance of [T] dependency and provides it to tree widget. @@ -184,6 +179,19 @@ class RtProvider extends ProviderBase lazyBuilder: builder, ); + void createInstance() { + Rt.create(instanceBuilder, id: id, mode: mode, ref: this); + } + + void disposeInstance() { + Rt.delete(id, this); + } + + @override + void dispose() { + disposeInstance(); + } + Widget buildWithChild(Widget? child) { if (id != null) { return ProvideImpl( @@ -238,14 +246,10 @@ class RtProvider extends ProviderBase class RtProviderElement extends ComponentElement with WrapperElementMixin> { bool get isRoot { - return Rt.getRefAt(0, widget.id) == _widget; + return Rt.getRefAt(0, widget.id) == widget; } - final RtProvider _widget; - - RtProviderElement(RtProvider widget) - : _widget = widget, - super(widget); + RtProviderElement(Widget widget) : super(widget); @override Widget build() { diff --git a/packages/flutter_reactter/lib/src/widgets/rt_providers.dart b/packages/flutter_reactter/lib/src/widgets/rt_providers.dart index 6176e7c6..89fd7322 100644 --- a/packages/flutter_reactter/lib/src/widgets/rt_providers.dart +++ b/packages/flutter_reactter/lib/src/widgets/rt_providers.dart @@ -89,6 +89,11 @@ class RtMultiProvider extends StatelessWidget implements ProviderWrapper { RtMultiProviderElement createElement() { return RtMultiProviderElement(this); } + + @override + void dispose() { + // TODO: implement dispose + } } class RtMultiProviderElement extends StatelessElement @@ -112,24 +117,33 @@ class RtMultiProviderElement extends StatelessElement ); } - if (nestedHook != null) { - /// We manually update [NestedElement] instead of letter widgets do their thing - /// because an item N may be constant but N+1 not. So, if we used widgets - /// then N+1 wouldn't rebuild because N didn't change - for (final node in nodes) { - node - ..wrappedChild = nestedHook!.wrappedWidget - ..injectedChild = nestedHook.injectedChild; - - final next = nestedHook.injectedChild; - if (next is NestedWidget) { - nestedHook = next; - } else { - break; - } + if (nestedHook == null) return nextNode; + + /// We manually update [NestedElement] instead of letter widgets do their thing + /// because an item N may be constant but N+1 not. So, if we used widgets + /// then N+1 wouldn't rebuild because N didn't change + for (final node in nodes.toList(growable: false)) { + node + ..wrappedWidget = nestedHook!.wrappedWidget + ..injectedChild = nestedHook.injectedChild; + + final next = nestedHook.injectedChild; + if (next is NestedWidget) { + nestedHook = next; + } else { + break; } } return nextNode; } + + @override + void removeNode(NestedElement node) { + super.removeNode(node); + + if (node.wrappedWidget is RtProvider) { + (node.wrappedWidget as RtProvider).disposeInstance(); + } + } } diff --git a/packages/flutter_reactter/test/widgets/rt_selector_test.dart b/packages/flutter_reactter/test/widgets/rt_selector_test.dart index 16859106..7444e9af 100644 --- a/packages/flutter_reactter/test/widgets/rt_selector_test.dart +++ b/packages/flutter_reactter/test/widgets/rt_selector_test.dart @@ -29,7 +29,7 @@ void main() { expect(find.text("total: 0"), findsOneWidget); - signalList.value.addAll([1, 1, 2]); + signalList.update((value) => value.addAll([1, 1, 2])); await tester.pumpAndSettle(); expect(find.text("total: 4"), findsOneWidget); diff --git a/packages/reactter/lib/src/core/dependency_register.dart b/packages/reactter/lib/src/core/dependency_register.dart index 515ddf79..27ad6bc3 100644 --- a/packages/reactter/lib/src/core/dependency_register.dart +++ b/packages/reactter/lib/src/core/dependency_register.dart @@ -17,7 +17,7 @@ class DependencyRegister extends DependencyRef { final DependencyMode mode; /// Stores the refs where the instance was created. - final refs = HashSet(); + final LinkedHashSet refs = LinkedHashSet(); T? _instance; T? get instance => _instance; From 9a2a20411ea1255ee14b7a946506714661a577cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Sat, 23 Nov 2024 01:50:30 -0600 Subject: [PATCH 056/141] refactor(devtools): Improve performance and folder structure and add dependencies panel. --- packages/reactter/lib/src/devtools.dart | 84 +++------- .../lib/src/bases/node.dart | 78 ++++++++++ .../lib/src/bases/node_info.dart | 5 + .../lib/src/bases/property_node.dart | 13 ++ .../lib/src/{data => bases}/tree_list.dart | 23 +-- .../lib/src/{data => bases}/tree_node.dart | 104 +++++++------ .../lib/src/{data => }/constants.dart | 1 + .../lib/src/controllers/nodes_controller.dart | 146 ++++++++++++------ .../lib/src/data/dependency_info.dart | 7 - .../lib/src/data/instance_info.dart | 6 - .../lib/src/interfaces/node.dart | 67 -------- .../lib/src/interfaces/node_info.dart | 5 - .../src/nodes/dependency/dependency_info.dart | 7 + .../dependency}/dependency_node.dart | 12 +- .../lib/src/nodes/instance/instance_info.dart | 5 + .../instance}/instance_node.dart | 6 +- .../property/property_async_node.dart} | 63 ++------ .../src/nodes/property/propery_sync_node.dart | 29 ++++ .../lib/src/nodes/sentinel_node.dart | 33 ++++ .../lib/src/{data => nodes}/slot_node.dart | 6 +- .../src/{data => nodes/state}/state_info.dart | 4 +- .../src/{data => nodes/state}/state_node.dart | 11 +- .../lib/src/reactter_devtools_extension.dart | 44 ++++-- .../lib/src/services/devtools_service.dart | 38 +++-- .../lib/src/utils/extensions.dart | 40 +++-- .../lib/src/widgets/dependencies_list.dart | 78 ++++++++++ .../lib/src/widgets/dependency_tile.dart | 55 +++++++ .../lib/src/widgets/instance_icon.dart | 3 +- .../lib/src/widgets/node_tile.dart | 6 +- .../lib/src/widgets/nodes_list.dart | 77 ++++----- .../lib/src/widgets/property_tile.dart | 7 +- .../lib/src/widgets/tile_builder.dart | 25 +-- 32 files changed, 678 insertions(+), 410 deletions(-) create mode 100644 packages/reactter_devtools_extension/lib/src/bases/node.dart create mode 100644 packages/reactter_devtools_extension/lib/src/bases/node_info.dart create mode 100644 packages/reactter_devtools_extension/lib/src/bases/property_node.dart rename packages/reactter_devtools_extension/lib/src/{data => bases}/tree_list.dart (64%) rename packages/reactter_devtools_extension/lib/src/{data => bases}/tree_node.dart (60%) rename packages/reactter_devtools_extension/lib/src/{data => }/constants.dart (97%) delete mode 100644 packages/reactter_devtools_extension/lib/src/data/dependency_info.dart delete mode 100644 packages/reactter_devtools_extension/lib/src/data/instance_info.dart delete mode 100644 packages/reactter_devtools_extension/lib/src/interfaces/node.dart delete mode 100644 packages/reactter_devtools_extension/lib/src/interfaces/node_info.dart create mode 100644 packages/reactter_devtools_extension/lib/src/nodes/dependency/dependency_info.dart rename packages/reactter_devtools_extension/lib/src/{data => nodes/dependency}/dependency_node.dart (84%) create mode 100644 packages/reactter_devtools_extension/lib/src/nodes/instance/instance_info.dart rename packages/reactter_devtools_extension/lib/src/{data => nodes/instance}/instance_node.dart (67%) rename packages/reactter_devtools_extension/lib/src/{data/property_node.dart => nodes/property/property_async_node.dart} (88%) create mode 100644 packages/reactter_devtools_extension/lib/src/nodes/property/propery_sync_node.dart create mode 100644 packages/reactter_devtools_extension/lib/src/nodes/sentinel_node.dart rename packages/reactter_devtools_extension/lib/src/{data => nodes}/slot_node.dart (72%) rename packages/reactter_devtools_extension/lib/src/{data => nodes/state}/state_info.dart (82%) rename packages/reactter_devtools_extension/lib/src/{data => nodes/state}/state_node.dart (85%) create mode 100644 packages/reactter_devtools_extension/lib/src/widgets/dependencies_list.dart create mode 100644 packages/reactter_devtools_extension/lib/src/widgets/dependency_tile.dart diff --git a/packages/reactter/lib/src/devtools.dart b/packages/reactter/lib/src/devtools.dart index 5eeaa1d2..12c5a8e5 100644 --- a/packages/reactter/lib/src/devtools.dart +++ b/packages/reactter/lib/src/devtools.dart @@ -35,43 +35,25 @@ class RtDevTools with RtStateObserver, RtDependencyObserver { @override void onStateCreated(RtState state) { final stateKey = state.hashCode.toString(); - - _addNode(state); - - // print("++ onStateCreated ++"); - // print("stateNode: ${stateNode.toJson()}"); - // print("++++++++++++++"); - // for (var e in nodes) { - // print("${e.toJson()}"); - // } - // print("______________"); + final stateNode = _addNode(state); dev.postEvent('ext.reactter.onStateCreated', { 'stateKey': stateKey, + 'state': stateNode.toJson(), }); } @override void onStateBound(RtState state, Object instance) { final stateKey = state.hashCode.toString(); - final instanceKey = instance.hashCode.toString(); final stateNode = _nodesByKey[stateKey]; final instanceNode = _addNode(instance); if (stateNode != null) instanceNode.addChild(stateNode); - // print("++ onStateBound ++"); - // print("stateNode: ${stateNode?.toJson()}"); - // print("instanceNode: ${instanceNode.toJson()}"); - // print("++++++++++++++"); - // for (var e in nodes) { - // print("${e.toJson()}"); - // } - // print("______________"); - dev.postEvent('ext.reactter.onStateBound', { - 'stateKey': stateKey, - 'instanceKey': instanceKey, + 'state': stateNode?.toJson(), + 'instance': instanceNode.toJson(), }); } @@ -86,15 +68,6 @@ class RtDevTools with RtStateObserver, RtDependencyObserver { final isInstanceRemoved = _removeNode(instanceKey); - // print("++ onStateUnbound ++"); - // print("stateNode: ${stateNode?.toJson()}"); - // print("instanceNode: ${instanceNode?.toJson()}"); - // print("++++++++++++++"); - // for (var e in nodes) { - // print("${e.toJson()}"); - // } - // print("______________"); - dev.postEvent('ext.reactter.onStateUnbound', { 'stateKey': stateKey, 'instanceKey': instanceKey, @@ -125,44 +98,24 @@ class RtDevTools with RtStateObserver, RtDependencyObserver { @override void onDependencyRegistered(DependencyRef dependency) { - final dependencyKey = dependency.hashCode.toString(); - - _addNode(dependency); - - // print("++ onDependencyRegistered ++"); - // print("dependencyNode: ${dependencyNode.toJson()}"); - // print("++++++++++++++"); - // for (var e in nodes) { - // print("${e.toJson()}"); - // } - // print("______________"); + final dependencyNode = _addNode(dependency); dev.postEvent('ext.reactter.onDependencyRegistered', { - 'dependencyKey': dependencyKey, + 'dependency': dependencyNode.toJson(), }); } @override void onDependencyCreated(DependencyRef dependency, Object? instance) { final dependencyKey = dependency.hashCode.toString(); - final instanceKey = instance.hashCode.toString(); - - if (instance != null) _addNode(instance); + final dependencyNode = _nodesByKey[dependencyKey]; - // print("++ onDependencyCreated ++"); - // if (instance != null) { - // final instanceNode = ; - // print("instanceNode: ${instanceNode.toJson()}"); - // } - // print("++++++++++++++"); - // for (var e in nodes) { - // print("${e.toJson()}"); - // } - // print("______________"); + _Node? instanceNode; + if (instance != null) instanceNode = _addNode(instance); dev.postEvent('ext.reactter.onDependencyCreated', { - 'dependencyKey': dependencyKey, - 'instanceKey': instanceKey, + 'dependency': dependencyNode?.toJson(), + 'instance': instanceNode?.toJson(), }); } @@ -434,7 +387,7 @@ abstract class _Node extends LinkedListEntry<_Node> { _Node({required this.instance}); - Map toJson() { + Map toJson() { final dependencyRef = Rt.getDependencyRef(instance); final dependencyId = dependencyRef?.id; @@ -448,6 +401,13 @@ abstract class _Node extends LinkedListEntry<_Node> { _Node? get lastDescendant => children.isEmpty ? this : children.last.lastDescendant as _Node; + @override + void unlink() { + if (list == null) return; + + super.unlink(); + } + void addChild(_Node node) { if (node._parent == this) return; @@ -529,7 +489,7 @@ class _InstanceNode extends _Node { } @override - Map toJson() { + Map toJson() { return { ...getInstanceInfo(instance), ...super.toJson(), @@ -568,7 +528,7 @@ class _StateNode extends _Node { } @override - Map toJson() { + Map toJson() { return { ...getInstanceInfo(instance), ...super.toJson(), @@ -604,7 +564,7 @@ class _DependencyNode extends _Node { } @override - Map toJson() { + Map toJson() { return { ...getInstanceInfo(instance), ...super.toJson(), diff --git a/packages/reactter_devtools_extension/lib/src/bases/node.dart b/packages/reactter_devtools_extension/lib/src/bases/node.dart new file mode 100644 index 00000000..ec53e9f9 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/bases/node.dart @@ -0,0 +1,78 @@ +import 'package:devtools_app_shared/service.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/bases/tree_node.dart'; +import 'package:reactter_devtools_extension/src/bases/node_info.dart'; +import 'package:reactter_devtools_extension/src/bases/property_node.dart'; +import 'package:reactter_devtools_extension/src/bases/tree_list.dart'; +import 'package:reactter_devtools_extension/src/nodes/property/property_async_node.dart'; +import 'package:reactter_devtools_extension/src/services/eval_service.dart'; +import 'package:vm_service/vm_service.dart'; + +abstract base class Node extends TreeNode { + final String key; + final String kind; + final String type; + + String? get label; + + final uInfo = UseState(null); + final uIsSelected = UseState(false); + final propertyNodes = TreeList(); + final isAlive = Disposable(); + + Node({ + required this.key, + required this.kind, + required this.type, + }); + + @override + void dispose() { + isAlive.dispose(); + super.dispose(); + } + + Future loadDetails() async { + await loadDependencyRef(); + } + + Future loadDependencyRef() async { + try { + final eval = await EvalService.devtoolsEval; + final dependencyKey = uInfo.value?.dependencyRef; + + final dependencyRefValue = await eval.evalInstance( + 'RtDevTools._instance?.getDependencyRef("$dependencyKey")', + isAlive: isAlive, + ); + + PropertyAsyncNode? propertyNode; + + for (final node in propertyNodes) { + if (node.key == 'dependencyRef' && node is PropertyAsyncNode) { + propertyNode = node; + break; + } + } + + if (dependencyRefValue.kind == InstanceKind.kNull) { + propertyNode?.remove(); + return; + } + + if (propertyNode == null) { + propertyNodes.add( + PropertyAsyncNode( + key: 'dependencyRef', + valueRef: dependencyRefValue, + isExpanded: false, + ), + ); + } else { + propertyNode.updateValueRef(dependencyRefValue); + } + } catch (e) { + print(e); + } + } +} diff --git a/packages/reactter_devtools_extension/lib/src/bases/node_info.dart b/packages/reactter_devtools_extension/lib/src/bases/node_info.dart new file mode 100644 index 00000000..c99bfbc0 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/bases/node_info.dart @@ -0,0 +1,5 @@ +abstract base class NodeInfo { + final String? dependencyRef; + + const NodeInfo({this.dependencyRef}); +} diff --git a/packages/reactter_devtools_extension/lib/src/bases/property_node.dart b/packages/reactter_devtools_extension/lib/src/bases/property_node.dart new file mode 100644 index 00000000..cf4dd2e8 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/bases/property_node.dart @@ -0,0 +1,13 @@ +import 'package:flutter_reactter/reactter.dart'; +import 'package:reactter_devtools_extension/src/bases/tree_node.dart'; + +abstract base class PropertyNode extends TreeNode { + final String key; + final uValue = UseState(null); + final uInstanceInfo = UseState?>(null); + + PropertyNode({required this.key, String? value, bool isExpanded = false}) { + uValue.value = value; + uIsExpanded.value = isExpanded; + } +} diff --git a/packages/reactter_devtools_extension/lib/src/data/tree_list.dart b/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart similarity index 64% rename from packages/reactter_devtools_extension/lib/src/data/tree_list.dart rename to packages/reactter_devtools_extension/lib/src/bases/tree_list.dart index 6841d4d7..9bd7240c 100644 --- a/packages/reactter_devtools_extension/lib/src/data/tree_list.dart +++ b/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart @@ -1,10 +1,12 @@ import 'dart:collection'; import 'package:flutter_reactter/reactter.dart'; -import 'package:reactter_devtools_extension/src/data/tree_node.dart'; +import 'package:reactter_devtools_extension/src/bases/tree_node.dart'; -final class TreeList> extends LinkedList +base class TreeList> extends LinkedList with RtContext, RtStateBase> { + final uMaxDepth = UseState(0); + TreeList._(); factory TreeList() => Rt.createState(() => TreeList._()); @@ -22,15 +24,14 @@ final class TreeList> extends LinkedList bool remove(E entry) => entry.remove(); @override - void clear() { - Rt.batch(() { - for (final entry in this) { - if (!entry.isDisposed) entry.dispose(); - } - - super.clear(); + void dispose() { + clear(); + super.dispose(); + } - notify(); - }); + @override + void clear() { + super.clear(); + if (!isDisposed) notify(); } } diff --git a/packages/reactter_devtools_extension/lib/src/data/tree_node.dart b/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart similarity index 60% rename from packages/reactter_devtools_extension/lib/src/data/tree_node.dart rename to packages/reactter_devtools_extension/lib/src/bases/tree_node.dart index 8a029b96..a1f73f93 100644 --- a/packages/reactter_devtools_extension/lib/src/data/tree_node.dart +++ b/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart @@ -5,12 +5,11 @@ abstract base class TreeNode> extends LinkedListEntry with RtStateBase, RtContext { final uChildren = UseState(LinkedHashSet()); final uIsExpanded = UseState(true); + final uDepth = UseState(0); E? _parent; E? get parent => _parent; - int get depth => (parent?.depth ?? 0) + 1; - E get lastDescendant { if (!uIsExpanded.value) return this as E; if (uChildren.value.isEmpty) return this as E; @@ -21,10 +20,7 @@ abstract base class TreeNode> extends LinkedListEntry int? getIndex() => list?.indexed.firstWhere((e) => e.$2 == this).$1; TreeNode() { - UseEffect( - _onIsExpandedChanged, - [uIsExpanded], - ); + UseEffect(_onIsExpandedChanged, [uIsExpanded]); if (list != null) bind(list!); } @@ -54,26 +50,21 @@ abstract base class TreeNode> extends LinkedListEntry if (list is RtState) (list as RtState).notify(); } - void replace(E entry) { + void replaceFor(E entry) { Rt.batch(() { - if (parent != null) { - entry._parent = parent; - insertBefore(entry); - } - - if (entry.list == null) { - list?.add(entry); - } + entry._toParent(parent); - entry.uIsExpanded.value = uIsExpanded.value; - - final children = uChildren.value; - - for (final child in [...children]) { + for (final child in [...uChildren.value]) { entry.addChild(child); } - children.clear(); + entry.uIsExpanded.update(() { + entry.uIsExpanded.value = uIsExpanded.value; + }); + + uChildren.update(() { + uChildren.value.clear(); + }); remove(); }); @@ -81,39 +72,28 @@ abstract base class TreeNode> extends LinkedListEntry void addChild(E entry) { Rt.batch(() { - if (entry.parent != this) { - entry.parent?.uChildren.value.remove(entry); - entry.parent?.uChildren.notify(); - entry._parent = this as E; - entry - ..unbind() - ..bind(this); - } + entry._toParent(this as E); - uChildren.value.add(entry); - uChildren.notify(); - - if (uIsExpanded.value) { - _showChildren(); - } else { - _hideChildren(); - } + uChildren.update(() { + uChildren.value.add(entry); + }); }); } bool remove() { Rt.batch(() { - final children = uChildren.value.toList(); + unlink(); - for (final node in children) { + for (final node in [...uChildren.value]) { node.remove(); } - unlink(); - - parent?.uChildren - ?..value.remove(this) - ..notify(); + final parentIsDisposed = parent?.uChildren.isDisposed ?? true; + if (!parentIsDisposed) { + parent?.uChildren.update(() { + parent?.uChildren.value.remove(this); + }); + } if (!isDisposed) dispose(); }); @@ -121,6 +101,34 @@ abstract base class TreeNode> extends LinkedListEntry return list == null; } + void _toParent(E? parentToSet) { + if (parentToSet == _parent) return; + + _parent?.uChildren.value.remove(this); + _parent?.uChildren.notify(); + _parent = parentToSet; + + unbind(); + unlink(); + + if (_parent != null) bind(_parent!); + + if (_parent?.uIsExpanded.value ?? false) { + _parent?.lastDescendant.insertAfter(this as E); + } + + _recalculateDepth(); + uIsExpanded.notify(); + } + + void _recalculateDepth() { + uDepth.value = parent?.uDepth.value == null ? 0 : parent!.uDepth.value + 1; + + for (final child in [...uChildren.value]) { + child._recalculateDepth(); + } + } + void _onIsExpandedChanged() { Rt.batch(() { if (uIsExpanded.value) { @@ -132,10 +140,12 @@ abstract base class TreeNode> extends LinkedListEntry } void _showChildren() { + if (list == null) return; + Rt.batch(() { E? prevChild; - for (final node in uChildren.value) { + for (final node in [...uChildren.value]) { node.unlink(); if (prevChild == null) { @@ -158,9 +168,7 @@ abstract base class TreeNode> extends LinkedListEntry void _hideChildren() { Rt.batch(() { for (final node in uChildren.value) { - if (node.list == null) { - continue; - } + if (node.list == null) continue; node._hideChildren(); node.unlink(); diff --git a/packages/reactter_devtools_extension/lib/src/data/constants.dart b/packages/reactter_devtools_extension/lib/src/constants.dart similarity index 97% rename from packages/reactter_devtools_extension/lib/src/data/constants.dart rename to packages/reactter_devtools_extension/lib/src/constants.dart index 6ded9f47..790fa39a 100644 --- a/packages/reactter_devtools_extension/lib/src/data/constants.dart +++ b/packages/reactter_devtools_extension/lib/src/constants.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; +const int kMaxValueLength = 50; const double kNodeTileHeight = 24; enum NodeType { diff --git a/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart b/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart index e9ddaa97..25bef8cc 100644 --- a/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart +++ b/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart @@ -1,32 +1,42 @@ import 'dart:async'; import 'dart:collection'; +import 'dart:math'; import 'package:devtools_extensions/devtools_extensions.dart'; import 'package:flutter/material.dart'; import 'package:flutter_reactter/reactter.dart'; -import 'package:reactter_devtools_extension/src/data/constants.dart'; - -import 'package:reactter_devtools_extension/src/data/instance_info.dart'; -import 'package:reactter_devtools_extension/src/data/instance_node.dart'; -import 'package:reactter_devtools_extension/src/data/slot_node.dart'; -import 'package:reactter_devtools_extension/src/data/state_info.dart'; -import 'package:reactter_devtools_extension/src/data/tree_list.dart'; -import 'package:reactter_devtools_extension/src/interfaces/node.dart'; +import 'package:reactter_devtools_extension/src/constants.dart'; +import 'package:reactter_devtools_extension/src/nodes/dependency/dependency_info.dart'; +import 'package:reactter_devtools_extension/src/nodes/dependency/dependency_node.dart'; + +import 'package:reactter_devtools_extension/src/nodes/instance/instance_info.dart'; +import 'package:reactter_devtools_extension/src/nodes/instance/instance_node.dart'; +import 'package:reactter_devtools_extension/src/nodes/sentinel_node.dart'; +import 'package:reactter_devtools_extension/src/nodes/slot_node.dart'; +import 'package:reactter_devtools_extension/src/nodes/state/state_info.dart'; +import 'package:reactter_devtools_extension/src/bases/tree_list.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; import 'package:reactter_devtools_extension/src/services/devtools_service.dart'; -import 'package:reactter_devtools_extension/src/data/state_node.dart'; +import 'package:reactter_devtools_extension/src/nodes/state/state_node.dart'; +import 'package:vm_service/vm_service.dart'; class NodesController { StreamSubscription? extEventSubscription; final devtoolsSevices = DevtoolsService(); - final nodesList = TreeList(); - final uNodes = UseState(LinkedHashMap()); + final nodesList = TreeList(); + final dependenciesList = TreeList(); + final uNodes = UseState(LinkedHashMap()); final uCurrentNodeKey = UseState(null); + final uMaxDepth = UseState(0); - final listViewKey = GlobalKey(); - final scrollControllerY = ScrollController(); + final nodesListViewKey = GlobalKey(); + final dependenciesListViewKey = GlobalKey(); - INode? get currentNode => uNodes.value[uCurrentNodeKey.value]; + final nodesListScrollControllerY = ScrollController(); + final dependencyListScrollControllerY = ScrollController(); + + Node? get currentNode => uNodes.value[uCurrentNodeKey.value]; NodesController() { init(); @@ -49,14 +59,14 @@ class NodesController { switch (event.extensionKind) { case 'ext.reactter.onStateCreated': - final String stateKey = eventData['stateKey']; - addNodeByKey(stateKey); + final Map state = eventData['state']; + addNodeByMapInfo(state); break; case 'ext.reactter.onStateBound': - final String instanceKey = eventData['instanceKey']; - final String stateKey = eventData['stateKey']; - addNodeByKey(instanceKey); - addNodeByKey(stateKey); + final Map state = eventData['state']; + final Map instance = eventData['instance']; + addNodeByMapInfo(state); + addNodeByMapInfo(instance); break; case 'ext.reactter.onStateUnbound': final String instanceKey = eventData['instanceKey']; @@ -72,14 +82,25 @@ class NodesController { final bool isStateRemoved = eventData['isStateRemoved']; if (isStateRemoved) removeNodeByKey(stateKey); break; + case 'ext.reactter.onDependencyRegistered': + final Map dependency = eventData['dependency']; + addNodeByMapInfo(dependency); + break; case 'ext.reactter.onDependencyCreated': - final String instanceKey = eventData['instanceKey']; - addNodeByKey(instanceKey); + final Map dependency = eventData['dependency']; + final Map instance = eventData['instance']; + addNodeByMapInfo(dependency); + addNodeByMapInfo(instance); break; case 'ext.reactter.onDependencyDeleted': final String instanceKey = eventData['instanceKey']; final bool isInstanceRemoved = eventData['isInstanceRemoved']; if (isInstanceRemoved) removeNodeByKey(instanceKey); + break; + case 'ext.reactter.onDependencyUnregistered': + final String dependencyKey = eventData['dependencyKey']; + removeNodeByKey(dependencyKey); + break; } }); } @@ -92,8 +113,10 @@ class NodesController { void resetState() { nodesList.clear(); + dependenciesList.clear(); uNodes.value.clear(); uCurrentNodeKey.value = null; + uMaxDepth.value = 0; } void selectNodeByKey(String nodeKey) { @@ -106,16 +129,26 @@ class NodesController { if (index == null) return; final offset = index * kNodeTileHeight; - - scrollControllerY.animateTo( + final list = node?.list; + final isDependenciesList = list == dependenciesList; + final scrollController = isDependenciesList + ? dependencyListScrollControllerY + : nodesListScrollControllerY; + final scrollBottom = + scrollController.offset + scrollController.position.viewportDimension; + final nodeBottom = offset + kNodeTileHeight; + + if (offset > scrollController.offset && nodeBottom < scrollBottom) return; + + scrollController.animateTo( offset, duration: const Duration(milliseconds: 300), curve: Curves.bounceIn, ); } - Future addNodeByKey(String nodeKey) async { - final nodeInfo = await devtoolsSevices.getNodeBykey(nodeKey); + Future addNodeByKey(String nodeKey, [Map fallback = const {}]) async { + final nodeInfo = await devtoolsSevices.getNodeBykey(nodeKey, fallback); addNodeByMapInfo(nodeInfo); } @@ -123,9 +156,6 @@ class NodesController { Rt.batch(() { for (final nodeInfo in nodesInfo) { addNodeByMapInfo(nodeInfo); - // print( - // "key: ${nodeInfo['key']}, label: ${nodeInfo['debugLabel']}, type: ${nodeInfo['type']}, kind: ${nodeInfo['kind']}, boundInstanceKey: ${nodeInfo['boundInstanceKey']}", - // ); } }); } @@ -134,10 +164,43 @@ class NodesController { final kind = nodeInfo['kind']; final key = nodeInfo['key']; final type = nodeInfo['type']; + final error = nodeInfo['error']; final dependencyRef = nodeInfo['dependencyRef']; + if (error is SentinelException) { + final nodePrev = uNodes.value[key]; + final node = nodePrev ?? + SentinelNode( + key: key, + kind: kind, + type: type ?? 'Unknown', + ); + + if (kind == 'dependency') { + addDependencyNode(node); + } else { + addNode(node); + } + + return; + } + switch (kind) { case 'dependency': + final nodePrev = uNodes.value[key]; + final node = nodePrev is DependencyNode + ? nodePrev + : DependencyNode( + key: key, + kind: kind, + type: type, + ); + + node.uInfo.value = DependencyInfo( + id: nodeInfo['id'], + ); + + addDependencyNode(node); break; case 'state': case 'hook': @@ -197,17 +260,7 @@ class NodesController { } } - void addNode(INode node) { - // if (node is SlotNode) { - // print("SloteNode(key: ${node.key})"); - // } else if (node is StateNode) { - // print("StateNode(key: ${node.key})"); - // } else if (node is InstanceNode) { - // print("InstanceNode(key: ${node.key})"); - // } else { - // print("Node(key: ${node.key})"); - // } - + void addNode(Node node) { final nodePrev = uNodes.value[node.key]; if (nodePrev == node) return; @@ -215,11 +268,18 @@ class NodesController { uNodes.value[node.key] = node; uNodes.notify(); - nodePrev?.replace(node); - if (node is SlotNode) return; if (node.list == null) nodesList.add(node); + + nodePrev?.replaceFor(node); + uMaxDepth.value = max(uMaxDepth.value, node.uDepth.value); + } + + void addDependencyNode(Node node) { + uNodes.value[node.key] = node; + uNodes.notify(); + if (node.list == null) dependenciesList.add(node); } void removeNodeByKey(String nodeKey) { @@ -235,7 +295,7 @@ class NodesController { ..notify(); } - Future selectNode(INode node) async { + Future selectNode(Node node) async { currentNode?.uIsSelected.value = false; uCurrentNodeKey.value = node.key; node.uIsSelected.value = true; diff --git a/packages/reactter_devtools_extension/lib/src/data/dependency_info.dart b/packages/reactter_devtools_extension/lib/src/data/dependency_info.dart deleted file mode 100644 index 39090df3..00000000 --- a/packages/reactter_devtools_extension/lib/src/data/dependency_info.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:reactter_devtools_extension/src/interfaces/node_info.dart'; - -class DependencyInfo extends INodeInfo { - final String? id; - - DependencyInfo({required this.id}); -} diff --git a/packages/reactter_devtools_extension/lib/src/data/instance_info.dart b/packages/reactter_devtools_extension/lib/src/data/instance_info.dart deleted file mode 100644 index 6aca46bf..00000000 --- a/packages/reactter_devtools_extension/lib/src/data/instance_info.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'package:reactter_devtools_extension/src/interfaces/node_info.dart'; - -class InstanceInfo extends INodeInfo { - - const InstanceInfo({super.dependencyRef}); -} diff --git a/packages/reactter_devtools_extension/lib/src/interfaces/node.dart b/packages/reactter_devtools_extension/lib/src/interfaces/node.dart deleted file mode 100644 index 73fd5127..00000000 --- a/packages/reactter_devtools_extension/lib/src/interfaces/node.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:devtools_app_shared/service.dart'; -import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:reactter_devtools_extension/src/data/tree_node.dart'; -import 'package:reactter_devtools_extension/src/interfaces/node_info.dart'; -import 'package:reactter_devtools_extension/src/data/property_node.dart'; -import 'package:reactter_devtools_extension/src/data/tree_list.dart'; -import 'package:reactter_devtools_extension/src/services/eval_service.dart'; -import 'package:vm_service/vm_service.dart'; - -abstract base class INode extends TreeNode { - final String key; - final String kind; - final String type; - - String? get label; - - final uInfo = UseState(null); - final uIsSelected = UseState(false); - final propertyNodes = TreeList(); - - INode({ - required this.key, - required this.kind, - required this.type, - }); - - Future loadDetails() async { - await loadDependencyRef(); - } - - Future loadDependencyRef() async { - final eval = await EvalService.devtoolsEval; - final isAlive = Disposable(); - final dependencyKey = uInfo.value?.dependencyRef; - - final dependencyRefValue = await eval.evalInstance( - 'RtDevTools._instance?.getDependencyRef("$dependencyKey")', - isAlive: isAlive, - ); - - PropertyAsyncNode? propertyNode; - - for (final node in propertyNodes) { - if (node.key == 'dependencyRef' && node is PropertyAsyncNode) { - propertyNode = node; - break; - } - } - - if (dependencyRefValue.kind == InstanceKind.kNull) { - propertyNode?.remove(); - return; - } - - if (propertyNode == null) { - propertyNodes.add( - PropertyAsyncNode( - key: 'dependencyRef', - valueRef: dependencyRefValue, - isExpanded: false, - ), - ); - } else { - propertyNode.updateValueRef(dependencyRefValue); - } - } -} diff --git a/packages/reactter_devtools_extension/lib/src/interfaces/node_info.dart b/packages/reactter_devtools_extension/lib/src/interfaces/node_info.dart deleted file mode 100644 index 8726b2d2..00000000 --- a/packages/reactter_devtools_extension/lib/src/interfaces/node_info.dart +++ /dev/null @@ -1,5 +0,0 @@ -abstract class INodeInfo { - final String? dependencyRef; - - const INodeInfo({this.dependencyRef}); -} diff --git a/packages/reactter_devtools_extension/lib/src/nodes/dependency/dependency_info.dart b/packages/reactter_devtools_extension/lib/src/nodes/dependency/dependency_info.dart new file mode 100644 index 00000000..8929119a --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/nodes/dependency/dependency_info.dart @@ -0,0 +1,7 @@ +import 'package:reactter_devtools_extension/src/bases/node_info.dart'; + +final class DependencyInfo extends NodeInfo { + final String? id; + + DependencyInfo({required this.id}); +} diff --git a/packages/reactter_devtools_extension/lib/src/data/dependency_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/dependency/dependency_node.dart similarity index 84% rename from packages/reactter_devtools_extension/lib/src/data/dependency_node.dart rename to packages/reactter_devtools_extension/lib/src/nodes/dependency/dependency_node.dart index b3f39ddb..ecb55d6b 100644 --- a/packages/reactter_devtools_extension/lib/src/data/dependency_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/dependency/dependency_node.dart @@ -1,11 +1,10 @@ -import 'package:devtools_app_shared/service.dart'; import 'package:flutter_reactter/reactter.dart'; -import 'package:reactter_devtools_extension/src/data/dependency_info.dart'; -import 'package:reactter_devtools_extension/src/interfaces/node.dart'; +import 'package:reactter_devtools_extension/src/nodes/dependency/dependency_info.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; import 'package:reactter_devtools_extension/src/services/eval_service.dart'; import 'package:vm_service/vm_service.dart'; -base class DependencyNode extends INode { +final class DependencyNode extends Node { final uIsLoading = UseState(false); @override @@ -15,9 +14,7 @@ base class DependencyNode extends INode { required super.key, required super.kind, required super.type, - }) { - _loadDependencyNode(); - } + }); factory DependencyNode({ required String key, @@ -37,7 +34,6 @@ base class DependencyNode extends INode { uIsLoading.value = true; final eval = await EvalService.devtoolsEval; - final isAlive = Disposable(); final dependencyNode = await eval.evalInstance( 'RtDevTools._instance?.getDependencyInfo("$key")', diff --git a/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_info.dart b/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_info.dart new file mode 100644 index 00000000..bbd35f3b --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_info.dart @@ -0,0 +1,5 @@ +import 'package:reactter_devtools_extension/src/bases/node_info.dart'; + +final class InstanceInfo extends NodeInfo { + const InstanceInfo({super.dependencyRef}); +} diff --git a/packages/reactter_devtools_extension/lib/src/data/instance_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart similarity index 67% rename from packages/reactter_devtools_extension/lib/src/data/instance_node.dart rename to packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart index 3dc1365f..4906e108 100644 --- a/packages/reactter_devtools_extension/lib/src/data/instance_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart @@ -1,8 +1,8 @@ import 'package:flutter_reactter/reactter.dart'; -import 'package:reactter_devtools_extension/src/data/instance_info.dart'; -import 'package:reactter_devtools_extension/src/interfaces/node.dart'; +import 'package:reactter_devtools_extension/src/nodes/instance/instance_info.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; -base class InstanceNode extends INode { +final class InstanceNode extends Node { @override final label = null; diff --git a/packages/reactter_devtools_extension/lib/src/data/property_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/property/property_async_node.dart similarity index 88% rename from packages/reactter_devtools_extension/lib/src/data/property_node.dart rename to packages/reactter_devtools_extension/lib/src/nodes/property/property_async_node.dart index 040093b8..0187878f 100644 --- a/packages/reactter_devtools_extension/lib/src/data/property_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/property/property_async_node.dart @@ -2,62 +2,23 @@ import 'dart:async'; import 'dart:collection'; import 'package:devtools_app_shared/service.dart'; - import 'package:flutter_reactter/reactter.dart'; -import 'package:reactter_devtools_extension/src/data/tree_node.dart'; +import 'package:reactter_devtools_extension/src/bases/property_node.dart'; +import 'package:reactter_devtools_extension/src/constants.dart'; +import 'package:reactter_devtools_extension/src/nodes/property/propery_sync_node.dart'; import 'package:reactter_devtools_extension/src/services/eval_service.dart'; import 'package:reactter_devtools_extension/src/utils/extensions.dart'; import 'package:vm_service/vm_service.dart'; -const _kMaxValueLength = 50; - -abstract base class IPropertyNode extends TreeNode { - final String key; - final uValue = UseState(null); - final uInstanceInfo = UseState?>(null); - - IPropertyNode({required this.key, String? value, bool isExpanded = false}) { - uValue.value = value; - uIsExpanded.value = isExpanded; - } -} - -base class PropertyNode extends IPropertyNode { - PropertyNode._({ - required super.key, - required super.value, - required super.isExpanded, - }); - - factory PropertyNode({ - IPropertyNode? parent, - required String key, - required String value, - bool isExpanded = false, - }) { - return Rt.createState( - () => PropertyNode._( - key: key, - value: value, - isExpanded: isExpanded, - ), - ); - } - - void updateValue(String value) { - uValue.value = value; - } -} - -base class PropertyAsyncNode extends IPropertyNode { +final class PropertyAsyncNode extends PropertyNode { InstanceRef _valueRef; InstanceRef get valueRef => _valueRef; Disposable? _isAlive; bool _isResolved = false; bool _isValueUpdating = false; - final LinkedHashMap _childNodeRefs = - LinkedHashMap(); + final LinkedHashMap _childNodeRefs = + LinkedHashMap(); final uValueFuture = UseState?>(null); final uIsLoading = UseState(false); @@ -279,7 +240,7 @@ base class PropertyAsyncNode extends IPropertyNode { final isAsyncValue = child.value is InstanceRef; final isValueTypeSame = (childCurrent is PropertyAsyncNode && isAsyncValue) || - (childCurrent is PropertyNode && !isAsyncValue); + (childCurrent is PropertySyncNode && !isAsyncValue); if (!isValueTypeSame) _childNodeRefs.remove(child.key); @@ -296,7 +257,7 @@ base class PropertyAsyncNode extends IPropertyNode { ); } - return PropertyNode( + return PropertySyncNode( key: child.key, value: child.value.toString(), ); @@ -306,7 +267,7 @@ base class PropertyAsyncNode extends IPropertyNode { if (isRemoveSkip) { if (childNode is PropertyAsyncNode) { childNode.updateValueRef(child.value); - } else if (childNode is PropertyNode) { + } else if (childNode is PropertySyncNode) { childNode.updateValue(child.value.toString()); } } else { @@ -333,7 +294,7 @@ base class PropertyAsyncNode extends IPropertyNode { final child = children[i]; final isLast = i == children.length - 1; - if (child is PropertyNode) continue; + if (child is PropertySyncNode) continue; assert(child is PropertyAsyncNode); @@ -357,7 +318,7 @@ base class PropertyAsyncNode extends IPropertyNode { "${buildValue(child.key, value)}${isLast ? '' : ', '}", ); - if (childrenValueBuffer.length > _kMaxValueLength) { + if (childrenValueBuffer.length > kMaxValueLength) { isFull = isLast; break; } @@ -365,7 +326,7 @@ base class PropertyAsyncNode extends IPropertyNode { final moreEllipsis = isFull ? '' : ', ...'; final maxValueBufferLength = - _kMaxValueLength - prefix.length - moreEllipsis.length - suffix.length; + kMaxValueLength - prefix.length - moreEllipsis.length - suffix.length; final childrenValue = childrenValueBuffer.toString(); final shouldBeCutted = childrenValue.length > maxValueBufferLength; final cuttedEllipsis = shouldBeCutted ? '...' : ''; diff --git a/packages/reactter_devtools_extension/lib/src/nodes/property/propery_sync_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/property/propery_sync_node.dart new file mode 100644 index 00000000..394deca6 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/nodes/property/propery_sync_node.dart @@ -0,0 +1,29 @@ +import 'package:flutter_reactter/reactter.dart'; +import 'package:reactter_devtools_extension/src/bases/property_node.dart'; + +final class PropertySyncNode extends PropertyNode { + PropertySyncNode._({ + required super.key, + required super.value, + required super.isExpanded, + }); + + factory PropertySyncNode({ + PropertyNode? parent, + required String key, + required String value, + bool isExpanded = false, + }) { + return Rt.createState( + () => PropertySyncNode._( + key: key, + value: value, + isExpanded: isExpanded, + ), + ); + } + + void updateValue(String value) { + uValue.value = value; + } +} diff --git a/packages/reactter_devtools_extension/lib/src/nodes/sentinel_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/sentinel_node.dart new file mode 100644 index 00000000..83a9ff1a --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/nodes/sentinel_node.dart @@ -0,0 +1,33 @@ +import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/bases/node_info.dart'; + +final class SentinelNode extends Node { + @override + String? get label => 'Sentinel'; + + SentinelNode._({ + required super.key, + required super.kind, + required super.type, + }); + + factory SentinelNode({ + required String key, + required String kind, + required String type, + }) { + return Rt.createState( + () => SentinelNode._( + key: key, + kind: kind, + type: type, + ), + ); + } + + @override + Future loadDetails() async { + // TODO: implement loadDetails + } +} diff --git a/packages/reactter_devtools_extension/lib/src/data/slot_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/slot_node.dart similarity index 72% rename from packages/reactter_devtools_extension/lib/src/data/slot_node.dart rename to packages/reactter_devtools_extension/lib/src/nodes/slot_node.dart index 7ac5f2b0..bf0bef3f 100644 --- a/packages/reactter_devtools_extension/lib/src/data/slot_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/slot_node.dart @@ -1,8 +1,8 @@ import 'package:flutter_reactter/reactter.dart'; -import 'package:reactter_devtools_extension/src/interfaces/node.dart'; -import 'package:reactter_devtools_extension/src/interfaces/node_info.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/bases/node_info.dart'; -base class SlotNode extends INode { +final class SlotNode extends Node { @override final String? label = null; diff --git a/packages/reactter_devtools_extension/lib/src/data/state_info.dart b/packages/reactter_devtools_extension/lib/src/nodes/state/state_info.dart similarity index 82% rename from packages/reactter_devtools_extension/lib/src/data/state_info.dart rename to packages/reactter_devtools_extension/lib/src/nodes/state/state_info.dart index a4edca9d..9dedef71 100644 --- a/packages/reactter_devtools_extension/lib/src/data/state_info.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/state/state_info.dart @@ -1,6 +1,6 @@ -import 'package:reactter_devtools_extension/src/interfaces/node_info.dart'; +import 'package:reactter_devtools_extension/src/bases/node_info.dart'; -class StateInfo extends INodeInfo { +final class StateInfo extends NodeInfo { final String? debugLabel; final String? boundInstanceKey; final List kinds; diff --git a/packages/reactter_devtools_extension/lib/src/data/state_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/state/state_node.dart similarity index 85% rename from packages/reactter_devtools_extension/lib/src/data/state_node.dart rename to packages/reactter_devtools_extension/lib/src/nodes/state/state_node.dart index 4249dea6..c4c2e719 100644 --- a/packages/reactter_devtools_extension/lib/src/data/state_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/state/state_node.dart @@ -1,11 +1,10 @@ -import 'package:devtools_app_shared/service.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:reactter_devtools_extension/src/interfaces/node.dart'; -import 'package:reactter_devtools_extension/src/data/property_node.dart'; -import 'package:reactter_devtools_extension/src/data/state_info.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/nodes/property/property_async_node.dart'; +import 'package:reactter_devtools_extension/src/nodes/state/state_info.dart'; import 'package:reactter_devtools_extension/src/services/eval_service.dart'; -base class StateNode extends INode { +base class StateNode extends Node { final uIsLoading = UseState(false); @override @@ -44,7 +43,6 @@ base class StateNode extends INode { Future loadBoundInstance() async { final eval = await EvalService.devtoolsEval; - final isAlive = Disposable(); final boundInstanceValue = await eval.evalInstance( 'RtDevTools._instance?.getBoundInstance("$key")', @@ -75,7 +73,6 @@ base class StateNode extends INode { Future loadDebugInfo() async { final eval = await EvalService.devtoolsEval; - final isAlive = Disposable(); final debugInfoValue = await eval.evalInstance( 'RtDevTools._instance?.getDebugInfo("$key")', diff --git a/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart b/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart index 65ef2546..88cb7957 100644 --- a/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart +++ b/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart @@ -1,6 +1,7 @@ import 'package:devtools_app_shared/ui.dart'; import 'package:flutter/material.dart' hide Split; import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/widgets/dependencies_list.dart'; import 'package:reactter_devtools_extension/src/widgets/instance_title.dart'; import 'package:reactter_devtools_extension/src/widgets/nodes_list.dart'; import 'package:reactter_devtools_extension/src/widgets/properties_list.dart'; @@ -15,19 +16,34 @@ class RtDevToolsExtension extends StatelessWidget { () => NodesController(), builder: (context, inst, child) { return SplitPane( - axis: SplitPane.axisFor(context, 0.8), - initialFractions: const [0.40, 0.60], + axis: SplitPane.axisFor(context, 1.2), + initialFractions: const [0.55, 0.45], children: [ - const RoundedOutlinedBorder( + RoundedOutlinedBorder( clip: true, - child: Column( - children: [ - AreaPaneHeader(title: Text("Instance Tree")), - Expanded( - child: NodesList(), - ), - ], - ), + child: LayoutBuilder(builder: (context, constraints) { + return FlexSplitColumn( + totalHeight: constraints.maxHeight, + initialFractions: const [0.25, 0.75], + minSizes: const [0.0, 0.0], + headers: const [ + AreaPaneHeader( + roundedTopBorder: false, + includeTopBorder: false, + title: Text("Dependencies"), + ), + AreaPaneHeader( + rightPadding: 0.0, + roundedTopBorder: false, + title: Text("State/Instance Tree"), + ), + ], + children: const [ + DependenciesList(), + NodesList(), + ], + ); + }), ), RoundedOutlinedBorder( clip: true, @@ -38,12 +54,16 @@ class RtDevToolsExtension extends StatelessWidget { final selectedNode = inst.currentNode; if (nKey == null) { - return const AreaPaneHeader(title: Text("Select a node")); + return const AreaPaneHeader( + roundedTopBorder: false, + title: Text("Select a node for details"), + ); } final info = watch(selectedNode!.uInfo).value; return AreaPaneHeader( + roundedTopBorder: false, title: Row( children: [ const Text( diff --git a/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart b/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart index bd552292..9c4f0976 100644 --- a/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart +++ b/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart @@ -1,4 +1,4 @@ -import 'package:devtools_app_shared/service.dart'; +import 'package:devtools_app_shared/service.dart' hide SentinelException; import 'package:reactter_devtools_extension/src/services/eval_service.dart'; import 'package:reactter_devtools_extension/src/utils/extensions.dart'; import 'package:vm_service/vm_service.dart'; @@ -33,23 +33,35 @@ class DevtoolsService { return nodeInfos + nextNodeInfos; } - Future getNodeBykey(String nodeKey) async { - final eval = await EvalService.devtoolsEval; + Future getNodeBykey(String nodeKey, [Map fallback = const {}]) async { + try { + final eval = await EvalService.devtoolsEval; + final isAlive = + _nodesDisposables.putIfAbsent(nodeKey, () => Disposable()); - final isAlive = _nodesDisposables.putIfAbsent(nodeKey, () => Disposable()); + final nodeInst = await EvalService.evalsQueue.add( + () => eval.evalInstance( + 'RtDevTools._instance?._nodesByKey["$nodeKey"]?.toJson()', + isAlive: isAlive, + ), + ); - final nodeInst = await EvalService.evalsQueue.add( - () => eval.evalInstance( - 'RtDevTools._instance?._nodesByKey["$nodeKey"]?.toJson()', - isAlive: isAlive, - ), - ); + if (nodeInst.kind == InstanceKind.kNull) return {}; - if (nodeInst.kind == InstanceKind.kNull) return {}; + assert(nodeInst.kind == InstanceKind.kMap); - assert(nodeInst.kind == InstanceKind.kMap); + return await nodeInst.evalValue(isAlive, null, true); + } catch (error) { + if (error is CancelledException) { + return {}; + } - return await nodeInst.evalValue(isAlive); + return { + ...fallback, + 'key': nodeKey, + 'error': error, + }; + } } void disposeNodeByKey(String nodeKey) { diff --git a/packages/reactter_devtools_extension/lib/src/utils/extensions.dart b/packages/reactter_devtools_extension/lib/src/utils/extensions.dart index 09c4990e..82b171cb 100644 --- a/packages/reactter_devtools_extension/lib/src/utils/extensions.dart +++ b/packages/reactter_devtools_extension/lib/src/utils/extensions.dart @@ -1,9 +1,13 @@ -import 'package:devtools_app_shared/service.dart'; +import 'package:devtools_app_shared/service.dart' hide SentinelException; import 'package:reactter_devtools_extension/src/services/eval_service.dart'; import 'package:vm_service/vm_service.dart'; extension InstanceExt on Instance { - Future evalValue([Disposable? isAlive, int? level]) async { + Future evalValue([ + Disposable? isAlive, + int? level, + throwOnError = false, + ]) async { if (level != null && level == 0) return this; switch (kind) { @@ -23,11 +27,14 @@ extension InstanceExt on Instance { for (final entry in associations!) { final InstanceRef keyRef = entry.key; final InstanceRef valueRef = entry.value; - - nodeInfo[await keyRef.evalValue(isAlive)] = await valueRef.evalValue( + final key = await keyRef.evalValue(isAlive, null, throwOnError); + final value = await valueRef.evalValue( isAlive, level == null ? null : level - 1, + throwOnError, ); + + nodeInfo[key] = value; } return nodeInfo; @@ -83,7 +90,10 @@ extension InstanceExt on Instance { } extension InstanceRefExt on InstanceRef { - Future safeGetInstance([Disposable? isAlive]) async { + Future safeGetInstance([ + Disposable? isAlive, + throwOnError = false, + ]) async { try { final eval = await EvalService.devtoolsEval; @@ -93,16 +103,26 @@ extension InstanceRefExt on InstanceRef { return instance; } catch (e) { - print('safeGetInstance error: $e'); - return null; + if (!throwOnError) return null; + rethrow; } } - Future evalValue([Disposable? isAlive, int? level]) async { + Future evalValue([ + Disposable? isAlive, + int? level, + throwOnError = false, + ]) async { if (level != null && level == 0) return this; - final instance = await safeGetInstance(isAlive); - return await instance?.evalValue(isAlive, level); + try { + final instance = await safeGetInstance(isAlive, throwOnError); + return await instance?.evalValue(isAlive, level); + } catch (e) { + if (!throwOnError) return null; + + rethrow; + } } Future evalValueFirstLevel([Disposable? isAlive]) async { diff --git a/packages/reactter_devtools_extension/lib/src/widgets/dependencies_list.dart b/packages/reactter_devtools_extension/lib/src/widgets/dependencies_list.dart new file mode 100644 index 00000000..5e0948ab --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/widgets/dependencies_list.dart @@ -0,0 +1,78 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/controllers/nodes_controller.dart'; +import 'package:reactter_devtools_extension/src/widgets/dependency_tile.dart'; +import 'package:reactter_devtools_extension/src/widgets/offset_scrollbar.dart'; + +class DependenciesList extends StatelessWidget { + const DependenciesList({super.key}); + + @override + Widget build(BuildContext context) { + final nodesController = context.use(); + final listViewKey = nodesController.dependenciesListViewKey; + final scrollControllerX = ScrollController(); + final scrollControllerY = nodesController.dependencyListScrollControllerY; + final focusNode = FocusNode(); + + return LayoutBuilder( + builder: (context, constraints) { + final viewportWidth = constraints.maxWidth; + + return Scrollbar( + controller: scrollControllerX, + thumbVisibility: true, + child: SingleChildScrollView( + controller: scrollControllerX, + scrollDirection: Axis.horizontal, + child: Material( + child: RtWatcher((context, watch) { + const minWith = 500.0; + + return ConstrainedBox( + constraints: BoxConstraints( + maxWidth: viewportWidth > minWith ? viewportWidth : minWith, + ), + child: OffsetScrollbar( + isAlwaysShown: true, + axis: Axis.vertical, + controller: scrollControllerY, + offsetController: scrollControllerX, + offsetControllerViewportDimension: viewportWidth, + child: SelectableRegion( + focusNode: focusNode, + selectionControls: materialTextSelectionControls, + child: RtWatcher((context, watch) { + final dependenciesList = + watch(nodesController.dependenciesList); + final length = dependenciesList.length; + + return ListView.custom( + key: listViewKey, + controller: scrollControllerY, + itemExtent: 24, + childrenDelegate: SliverChildBuilderDelegate( + (context, index) { + final node = dependenciesList.elementAt(index); + + return DependencyTile( + key: Key(node.key), + node: node, + onTap: () => nodesController.selectNode(node), + ); + }, + childCount: length, + ), + ); + }), + ), + ), + ); + }), + ), + ), + ); + }, + ); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/widgets/dependency_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/dependency_tile.dart new file mode 100644 index 00000000..6073d508 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/widgets/dependency_tile.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/widgets/instance_title.dart'; +import 'package:reactter_devtools_extension/src/widgets/tile_builder.dart'; + +class DependencyTile extends StatelessWidget { + final Node node; + final void Function()? onTap; + + const DependencyTile({ + super.key, + required this.node, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return RtWatcher((context, watch) { + final isSelected = watch(node.uIsSelected).value; + + return TreeNodeTileBuilder( + treeNode: node, + title: NodeTileTitle(node: node), + isSelected: isSelected, + onTap: onTap, + ); + }); + } +} + +class NodeTileTitle extends StatelessWidget { + final Node node; + + const NodeTileTitle({ + super.key, + required this.node, + }); + + @override + Widget build(BuildContext context) { + return RtWatcher((context, watch) { + final info = watch(node.uInfo).value; + final dependencyRef = info?.dependencyRef; + + return InstanceTitle( + nKey: node.key, + type: node.type, + kind: node.kind, + label: node.label, + isDependency: dependencyRef != null, + ); + }); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/widgets/instance_icon.dart b/packages/reactter_devtools_extension/lib/src/widgets/instance_icon.dart index a12d77fc..a4014c25 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/instance_icon.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/instance_icon.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:reactter_devtools_extension/src/data/constants.dart'; +import 'package:reactter_devtools_extension/src/constants.dart'; class InstanceIcon extends StatelessWidget { final String kind; @@ -16,6 +16,7 @@ class InstanceIcon extends StatelessWidget { final nodeKind = NodeKind.getKind(kind); if (nodeKind == null) return const SizedBox(); + return SizedBox.square( dimension: 24, child: Padding( diff --git a/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart index 4ca4ba0d..f4456e81 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:reactter_devtools_extension/src/interfaces/node.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; import 'package:reactter_devtools_extension/src/widgets/instance_title.dart'; import 'package:reactter_devtools_extension/src/widgets/tile_builder.dart'; class NodeTile extends StatelessWidget { - final INode node; + final Node node; final void Function()? onTap; const NodeTile({ @@ -30,7 +30,7 @@ class NodeTile extends StatelessWidget { } class NodeTileTitle extends StatelessWidget { - final INode node; + final Node node; const NodeTileTitle({ super.key, diff --git a/packages/reactter_devtools_extension/lib/src/widgets/nodes_list.dart b/packages/reactter_devtools_extension/lib/src/widgets/nodes_list.dart index 26d394b7..3d8f0b00 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/nodes_list.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/nodes_list.dart @@ -10,9 +10,9 @@ class NodesList extends StatelessWidget { @override Widget build(BuildContext context) { final nodesController = context.use(); - final listViewKey = nodesController.listViewKey; + final listViewKey = nodesController.nodesListViewKey; final scrollControllerX = ScrollController(); - final scrollControllerY = nodesController.scrollControllerY; + final scrollControllerY = nodesController.nodesListScrollControllerY; final focusNode = FocusNode(); return LayoutBuilder( @@ -26,42 +26,49 @@ class NodesList extends StatelessWidget { controller: scrollControllerX, scrollDirection: Axis.horizontal, child: Material( - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 1000.00), - child: OffsetScrollbar( - isAlwaysShown: true, - axis: Axis.vertical, - controller: scrollControllerY, - offsetController: scrollControllerX, - offsetControllerViewportDimension: viewportWidth, - child: SelectableRegion( - focusNode: focusNode, - selectionControls: materialTextSelectionControls, - child: RtWatcher((context, watch) { - final length = watch(nodesController.nodesList).length; + child: RtWatcher((context, watch) { + final maxDepth = watch(nodesController.uMaxDepth).value; + final minWith = 400 + (24.0 * maxDepth); - return ListView.custom( - key: listViewKey, - controller: scrollControllerY, - itemExtent: 24, - childrenDelegate: SliverChildBuilderDelegate( - (context, index) { - final node = - nodesController.nodesList.elementAt(index); + return ConstrainedBox( + constraints: BoxConstraints( + maxWidth: viewportWidth > minWith ? viewportWidth : minWith, + ), + child: OffsetScrollbar( + isAlwaysShown: true, + axis: Axis.vertical, + controller: scrollControllerY, + offsetController: scrollControllerX, + offsetControllerViewportDimension: viewportWidth, + child: SelectableRegion( + focusNode: focusNode, + selectionControls: materialTextSelectionControls, + child: RtWatcher((context, watch) { + final nodesList = watch(nodesController.nodesList); + final length = nodesList.length; + + return ListView.custom( + key: listViewKey, + controller: scrollControllerY, + itemExtent: 24, + childrenDelegate: SliverChildBuilderDelegate( + (context, index) { + final node = nodesList.elementAt(index); - return NodeTile( - key: Key(node.key), - node: node, - onTap: () => nodesController.selectNode(node), - ); - }, - childCount: length, - ), - ); - }), + return NodeTile( + key: Key(node.key), + node: node, + onTap: () => nodesController.selectNode(node), + ); + }, + childCount: length, + ), + ); + }), + ), ), - ), - ), + ); + }), ), ), ); diff --git a/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart index 2f00e5bf..6c2ad7b1 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart @@ -1,8 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; import 'package:reactter_devtools_extension/src/controllers/nodes_controller.dart'; -import 'package:reactter_devtools_extension/src/data/constants.dart'; -import 'package:reactter_devtools_extension/src/data/property_node.dart'; +import 'package:reactter_devtools_extension/src/constants.dart'; +import 'package:reactter_devtools_extension/src/bases/property_node.dart'; +import 'package:reactter_devtools_extension/src/nodes/property/property_async_node.dart'; import 'package:reactter_devtools_extension/src/widgets/instance_title.dart'; import 'package:reactter_devtools_extension/src/widgets/loading.dart'; import 'package:reactter_devtools_extension/src/widgets/tile_builder.dart'; @@ -13,7 +14,7 @@ class PropertyTile extends StatelessWidget { required this.propertyNode, }); - final IPropertyNode propertyNode; + final PropertyNode propertyNode; @override Widget build(BuildContext context) { diff --git a/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart b/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart index 01e7d271..7f029af9 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:reactter_devtools_extension/src/data/tree_node.dart'; +import 'package:reactter_devtools_extension/src/bases/tree_node.dart'; class TreeNodeTileBuilder extends StatelessWidget { final TreeNode treeNode; @@ -32,16 +32,21 @@ class TreeNodeTileBuilder extends StatelessWidget { title: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - ...List.generate( - treeNode.depth.toInt() - 1, - (index) => Container( - padding: EdgeInsets.only( - left: 10, - right: (index == treeNode.depth.toInt() - 2) ? 0 : 0, + RtWatcher((context, watch) { + final depth = watch(treeNode.uDepth).value; + + return Row( + children: List.generate( + depth.toInt(), + (index) => Container( + padding: const EdgeInsets.only( + left: 11, + ), + child: const VerticalDivider(width: 1), + ), ), - child: const VerticalDivider(width: 1), - ), - ), + ); + }), TileExpandable(treeNode: treeNode), title, ], From 48f041eef2d37ee3a5052a37ad72ac737d3d99db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Thu, 28 Nov 2024 10:13:57 -0600 Subject: [PATCH 057/141] feat(devtools): Enhance node handling, and refactor structure --- packages/reactter/lib/src/devtools.dart | 80 ++-- .../lib/src/bases/async_node.dart | 33 ++ .../lib/src/bases/node.dart | 56 +-- .../lib/src/bases/node_info.dart | 38 +- .../lib/src/bases/property_node.dart | 13 - .../lib/src/bases/tree_list.dart | 1 + .../lib/src/bases/tree_node.dart | 14 +- .../lib/src/constants.dart | 36 +- .../controllers/node_details_controller.dart | 36 ++ .../lib/src/controllers/nodes_controller.dart | 174 ++++---- .../lib/src/nodes/dart/null_node.dart | 26 ++ .../src/nodes/dart/plain_instance_node.dart | 114 +++++ .../src/nodes/dependency/dependency_info.dart | 16 +- .../src/nodes/dependency/dependency_node.dart | 81 +--- .../lib/src/nodes/instance/instance_info.dart | 15 +- .../lib/src/nodes/instance/instance_node.dart | 51 ++- .../nodes/property/property_async_node.dart | 395 ------------------ .../src/nodes/property/propery_sync_node.dart | 29 -- .../lib/src/nodes/sentinel_node.dart | 30 +- .../lib/src/nodes/slot_node.dart | 27 +- .../lib/src/nodes/state/state_info.dart | 28 +- .../lib/src/nodes/state/state_node.dart | 112 ++--- .../lib/src/reactter_devtools_extension.dart | 37 +- .../lib/src/services/devtools_service.dart | 8 +- .../lib/src/utils/extensions.dart | 21 +- .../widgets/bidirectional_scroll_view.dart | 53 +++ .../lib/src/widgets/dependencies_list.dart | 86 ++-- .../lib/src/widgets/dependency_tile.dart | 15 +- .../lib/src/widgets/detail_node_list.dart | 52 +++ .../lib/src/widgets/detail_node_tile.dart | 77 ++++ .../lib/src/widgets/instance_icon.dart | 10 +- .../lib/src/widgets/instance_title.dart | 19 +- .../lib/src/widgets/node_tile.dart | 16 +- .../lib/src/widgets/nodes_list.dart | 94 ++--- .../lib/src/widgets/properties_list.dart | 74 ---- .../lib/src/widgets/property_tile.dart | 89 ---- 36 files changed, 846 insertions(+), 1210 deletions(-) create mode 100644 packages/reactter_devtools_extension/lib/src/bases/async_node.dart delete mode 100644 packages/reactter_devtools_extension/lib/src/bases/property_node.dart create mode 100644 packages/reactter_devtools_extension/lib/src/controllers/node_details_controller.dart create mode 100644 packages/reactter_devtools_extension/lib/src/nodes/dart/null_node.dart create mode 100644 packages/reactter_devtools_extension/lib/src/nodes/dart/plain_instance_node.dart delete mode 100644 packages/reactter_devtools_extension/lib/src/nodes/property/property_async_node.dart delete mode 100644 packages/reactter_devtools_extension/lib/src/nodes/property/propery_sync_node.dart create mode 100644 packages/reactter_devtools_extension/lib/src/widgets/bidirectional_scroll_view.dart create mode 100644 packages/reactter_devtools_extension/lib/src/widgets/detail_node_list.dart create mode 100644 packages/reactter_devtools_extension/lib/src/widgets/detail_node_tile.dart delete mode 100644 packages/reactter_devtools_extension/lib/src/widgets/properties_list.dart delete mode 100644 packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart diff --git a/packages/reactter/lib/src/devtools.dart b/packages/reactter/lib/src/devtools.dart index 12c5a8e5..328cfa60 100644 --- a/packages/reactter/lib/src/devtools.dart +++ b/packages/reactter/lib/src/devtools.dart @@ -297,38 +297,12 @@ class RtDevTools with RtStateObserver, RtDependencyObserver { return dependencyRef.instance; } - Map getInstanceInfo(Object instance) { - if (instance is Enum) { - return { - ..._InstanceNode.getInstanceInfo(instance), - 'type': instance.toString(), - }; - } - - if (instance is Iterable) { - return { - ..._InstanceNode.getInstanceInfo(instance), - 'fields': { - 'length': instance.length, - 'items': instance.toList(), - }, - }; - } + Map getDependencyInfo(String dependencyKey) { + final dependencyRef = _nodesByKey[dependencyKey]; - return _InstanceNode.getInstanceInfo(instance); - } + if (dependencyRef is! _DependencyNode) return {}; - String getPropertyValue(value) { - if (value is List) { - return getListString(value); - } - if (value is Map) { - return getMapString(value); - } - if (value is Set) { - return getSetString(value); - } - return value.toString(); + return dependencyRef.toJson(); } Map getPlainInstanceInfo(Object instance) { @@ -343,28 +317,25 @@ class RtDevTools with RtStateObserver, RtDependencyObserver { return getInstanceInfo(instance); } - String getListString(List data) { - var listString = data.toString(); - if (listString.length > 60) { - listString = '${listString.substring(0, 60)}...]'; + Map getInstanceInfo(Object instance) { + if (instance is Enum) { + return { + ..._InstanceNode.getInstanceInfo(instance), + 'type': instance.toString(), + }; } - return listString; - } - String getMapString(Map data) { - var mapString = data.toString(); - if (mapString.length > 60) { - mapString = '${mapString.substring(0, 60)}...}'; + if (instance is Iterable) { + return { + ..._InstanceNode.getInstanceInfo(instance), + 'fields': { + 'length': instance.length, + 'items': instance.toList(), + }, + }; } - return mapString; - } - String getSetString(Set data) { - var setString = data.toString(); - if (setString.length > 60) { - setString = '${setString.substring(0, 60)}...}'; - } - return setString; + return _InstanceNode.getInstanceInfo(instance); } } @@ -394,7 +365,7 @@ abstract class _Node extends LinkedListEntry<_Node> { return { 'key': key, 'dependencyId': dependencyId, - 'dependencyRef': dependencyRef?.hashCode.toString(), + 'dependencyKey': dependencyRef?.hashCode.toString(), }; } @@ -500,7 +471,7 @@ class _InstanceNode extends _Node { bool remove() { final json = toJson(); - if (json['dependencyRef'] == null) { + if (json['dependencyKey'] == null) { return super.remove(); } @@ -539,7 +510,7 @@ class _StateNode extends _Node { bool remove() { final json = toJson(); - if (json['dependencyRef'] == null) { + if (json['dependencyKey'] == null) { return super.remove(); } @@ -552,14 +523,15 @@ class _DependencyNode extends _Node { : super(instance: instance); static Map getInstanceInfo(DependencyRef instance) { + final dependencyRef = Rt.getDependencyRegisterByRef(instance); + return { 'kind': _NodeKind.dependency, 'key': instance.hashCode.toString(), 'type': instance.type.toString(), 'id': instance.id, - 'instanceKey': Rt.getDependencyRegisterByRef( - instance, - )?.instance.hashCode.toString(), + 'mode': dependencyRef?.mode.toString(), + 'instanceKey': dependencyRef?.instance.hashCode.toString(), }; } diff --git a/packages/reactter_devtools_extension/lib/src/bases/async_node.dart b/packages/reactter_devtools_extension/lib/src/bases/async_node.dart new file mode 100644 index 00000000..98df6150 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/bases/async_node.dart @@ -0,0 +1,33 @@ +import 'package:flutter_reactter/reactter.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/bases/node_info.dart'; +import 'package:vm_service/vm_service.dart'; + +abstract base class AsyncNode extends Node { + final InstanceRef instanceRef; + final uIsLoading = UseState(false); + + AsyncNode({ + required super.key, + required this.instanceRef, + required super.kind, + }); + + Future getNodeInfo(); + + Future loadNodeInfo() async { + await Rt.batch(() async { + uIsLoading.value = true; + uInfo.value = await getNodeInfo(); + uIsLoading.value = false; + }); + } + + @override + Future loadNode() async { + await Future.wait([ + super.loadNode(), + loadNodeInfo(), + ]); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/bases/node.dart b/packages/reactter_devtools_extension/lib/src/bases/node.dart index ec53e9f9..b2db8226 100644 --- a/packages/reactter_devtools_extension/lib/src/bases/node.dart +++ b/packages/reactter_devtools_extension/lib/src/bases/node.dart @@ -2,28 +2,18 @@ import 'package:devtools_app_shared/service.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; import 'package:reactter_devtools_extension/src/bases/tree_node.dart'; import 'package:reactter_devtools_extension/src/bases/node_info.dart'; -import 'package:reactter_devtools_extension/src/bases/property_node.dart'; -import 'package:reactter_devtools_extension/src/bases/tree_list.dart'; -import 'package:reactter_devtools_extension/src/nodes/property/property_async_node.dart'; -import 'package:reactter_devtools_extension/src/services/eval_service.dart'; -import 'package:vm_service/vm_service.dart'; abstract base class Node extends TreeNode { final String key; final String kind; - final String type; - - String? get label; final uInfo = UseState(null); final uIsSelected = UseState(false); - final propertyNodes = TreeList(); final isAlive = Disposable(); Node({ required this.key, required this.kind, - required this.type, }); @override @@ -32,47 +22,15 @@ abstract base class Node extends TreeNode { super.dispose(); } - Future loadDetails() async { - await loadDependencyRef(); - } - - Future loadDependencyRef() async { - try { - final eval = await EvalService.devtoolsEval; - final dependencyKey = uInfo.value?.dependencyRef; - - final dependencyRefValue = await eval.evalInstance( - 'RtDevTools._instance?.getDependencyRef("$dependencyKey")', - isAlive: isAlive, - ); + Future> getDetails(); - PropertyAsyncNode? propertyNode; - - for (final node in propertyNodes) { - if (node.key == 'dependencyRef' && node is PropertyAsyncNode) { - propertyNode = node; - break; - } - } - - if (dependencyRefValue.kind == InstanceKind.kNull) { - propertyNode?.remove(); - return; - } + Future loadNode() async { + await Rt.batch(() async { + final nodes = await getDetails(); - if (propertyNode == null) { - propertyNodes.add( - PropertyAsyncNode( - key: 'dependencyRef', - valueRef: dependencyRefValue, - isExpanded: false, - ), - ); - } else { - propertyNode.updateValueRef(dependencyRefValue); + for (final node in nodes) { + addChild(node); } - } catch (e) { - print(e); - } + }); } } diff --git a/packages/reactter_devtools_extension/lib/src/bases/node_info.dart b/packages/reactter_devtools_extension/lib/src/bases/node_info.dart index c99bfbc0..5ce3d37f 100644 --- a/packages/reactter_devtools_extension/lib/src/bases/node_info.dart +++ b/packages/reactter_devtools_extension/lib/src/bases/node_info.dart @@ -1,5 +1,37 @@ -abstract base class NodeInfo { - final String? dependencyRef; +import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/constants.dart'; - const NodeInfo({this.dependencyRef}); +base class NodeInfo { + final Node node; + final NodeKind? nodeKind; + final String? type; + final String? identify; + final String? identityHashCode; + final String? value; + + const NodeInfo( + this.node, { + this.nodeKind, + this.type, + this.identify, + this.identityHashCode, + this.value, + }); + + NodeInfo copyWith({ + NodeKind? nodeKind, + String? type, + String? identify, + String? identityHashCode, + String? value, + }) { + return NodeInfo( + node, + nodeKind: nodeKind ?? this.nodeKind, + type: type ?? this.type, + identify: identify ?? this.identify, + identityHashCode: identityHashCode ?? this.identityHashCode, + value: value ?? this.value, + ); + } } diff --git a/packages/reactter_devtools_extension/lib/src/bases/property_node.dart b/packages/reactter_devtools_extension/lib/src/bases/property_node.dart deleted file mode 100644 index cf4dd2e8..00000000 --- a/packages/reactter_devtools_extension/lib/src/bases/property_node.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:flutter_reactter/reactter.dart'; -import 'package:reactter_devtools_extension/src/bases/tree_node.dart'; - -abstract base class PropertyNode extends TreeNode { - final String key; - final uValue = UseState(null); - final uInstanceInfo = UseState?>(null); - - PropertyNode({required this.key, String? value, bool isExpanded = false}) { - uValue.value = value; - uIsExpanded.value = isExpanded; - } -} diff --git a/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart b/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart index 9bd7240c..8a23e6e8 100644 --- a/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart +++ b/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart @@ -32,6 +32,7 @@ base class TreeList> extends LinkedList @override void clear() { super.clear(); + uMaxDepth.value = 0; if (!isDisposed) notify(); } } diff --git a/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart b/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart index a1f73f93..decfa6bf 100644 --- a/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart +++ b/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart @@ -1,10 +1,11 @@ import 'dart:collection'; import 'package:flutter_reactter/reactter.dart'; +import 'package:reactter_devtools_extension/src/bases/tree_list.dart'; abstract base class TreeNode> extends LinkedListEntry with RtStateBase, RtContext { final uChildren = UseState(LinkedHashSet()); - final uIsExpanded = UseState(true); + final uIsExpanded = UseState(false); final uDepth = UseState(0); E? _parent; @@ -21,6 +22,17 @@ abstract base class TreeNode> extends LinkedListEntry TreeNode() { UseEffect(_onIsExpandedChanged, [uIsExpanded]); + UseEffect(() { + if (list is! TreeList) return; + + final rList = list as TreeList; + final nodeDepth = uDepth.value; + final listMaxDepth = rList.uMaxDepth.value; + + if (listMaxDepth >= nodeDepth) return; + + rList.uMaxDepth.value = nodeDepth; + }, [uDepth]); if (list != null) bind(list!); } diff --git a/packages/reactter_devtools_extension/lib/src/constants.dart b/packages/reactter_devtools_extension/lib/src/constants.dart index 790fa39a..fa9a8368 100644 --- a/packages/reactter_devtools_extension/lib/src/constants.dart +++ b/packages/reactter_devtools_extension/lib/src/constants.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; const int kMaxValueLength = 50; const double kNodeTileHeight = 24; @@ -20,45 +21,60 @@ enum NodeType { } } +abstract class NodeKindKey { + static const String dependency = 'dependency'; + static const String instance = 'instance'; + static const String state = 'state'; + static const String hook = 'hook'; + static const String signal = 'signal'; +} + enum NodeKind { dependency( - key: 'dependency', + type: NodeType.dependency, + key: NodeKindKey.dependency, label: 'Dependency', abbr: 'D', color: Colors.teal, ), instance( - key: 'instance', + type: NodeType.instance, + key: NodeKindKey.instance, label: 'Instance', abbr: 'I', color: Colors.orange, ), state( - key: 'state', + type: NodeType.state, + key: NodeKindKey.state, label: 'State', abbr: 'S', color: Colors.blue, ), hook( - key: 'hook', + type: NodeType.state, + key: NodeKindKey.hook, label: 'Hook', abbr: 'H', color: Colors.purple, ), signal( - key: 'signal', + type: NodeType.state, + key: NodeKindKey.signal, label: 'Signal', abbr: 'S', color: Colors.green, ); const NodeKind({ + required this.type, required this.key, required this.label, required this.abbr, required this.color, }); + final NodeType type; final String key; final String label; final String abbr; @@ -66,15 +82,15 @@ enum NodeKind { static NodeKind? getKind(String kind) { switch (kind) { - case 'dependency': + case NodeKindKey.dependency: return dependency; - case 'instance': + case NodeKindKey.instance: return instance; - case 'state': + case NodeKindKey.state: return state; - case 'hook': + case NodeKindKey.hook: return hook; - case 'signal': + case NodeKindKey.signal: return signal; default: return null; diff --git a/packages/reactter_devtools_extension/lib/src/controllers/node_details_controller.dart b/packages/reactter_devtools_extension/lib/src/controllers/node_details_controller.dart new file mode 100644 index 00000000..03840c2d --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/controllers/node_details_controller.dart @@ -0,0 +1,36 @@ +import 'package:flutter_reactter/reactter.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/bases/tree_list.dart'; + +class NodeDetailsController { + final uCurrentNode = UseState(null); + final detailNodeList = TreeList(); + + Future loadDetails(Node node) async { + await Rt.batch(() async { + uCurrentNode.value = node; + + final detailNodes = await node.getDetails(); + + detailNodeList.clear(); + detailNodeList.addAll(detailNodes); + + for (var detailNode in detailNodeList) { + detailNode.uIsExpanded.value = true; + } + }); + } + + void reloadDetails() async { + final node = uCurrentNode.value; + + if (node == null) return; + + await loadDetails(node); + } + + void resetState() { + uCurrentNode.value = null; + detailNodeList.clear(); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart b/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart index 25bef8cc..1fd41f9e 100644 --- a/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart +++ b/packages/reactter_devtools_extension/lib/src/controllers/nodes_controller.dart @@ -1,34 +1,31 @@ import 'dart:async'; import 'dart:collection'; -import 'dart:math'; import 'package:devtools_extensions/devtools_extensions.dart'; import 'package:flutter/material.dart'; import 'package:flutter_reactter/reactter.dart'; +import 'package:reactter_devtools_extension/src/controllers/node_details_controller.dart'; +import 'package:reactter_devtools_extension/src/nodes/instance/instance_info.dart'; +import 'package:reactter_devtools_extension/src/nodes/instance/instance_node.dart'; import 'package:reactter_devtools_extension/src/constants.dart'; import 'package:reactter_devtools_extension/src/nodes/dependency/dependency_info.dart'; import 'package:reactter_devtools_extension/src/nodes/dependency/dependency_node.dart'; - -import 'package:reactter_devtools_extension/src/nodes/instance/instance_info.dart'; -import 'package:reactter_devtools_extension/src/nodes/instance/instance_node.dart'; import 'package:reactter_devtools_extension/src/nodes/sentinel_node.dart'; import 'package:reactter_devtools_extension/src/nodes/slot_node.dart'; -import 'package:reactter_devtools_extension/src/nodes/state/state_info.dart'; import 'package:reactter_devtools_extension/src/bases/tree_list.dart'; import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/nodes/state/state_info.dart'; import 'package:reactter_devtools_extension/src/services/devtools_service.dart'; import 'package:reactter_devtools_extension/src/nodes/state/state_node.dart'; import 'package:vm_service/vm_service.dart'; class NodesController { StreamSubscription? extEventSubscription; - + final uNodeDetailsController = UseDependency(); final devtoolsSevices = DevtoolsService(); final nodesList = TreeList(); final dependenciesList = TreeList(); final uNodes = UseState(LinkedHashMap()); - final uCurrentNodeKey = UseState(null); - final uMaxDepth = UseState(0); final nodesListViewKey = GlobalKey(); final dependenciesListViewKey = GlobalKey(); @@ -36,7 +33,10 @@ class NodesController { final nodesListScrollControllerY = ScrollController(); final dependencyListScrollControllerY = ScrollController(); - Node? get currentNode => uNodes.value[uCurrentNodeKey.value]; + NodeDetailsController get nodeDetailsController => + uNodeDetailsController.instance!; + Node? get uCurrentNode => nodeDetailsController.uCurrentNode.value; + String? get currentNodeKey => uCurrentNode?.key; NodesController() { init(); @@ -60,13 +60,13 @@ class NodesController { switch (event.extensionKind) { case 'ext.reactter.onStateCreated': final Map state = eventData['state']; - addNodeByMapInfo(state); + addNodeByData(state); break; case 'ext.reactter.onStateBound': final Map state = eventData['state']; final Map instance = eventData['instance']; - addNodeByMapInfo(state); - addNodeByMapInfo(instance); + addNodeByData(state); + addNodeByData(instance); break; case 'ext.reactter.onStateUnbound': final String instanceKey = eventData['instanceKey']; @@ -75,7 +75,7 @@ class NodesController { break; case 'ext.reactter.onStateUpdated': final String stateKey = eventData['stateKey']; - if (uCurrentNodeKey.value == stateKey) currentNode?.loadDetails(); + if (currentNodeKey == stateKey) nodeDetailsController.reloadDetails(); break; case 'ext.reactter.onStateDisposed': final String stateKey = eventData['stateKey']; @@ -84,13 +84,13 @@ class NodesController { break; case 'ext.reactter.onDependencyRegistered': final Map dependency = eventData['dependency']; - addNodeByMapInfo(dependency); + addNodeByData(dependency); break; case 'ext.reactter.onDependencyCreated': final Map dependency = eventData['dependency']; final Map instance = eventData['instance']; - addNodeByMapInfo(dependency); - addNodeByMapInfo(instance); + addNodeByData(dependency); + addNodeByData(instance); break; case 'ext.reactter.onDependencyDeleted': final String instanceKey = eventData['instanceKey']; @@ -115,8 +115,7 @@ class NodesController { nodesList.clear(); dependenciesList.clear(); uNodes.value.clear(); - uCurrentNodeKey.value = null; - uMaxDepth.value = 0; + nodeDetailsController.resetState(); } void selectNodeByKey(String nodeKey) { @@ -148,89 +147,94 @@ class NodesController { } Future addNodeByKey(String nodeKey, [Map fallback = const {}]) async { - final nodeInfo = await devtoolsSevices.getNodeBykey(nodeKey, fallback); - addNodeByMapInfo(nodeInfo); + final dataNode = await devtoolsSevices.getNodeBykey(nodeKey, fallback); + addNodeByData(dataNode); } - void addNodes(List nodesInfo) { + void addNodes(List dataNodes) { Rt.batch(() { - for (final nodeInfo in nodesInfo) { - addNodeByMapInfo(nodeInfo); + for (final dataNode in dataNodes) { + addNodeByData(dataNode); } }); } - void addNodeByMapInfo(Map nodeInfo) { - final kind = nodeInfo['kind']; - final key = nodeInfo['key']; - final type = nodeInfo['type']; - final error = nodeInfo['error']; - final dependencyRef = nodeInfo['dependencyRef']; + void addNodeByData(Map dataNode) { + final kind = dataNode['kind']; + final key = dataNode['key']; + final type = dataNode['type']; + final error = dataNode['error']; + final dependencyKey = dataNode['dependencyKey']; if (error is SentinelException) { final nodePrev = uNodes.value[key]; - final node = nodePrev ?? - SentinelNode( - key: key, - kind: kind, - type: type ?? 'Unknown', - ); + final node = nodePrev ?? SentinelNode(key: key); if (kind == 'dependency') { - addDependencyNode(node); + addDependencyNode(node as DependencyNode); } else { - addNode(node); + addNode(node as InstanceNode); } return; } switch (kind) { - case 'dependency': + case NodeKindKey.dependency: final nodePrev = uNodes.value[key]; - final node = nodePrev is DependencyNode - ? nodePrev - : DependencyNode( - key: key, - kind: kind, - type: type, - ); + final node = + nodePrev is DependencyNode ? nodePrev : DependencyNode(key: key); node.uInfo.value = DependencyInfo( - id: nodeInfo['id'], + node, + type: dataNode['type'], + mode: dataNode['mode'], + identify: dataNode['id'], ); + node.uIsExpanded.value = true; addDependencyNode(node); break; - case 'state': - case 'hook': - case 'signal': + case NodeKindKey.instance: final nodePrev = uNodes.value[key]; - final node = nodePrev is StateNode - ? nodePrev - : StateNode( - key: key, - kind: kind, - type: type, - ); + final node = + nodePrev is InstanceNode ? nodePrev : InstanceNode(key: key); - final debugLabel = nodeInfo['debugLabel']; - final boundInstanceKey = nodeInfo['boundInstanceKey']; + node.uInfo.value = InstanceInfo( + node, + type: type, + identityHashCode: key, + dependencyKey: dependencyKey, + ); + node.uIsExpanded.value = true; + + addNode(node); + + break; + case NodeKindKey.state: + case NodeKindKey.hook: + case NodeKindKey.signal: + final nodePrev = uNodes.value[key]; + final node = nodePrev is StateNode ? nodePrev : StateNode(key: key); + + final debugLabel = dataNode['debugLabel']; + final boundInstanceKey = dataNode['boundInstanceKey']; node.uInfo.value = StateInfo( - dependencyRef: dependencyRef, - debugLabel: debugLabel, + node, + nodeKind: NodeKind.getKind(kind), + type: type, + identify: debugLabel, + identityHashCode: key, boundInstanceKey: boundInstanceKey, + dependencyKey: dependencyKey, ); + node.uIsExpanded.value = true; if (boundInstanceKey != null) { final boundInstanceNodePrev = uNodes.value[boundInstanceKey]; - final boundInstanceNode = boundInstanceNodePrev ?? - SlotNode( - key: boundInstanceKey, - kind: kind, - type: 'Unknown', - ); + final boundInstanceNode = + boundInstanceNodePrev ?? SlotNode(key: boundInstanceKey); addNode(boundInstanceNode); @@ -239,23 +243,6 @@ class NodesController { addNode(node); - break; - case 'instance': - final nodePrev = uNodes.value[key]; - final node = nodePrev is InstanceNode - ? nodePrev - : InstanceNode( - key: key, - kind: kind, - type: type, - ); - - node.uInfo.value = InstanceInfo( - dependencyRef: dependencyRef, - ); - - addNode(node); - break; } } @@ -270,13 +257,14 @@ class NodesController { if (node is SlotNode) return; - if (node.list == null) nodesList.add(node); + if (node.list == null) { + nodesList.add(node); + } nodePrev?.replaceFor(node); - uMaxDepth.value = max(uMaxDepth.value, node.uDepth.value); } - void addDependencyNode(Node node) { + void addDependencyNode(DependencyNode node) { uNodes.value[node.key] = node; uNodes.notify(); if (node.list == null) dependenciesList.add(node); @@ -285,8 +273,8 @@ class NodesController { void removeNodeByKey(String nodeKey) { devtoolsSevices.disposeNodeByKey(nodeKey); - if (uCurrentNodeKey.value == nodeKey) { - uCurrentNodeKey.value = null; + if (currentNodeKey == nodeKey) { + nodeDetailsController.resetState(); } uNodes @@ -296,10 +284,12 @@ class NodesController { } Future selectNode(Node node) async { - currentNode?.uIsSelected.value = false; - uCurrentNodeKey.value = node.key; - node.uIsSelected.value = true; - node.loadDetails(); + if (uCurrentNode != node) { + uCurrentNode?.uIsSelected.value = false; + node.uIsSelected.value = true; + } + + nodeDetailsController.loadDetails(node); } } diff --git a/packages/reactter_devtools_extension/lib/src/nodes/dart/null_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/dart/null_node.dart new file mode 100644 index 00000000..18b5c92a --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/nodes/dart/null_node.dart @@ -0,0 +1,26 @@ +import 'package:flutter_reactter/reactter.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/bases/node_info.dart'; +import 'package:vm_service/vm_service.dart'; + +final class NullNode extends Node { + NullNode.$({required super.key}) : super(kind: InstanceKind.kNull) { + uInfo.value = NodeInfo( + this, + type: 'null', + identify: 'null', + value: 'null', + ); + } + + factory NullNode({required String key}) { + return Rt.createState( + () => NullNode.$(key: key), + ); + } + + @override + Future> getDetails() async { + return []; + } +} diff --git a/packages/reactter_devtools_extension/lib/src/nodes/dart/plain_instance_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/dart/plain_instance_node.dart new file mode 100644 index 00000000..b585acef --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/nodes/dart/plain_instance_node.dart @@ -0,0 +1,114 @@ +import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/bases/async_node.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/bases/node_info.dart'; +import 'package:reactter_devtools_extension/src/constants.dart'; +import 'package:reactter_devtools_extension/src/nodes/dependency/dependency_info.dart'; +import 'package:reactter_devtools_extension/src/nodes/instance/instance_info.dart'; +import 'package:reactter_devtools_extension/src/nodes/state/state_info.dart'; +import 'package:reactter_devtools_extension/src/services/eval_service.dart'; +import 'package:reactter_devtools_extension/src/utils/extensions.dart'; +import 'package:vm_service/vm_service.dart'; + +base class PlainInstanceNode extends AsyncNode { + PlainInstanceNode._({required super.key, required super.instanceRef}) + : super(kind: InstanceKind.kPlainInstance); + + factory PlainInstanceNode({ + required String key, + required InstanceRef instanceRef, + }) { + return Rt.createState( + () => PlainInstanceNode._( + key: key, + instanceRef: instanceRef, + ), + ); + } + + @override + Future getNodeInfo() async { + final eval = await EvalService.devtoolsEval; + final instanceInfo = await EvalService.evalsQueue.add( + () => eval.evalInstance( + 'RtDevTools._instance?.getPlainInstanceInfo(instance)', + isAlive: isAlive, + scope: {'instance': instanceRef.id!}, + ), + ); + + if (instanceInfo.kind != InstanceKind.kMap) return null; + + final instanceInfoMap = await instanceInfo.evalValue(isAlive, 2); + + if (instanceInfoMap is! Map) return null; + + final String kind = instanceInfoMap['kind']; + final String key = instanceInfoMap['key']; + final String type = instanceInfoMap['type']; + final String? id = instanceInfoMap['id']; + final String? debugLabel = instanceInfoMap['debugLabel']; + final String? identify = id ?? debugLabel; + final String value = + identify != null ? "$type($identify) #$key" : "$type #$key"; + final nodeKind = NodeKind.getKind(kind)!; + + switch (nodeKind) { + case NodeKind.dependency: + final mode = instanceInfoMap['mode']; + + return DependencyInfo( + this, + type: type, + identify: id, + mode: mode, + ); + case NodeKind.instance: + final dependencyKey = instanceInfoMap['dependencyKey']; + + return InstanceInfo( + this, + type: type, + identityHashCode: key, + dependencyKey: dependencyKey, + ); + case NodeKind.state: + case NodeKind.hook: + case NodeKind.signal: + return StateInfo( + this, + nodeKind: NodeKind.getKind(kind)!, + type: type, + identify: debugLabel, + identityHashCode: key, + value: value, + debugLabel: debugLabel, + ); + default: + return NodeInfo( + this, + nodeKind: NodeKind.getKind(kind), + type: type, + identityHashCode: key, + value: value, + ); + } + } + + @override + Future>> getDetails() async { + final instance = await instanceRef.safeGetInstance(isAlive); + final fields = instance?.fields?.cast() ?? []; + final nodes = fields.map((field) { + if (field.value is! InstanceRef) return null; + + if (field.name.startsWith('_') || field.name.startsWith('\$')) { + return null; + } + + return (field.value as InstanceRef).getNode(field.name); + }).toList(); + + return nodes.whereType().toList(); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/nodes/dependency/dependency_info.dart b/packages/reactter_devtools_extension/lib/src/nodes/dependency/dependency_info.dart index 8929119a..846320a3 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/dependency/dependency_info.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/dependency/dependency_info.dart @@ -1,7 +1,15 @@ -import 'package:reactter_devtools_extension/src/bases/node_info.dart'; +import 'package:reactter_devtools_extension/src/constants.dart'; +import 'package:reactter_devtools_extension/src/nodes/instance/instance_info.dart'; -final class DependencyInfo extends NodeInfo { - final String? id; +final class DependencyInfo extends InstanceInfo { + final String? mode; - DependencyInfo({required this.id}); + DependencyInfo( + super.node, { + this.mode, + super.type, + super.identify, + super.identityHashCode, + super.value, + }) : super(nodeKind: NodeKind.dependency); } diff --git a/packages/reactter_devtools_extension/lib/src/nodes/dependency/dependency_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/dependency/dependency_node.dart index ecb55d6b..e16f3cea 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/dependency/dependency_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/dependency/dependency_node.dart @@ -1,86 +1,15 @@ import 'package:flutter_reactter/reactter.dart'; import 'package:reactter_devtools_extension/src/nodes/dependency/dependency_info.dart'; -import 'package:reactter_devtools_extension/src/bases/node.dart'; -import 'package:reactter_devtools_extension/src/services/eval_service.dart'; -import 'package:vm_service/vm_service.dart'; +import 'package:reactter_devtools_extension/src/nodes/instance/instance_node.dart'; -final class DependencyNode extends Node { +final class DependencyNode extends InstanceNode { final uIsLoading = UseState(false); - @override - String? get label => uInfo.value?.id; + DependencyNode.$({required super.key}) : super.$(); - DependencyNode._({ - required super.key, - required super.kind, - required super.type, - }); - - factory DependencyNode({ - required String key, - required String kind, - required String type, - }) { + factory DependencyNode({required String key}) { return Rt.createState( - () => DependencyNode._( - key: key, - kind: kind, - type: type, - ), - ); - } - - Future _loadDependencyNode() async { - uIsLoading.value = true; - - final eval = await EvalService.devtoolsEval; - - final dependencyNode = await eval.evalInstance( - 'RtDevTools._instance?.getDependencyInfo("$key")', - isAlive: isAlive, + () => DependencyNode.$(key: key), ); - - assert(dependencyNode.kind == InstanceKind.kMap); - - final dependencyNodeAssociations = - dependencyNode.associations?.cast(); - - assert(dependencyNodeAssociations != null); - - String? type; - String? id; - - for (var element in dependencyNodeAssociations!) { - assert(element.key != null && element.value != null); - - final eKey = element.key!.valueAsString!; - final eValue = element.value!.valueAsString; - - if (element.value!.kind == InstanceKind.kNull) continue; - - if (eValue == null) continue; - - switch (eKey) { - case 'type': - type = eValue; - break; - case 'id': - id = eValue; - break; - } - } - - assert(type != null); - - uInfo.value = DependencyInfo( - id: id, - ); - - uIsLoading.value = false; - } - - @override - Future loadDetails() async { - // TODO: implement loadDetails } } diff --git a/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_info.dart b/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_info.dart index bbd35f3b..e9218bee 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_info.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_info.dart @@ -1,5 +1,16 @@ import 'package:reactter_devtools_extension/src/bases/node_info.dart'; +import 'package:reactter_devtools_extension/src/constants.dart'; -final class InstanceInfo extends NodeInfo { - const InstanceInfo({super.dependencyRef}); +base class InstanceInfo extends NodeInfo { + final String? dependencyKey; + + InstanceInfo( + super.node, { + super.nodeKind = NodeKind.instance, + super.type, + super.identify, + super.identityHashCode, + super.value, + this.dependencyKey, + }); } diff --git a/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart index 4906e108..80f19dbc 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart @@ -1,28 +1,39 @@ import 'package:flutter_reactter/reactter.dart'; -import 'package:reactter_devtools_extension/src/nodes/instance/instance_info.dart'; import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/bases/node_info.dart'; +import 'package:reactter_devtools_extension/src/nodes/instance/instance_info.dart'; +import 'package:reactter_devtools_extension/src/services/eval_service.dart'; +import 'package:reactter_devtools_extension/src/utils/extensions.dart'; +import 'package:vm_service/vm_service.dart'; -final class InstanceNode extends Node { - @override - final label = null; - - InstanceNode._({ - required super.key, - required super.kind, - required super.type, - }); +base class InstanceNode extends Node { + InstanceNode.$({required super.key}) + : super(kind: InstanceKind.kPlainInstance); - factory InstanceNode({ - required String key, - required String kind, - required String type, - }) { + factory InstanceNode({required String key}) { return Rt.createState( - () => InstanceNode._( - key: key, - kind: kind, - type: type, - ), + () => InstanceNode.$(key: key), ); } + + Future getDependency() async { + try { + final eval = await EvalService.devtoolsEval; + final dependencyKey = uInfo.value?.dependencyKey; + final dependencyRef = await eval.safeEval( + 'RtDevTools._instance?.getDependencyRef("$dependencyKey")', + isAlive: isAlive, + ); + return dependencyRef.getNode('dependency'); + } catch (e) { + print(e); + } + + return null; + } + + @override + Future>> getDetails() async => [ + await getDependency(), + ].whereType().toList(); } diff --git a/packages/reactter_devtools_extension/lib/src/nodes/property/property_async_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/property/property_async_node.dart deleted file mode 100644 index 0187878f..00000000 --- a/packages/reactter_devtools_extension/lib/src/nodes/property/property_async_node.dart +++ /dev/null @@ -1,395 +0,0 @@ -import 'dart:async'; -import 'dart:collection'; - -import 'package:devtools_app_shared/service.dart'; -import 'package:flutter_reactter/reactter.dart'; -import 'package:reactter_devtools_extension/src/bases/property_node.dart'; -import 'package:reactter_devtools_extension/src/constants.dart'; -import 'package:reactter_devtools_extension/src/nodes/property/propery_sync_node.dart'; -import 'package:reactter_devtools_extension/src/services/eval_service.dart'; -import 'package:reactter_devtools_extension/src/utils/extensions.dart'; -import 'package:vm_service/vm_service.dart'; - -final class PropertyAsyncNode extends PropertyNode { - InstanceRef _valueRef; - InstanceRef get valueRef => _valueRef; - - Disposable? _isAlive; - bool _isResolved = false; - bool _isValueUpdating = false; - final LinkedHashMap _childNodeRefs = - LinkedHashMap(); - - final uValueFuture = UseState?>(null); - final uIsLoading = UseState(false); - - PropertyAsyncNode._({ - required super.key, - required InstanceRef valueRef, - bool isExpanded = false, - }) : _valueRef = valueRef { - uIsExpanded.value = isExpanded; - } - - factory PropertyAsyncNode({ - PropertyAsyncNode? parent, - required String key, - required InstanceRef valueRef, - bool isExpanded = false, - }) { - return Rt.createState( - () => PropertyAsyncNode._( - key: key, - valueRef: valueRef, - isExpanded: isExpanded, - ), - ); - } - - Future updateValueRef(InstanceRef valueRef) async { - if (uValueFuture.value == null || _isValueUpdating) return; - - _isValueUpdating = true; - - await uValueFuture.value; - - uValueFuture.value = null; - _valueRef = valueRef; - _isResolved = false; - uIsLoading.value = false; - } - - Future getValueAsync() async { - if (uValueFuture.value != null) return uValueFuture.value!; - if (_isResolved) return Future.value(uValue.value); - if (uIsLoading.value) return Future.value(uValue.value); - - try { - return uValueFuture.value = Rt.batch(() async { - uIsLoading.value = true; - _isAlive = Disposable(); - String? value; - - try { - switch (valueRef.kind) { - case InstanceKind.kList: - value = await resolveValueByList(); - break; - case InstanceKind.kSet: - value = await resolveValueBySet(); - break; - case InstanceKind.kMap: - value = await resolveValueByMap(); - break; - case InstanceKind.kRecord: - value = await resolveValueByRecord(); - break; - case InstanceKind.kPlainInstance: - value = await resolveValueByPlainInstance(); - break; - case InstanceKind.kNull: - value = 'null'; - uChildren.value.clear(); - break; - case InstanceKind.kString: - value = '"${valueRef.valueAsString}"'; - uChildren.value.clear(); - break; - case InstanceKind.kClosure: - final instance = await valueRef.safeGetInstance(_isAlive); - - final children = SplayTreeMap(); - - final name = instance?.closureFunction?.name; - final location = instance?.closureFunction?.location?.script?.uri; - final locationLine = instance?.closureFunction?.location?.line; - final locationColumn = - instance?.closureFunction?.location?.column; - - children['location'] = location; - children['locationLine'] = locationLine; - children['locationColumn'] = locationColumn; - - await addChildren(children); - - uInstanceInfo.value = { - 'name': name, - 'key': valueRef.identityHashCode.toString(), - 'type': 'Function', - 'kind': 'closure', - ...children, - }; - - value = "Function($name) $location $locationLine:$locationColumn"; - - break; - default: - value = valueRef.valueAsString; - uChildren.value.clear(); - break; - } - } catch (e) { - value = null; - } - - value ??= 'Unknown - Cannot load value'; - - uIsLoading.value = false; - uValue.value = value; - - return value; - }); - } finally { - _isResolved = true; - _isValueUpdating = false; - } - } - - Future resolveValueByList() async { - assert(valueRef.kind == InstanceKind.kList); - - final instance = await valueRef.safeGetInstance(_isAlive); - final elements = instance?.elements?.cast() ?? []; - final SplayTreeMap children = SplayTreeMap( - (a, b) => int.parse(a).compareTo(int.parse(b)), - ); - - for (var i = 0; i < elements.length; i++) { - children[i.toString()] = elements[i]; - } - - await addChildren(children); - - return await resolveValueByChildren( - buildValue: (key, value) => "$value", - prefix: '[', - suffix: ']', - ); - } - - Future resolveValueBySet() async { - assert(valueRef.kind == InstanceKind.kSet); - - final instance = await valueRef.safeGetInstance(_isAlive); - final elements = instance?.elements?.cast() ?? []; - final SplayTreeMap children = SplayTreeMap( - (a, b) => int.parse(a).compareTo(int.parse(b)), - ); - - for (var i = 0; i < elements.length; i++) { - children[i.toString()] = elements[i]; - } - - await addChildren(children); - - return await resolveValueByChildren( - buildValue: (key, value) => '$value', - prefix: '{', - suffix: '}', - ); - } - - Future resolveValueByMap() async { - assert(valueRef.kind == InstanceKind.kMap); - - final instance = await valueRef.safeGetInstance(_isAlive); - final associations = instance?.associations?.cast() ?? []; - final SplayTreeMap children = SplayTreeMap(); - - for (final entry in associations) { - final keyRef = entry.key as InstanceRef; - final key = await keyRef.evalValueFirstLevel(_isAlive); - - children[key.toString()] = entry.value; - } - - await addChildren(children); - - return await resolveValueByChildren( - buildValue: (key, value) => '$key: $value', - prefix: '{', - suffix: '}', - ); - } - - Future resolveValueByRecord() async { - assert(valueRef.kind == InstanceKind.kRecord); - - final instance = await valueRef.safeGetInstance(_isAlive); - final fields = instance?.fields?.cast() ?? []; - final SplayTreeMap children = SplayTreeMap(); - - for (final field in fields) { - children[field.name.toString()] = field.value as InstanceRef; - } - - await addChildren(children); - - return await resolveValueByChildren( - buildValue: (key, value) => '$key: $value', - prefix: '(', - suffix: ')', - ); - } - - Future addChildren(SplayTreeMap children) async { - final childrenToRemove = _childNodeRefs.keys.toSet(); - - for (final child in children.entries) { - final childCurrent = _childNodeRefs[child.key]; - final isAsyncValue = child.value is InstanceRef; - final isValueTypeSame = - (childCurrent is PropertyAsyncNode && isAsyncValue) || - (childCurrent is PropertySyncNode && !isAsyncValue); - - if (!isValueTypeSame) _childNodeRefs.remove(child.key); - - final isRemoveSkip = - isValueTypeSame && childrenToRemove.remove(child.key); - - final childNode = _childNodeRefs.putIfAbsent( - child.key, - () { - if (isAsyncValue) { - return PropertyAsyncNode( - key: child.key, - valueRef: child.value, - ); - } - - return PropertySyncNode( - key: child.key, - value: child.value.toString(), - ); - }, - ); - - if (isRemoveSkip) { - if (childNode is PropertyAsyncNode) { - childNode.updateValueRef(child.value); - } else if (childNode is PropertySyncNode) { - childNode.updateValue(child.value.toString()); - } - } else { - addChild(childNode); - } - } - - for (final childKey in childrenToRemove) { - final childNode = _childNodeRefs.remove(childKey); - childNode?.remove(); - } - } - - Future resolveValueByChildren({ - required String Function(String key, String? value) buildValue, - String prefix = '{', - String suffix = '}', - }) async { - final children = uChildren.value.toList(); - final childrenValueBuffer = StringBuffer(); - var isFull = true; - - for (var i = 0; i < children.length; i++) { - final child = children[i]; - final isLast = i == children.length - 1; - - if (child is PropertySyncNode) continue; - - assert(child is PropertyAsyncNode); - - String? value; - - switch ((child as PropertyAsyncNode).valueRef.kind) { - case InstanceKind.kMap: - case InstanceKind.kSet: - value = '{...}'; - break; - case InstanceKind.kList: - value = '[...]'; - break; - default: - await child.getValueAsync(); - value = child.uValue.value; - break; - } - - childrenValueBuffer.write( - "${buildValue(child.key, value)}${isLast ? '' : ', '}", - ); - - if (childrenValueBuffer.length > kMaxValueLength) { - isFull = isLast; - break; - } - } - - final moreEllipsis = isFull ? '' : ', ...'; - final maxValueBufferLength = - kMaxValueLength - prefix.length - moreEllipsis.length - suffix.length; - final childrenValue = childrenValueBuffer.toString(); - final shouldBeCutted = childrenValue.length > maxValueBufferLength; - final cuttedEllipsis = shouldBeCutted ? '...' : ''; - final childrenValueCutted = shouldBeCutted - ? childrenValue.substring( - 0, - maxValueBufferLength - cuttedEllipsis.length, - ) - : childrenValue; - - return "$prefix$childrenValueCutted$cuttedEllipsis$moreEllipsis$suffix"; - } - - Future resolveValueByPlainInstance() async { - assert(valueRef.kind == InstanceKind.kPlainInstance); - - final eval = await EvalService.devtoolsEval; - final valueInfo = await EvalService.evalsQueue.add( - () => eval.evalInstance( - 'RtDevTools._instance?.getPlainInstanceInfo(value)', - isAlive: _isAlive, - scope: {'value': valueRef.id!}, - ), - ); - - if (valueInfo.kind != InstanceKind.kMap) return null; - - final valueInfoMap = await valueInfo.evalValue(_isAlive, 2); - - if (valueInfoMap is! Map) return null; - - uInstanceInfo.value = valueInfoMap.cast(); - - final String key = valueInfoMap['key']; - final String type = valueInfoMap['type']; - final String? id = valueInfoMap['id']; - final String? debugLabel = valueInfoMap['debugLabel']; - final Map? valueInfoFields = valueInfoMap['fields']; - final String? idOrDebugLabel = id ?? debugLabel; - - final instance = await valueRef.safeGetInstance(_isAlive); - final fields = instance?.fields?.cast() ?? []; - final SplayTreeMap children = SplayTreeMap(); - - for (final field in fields) { - if (field.value is InstanceRef) { - if (field.name.startsWith('_') || field.name.startsWith('\$')) { - continue; - } - children[field.name] = field.value as InstanceRef; - } - } - - if (valueInfoFields != null) { - for (final entry in valueInfoFields.entries) { - children[entry.key] = entry.value; - } - } - - await addChildren(children); - - return idOrDebugLabel != null - ? "$type($idOrDebugLabel) #$key" - : "$type #$key"; - } -} diff --git a/packages/reactter_devtools_extension/lib/src/nodes/property/propery_sync_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/property/propery_sync_node.dart deleted file mode 100644 index 394deca6..00000000 --- a/packages/reactter_devtools_extension/lib/src/nodes/property/propery_sync_node.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter_reactter/reactter.dart'; -import 'package:reactter_devtools_extension/src/bases/property_node.dart'; - -final class PropertySyncNode extends PropertyNode { - PropertySyncNode._({ - required super.key, - required super.value, - required super.isExpanded, - }); - - factory PropertySyncNode({ - PropertyNode? parent, - required String key, - required String value, - bool isExpanded = false, - }) { - return Rt.createState( - () => PropertySyncNode._( - key: key, - value: value, - isExpanded: isExpanded, - ), - ); - } - - void updateValue(String value) { - uValue.value = value; - } -} diff --git a/packages/reactter_devtools_extension/lib/src/nodes/sentinel_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/sentinel_node.dart index 83a9ff1a..2540ce61 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/sentinel_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/sentinel_node.dart @@ -3,31 +3,21 @@ import 'package:reactter_devtools_extension/src/bases/node.dart'; import 'package:reactter_devtools_extension/src/bases/node_info.dart'; final class SentinelNode extends Node { - @override - String? get label => 'Sentinel'; - - SentinelNode._({ - required super.key, - required super.kind, - required super.type, - }); + SentinelNode._({required super.key}) : super(kind: 'sentinel'); - factory SentinelNode({ - required String key, - required String kind, - required String type, - }) { + factory SentinelNode({required String key}) { return Rt.createState( - () => SentinelNode._( - key: key, - kind: kind, - type: type, - ), + () => SentinelNode._(key: key), ); } @override - Future loadDetails() async { - // TODO: implement loadDetails + Future loadNode() { + throw UnimplementedError(); + } + + @override + Future>> getDetails() { + throw UnimplementedError(); } } diff --git a/packages/reactter_devtools_extension/lib/src/nodes/slot_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/slot_node.dart index bf0bef3f..62f10d3d 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/slot_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/slot_node.dart @@ -3,27 +3,22 @@ import 'package:reactter_devtools_extension/src/bases/node.dart'; import 'package:reactter_devtools_extension/src/bases/node_info.dart'; final class SlotNode extends Node { - @override - final String? label = null; - - SlotNode._({required super.key, required super.kind, required super.type}); + SlotNode._({required super.key}) : super(kind: 'slot'); - factory SlotNode({ - required String key, - required String kind, - required String type, - }) { + factory SlotNode({required String key}) { return Rt.createState( - () => SlotNode._( - key: key, - kind: kind, - type: type, - ), + () => SlotNode._(key: key), ); } @override - Future loadDetails() async { - // TODO: implement loadDetails + Future loadNode() { + throw UnimplementedError(); + } + + @override + Future>> getDetails() { + // TODO: implement getDetails + throw UnimplementedError(); } } diff --git a/packages/reactter_devtools_extension/lib/src/nodes/state/state_info.dart b/packages/reactter_devtools_extension/lib/src/nodes/state/state_info.dart index 9dedef71..ec05f0fe 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/state/state_info.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/state/state_info.dart @@ -1,26 +1,18 @@ -import 'package:reactter_devtools_extension/src/bases/node_info.dart'; +import 'package:reactter_devtools_extension/src/nodes/instance/instance_info.dart'; -final class StateInfo extends NodeInfo { +final class StateInfo extends InstanceInfo { final String? debugLabel; final String? boundInstanceKey; - final List kinds; - StateInfo({ - super.dependencyRef, + StateInfo( + super.node, { + super.nodeKind, + super.type, + super.identify, + super.identityHashCode, + super.value, + super.dependencyKey, this.debugLabel, this.boundInstanceKey, - this.kinds = const ['RtState'], }); - - StateInfo copyWith({ - String? dependencyRef, - String? debugLabel, - String? boundInstanceKey, - }) { - return StateInfo( - dependencyRef: dependencyRef ?? this.dependencyRef, - debugLabel: debugLabel ?? this.debugLabel, - boundInstanceKey: boundInstanceKey ?? this.boundInstanceKey, - ); - } } diff --git a/packages/reactter_devtools_extension/lib/src/nodes/state/state_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/state/state_node.dart index c4c2e719..cfcd4eda 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/state/state_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/state/state_node.dart @@ -1,103 +1,57 @@ import 'package:flutter_reactter/flutter_reactter.dart'; import 'package:reactter_devtools_extension/src/bases/node.dart'; -import 'package:reactter_devtools_extension/src/nodes/property/property_async_node.dart'; +import 'package:reactter_devtools_extension/src/nodes/instance/instance_node.dart'; import 'package:reactter_devtools_extension/src/nodes/state/state_info.dart'; import 'package:reactter_devtools_extension/src/services/eval_service.dart'; +import 'package:reactter_devtools_extension/src/utils/extensions.dart'; -base class StateNode extends Node { +final class StateNode extends InstanceNode { final uIsLoading = UseState(false); - @override - String? get label => uInfo.value?.debugLabel; - - StateNode._({ - required super.key, - required super.kind, - required super.type, - }) { - UseEffect(loadDependencyRef, [uInfo]); - } + StateNode.$({required super.key}) : super.$(); - factory StateNode({ - required String key, - required String kind, - required String type, - }) { + factory StateNode({required String key}) { return Rt.createState( - () => StateNode._( - key: key, - kind: kind, - type: type, - ), + () => StateNode.$(key: key), ); } @override - Future loadDetails() async { - await Future.wait([ - super.loadDetails(), - loadBoundInstance(), - loadDebugInfo(), + Future> getDetails() async { + final instanceRefs = await Future.wait([ + getDependency(), + getBoundInstance(), + getDebugInfo(), ]); - } - - Future loadBoundInstance() async { - final eval = await EvalService.devtoolsEval; - - final boundInstanceValue = await eval.evalInstance( - 'RtDevTools._instance?.getBoundInstance("$key")', - isAlive: isAlive, - ); - - PropertyAsyncNode? propertyNode; - for (final node in propertyNodes) { - if (node.key == 'boundInstance' && node is PropertyAsyncNode) { - propertyNode = node; - break; - } - } + return instanceRefs.whereType().toList(); + } - if (propertyNode == null) { - propertyNodes.add( - PropertyAsyncNode( - key: 'boundInstance', - valueRef: boundInstanceValue, - isExpanded: false, - ), + Future getBoundInstance() async { + try { + final eval = await EvalService.devtoolsEval; + final boundInstanceRef = await eval.evalInstance( + 'RtDevTools._instance?.getBoundInstance("$key")', + isAlive: isAlive, ); - } else { - propertyNode.updateValueRef(boundInstanceValue); + return boundInstanceRef.getNode('boundInstance'); + } catch (e) { + print(e); } + return null; } - Future loadDebugInfo() async { - final eval = await EvalService.devtoolsEval; - - final debugInfoValue = await eval.evalInstance( - 'RtDevTools._instance?.getDebugInfo("$key")', - isAlive: isAlive, - ); - - PropertyAsyncNode? propertyNode; - - for (final node in propertyNodes) { - if (node.key == 'debugInfo' && node is PropertyAsyncNode) { - propertyNode = node; - break; - } - } - - if (propertyNode == null) { - propertyNodes.add( - PropertyAsyncNode( - key: 'debugInfo', - valueRef: debugInfoValue, - isExpanded: true, - ), + Future getDebugInfo() async { + try { + final eval = await EvalService.devtoolsEval; + final debugInfoRef = await eval.evalInstance( + 'RtDevTools._instance?.getDebugInfo("$key")', + isAlive: isAlive, ); - } else { - propertyNode.updateValueRef(debugInfoValue); + return debugInfoRef.getNode('debugInfo'); + } catch (e) { + print(e); } + return null; } } diff --git a/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart b/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart index 88cb7957..4d059967 100644 --- a/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart +++ b/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart @@ -1,10 +1,12 @@ import 'package:devtools_app_shared/ui.dart'; import 'package:flutter/material.dart' hide Split; import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/controllers/node_details_controller.dart'; +import 'package:reactter_devtools_extension/src/nodes/instance/instance_info.dart'; import 'package:reactter_devtools_extension/src/widgets/dependencies_list.dart'; +import 'package:reactter_devtools_extension/src/widgets/detail_node_list.dart'; import 'package:reactter_devtools_extension/src/widgets/instance_title.dart'; import 'package:reactter_devtools_extension/src/widgets/nodes_list.dart'; -import 'package:reactter_devtools_extension/src/widgets/properties_list.dart'; import 'controllers/nodes_controller.dart'; class RtDevToolsExtension extends StatelessWidget { @@ -12,9 +14,12 @@ class RtDevToolsExtension extends StatelessWidget { @override Widget build(BuildContext context) { - return RtProvider( - () => NodesController(), - builder: (context, inst, child) { + return RtMultiProvider( + [ + RtProvider(() => NodeDetailsController()), + RtProvider(() => NodesController()), + ], + builder: (context, child) { return SplitPane( axis: SplitPane.axisFor(context, 1.2), initialFractions: const [0.55, 0.45], @@ -50,17 +55,19 @@ class RtDevToolsExtension extends StatelessWidget { child: Column( children: [ RtWatcher((context, watch) { - final nKey = watch(inst.uCurrentNodeKey).value; - final selectedNode = inst.currentNode; + final nodeDetailsController = + context.use(); + final selectedNode = + watch(nodeDetailsController.uCurrentNode).value; - if (nKey == null) { + if (selectedNode == null) { return const AreaPaneHeader( roundedTopBorder: false, title: Text("Select a node for details"), ); } - final info = watch(selectedNode!.uInfo).value; + final nodeInfo = watch(selectedNode.uInfo).value; return AreaPaneHeader( roundedTopBorder: false, @@ -70,18 +77,20 @@ class RtDevToolsExtension extends StatelessWidget { "Details of ", ), InstanceTitle( - nKey: nKey, - type: selectedNode.type, - kind: selectedNode.kind, - label: selectedNode.label, - isDependency: info?.dependencyRef != null, + nodeKey: selectedNode.key, + type: nodeInfo?.type, + nodeKind: nodeInfo?.nodeKind, + label: nodeInfo?.identify, + isDependency: nodeInfo is InstanceInfo + ? nodeInfo.dependencyKey != null + : false, ), ], ), ); }), const Expanded( - child: PropertiesList(), + child: DetailNodeList(), ), ], ), diff --git a/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart b/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart index 9c4f0976..9bef28ea 100644 --- a/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart +++ b/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart @@ -24,13 +24,13 @@ class DevtoolsService { final Map pageInfo = await pageNodes.evalValue(isAlive); final totalPages = pageInfo['totalPages'] as int; - final nodeInfos = (pageInfo['nodes'] as List).cast(); + final dataNodes = (pageInfo['nodes'] as List).cast(); - if (page >= totalPages - 1) return nodeInfos; + if (page >= totalPages - 1) return dataNodes; - final nextNodeInfos = await getAllNodes(page: page + 1, pageSize: pageSize); + final nextDataNodes = await getAllNodes(page: page + 1, pageSize: pageSize); - return nodeInfos + nextNodeInfos; + return dataNodes + nextDataNodes; } Future getNodeBykey(String nodeKey, [Map fallback = const {}]) async { diff --git a/packages/reactter_devtools_extension/lib/src/utils/extensions.dart b/packages/reactter_devtools_extension/lib/src/utils/extensions.dart index 82b171cb..60deca3a 100644 --- a/packages/reactter_devtools_extension/lib/src/utils/extensions.dart +++ b/packages/reactter_devtools_extension/lib/src/utils/extensions.dart @@ -1,4 +1,7 @@ import 'package:devtools_app_shared/service.dart' hide SentinelException; +import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/nodes/dart/plain_instance_node.dart'; +import 'package:reactter_devtools_extension/src/nodes/dart/null_node.dart'; import 'package:reactter_devtools_extension/src/services/eval_service.dart'; import 'package:vm_service/vm_service.dart'; @@ -22,7 +25,7 @@ extension InstanceExt on Instance { case InstanceKind.kBool: return bool.tryParse(valueAsString!); case InstanceKind.kMap: - final nodeInfo = {}; + final dataNode = {}; for (final entry in associations!) { final InstanceRef keyRef = entry.key; @@ -34,10 +37,10 @@ extension InstanceExt on Instance { throwOnError, ); - nodeInfo[key] = value; + dataNode[key] = value; } - return nodeInfo; + return dataNode; case InstanceKind.kList: final list = elements!.cast(); final listValues = []; @@ -90,6 +93,18 @@ extension InstanceExt on Instance { } extension InstanceRefExt on InstanceRef { + Node getNode(String key) { + switch (kind) { + case InstanceKind.kNull: + return NullNode(key: key); + case InstanceKind.kPlainInstance: + return PlainInstanceNode(key: key, instanceRef: this); + case InstanceKind.kClosure: + default: + return PlainInstanceNode(key: key, instanceRef: this); + } + } + Future safeGetInstance([ Disposable? isAlive, throwOnError = false, diff --git a/packages/reactter_devtools_extension/lib/src/widgets/bidirectional_scroll_view.dart b/packages/reactter_devtools_extension/lib/src/widgets/bidirectional_scroll_view.dart new file mode 100644 index 00000000..dc90dfca --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/widgets/bidirectional_scroll_view.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:reactter_devtools_extension/src/widgets/offset_scrollbar.dart'; + +class BidirectionalScrollView extends StatelessWidget { + final double maxWidth; + final Widget Function(BuildContext, BoxConstraints) builder; + final ScrollController? scrollControllerX; + final ScrollController? scrollControllerY; + + const BidirectionalScrollView({ + super.key, + this.scrollControllerX, + this.scrollControllerY, + required this.maxWidth, + required this.builder, + }); + + @override + Widget build(BuildContext context) { + final scrollControllerX = this.scrollControllerX ?? ScrollController(); + final scrollControllerY = this.scrollControllerY ?? ScrollController(); + + return LayoutBuilder( + builder: (context, constraints) { + final viewportWidth = constraints.maxWidth; + + return Scrollbar( + controller: scrollControllerX, + thumbVisibility: true, + child: SingleChildScrollView( + controller: scrollControllerX, + scrollDirection: Axis.horizontal, + child: Material( + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: viewportWidth > maxWidth ? viewportWidth : maxWidth, + ), + child: OffsetScrollbar( + isAlwaysShown: true, + axis: Axis.vertical, + controller: scrollControllerY, + offsetController: scrollControllerX, + offsetControllerViewportDimension: viewportWidth, + child: builder(context, constraints), + ), + ), + ), + ), + ); + }, + ); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/widgets/dependencies_list.dart b/packages/reactter_devtools_extension/lib/src/widgets/dependencies_list.dart index 5e0948ab..3a48d5af 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/dependencies_list.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/dependencies_list.dart @@ -1,76 +1,48 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; import 'package:reactter_devtools_extension/src/controllers/nodes_controller.dart'; +import 'package:reactter_devtools_extension/src/widgets/bidirectional_scroll_view.dart'; import 'package:reactter_devtools_extension/src/widgets/dependency_tile.dart'; -import 'package:reactter_devtools_extension/src/widgets/offset_scrollbar.dart'; class DependenciesList extends StatelessWidget { const DependenciesList({super.key}); @override Widget build(BuildContext context) { + final focusNode = FocusNode(); final nodesController = context.use(); - final listViewKey = nodesController.dependenciesListViewKey; - final scrollControllerX = ScrollController(); final scrollControllerY = nodesController.dependencyListScrollControllerY; - final focusNode = FocusNode(); + final listViewKey = nodesController.dependenciesListViewKey; - return LayoutBuilder( + return BidirectionalScrollView( + scrollControllerY: scrollControllerY, + maxWidth: 500, builder: (context, constraints) { - final viewportWidth = constraints.maxWidth; - - return Scrollbar( - controller: scrollControllerX, - thumbVisibility: true, - child: SingleChildScrollView( - controller: scrollControllerX, - scrollDirection: Axis.horizontal, - child: Material( - child: RtWatcher((context, watch) { - const minWith = 500.0; - - return ConstrainedBox( - constraints: BoxConstraints( - maxWidth: viewportWidth > minWith ? viewportWidth : minWith, - ), - child: OffsetScrollbar( - isAlwaysShown: true, - axis: Axis.vertical, - controller: scrollControllerY, - offsetController: scrollControllerX, - offsetControllerViewportDimension: viewportWidth, - child: SelectableRegion( - focusNode: focusNode, - selectionControls: materialTextSelectionControls, - child: RtWatcher((context, watch) { - final dependenciesList = - watch(nodesController.dependenciesList); - final length = dependenciesList.length; + return SelectableRegion( + focusNode: focusNode, + selectionControls: materialTextSelectionControls, + child: RtWatcher((context, watch) { + final dependenciesList = watch(nodesController.dependenciesList); + final length = dependenciesList.length; - return ListView.custom( - key: listViewKey, - controller: scrollControllerY, - itemExtent: 24, - childrenDelegate: SliverChildBuilderDelegate( - (context, index) { - final node = dependenciesList.elementAt(index); + return ListView.custom( + key: listViewKey, + controller: scrollControllerY, + itemExtent: 24, + childrenDelegate: SliverChildBuilderDelegate( + (context, index) { + final node = dependenciesList.elementAt(index); - return DependencyTile( - key: Key(node.key), - node: node, - onTap: () => nodesController.selectNode(node), - ); - }, - childCount: length, - ), - ); - }), - ), - ), - ); - }), - ), - ), + return DependencyTile( + key: Key(node.key), + node: node, + onTap: () => nodesController.selectNode(node), + ); + }, + childCount: length, + ), + ); + }), ); }, ); diff --git a/packages/reactter_devtools_extension/lib/src/widgets/dependency_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/dependency_tile.dart index 6073d508..1e114f1d 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/dependency_tile.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/dependency_tile.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/nodes/instance/instance_info.dart'; import 'package:reactter_devtools_extension/src/widgets/instance_title.dart'; import 'package:reactter_devtools_extension/src/widgets/tile_builder.dart'; @@ -40,15 +41,15 @@ class NodeTileTitle extends StatelessWidget { @override Widget build(BuildContext context) { return RtWatcher((context, watch) { - final info = watch(node.uInfo).value; - final dependencyRef = info?.dependencyRef; + final nodeInfo = watch(node.uInfo).value; return InstanceTitle( - nKey: node.key, - type: node.type, - kind: node.kind, - label: node.label, - isDependency: dependencyRef != null, + nodeKey: node.key, + nodeKind: nodeInfo?.nodeKind, + type: nodeInfo?.type, + label: nodeInfo?.identify, + isDependency: + nodeInfo is InstanceInfo ? nodeInfo.dependencyKey != null : false, ); }); } diff --git a/packages/reactter_devtools_extension/lib/src/widgets/detail_node_list.dart b/packages/reactter_devtools_extension/lib/src/widgets/detail_node_list.dart new file mode 100644 index 00000000..6cec4f82 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/widgets/detail_node_list.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/controllers/node_details_controller.dart'; +import 'package:reactter_devtools_extension/src/widgets/bidirectional_scroll_view.dart'; +import 'package:reactter_devtools_extension/src/widgets/detail_node_tile.dart'; + +class DetailNodeList extends StatelessWidget { + const DetailNodeList({super.key}); + + @override + Widget build(BuildContext context) { + final focusNode = FocusNode(); + final scrollControllerY = ScrollController(); + final nodeDetailsController = context.use(); + + return RtWatcher((context, watch) { + final maxDepth = + watch(nodeDetailsController.detailNodeList.uMaxDepth).value; + + return BidirectionalScrollView( + scrollControllerY: scrollControllerY, + maxWidth: 600 + (24.0 * maxDepth), + builder: (context, constraints) { + return SelectableRegion( + focusNode: focusNode, + selectionControls: materialTextSelectionControls, + child: RtWatcher((context, watch) { + final detailNodeList = + watch(nodeDetailsController.detailNodeList); + + final length = detailNodeList.length; + + return ListView.custom( + key: ObjectKey(detailNodeList), + controller: scrollControllerY, + itemExtent: 24, + childrenDelegate: SliverChildBuilderDelegate( + (context, index) { + final detailNode = detailNodeList.elementAt(index); + + return DetailNodeTile(node: detailNode); + }, + childCount: length, + ), + ); + }), + ); + }, + ); + }); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/widgets/detail_node_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/detail_node_tile.dart new file mode 100644 index 00000000..ab896fdd --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/widgets/detail_node_tile.dart @@ -0,0 +1,77 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/bases/async_node.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/controllers/nodes_controller.dart'; +import 'package:reactter_devtools_extension/src/nodes/instance/instance_info.dart'; +import 'package:reactter_devtools_extension/src/widgets/instance_title.dart'; +import 'package:reactter_devtools_extension/src/widgets/loading.dart'; +import 'package:reactter_devtools_extension/src/widgets/tile_builder.dart'; + +class DetailNodeTile extends StatelessWidget { + const DetailNodeTile({ + super.key, + required this.node, + }); + + final Node node; + + @override + Widget build(BuildContext context) { + return TreeNodeTileBuilder( + treeNode: node, + title: Row( + children: [ + Text( + "${node.key}: ", + style: Theme.of(context) + .textTheme + .labelSmall + ?.copyWith(color: Theme.of(context).colorScheme.primary), + ), + RtWatcher((context, watch) { + if (node is AsyncNode) { + final asyncNode = node as AsyncNode; + final isLoading = watch(asyncNode.uIsLoading).value; + + if (isLoading) return const Loading(); + + if (watch(asyncNode.uInfo).value == null) { + asyncNode.loadNode(); + } + } + + final nodeInfo = watch(node.uInfo).value; + final value = nodeInfo?.value; + final dependencyKey = + nodeInfo is InstanceInfo ? nodeInfo.dependencyKey : null; + + if (nodeInfo != null) { + final nodesController = context.use(); + final nodeKey = nodeInfo.identityHashCode ?? node.key; + final nodeOrigin = nodesController.uNodes.value[nodeKey]; + + return InstanceTitle( + nodeKey: nodeKey, + type: nodeInfo.type, + nodeKind: nodeInfo.nodeKind, + label: nodeInfo.identify, + isDependency: dependencyKey != null, + onTapIcon: nodeOrigin != null + ? () => nodesController.selectNodeByKey(nodeKey) + : null, + ); + } + + return Text( + value ?? '...', + style: Theme.of(context).textTheme.labelSmall, + ); + }), + ], + ), + isSelected: false, + onTap: () {}, + ); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/widgets/instance_icon.dart b/packages/reactter_devtools_extension/lib/src/widgets/instance_icon.dart index a4014c25..6ab2580f 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/instance_icon.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/instance_icon.dart @@ -2,19 +2,17 @@ import 'package:flutter/material.dart'; import 'package:reactter_devtools_extension/src/constants.dart'; class InstanceIcon extends StatelessWidget { - final String kind; + final NodeKind? nodeKind; final bool isDependency; const InstanceIcon({ super.key, - required this.kind, + required this.nodeKind, this.isDependency = false, }); @override Widget build(BuildContext context) { - final nodeKind = NodeKind.getKind(kind); - if (nodeKind == null) return const SizedBox(); return SizedBox.square( @@ -27,9 +25,9 @@ class InstanceIcon extends StatelessWidget { padding: const EdgeInsets.all(2.0), child: Center( child: CircleAvatar( - backgroundColor: nodeKind.color, + backgroundColor: nodeKind!.color, child: Text( - nodeKind.abbr, + nodeKind!.abbr, style: Theme.of(context).textTheme.labelSmall?.copyWith( color: Colors.white, fontWeight: FontWeight.bold, diff --git a/packages/reactter_devtools_extension/lib/src/widgets/instance_title.dart b/packages/reactter_devtools_extension/lib/src/widgets/instance_title.dart index b596a3ab..59e35101 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/instance_title.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/instance_title.dart @@ -1,19 +1,20 @@ import 'package:flutter/material.dart'; +import 'package:reactter_devtools_extension/src/constants.dart'; import 'package:reactter_devtools_extension/src/widgets/instance_icon.dart'; class InstanceTitle extends StatelessWidget { - final String nKey; - final String type; - final String? kind; + final String nodeKey; + final String? type; + final NodeKind? nodeKind; final String? label; final bool isDependency; final void Function()? onTapIcon; const InstanceTitle({ + required this.nodeKey, super.key, - required this.nKey, - required this.type, - this.kind, + this.type, + this.nodeKind, this.label, this.isDependency = false, this.onTapIcon, @@ -23,11 +24,11 @@ class InstanceTitle extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - if (kind != null) + if (nodeKind != null) InkWell( onTap: onTapIcon, child: InstanceIcon( - kind: kind!, + nodeKind: nodeKind, isDependency: isDependency, ), ), @@ -58,7 +59,7 @@ class InstanceTitle extends StatelessWidget { ], ), TextSpan( - text: " #$nKey", + text: " #$nodeKey", style: Theme.of(context).textTheme.labelSmall?.copyWith( color: Theme.of(context).colorScheme.secondary, ), diff --git a/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart index f4456e81..c59ae92a 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/nodes/instance/instance_info.dart'; import 'package:reactter_devtools_extension/src/widgets/instance_title.dart'; import 'package:reactter_devtools_extension/src/widgets/tile_builder.dart'; @@ -40,15 +41,16 @@ class NodeTileTitle extends StatelessWidget { @override Widget build(BuildContext context) { return RtWatcher((context, watch) { - final info = watch(node.uInfo).value; - final dependencyRef = info?.dependencyRef; + final nodeInfo = watch(node.uInfo).value; + final dependencyKey = + nodeInfo is InstanceInfo ? nodeInfo.dependencyKey : null; return InstanceTitle( - nKey: node.key, - type: node.type, - kind: node.kind, - label: node.label, - isDependency: dependencyRef != null, + nodeKey: node.key, + nodeKind: nodeInfo?.nodeKind, + type: nodeInfo?.type, + label: nodeInfo?.identify, + isDependency: dependencyKey != null, ); }); } diff --git a/packages/reactter_devtools_extension/lib/src/widgets/nodes_list.dart b/packages/reactter_devtools_extension/lib/src/widgets/nodes_list.dart index 3d8f0b00..1f673bef 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/nodes_list.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/nodes_list.dart @@ -1,78 +1,54 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; import 'package:reactter_devtools_extension/src/controllers/nodes_controller.dart'; +import 'package:reactter_devtools_extension/src/widgets/bidirectional_scroll_view.dart'; import 'package:reactter_devtools_extension/src/widgets/node_tile.dart'; -import 'package:reactter_devtools_extension/src/widgets/offset_scrollbar.dart'; class NodesList extends StatelessWidget { const NodesList({super.key}); @override Widget build(BuildContext context) { + final focusNode = FocusNode(); final nodesController = context.use(); final listViewKey = nodesController.nodesListViewKey; - final scrollControllerX = ScrollController(); final scrollControllerY = nodesController.nodesListScrollControllerY; - final focusNode = FocusNode(); - - return LayoutBuilder( - builder: (context, constraints) { - final viewportWidth = constraints.maxWidth; - return Scrollbar( - controller: scrollControllerX, - thumbVisibility: true, - child: SingleChildScrollView( - controller: scrollControllerX, - scrollDirection: Axis.horizontal, - child: Material( - child: RtWatcher((context, watch) { - final maxDepth = watch(nodesController.uMaxDepth).value; - final minWith = 400 + (24.0 * maxDepth); + return RtWatcher((context, watch) { + final maxDepth = watch(nodesController.nodesList.uMaxDepth).value; - return ConstrainedBox( - constraints: BoxConstraints( - maxWidth: viewportWidth > minWith ? viewportWidth : minWith, - ), - child: OffsetScrollbar( - isAlwaysShown: true, - axis: Axis.vertical, - controller: scrollControllerY, - offsetController: scrollControllerX, - offsetControllerViewportDimension: viewportWidth, - child: SelectableRegion( - focusNode: focusNode, - selectionControls: materialTextSelectionControls, - child: RtWatcher((context, watch) { - final nodesList = watch(nodesController.nodesList); - final length = nodesList.length; + return BidirectionalScrollView( + scrollControllerY: scrollControllerY, + maxWidth: 400 + (24.0 * maxDepth), + builder: (context, constraints) { + return SelectableRegion( + focusNode: focusNode, + selectionControls: materialTextSelectionControls, + child: RtWatcher((context, watch) { + final nodesList = watch(nodesController.nodesList); + final length = nodesList.length; - return ListView.custom( - key: listViewKey, - controller: scrollControllerY, - itemExtent: 24, - childrenDelegate: SliverChildBuilderDelegate( - (context, index) { - final node = nodesList.elementAt(index); + return ListView.custom( + key: listViewKey, + controller: scrollControllerY, + itemExtent: 24, + childrenDelegate: SliverChildBuilderDelegate( + (context, index) { + final node = nodesList.elementAt(index); - return NodeTile( - key: Key(node.key), - node: node, - onTap: () => nodesController.selectNode(node), - ); - }, - childCount: length, - ), - ); - }), - ), - ), - ); - }), - ), - ), - ); - }, - ); + return NodeTile( + key: Key(node.key), + node: node, + onTap: () => nodesController.selectNode(node), + ); + }, + childCount: length, + ), + ); + }), + ); + }, + ); + }); } } diff --git a/packages/reactter_devtools_extension/lib/src/widgets/properties_list.dart b/packages/reactter_devtools_extension/lib/src/widgets/properties_list.dart deleted file mode 100644 index 2c10910f..00000000 --- a/packages/reactter_devtools_extension/lib/src/widgets/properties_list.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:reactter_devtools_extension/src/controllers/nodes_controller.dart'; -import 'package:reactter_devtools_extension/src/widgets/offset_scrollbar.dart'; -import 'package:reactter_devtools_extension/src/widgets/property_tile.dart'; - -class PropertiesList extends StatelessWidget { - const PropertiesList({super.key}); - - @override - Widget build(BuildContext context) { - final scrollControllerX = ScrollController(); - final scrollControllerY = ScrollController(); - final focusNode = FocusNode(); - final nodesController = context.use(); - - return LayoutBuilder( - builder: (context, constraints) { - final viewportWidth = constraints.maxWidth; - - return Scrollbar( - controller: scrollControllerX, - thumbVisibility: true, - child: SingleChildScrollView( - controller: scrollControllerX, - scrollDirection: Axis.horizontal, - child: Material( - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 1000.00), - child: OffsetScrollbar( - isAlwaysShown: true, - axis: Axis.vertical, - controller: scrollControllerY, - offsetController: scrollControllerX, - offsetControllerViewportDimension: viewportWidth, - child: SelectableRegion( - focusNode: focusNode, - selectionControls: materialTextSelectionControls, - child: RtWatcher((context, watch) { - watch(nodesController.uCurrentNodeKey); - - final propertyNodes = nodesController.currentNode != null - ? watch(nodesController.currentNode!.propertyNodes) - : null; - - final length = propertyNodes?.length ?? 0; - - return ListView.custom( - key: ObjectKey(propertyNodes), - controller: scrollControllerY, - itemExtent: 24, - childrenDelegate: SliverChildBuilderDelegate( - (context, index) { - final propertyNode = - propertyNodes!.elementAt(index); - - return PropertyTile( - propertyNode: propertyNode, - ); - }, - childCount: length, - ), - ); - }), - ), - ), - ), - ), - ), - ); - }, - ); - } -} diff --git a/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart deleted file mode 100644 index 6c2ad7b1..00000000 --- a/packages/reactter_devtools_extension/lib/src/widgets/property_tile.dart +++ /dev/null @@ -1,89 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:reactter_devtools_extension/src/controllers/nodes_controller.dart'; -import 'package:reactter_devtools_extension/src/constants.dart'; -import 'package:reactter_devtools_extension/src/bases/property_node.dart'; -import 'package:reactter_devtools_extension/src/nodes/property/property_async_node.dart'; -import 'package:reactter_devtools_extension/src/widgets/instance_title.dart'; -import 'package:reactter_devtools_extension/src/widgets/loading.dart'; -import 'package:reactter_devtools_extension/src/widgets/tile_builder.dart'; - -class PropertyTile extends StatelessWidget { - const PropertyTile({ - super.key, - required this.propertyNode, - }); - - final PropertyNode propertyNode; - - @override - Widget build(BuildContext context) { - return TreeNodeTileBuilder( - treeNode: propertyNode, - title: Row( - children: [ - Text( - "${propertyNode.key}: ", - style: Theme.of(context) - .textTheme - .labelSmall - ?.copyWith(color: Theme.of(context).colorScheme.primary), - ), - RtWatcher((context, watch) { - if (propertyNode is PropertyAsyncNode) { - final propertyAsynNode = propertyNode as PropertyAsyncNode; - final isLoading = watch(propertyAsynNode.uIsLoading).value; - - if (isLoading) return const Loading(); - - watch(propertyAsynNode.uValueFuture).value ?? - propertyAsynNode.getValueAsync(); - } - - final value = watch(propertyNode.uValue).value; - - final instanceInfo = watch(propertyNode.uInstanceInfo).value; - - if (instanceInfo != null) { - final nKey = instanceInfo['key'] as String; - final nodesController = context.use(); - final node = nodesController.uNodes.value[nKey]; - - if (node != null) watch(node.uInfo); - - final type = node != null ? node.type : instanceInfo['type']; - final kind = node != null ? node.kind : instanceInfo['kind']; - final label = node != null - ? node.label - : instanceInfo['id'] ?? - instanceInfo['debugLabel'] ?? - instanceInfo['name']; - final dependencyRef = node != null - ? node.uInfo.value?.dependencyRef - : instanceInfo['dependencyRef']; - - return InstanceTitle( - nKey: nKey, - type: type, - kind: - node == null && kind == NodeKind.instance.key ? null : kind, - label: label, - isDependency: dependencyRef != null, - onTapIcon: node != null - ? () => nodesController.selectNodeByKey(nKey) - : null, - ); - } - - return Text( - value ?? '...', - style: Theme.of(context).textTheme.labelSmall, - ); - }), - ], - ), - isSelected: false, - onTap: () {}, - ); - } -} From c996c871064a9128b359082cd0aa467ea31e566a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Mon, 9 Dec 2024 00:44:49 -0600 Subject: [PATCH 058/141] refactor(core, framework, hooks): Enhance event handling and notifier logic for improved stability and performance. --- .../reactter/lib/src/core/event_handler.dart | 12 +++++-- .../reactter/lib/src/core/event_notifier.dart | 29 +++++++-------- packages/reactter/lib/src/core/notifier.dart | 25 +++++++------ .../lib/src/framework/rt_state_base.dart | 36 +++++++++++-------- .../reactter/lib/src/hooks/use_effect.dart | 2 ++ 5 files changed, 61 insertions(+), 43 deletions(-) diff --git a/packages/reactter/lib/src/core/event_handler.dart b/packages/reactter/lib/src/core/event_handler.dart index 4167098f..97e0921e 100644 --- a/packages/reactter/lib/src/core/event_handler.dart +++ b/packages/reactter/lib/src/core/event_handler.dart @@ -80,10 +80,15 @@ abstract class EventHandler implements IContext { final notifiers = _notifiers.where((notifier) { if (!generic && notifier._dependencyRef != null) return false; - return notifier == instance; + return instance != null && notifier.isInstanceOrDependencyRef(instance); }); + final boundInstance = instance is RtState ? instance.boundInstance : null; + for (final notifier in notifiers.toList(growable: false)) { + if (boundInstance != null && + notifier.isInstanceOrDependencyRef(boundInstance)) continue; + notifier.dispose(); _notifiers.remove(notifier); stateManagement._deferredEvents.remove(notifier); @@ -92,7 +97,10 @@ abstract class EventHandler implements IContext { /// Checks if an object has any listeners. bool _hasListeners(Object? instance) { - return _notifiers.any((notifier) => notifier == instance); + return instance != null && + _notifiers.any( + (notifier) => notifier.isInstanceOrDependencyRef(instance), + ); } /// Retrieves the [EventNotifier] for the given [instance] and [eventName]. diff --git a/packages/reactter/lib/src/core/event_notifier.dart b/packages/reactter/lib/src/core/event_notifier.dart index d3e385a5..46e403b9 100644 --- a/packages/reactter/lib/src/core/event_notifier.dart +++ b/packages/reactter/lib/src/core/event_notifier.dart @@ -59,7 +59,7 @@ class EventNotifier extends EventNotifierRef with Notifier { String get target => "$instanceObj about $event"; final void Function(EventNotifier notifier) onNotifyComplete; - DependencyRef? get instanceRef => + DependencyRef? get dependencyRef => _dependencyRef ?? dependencyInjection.getDependencyRef(_instanceObj); Object? get instanceObj => @@ -82,21 +82,15 @@ class EventNotifier extends EventNotifierRef with Notifier { @override bool operator ==(Object other) { - if (other is EventNotifierRef) { - return super == other; - } - - if (other is DependencyRef) { - return instanceRef == other; - } + return other is EventNotifierRef && super == other; + } - final instanceRefSelf = instanceRef; + bool isInstanceOrDependencyRef(Object other) { + if (other == _instanceObj || other == _dependencyRef) return true; - if (instanceRefSelf != null) { - return instanceRefSelf == dependencyInjection.getDependencyRef(other); - } + if (other is DependencyRef) return other == dependencyRef; - return instanceObj == other; + return other == instanceObj; } /// Copied from Flutter @@ -118,10 +112,13 @@ class EventNotifier extends EventNotifierRef with Notifier { void notifyListeners(Object? param) { try { super.notifyListeners(param); - } catch (e) { - if (e is! AssertionError) rethrow; - } finally { onNotifyComplete(this); + } catch (err) { + if (err is AssertionError) { + onNotifyComplete(this); + } + + rethrow; } } diff --git a/packages/reactter/lib/src/core/notifier.dart b/packages/reactter/lib/src/core/notifier.dart index 29168f02..d3b8f301 100644 --- a/packages/reactter/lib/src/core/notifier.dart +++ b/packages/reactter/lib/src/core/notifier.dart @@ -206,6 +206,7 @@ abstract class Notifier { // effectively shrink the list. _removeAt(i); } + break; } } @@ -275,6 +276,8 @@ abstract class Notifier { _notificationCallStackDepth++; final int end = _count; + Object? error; + for (int i = 0; i < end; i++) { final listener = _listeners[i]; @@ -287,16 +290,8 @@ abstract class Notifier { } listenerCall(listener, param); - } catch (error) { - assert(() { - throw AssertionError( - 'An error was thrown by a listener of $target.\n' - 'The error thrown was:\n' - ' $error\n', - ); - }()); - - rethrow; + } catch (err) { + error = err; } finally { // ignore: control_flow_in_finally continue; @@ -344,6 +339,16 @@ abstract class Notifier { _reentrantlyRemovedListeners = 0; _count = newLength; } + + assert(() { + if (error == null) return true; + + throw AssertionError( + 'An error was thrown by a listener of $target.\n' + 'The error thrown was:\n' + ' $error\n', + ); + }()); } /// Calls the [listener] with the given [param]. diff --git a/packages/reactter/lib/src/framework/rt_state_base.dart b/packages/reactter/lib/src/framework/rt_state_base.dart index dba293eb..00ea9c24 100644 --- a/packages/reactter/lib/src/framework/rt_state_base.dart +++ b/packages/reactter/lib/src/framework/rt_state_base.dart @@ -112,18 +112,21 @@ abstract class RtStateBase> implements RtState { "The fnUpdate must be a function without arguments", ); - if (!_hasListeners || _isUpdating) { + if (_isUpdating || !_hasListeners) { fnUpdate?.call(); _notifyUpdated(); return; } - _isUpdating = true; - _notify(Lifecycle.willUpdate); - fnUpdate?.call(); - _notify(Lifecycle.didUpdate); - _notifyUpdated(); - _isUpdating = false; + try { + _isUpdating = true; + _notify(Lifecycle.willUpdate); + fnUpdate?.call(); + _notify(Lifecycle.didUpdate); + _notifyUpdated(); + } finally { + _isUpdating = false; + } } @override @@ -131,15 +134,18 @@ abstract class RtStateBase> implements RtState { void notify() { assert(!_isDisposed, "Can't refresh when it's been disposed"); - if (!_hasListeners || _isUpdating) { + if (_isUpdating || !_hasListeners) { _notifyUpdated(); return; } - _isUpdating = true; - _notify(Lifecycle.didUpdate); - _notifyUpdated(); - _isUpdating = false; + try { + _isUpdating = true; + _notify(Lifecycle.didUpdate); + _notifyUpdated(); + } finally { + _isUpdating = false; + } } @override @@ -147,13 +153,13 @@ abstract class RtStateBase> implements RtState { void dispose() { assert(!_isDisposed, "Can't dispose when it's been disposed"); + eventHandler.emit(this, Lifecycle.deleted); + eventHandler.offAll(this); + if (_boundInstance != null) { unbind(); } - eventHandler.emit(this, Lifecycle.deleted); - eventHandler.offAll(this); - if (_isDisposed) return; _notifyDisponsed(); diff --git a/packages/reactter/lib/src/hooks/use_effect.dart b/packages/reactter/lib/src/hooks/use_effect.dart index 6e9e0423..b4cacfc4 100644 --- a/packages/reactter/lib/src/hooks/use_effect.dart +++ b/packages/reactter/lib/src/hooks/use_effect.dart @@ -227,6 +227,8 @@ class UseEffect extends RtHook { } void _unwatchInstanceAttached() { + if (boundInstance == null) return; + Rt.off( boundInstance!, Lifecycle.didMount, From 5250792ecceac2e72725f2c047416aa2bf7bd1b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Mon, 9 Dec 2024 00:46:37 -0600 Subject: [PATCH 059/141] refactor(framework): Clean up exports, remove deprecated code, and improve event handling. --- .../flutter_reactter/lib/flutter_reactter.dart | 3 +-- .../lib/src/framework/dependency.dart | 14 +++++++------- .../lib/src/framework/provider_impl.dart | 2 ++ .../lib/src/framework/scope_element_mixin.dart | 17 +++++++++++++---- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/packages/flutter_reactter/lib/flutter_reactter.dart b/packages/flutter_reactter/lib/flutter_reactter.dart index a8921473..75c4c757 100644 --- a/packages/flutter_reactter/lib/flutter_reactter.dart +++ b/packages/flutter_reactter/lib/flutter_reactter.dart @@ -1,7 +1,6 @@ library flutter_reactter; -export 'src/framework.dart' - show ReactterDependencyNotFoundException, RtDependencyNotFoundException; +export 'src/framework.dart' show RtDependencyNotFoundException; export 'src/extensions.dart'; export 'src/types.dart'; export 'src/widgets.dart' hide ProviderWrapper; diff --git a/packages/flutter_reactter/lib/src/framework/dependency.dart b/packages/flutter_reactter/lib/src/framework/dependency.dart index fd6aade0..e678a853 100644 --- a/packages/flutter_reactter/lib/src/framework/dependency.dart +++ b/packages/flutter_reactter/lib/src/framework/dependency.dart @@ -70,13 +70,6 @@ class MasterDependency extends Dependency { super.dispose(); } - // coverage:ignore-start - @override - void commitToMaster(MasterDependency masterDependency) { - assert(true, 'This method should not be called'); - } - // coverage:ignore-end - @override void listen(void Function() callback) { final eventListener = _buildListenerCallback(callback); @@ -109,6 +102,13 @@ class MasterDependency extends Dependency { super.unlisten(); } + // coverage:ignore-start + @override + void commitToMaster(MasterDependency masterDependency) { + assert(true, 'This method should not be called'); + } + // coverage:ignore-end + // coverage:ignore-start @override MasterDependency toMaster() => this; diff --git a/packages/flutter_reactter/lib/src/framework/provider_impl.dart b/packages/flutter_reactter/lib/src/framework/provider_impl.dart index 1b0b9762..19e0b971 100644 --- a/packages/flutter_reactter/lib/src/framework/provider_impl.dart +++ b/packages/flutter_reactter/lib/src/framework/provider_impl.dart @@ -60,8 +60,10 @@ class ProvideImpl extends ProviderBase return super.child!; } + // coverage:ignore-start @override bool updateShouldNotify(covariant InheritedWidget oldWidget) => false; + // coverage:ignore-end @override ProviderElement createElement() => ProviderElement(widget: this); diff --git a/packages/flutter_reactter/lib/src/framework/scope_element_mixin.dart b/packages/flutter_reactter/lib/src/framework/scope_element_mixin.dart index 63f121a0..ded223a4 100644 --- a/packages/flutter_reactter/lib/src/framework/scope_element_mixin.dart +++ b/packages/flutter_reactter/lib/src/framework/scope_element_mixin.dart @@ -22,7 +22,6 @@ mixin ScopeElementMixin on InheritedElement { @override void updated(InheritedWidget oldWidget) { - // This point is generally reached when applying the hot reload. if (_updatedShouldNotify) { // If the widget tree is updated, we need to reset the state // to avoid memory leaks. @@ -30,7 +29,7 @@ mixin ScopeElementMixin on InheritedElement { notifyClients(oldWidget); return; } - + // coverage:ignore-line super.updated(oldWidget); } @@ -65,6 +64,7 @@ mixin ScopeElementMixin on InheritedElement { // If the aspect is not a Dependency or if the widget tree is marked as // needing build, we can skip the update of the dependencies. if (aspect is! Dependency || _isMarkNeedsBuild) { + // coverage:ignore-line return super.updateDependencies(dependent, aspect); } @@ -72,6 +72,7 @@ mixin ScopeElementMixin on InheritedElement { // If no MasterDependency is stored, we can skip the update of the dependencies. if (dependency != null && dependency is! MasterDependency) { + // coverage:ignore-line return super.updateDependencies(dependent, aspect); } @@ -128,7 +129,7 @@ mixin ScopeElementMixin on InheritedElement { _isFlushDependentsScheduled = true; - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + WidgetsBinding.instance.addPostFrameCallback((_) { _isFlushDependentsScheduled = false; _dependentsFlushReady.clear(); }); @@ -163,7 +164,15 @@ mixin ScopeElementMixin on InheritedElement { if (_isMarkNeedsBuild) return; _isMarkNeedsBuild = true; - super.markNeedsBuild(); + try { + super.markNeedsBuild(); + } catch (error) { + if (error is! AssertionError) rethrow; + + WidgetsBinding.instance.addPostFrameCallback( + (_) => super.markNeedsBuild(), + ); + } } void _removeDependencies(Element dependent) { From d4577b8a488da1f2ed43ec07fc55a9712663f1d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Mon, 9 Dec 2024 00:47:16 -0600 Subject: [PATCH 060/141] test: Add tests for dependency state watching and useEffect disposal. --- .../build_context_extension_test.dart | 61 +++++++++++++++++++ .../test/hooks/use_dependency_test.dart | 5 +- .../reactter/test/hooks/use_effect_test.dart | 10 ++- 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/packages/flutter_reactter/test/extensions/build_context_extension_test.dart b/packages/flutter_reactter/test/extensions/build_context_extension_test.dart index f78fca79..d6043aca 100644 --- a/packages/flutter_reactter/test/extensions/build_context_extension_test.dart +++ b/packages/flutter_reactter/test/extensions/build_context_extension_test.dart @@ -147,6 +147,67 @@ void main() { expect(find.text("stateInt: 2"), findsOneWidget); }); + testWidgets("should watch dependency's states while rebuilding", + (tester) async { + late TestController instanceObtained; + late TestController instanceObtainedWithId; + + await tester.pumpWidget( + TestBuilder( + child: RtMultiProviderBuilder( + builder: (context, _) { + instanceObtained = context.watch( + (inst) => [inst.stateInt], + ); + instanceObtainedWithId = context.watch( + (inst) => [inst.stateInt], + 'uniqueId', + ); + + return Column( + children: [ + Text("stateString: ${instanceObtained.stateString.value}"), + Text("stateInt: ${instanceObtained.stateInt.value}"), + Text( + "stateStringWithId: ${instanceObtainedWithId.stateString.value}", + ), + Text( + "stateIntWithId: ${instanceObtainedWithId.stateInt.value}", + ), + Builder(builder: (context) { + if (instanceObtained.stateInt.value == 1) { + instanceObtained.stateInt.value += 1; + } + + return const SizedBox(); + }) + ], + ); + }, + ), + ), + ); + + await tester.pumpAndSettle(); + + expectLater(instanceObtained, isInstanceOf()); + expectLater(instanceObtainedWithId, isInstanceOf()); + + expect(find.text("stateString: initial"), findsOneWidget); + expect(find.text("stateInt: 0"), findsOneWidget); + expect(find.text("stateStringWithId: from uniqueId"), findsOneWidget); + expect(find.text("stateIntWithId: 0"), findsOneWidget); + + instanceObtained.stateInt.value += 1; + instanceObtainedWithId.stateInt.value += 1; + await tester.pumpAndSettle(); + + expect(find.text("stateString: initial"), findsOneWidget); + expect(find.text("stateInt: 2"), findsOneWidget); + expect(find.text("stateStringWithId: from uniqueId"), findsOneWidget); + expect(find.text("stateIntWithId: 1"), findsOneWidget); + }); + testWidgets( "should watch multiple dependency's states, using different context.watch", (tester) async { diff --git a/packages/reactter/test/hooks/use_dependency_test.dart b/packages/reactter/test/hooks/use_dependency_test.dart index 5a4b9e2e..e72843fe 100644 --- a/packages/reactter/test/hooks/use_dependency_test.dart +++ b/packages/reactter/test/hooks/use_dependency_test.dart @@ -286,7 +286,7 @@ void _testControllerLate([String? id]) { late final TestController instance; final useDependency = UseDependency(id: id); - UseEffect(() { + final uEffect = UseEffect(() { if (useDependency.instance != null) { instance = useDependency.instance!; } @@ -295,5 +295,8 @@ void _testControllerLate([String? id]) { Rt.create(() => TestController(), id: id); Rt.destroy(id: id); + uEffect.dispose(); + useDependency.dispose(); + expectLater(instance, isA()); } diff --git a/packages/reactter/test/hooks/use_effect_test.dart b/packages/reactter/test/hooks/use_effect_test.dart index c45fb227..d55d1235 100644 --- a/packages/reactter/test/hooks/use_effect_test.dart +++ b/packages/reactter/test/hooks/use_effect_test.dart @@ -254,17 +254,21 @@ void main() { UseEffect( () { return () { - nCalls += 1; + nCalls++; if (nCalls == 1) { - expect(stateA.value, true); + expect(stateA.value, false); expect(stateB.value, 0); expect(stateC.value, "initial"); } else if (nCalls == 2) { expect(stateA.value, true); - expect(stateB.value, 1); + expect(stateB.value, 0); expect(stateC.value, "initial"); } else if (nCalls == 3) { + expect(stateA.value, true); + expect(stateB.value, 1); + expect(stateC.value, "initial"); + } else if (nCalls == 4) { expect(stateA.value, true); expect(stateB.value, 1); expect(stateC.value, "new value"); From a84ef149790686b5527116261bad2585fe983875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Mon, 9 Dec 2024 00:49:42 -0600 Subject: [PATCH 061/141] feat(devtools): Enhance node handling; update node references and improve loading logic. --- packages/reactter/lib/src/devtools.dart | 6 +- .../lib/src/bases/async_node.dart | 61 +++++++++-- .../lib/src/bases/node.dart | 11 +- .../lib/src/bases/tree_node.dart | 6 +- .../lib/src/constants.dart | 5 +- .../controllers/node_details_controller.dart | 37 +++++-- .../lib/src/nodes/dart/closure_node.dart | 101 ++++++++++++++++++ .../lib/src/nodes/dart/iterable_node.dart | 72 +++++++++++++ .../lib/src/nodes/dart/key_value_node.dart | 38 +++++++ .../lib/src/nodes/dart/map_node.dart | 63 +++++++++++ .../lib/src/nodes/dart/null_node.dart | 26 ----- .../src/nodes/dart/plain_instance_node.dart | 23 ++-- .../lib/src/nodes/dart/record_node.dart | 65 +++++++++++ .../lib/src/nodes/instance/instance_node.dart | 13 ++- .../lib/src/nodes/sentinel_node.dart | 8 ++ .../lib/src/nodes/slot_node.dart | 5 + .../lib/src/reactter_devtools_extension.dart | 2 +- .../lib/src/utils/color_palette.dart | 84 +++++++++++++++ .../lib/src/utils/extensions.dart | 62 ++++++++--- .../lib/src/utils/helper.dart | 41 +++++++ .../lib/src/widgets/dependency_tile.dart | 2 +- .../lib/src/widgets/detail_node_list.dart | 5 +- .../lib/src/widgets/detail_node_tile.dart | 46 ++------ .../lib/src/widgets/instance_title.dart | 22 ++-- .../lib/src/widgets/node_tile.dart | 2 +- .../lib/src/widgets/node_title.dart | 77 +++++++++++++ .../lib/src/widgets/tile_builder.dart | 3 +- 27 files changed, 759 insertions(+), 127 deletions(-) create mode 100644 packages/reactter_devtools_extension/lib/src/nodes/dart/closure_node.dart create mode 100644 packages/reactter_devtools_extension/lib/src/nodes/dart/iterable_node.dart create mode 100644 packages/reactter_devtools_extension/lib/src/nodes/dart/key_value_node.dart create mode 100644 packages/reactter_devtools_extension/lib/src/nodes/dart/map_node.dart delete mode 100644 packages/reactter_devtools_extension/lib/src/nodes/dart/null_node.dart create mode 100644 packages/reactter_devtools_extension/lib/src/nodes/dart/record_node.dart create mode 100644 packages/reactter_devtools_extension/lib/src/utils/color_palette.dart create mode 100644 packages/reactter_devtools_extension/lib/src/utils/helper.dart create mode 100644 packages/reactter_devtools_extension/lib/src/widgets/node_title.dart diff --git a/packages/reactter/lib/src/devtools.dart b/packages/reactter/lib/src/devtools.dart index 328cfa60..01733069 100644 --- a/packages/reactter/lib/src/devtools.dart +++ b/packages/reactter/lib/src/devtools.dart @@ -452,10 +452,14 @@ class _InstanceNode extends _Node { _InstanceNode({required Object instance}) : super(instance: instance); static Map getInstanceInfo(Object instance) { + final value = instance.toString(); + final type = instance.runtimeType.toString(); + return { 'kind': _NodeKind.instance, 'key': instance.hashCode.toString(), - 'type': instance.runtimeType.toString(), + 'type': type, + 'value': value.startsWith("Instance of") || value == type ? null : value, }; } diff --git a/packages/reactter_devtools_extension/lib/src/bases/async_node.dart b/packages/reactter_devtools_extension/lib/src/bases/async_node.dart index 98df6150..e847f65f 100644 --- a/packages/reactter_devtools_extension/lib/src/bases/async_node.dart +++ b/packages/reactter_devtools_extension/lib/src/bases/async_node.dart @@ -1,33 +1,74 @@ +import 'dart:async'; + import 'package:flutter_reactter/reactter.dart'; import 'package:reactter_devtools_extension/src/bases/node.dart'; import 'package:reactter_devtools_extension/src/bases/node_info.dart'; import 'package:vm_service/vm_service.dart'; abstract base class AsyncNode extends Node { - final InstanceRef instanceRef; + bool _isValueUpdating = false; + FutureOr? _futureLoadNode; + + InstanceRef? _lastInstanceRef; + InstanceRef _instanceRef; + InstanceRef get instanceRef => _instanceRef; + + final uNeedToLoadNode = UseState(true); final uIsLoading = UseState(false); AsyncNode({ required super.key, - required this.instanceRef, - required super.kind, - }); + required InstanceRef instanceRef, + }) : _instanceRef = instanceRef, + super(kind: instanceRef.kind!); Future getNodeInfo(); Future loadNodeInfo() async { await Rt.batch(() async { - uIsLoading.value = true; uInfo.value = await getNodeInfo(); - uIsLoading.value = false; }); } @override Future loadNode() async { - await Future.wait([ - super.loadNode(), - loadNodeInfo(), - ]); + if (_futureLoadNode != null) return _futureLoadNode; + + await (_futureLoadNode = Rt.batch(() async { + uIsLoading.value = true; + try { + await Future.wait([ + loadNodeInfo(), + super.loadNode(), + ]); + } catch (e) { + print(e); + } + _isValueUpdating = false; + uNeedToLoadNode.value = false; + uIsLoading.value = false; + })); + + if (_lastInstanceRef != null && _instanceRef != _lastInstanceRef) { + reloadNode(); + } + } + + Future updateInstanceRef(InstanceRef instanceRef) async { + _lastInstanceRef = instanceRef; + + if (_isValueUpdating) return; + + _isValueUpdating = true; + + if (_futureLoadNode != null) await _futureLoadNode; + + reloadNode(); + } + + void reloadNode() { + _instanceRef = _lastInstanceRef!; + _futureLoadNode = null; + uNeedToLoadNode.value = true; } } diff --git a/packages/reactter_devtools_extension/lib/src/bases/node.dart b/packages/reactter_devtools_extension/lib/src/bases/node.dart index b2db8226..bc1f1337 100644 --- a/packages/reactter_devtools_extension/lib/src/bases/node.dart +++ b/packages/reactter_devtools_extension/lib/src/bases/node.dart @@ -1,12 +1,18 @@ +import 'dart:async'; +import 'dart:collection'; + import 'package:devtools_app_shared/service.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; import 'package:reactter_devtools_extension/src/bases/tree_node.dart'; import 'package:reactter_devtools_extension/src/bases/node_info.dart'; +import 'package:reactter_devtools_extension/src/utils/helper.dart'; abstract base class Node extends TreeNode { final String key; final String kind; + final LinkedHashMap nodeRefs = LinkedHashMap(); + final uInfo = UseState(null); final uIsSelected = UseState(false); final isAlive = Disposable(); @@ -27,10 +33,7 @@ abstract base class Node extends TreeNode { Future loadNode() async { await Rt.batch(() async { final nodes = await getDetails(); - - for (final node in nodes) { - addChild(node); - } + addNodes(nodeRefs, nodes, addChild); }); } } diff --git a/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart b/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart index decfa6bf..8aa83ea6 100644 --- a/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart +++ b/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart @@ -64,7 +64,11 @@ abstract base class TreeNode> extends LinkedListEntry void replaceFor(E entry) { Rt.batch(() { - entry._toParent(parent); + if (parent != null) { + entry._toParent(parent); + } else { + insertAfter(entry); + } for (final child in [...uChildren.value]) { entry.addChild(child); diff --git a/packages/reactter_devtools_extension/lib/src/constants.dart b/packages/reactter_devtools_extension/lib/src/constants.dart index fa9a8368..1ea66634 100644 --- a/packages/reactter_devtools_extension/lib/src/constants.dart +++ b/packages/reactter_devtools_extension/lib/src/constants.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:reactter_devtools_extension/src/bases/node.dart'; -const int kMaxValueLength = 50; +const int kMaxValueLength = 150; const double kNodeTileHeight = 24; enum NodeType { @@ -63,7 +62,7 @@ enum NodeKind { key: NodeKindKey.signal, label: 'Signal', abbr: 'S', - color: Colors.green, + color: Colors.deepPurpleAccent, ); const NodeKind({ diff --git a/packages/reactter_devtools_extension/lib/src/controllers/node_details_controller.dart b/packages/reactter_devtools_extension/lib/src/controllers/node_details_controller.dart index 03840c2d..d82027fd 100644 --- a/packages/reactter_devtools_extension/lib/src/controllers/node_details_controller.dart +++ b/packages/reactter_devtools_extension/lib/src/controllers/node_details_controller.dart @@ -1,24 +1,45 @@ +import 'dart:async'; +import 'dart:collection'; + import 'package:flutter_reactter/reactter.dart'; import 'package:reactter_devtools_extension/src/bases/node.dart'; import 'package:reactter_devtools_extension/src/bases/tree_list.dart'; +import 'package:reactter_devtools_extension/src/utils/helper.dart'; class NodeDetailsController { + bool _isMarked = false; + FutureOr? _futureLoadDetails; + final LinkedHashMap _nodeRefs = LinkedHashMap(); + final uCurrentNode = UseState(null); final detailNodeList = TreeList(); Future loadDetails(Node node) async { - await Rt.batch(() async { + if (_isMarked) return; + + if (_futureLoadDetails != null) { + _isMarked = true; + await _futureLoadDetails; + } + + await (_futureLoadDetails = Rt.batch(() async { + if (uCurrentNode.value != node) { + _nodeRefs.clear(); + detailNodeList.clear(); + } + uCurrentNode.value = node; - final detailNodes = await node.getDetails(); + final nodes = await node.getDetails(); - detailNodeList.clear(); - detailNodeList.addAll(detailNodes); + addNodes(_nodeRefs, nodes, (node) { + node.uIsExpanded.value = true; + detailNodeList.add(node); + }); + })); - for (var detailNode in detailNodeList) { - detailNode.uIsExpanded.value = true; - } - }); + _futureLoadDetails = null; + _isMarked = false; } void reloadDetails() async { diff --git a/packages/reactter_devtools_extension/lib/src/nodes/dart/closure_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/dart/closure_node.dart new file mode 100644 index 00000000..d14b339e --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/nodes/dart/closure_node.dart @@ -0,0 +1,101 @@ +import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/bases/async_node.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/bases/node_info.dart'; +import 'package:reactter_devtools_extension/src/constants.dart'; +import 'package:reactter_devtools_extension/src/nodes/dart/key_value_node.dart'; +import 'package:reactter_devtools_extension/src/utils/extensions.dart'; +import 'package:vm_service/vm_service.dart'; + +final class ClosureNode extends AsyncNode { + Future? futureFuncRef; + + ClosureNode.$({ + required super.key, + required super.instanceRef, + }); + + factory ClosureNode({required String key, required InstanceRef instanceRef}) { + return Rt.createState( + () => ClosureNode.$( + key: key, + instanceRef: instanceRef, + ), + ); + } + + Future getFuncRef() async { + final instance = await instanceRef.safeGetInstance(isAlive); + return instance?.closureFunction; + } + + @override + Future loadNode() async { + await super.loadNode(); + futureFuncRef = null; + } + + @override + Future> getDetails() async { + final funcRef = await (futureFuncRef ??= getFuncRef()); + + final name = funcRef?.name; + final location = funcRef?.location?.script?.uri; + final locationLine = funcRef?.location?.line; + final locationColumn = funcRef?.location?.column; + + return [ + if (name != null) + KeyValueNode( + key: 'name', + value: '"$name"', + kind: InstanceKind.kString, + ), + if (location != null) + KeyValueNode( + key: 'location', + value: '"$location"', + kind: InstanceKind.kString, + ), + if (locationLine != null) + KeyValueNode( + key: 'locationLine', + value: "$locationLine", + kind: InstanceKind.kInt, + ), + if (locationColumn != null) + KeyValueNode( + key: 'locationColumn', + value: '$locationColumn', + kind: InstanceKind.kInt, + ), + ]; + } + + @override + Future getNodeInfo() async { + final funcRef = await (futureFuncRef ??= getFuncRef()); + + final name = funcRef?.name; + final location = funcRef?.location?.script?.uri; + final locationLine = funcRef?.location?.line; + final locationColumn = funcRef?.location?.column; + final locationStr = "$location ${[ + locationLine, + locationColumn + ].where((e) => e != null).join(':')}"; + + final value = name != null + ? "Function($name) $locationStr" + : "Unknown - Cannot load value"; + + return NodeInfo( + this, + nodeKind: NodeKind.getKind(kind), + type: 'Function', + identify: name, + identityHashCode: locationStr, + value: value, + ); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/nodes/dart/iterable_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/dart/iterable_node.dart new file mode 100644 index 00000000..a31262a1 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/nodes/dart/iterable_node.dart @@ -0,0 +1,72 @@ +import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/bases/async_node.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/bases/node_info.dart'; +import 'package:reactter_devtools_extension/src/constants.dart'; +import 'package:reactter_devtools_extension/src/utils/extensions.dart'; +import 'package:vm_service/vm_service.dart'; + +final class IterableNode extends AsyncNode { + Future>? futureElementNodes; + + IterableNode.$({ + required super.key, + required super.instanceRef, + }); + + factory IterableNode({ + required String key, + required InstanceRef instanceRef, + }) { + return Rt.createState( + () => IterableNode.$( + key: key, + instanceRef: instanceRef, + ), + ); + } + + Future> getElementNodes() async { + final instance = await instanceRef.safeGetInstance(isAlive); + final elements = instance?.elements?.cast() ?? []; + final elementNodes = []; + + for (var i = 0; i < elements.length; i++) { + final element = elements[i]; + final node = element.getNode(i.toString()); + elementNodes.add(node); + } + + return elementNodes; + } + + @override + Future loadNode() async { + await super.loadNode(); + futureElementNodes = null; + } + + @override + Future getNodeInfo() async { + final nodes = await (futureElementNodes ??= getElementNodes()); + final value = switch (kind) { + InstanceKind.kList => "List(length: ${nodes.length})", + InstanceKind.kSet => "Set(length: ${nodes.length})", + InstanceKind.kMap => "Map(length: ${nodes.length})", + _ => "Iterable(length: ${nodes.length})", + }; + + return NodeInfo( + this, + nodeKind: NodeKind.getKind(kind), + type: kind, + identify: "length: ${nodes.length}", + value: value, + ); + } + + @override + Future> getDetails() async { + return await (futureElementNodes ??= getElementNodes()); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/nodes/dart/key_value_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/dart/key_value_node.dart new file mode 100644 index 00000000..b92657d1 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/nodes/dart/key_value_node.dart @@ -0,0 +1,38 @@ +import 'package:flutter_reactter/reactter.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/bases/node_info.dart'; + +final class KeyValueNode extends Node { + String _value; + + String get value => _value; + set value(String value) { + if (_value == value) return; + + _value = value; + uInfo.value = NodeInfo(this, type: kind, value: _value); + } + + KeyValueNode.$({ + required super.key, + required super.kind, + required String value, + }) : _value = value { + uInfo.value = NodeInfo(this, type: kind, value: _value); + } + + factory KeyValueNode({ + required String key, + required String value, + required String kind, + }) { + return Rt.createState( + () => KeyValueNode.$(key: key, value: value, kind: kind), + ); + } + + @override + Future> getDetails() async { + return []; + } +} diff --git a/packages/reactter_devtools_extension/lib/src/nodes/dart/map_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/dart/map_node.dart new file mode 100644 index 00000000..0d407091 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/nodes/dart/map_node.dart @@ -0,0 +1,63 @@ +import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/bases/async_node.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/bases/node_info.dart'; +import 'package:reactter_devtools_extension/src/constants.dart'; +import 'package:reactter_devtools_extension/src/utils/extensions.dart'; +import 'package:vm_service/vm_service.dart'; + +final class MapNode extends AsyncNode { + Future>? futureEntryNodes; + + MapNode.$({ + required super.key, + required super.instanceRef, + }); + + factory MapNode({required String key, required InstanceRef instanceRef}) { + return Rt.createState( + () => MapNode.$(key: key, instanceRef: instanceRef), + ); + } + + Future> getEntryNodes() async { + final instance = await instanceRef.safeGetInstance(isAlive); + final associations = instance?.associations?.cast() ?? []; + final entryNodes = []; + + for (final entry in associations) { + final keyRef = entry.key as InstanceRef; + final keyStr = await keyRef.safeValue(isAlive); + final valueRef = entry.value as InstanceRef; + + entryNodes.add(valueRef.getNode(keyStr)); + } + + return entryNodes; + } + + @override + Future loadNode() async { + await super.loadNode(); + futureEntryNodes = null; + } + + @override + Future getNodeInfo() async { + final nodes = await (futureEntryNodes ??= getEntryNodes()); + final value = "Map(length: ${nodes.length})"; + + return NodeInfo( + this, + nodeKind: NodeKind.getKind(kind), + type: kind, + identify: "length: ${nodes.length}", + value: value, + ); + } + + @override + Future> getDetails() async { + return await (futureEntryNodes ??= getEntryNodes()); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/nodes/dart/null_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/dart/null_node.dart deleted file mode 100644 index 18b5c92a..00000000 --- a/packages/reactter_devtools_extension/lib/src/nodes/dart/null_node.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter_reactter/reactter.dart'; -import 'package:reactter_devtools_extension/src/bases/node.dart'; -import 'package:reactter_devtools_extension/src/bases/node_info.dart'; -import 'package:vm_service/vm_service.dart'; - -final class NullNode extends Node { - NullNode.$({required super.key}) : super(kind: InstanceKind.kNull) { - uInfo.value = NodeInfo( - this, - type: 'null', - identify: 'null', - value: 'null', - ); - } - - factory NullNode({required String key}) { - return Rt.createState( - () => NullNode.$(key: key), - ); - } - - @override - Future> getDetails() async { - return []; - } -} diff --git a/packages/reactter_devtools_extension/lib/src/nodes/dart/plain_instance_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/dart/plain_instance_node.dart index b585acef..875fc5b5 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/dart/plain_instance_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/dart/plain_instance_node.dart @@ -10,9 +10,11 @@ import 'package:reactter_devtools_extension/src/services/eval_service.dart'; import 'package:reactter_devtools_extension/src/utils/extensions.dart'; import 'package:vm_service/vm_service.dart'; -base class PlainInstanceNode extends AsyncNode { - PlainInstanceNode._({required super.key, required super.instanceRef}) - : super(kind: InstanceKind.kPlainInstance); +base class PlainInstanceNode extends AsyncNode { + PlainInstanceNode._({ + required super.key, + required super.instanceRef, + }); factory PlainInstanceNode({ required String key, @@ -48,8 +50,9 @@ base class PlainInstanceNode extends AsyncNode { final String type = instanceInfoMap['type']; final String? id = instanceInfoMap['id']; final String? debugLabel = instanceInfoMap['debugLabel']; - final String? identify = id ?? debugLabel; - final String value = + final String? value = instanceInfoMap['value']; + final String? identify = id ?? debugLabel ?? value; + final String formattedValue = identify != null ? "$type($identify) #$key" : "$type #$key"; final nodeKind = NodeKind.getKind(kind)!; @@ -62,6 +65,7 @@ base class PlainInstanceNode extends AsyncNode { type: type, identify: id, mode: mode, + value: formattedValue, ); case NodeKind.instance: final dependencyKey = instanceInfoMap['dependencyKey']; @@ -70,7 +74,9 @@ base class PlainInstanceNode extends AsyncNode { this, type: type, identityHashCode: key, + identify: value, dependencyKey: dependencyKey, + value: formattedValue, ); case NodeKind.state: case NodeKind.hook: @@ -81,7 +87,7 @@ base class PlainInstanceNode extends AsyncNode { type: type, identify: debugLabel, identityHashCode: key, - value: value, + value: formattedValue, debugLabel: debugLabel, ); default: @@ -89,14 +95,15 @@ base class PlainInstanceNode extends AsyncNode { this, nodeKind: NodeKind.getKind(kind), type: type, + identify: value, identityHashCode: key, - value: value, + value: formattedValue, ); } } @override - Future>> getDetails() async { + Future> getDetails() async { final instance = await instanceRef.safeGetInstance(isAlive); final fields = instance?.fields?.cast() ?? []; final nodes = fields.map((field) { diff --git a/packages/reactter_devtools_extension/lib/src/nodes/dart/record_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/dart/record_node.dart new file mode 100644 index 00000000..5d4fb311 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/nodes/dart/record_node.dart @@ -0,0 +1,65 @@ +import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/bases/async_node.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/bases/node_info.dart'; +import 'package:reactter_devtools_extension/src/constants.dart'; +import 'package:reactter_devtools_extension/src/utils/extensions.dart'; +import 'package:vm_service/vm_service.dart'; + +final class RecordNode extends AsyncNode { + Future>? futureFieldNodes; + + RecordNode.$({ + required super.key, + required super.instanceRef, + }); + + factory RecordNode({required String key, required InstanceRef instanceRef}) { + return Rt.createState( + () => RecordNode.$( + key: key, + instanceRef: instanceRef, + ), + ); + } + + Future> getFieldNodes() async { + final instance = await instanceRef.safeGetInstance(isAlive); + final fields = instance?.fields?.cast() ?? []; + final fieldNodes = []; + + for (final field in fields) { + final node = (field.value as InstanceRef).getNode("${field.name}"); + fieldNodes.add(node); + } + + return fieldNodes; + } + + @override + Future loadNode() async { + await super.loadNode(); + futureFieldNodes = null; + } + + @override + Future> getDetails() async { + return await (futureFieldNodes ??= getFieldNodes()); + } + + @override + Future getNodeInfo() async { + if (uNeedToLoadNode.value) return null; + + final nodes = await (futureFieldNodes ??= getFieldNodes()); + final value = "Record(length: ${nodes.length})"; + + return NodeInfo( + this, + nodeKind: NodeKind.getKind(kind), + type: kind, + identify: "length: ${nodes.length}", + value: value, + ); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart index 80f19dbc..a8d76fb9 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart @@ -20,9 +20,11 @@ base class InstanceNode extends Node { try { final eval = await EvalService.devtoolsEval; final dependencyKey = uInfo.value?.dependencyKey; - final dependencyRef = await eval.safeEval( - 'RtDevTools._instance?.getDependencyRef("$dependencyKey")', - isAlive: isAlive, + final dependencyRef = await EvalService.evalsQueue.add( + () => eval.safeEval( + 'RtDevTools._instance?.getDependencyRef("$dependencyKey")', + isAlive: isAlive, + ), ); return dependencyRef.getNode('dependency'); } catch (e) { @@ -36,4 +38,9 @@ base class InstanceNode extends Node { Future>> getDetails() async => [ await getDependency(), ].whereType().toList(); + + @override + void markToLoadNode(covariant Function? onUpdate) { + onUpdate?.call(); + } } diff --git a/packages/reactter_devtools_extension/lib/src/nodes/sentinel_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/sentinel_node.dart index 2540ce61..7522ee0f 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/sentinel_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/sentinel_node.dart @@ -13,11 +13,19 @@ final class SentinelNode extends Node { @override Future loadNode() { + assert(false, 'SentinelNode should not be loaded'); throw UnimplementedError(); } @override Future>> getDetails() { + assert(false, 'SentinelNode should not be expanded'); + throw UnimplementedError(); + } + + @override + void markToLoadNode(covariant Function? onUpdate) { + assert(false, 'SentinelNode should not be updated'); throw UnimplementedError(); } } diff --git a/packages/reactter_devtools_extension/lib/src/nodes/slot_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/slot_node.dart index 62f10d3d..b8c3ccd5 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/slot_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/slot_node.dart @@ -21,4 +21,9 @@ final class SlotNode extends Node { // TODO: implement getDetails throw UnimplementedError(); } + + @override + void markToLoadNode(covariant Function? onUpdate) { + // TODO: implement updateNode + } } diff --git a/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart b/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart index 4d059967..796f2dd8 100644 --- a/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart +++ b/packages/reactter_devtools_extension/lib/src/reactter_devtools_extension.dart @@ -77,7 +77,7 @@ class RtDevToolsExtension extends StatelessWidget { "Details of ", ), InstanceTitle( - nodeKey: selectedNode.key, + identifyHashCode: selectedNode.key, type: nodeInfo?.type, nodeKind: nodeInfo?.nodeKind, label: nodeInfo?.identify, diff --git a/packages/reactter_devtools_extension/lib/src/utils/color_palette.dart b/packages/reactter_devtools_extension/lib/src/utils/color_palette.dart new file mode 100644 index 00000000..884ad249 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/utils/color_palette.dart @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:vm_service/vm_service.dart'; + +class ColorPalette { + final Color selected; + final Color key; + final Color type; + final Color label; + final Color identifyHashCode; + final Color nullValue; + final Color boolValue; + final Color stringValue; + final Color numberValue; + + const ColorPalette._({ + required this.selected, + required this.key, + required this.type, + required this.label, + required this.identifyHashCode, + required this.nullValue, + required this.boolValue, + required this.stringValue, + required this.numberValue, + }); + + static ColorPalette? dark; + static ColorPalette? light; + + factory ColorPalette._dark(BuildContext context) { + return ColorPalette._( + selected: Theme.of(context).primaryColorDark, + key: Theme.of(context).colorScheme.secondary, + type: Colors.amber, + label: Theme.of(context).colorScheme.primary, + identifyHashCode: Colors.grey, + nullValue: Colors.grey, + boolValue: Colors.lightBlue.shade300, + stringValue: Colors.green, + numberValue: Colors.orange, + ); + } + + factory ColorPalette._light(BuildContext context) { + return ColorPalette._( + selected: Theme.of(context).primaryColorLight, + key: Theme.of(context).colorScheme.secondary, + type: Colors.amber.shade800, + label: Theme.of(context).colorScheme.primary, + identifyHashCode: Colors.grey.shade800, + nullValue: Colors.lightBlue.shade700, + boolValue: Colors.purple.shade800, + stringValue: Colors.green.shade800, + numberValue: Colors.orange.shade800, + ); + } + + static ColorPalette of(BuildContext context) { + final theme = Theme.of(context); + + if (theme.brightness == Brightness.dark) { + return ColorPalette.dark ??= ColorPalette._dark(context); + } + + return ColorPalette.light ??= ColorPalette._light(context); + } + + static Color? getColorForNodeValue(BuildContext context, Node node) { + switch (node.kind) { + case InstanceKind.kNull: + return ColorPalette.of(context).nullValue; + case InstanceKind.kBool: + return ColorPalette.of(context).boolValue; + case InstanceKind.kDouble: + case InstanceKind.kInt: + return ColorPalette.of(context).numberValue; + case InstanceKind.kString: + return ColorPalette.of(context).stringValue; + default: + return null; + } + } +} diff --git a/packages/reactter_devtools_extension/lib/src/utils/extensions.dart b/packages/reactter_devtools_extension/lib/src/utils/extensions.dart index 60deca3a..aaad99e9 100644 --- a/packages/reactter_devtools_extension/lib/src/utils/extensions.dart +++ b/packages/reactter_devtools_extension/lib/src/utils/extensions.dart @@ -1,7 +1,11 @@ import 'package:devtools_app_shared/service.dart' hide SentinelException; import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/nodes/dart/closure_node.dart'; +import 'package:reactter_devtools_extension/src/nodes/dart/iterable_node.dart'; +import 'package:reactter_devtools_extension/src/nodes/dart/key_value_node.dart'; +import 'package:reactter_devtools_extension/src/nodes/dart/map_node.dart'; import 'package:reactter_devtools_extension/src/nodes/dart/plain_instance_node.dart'; -import 'package:reactter_devtools_extension/src/nodes/dart/null_node.dart'; +import 'package:reactter_devtools_extension/src/nodes/dart/record_node.dart'; import 'package:reactter_devtools_extension/src/services/eval_service.dart'; import 'package:vm_service/vm_service.dart'; @@ -83,9 +87,13 @@ extension InstanceExt on Instance { case InstanceKind.kString: return '"$valueAsString"'; case InstanceKind.kMap: - return '{...}'; + return 'Map{...}(length: ${associations?.length})'; case InstanceKind.kList: - return '[...]'; + return 'List[...](length: ${elements?.length})'; + case InstanceKind.kSet: + return 'Set{...}(length: ${elements?.length})'; + case InstanceKind.kRecord: + return 'Record(...)(length: ${fields?.length})'; default: return valueAsString ?? 'unknown'; } @@ -94,15 +102,44 @@ extension InstanceExt on Instance { extension InstanceRefExt on InstanceRef { Node getNode(String key) { - switch (kind) { - case InstanceKind.kNull: - return NullNode(key: key); - case InstanceKind.kPlainInstance: - return PlainInstanceNode(key: key, instanceRef: this); - case InstanceKind.kClosure: - default: - return PlainInstanceNode(key: key, instanceRef: this); - } + return switch (kind) { + InstanceKind.kNull || + InstanceKind.kBool || + InstanceKind.kDouble || + InstanceKind.kInt => + KeyValueNode( + kind: kind!, + key: key, + value: '$valueAsString', + ), + InstanceKind.kString => KeyValueNode( + kind: kind!, + key: key, + value: '"$valueAsString"', + ), + InstanceKind.kMap => MapNode(key: key, instanceRef: this), + InstanceKind.kList => IterableNode( + key: key, + instanceRef: this, + ), + InstanceKind.kSet => IterableNode( + key: key, + instanceRef: this, + ), + InstanceKind.kRecord => RecordNode( + key: key, + instanceRef: this, + ), + InstanceKind.kClosure => ClosureNode( + key: key, + instanceRef: this, + ), + InstanceKind.kPlainInstance => PlainInstanceNode( + key: key, + instanceRef: this, + ), + _ => PlainInstanceNode(key: key, instanceRef: this), + }; } Future safeGetInstance([ @@ -111,7 +148,6 @@ extension InstanceRefExt on InstanceRef { ]) async { try { final eval = await EvalService.devtoolsEval; - final instance = await EvalService.evalsQueue.add( () => eval.safeGetInstance(this, isAlive), ); diff --git a/packages/reactter_devtools_extension/lib/src/utils/helper.dart b/packages/reactter_devtools_extension/lib/src/utils/helper.dart new file mode 100644 index 00000000..001534ba --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/utils/helper.dart @@ -0,0 +1,41 @@ +import 'dart:collection'; + +import 'package:reactter_devtools_extension/src/bases/async_node.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/nodes/dart/key_value_node.dart'; + +void addNodes( + LinkedHashMap nodeRefs, + List nodes, + void Function(Node node) onAdd, +) { + final nodeToRemove = nodeRefs.keys.toSet(); + + for (final node in nodes) { + final nodeRef = nodeRefs[node.key]; + final isEqual = node.kind == nodeRef?.kind; + + if (isEqual) { + if (nodeRef is AsyncNode) { + nodeRef.updateInstanceRef((node as AsyncNode).instanceRef); + } else if (nodeRef is KeyValueNode) { + nodeRef.value = (node as KeyValueNode).value; + } + } else { + if (nodeRef != null) { + nodeRef.replaceFor(node); + } else { + onAdd(node); + } + + nodeRefs.putIfAbsent(node.key, () => node); + } + + nodeToRemove.remove(node.key); + } + + for (final key in nodeToRemove) { + final node = nodeRefs.remove(key); + node?.remove(); + } +} diff --git a/packages/reactter_devtools_extension/lib/src/widgets/dependency_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/dependency_tile.dart index 1e114f1d..1e81cef9 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/dependency_tile.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/dependency_tile.dart @@ -44,7 +44,7 @@ class NodeTileTitle extends StatelessWidget { final nodeInfo = watch(node.uInfo).value; return InstanceTitle( - nodeKey: node.key, + identifyHashCode: node.key, nodeKind: nodeInfo?.nodeKind, type: nodeInfo?.type, label: nodeInfo?.identify, diff --git a/packages/reactter_devtools_extension/lib/src/widgets/detail_node_list.dart b/packages/reactter_devtools_extension/lib/src/widgets/detail_node_list.dart index 6cec4f82..1fd977d7 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/detail_node_list.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/detail_node_list.dart @@ -38,7 +38,10 @@ class DetailNodeList extends StatelessWidget { (context, index) { final detailNode = detailNodeList.elementAt(index); - return DetailNodeTile(node: detailNode); + return DetailNodeTile( + key: ObjectKey(detailNode), + node: detailNode, + ); }, childCount: length, ), diff --git a/packages/reactter_devtools_extension/lib/src/widgets/detail_node_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/detail_node_tile.dart index ab896fdd..6aa9baaa 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/detail_node_tile.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/detail_node_tile.dart @@ -2,20 +2,19 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; import 'package:reactter_devtools_extension/src/bases/async_node.dart'; import 'package:reactter_devtools_extension/src/bases/node.dart'; -import 'package:reactter_devtools_extension/src/controllers/nodes_controller.dart'; -import 'package:reactter_devtools_extension/src/nodes/instance/instance_info.dart'; -import 'package:reactter_devtools_extension/src/widgets/instance_title.dart'; +import 'package:reactter_devtools_extension/src/utils/color_palette.dart'; import 'package:reactter_devtools_extension/src/widgets/loading.dart'; +import 'package:reactter_devtools_extension/src/widgets/node_title.dart'; import 'package:reactter_devtools_extension/src/widgets/tile_builder.dart'; class DetailNodeTile extends StatelessWidget { + final Node node; + const DetailNodeTile({ super.key, required this.node, }); - final Node node; - @override Widget build(BuildContext context) { return TreeNodeTileBuilder( @@ -27,45 +26,22 @@ class DetailNodeTile extends StatelessWidget { style: Theme.of(context) .textTheme .labelSmall - ?.copyWith(color: Theme.of(context).colorScheme.primary), + ?.copyWith(color: ColorPalette.of(context).key), ), RtWatcher((context, watch) { if (node is AsyncNode) { final asyncNode = node as AsyncNode; - final isLoading = watch(asyncNode.uIsLoading).value; + final isLoading = watch(asyncNode.uIsLoading).value; if (isLoading) return const Loading(); - if (watch(asyncNode.uInfo).value == null) { - asyncNode.loadNode(); - } - } - - final nodeInfo = watch(node.uInfo).value; - final value = nodeInfo?.value; - final dependencyKey = - nodeInfo is InstanceInfo ? nodeInfo.dependencyKey : null; - - if (nodeInfo != null) { - final nodesController = context.use(); - final nodeKey = nodeInfo.identityHashCode ?? node.key; - final nodeOrigin = nodesController.uNodes.value[nodeKey]; - - return InstanceTitle( - nodeKey: nodeKey, - type: nodeInfo.type, - nodeKind: nodeInfo.nodeKind, - label: nodeInfo.identify, - isDependency: dependencyKey != null, - onTapIcon: nodeOrigin != null - ? () => nodesController.selectNodeByKey(nodeKey) - : null, - ); + final isNeedToLoadNode = watch(asyncNode.uNeedToLoadNode).value; + if (isNeedToLoadNode) asyncNode.loadNode(); } - return Text( - value ?? '...', - style: Theme.of(context).textTheme.labelSmall, + return NodeTitle( + key: ObjectKey(node), + node: node, ); }), ], diff --git a/packages/reactter_devtools_extension/lib/src/widgets/instance_title.dart b/packages/reactter_devtools_extension/lib/src/widgets/instance_title.dart index 59e35101..9c4ebb85 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/instance_title.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/instance_title.dart @@ -1,9 +1,10 @@ import 'package:flutter/material.dart'; import 'package:reactter_devtools_extension/src/constants.dart'; +import 'package:reactter_devtools_extension/src/utils/color_palette.dart'; import 'package:reactter_devtools_extension/src/widgets/instance_icon.dart'; class InstanceTitle extends StatelessWidget { - final String nodeKey; + final String? identifyHashCode; final String? type; final NodeKind? nodeKind; final String? label; @@ -11,8 +12,8 @@ class InstanceTitle extends StatelessWidget { final void Function()? onTapIcon; const InstanceTitle({ - required this.nodeKey, super.key, + this.identifyHashCode, this.type, this.nodeKind, this.label, @@ -43,7 +44,7 @@ class InstanceTitle extends StatelessWidget { style: Theme.of(context) .textTheme .labelSmall - ?.copyWith(color: Colors.amber), + ?.copyWith(color: ColorPalette.of(context).type), ), if (label != null) TextSpan( @@ -52,18 +53,19 @@ class InstanceTitle extends StatelessWidget { TextSpan( text: label!, style: Theme.of(context).textTheme.labelSmall?.copyWith( - color: Theme.of(context).colorScheme.primary, + color: ColorPalette.of(context).label, ), ), const TextSpan(text: ")"), ], ), - TextSpan( - text: " #$nodeKey", - style: Theme.of(context).textTheme.labelSmall?.copyWith( - color: Theme.of(context).colorScheme.secondary, - ), - ), + if (identifyHashCode != null) + TextSpan( + text: " #$identifyHashCode", + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: ColorPalette.of(context).identifyHashCode, + ), + ), ], ), ), diff --git a/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart b/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart index c59ae92a..2196b8da 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/node_tile.dart @@ -46,7 +46,7 @@ class NodeTileTitle extends StatelessWidget { nodeInfo is InstanceInfo ? nodeInfo.dependencyKey : null; return InstanceTitle( - nodeKey: node.key, + identifyHashCode: node.key, nodeKind: nodeInfo?.nodeKind, type: nodeInfo?.type, label: nodeInfo?.identify, diff --git a/packages/reactter_devtools_extension/lib/src/widgets/node_title.dart b/packages/reactter_devtools_extension/lib/src/widgets/node_title.dart new file mode 100644 index 00000000..64e53064 --- /dev/null +++ b/packages/reactter_devtools_extension/lib/src/widgets/node_title.dart @@ -0,0 +1,77 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; +import 'package:reactter_devtools_extension/src/bases/node.dart'; +import 'package:reactter_devtools_extension/src/bases/node_info.dart'; +import 'package:reactter_devtools_extension/src/controllers/nodes_controller.dart'; +import 'package:reactter_devtools_extension/src/nodes/dart/closure_node.dart'; +import 'package:reactter_devtools_extension/src/nodes/dart/iterable_node.dart'; +import 'package:reactter_devtools_extension/src/nodes/dart/key_value_node.dart'; +import 'package:reactter_devtools_extension/src/nodes/dart/map_node.dart'; +import 'package:reactter_devtools_extension/src/nodes/dart/plain_instance_node.dart'; +import 'package:reactter_devtools_extension/src/nodes/dart/record_node.dart'; +import 'package:reactter_devtools_extension/src/nodes/instance/instance_info.dart'; +import 'package:reactter_devtools_extension/src/utils/color_palette.dart'; +import 'package:reactter_devtools_extension/src/widgets/instance_title.dart'; + +class NodeTitle extends StatelessWidget { + final Node node; + + const NodeTitle({super.key, required this.node}); + + @override + Widget build(BuildContext context) { + context.watch((_) => [node.uInfo]); + + final nodeInfo = node.uInfo.value; + final nodeKey = nodeInfo?.identityHashCode; + + if (nodeInfo == null) { + return Text( + '...', + style: Theme.of(context).textTheme.labelSmall, + ); + } + + Widget getInstanceTitle(NodeInfo? nodeInfo) { + final nodesController = context.use(); + final nodeOrigin = nodesController.uNodes.value[nodeKey]; + final nodeInfoOrigin = nodeOrigin?.uInfo.value; + final isDependency = nodeInfoOrigin is InstanceInfo && + nodeInfoOrigin.dependencyKey != null; + final onTapIcon = nodeKey != null && nodeOrigin != null + ? () => nodesController.selectNodeByKey(nodeKey) + : null; + + return InstanceTitle( + identifyHashCode: nodeKey, + type: nodeInfo?.type, + nodeKind: nodeInfo?.nodeKind, + label: nodeInfo?.identify, + isDependency: isDependency, + onTapIcon: onTapIcon, + ); + } + + return switch (node) { + KeyValueNode(uInfo: final nodeInfo) => Text( + '${nodeInfo.value?.value}', + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: ColorPalette.getColorForNodeValue(context, node), + ), + ), + PlainInstanceNode(uInfo: final nodeInfo) => + getInstanceTitle(nodeInfo.value), + MapNode(uInfo: final nodeInfo) || + IterableNode(uInfo: final nodeInfo) || + RecordNode(uInfo: final nodeInfo) || + ClosureNode(uInfo: final nodeInfo) || + Node(uInfo: final nodeInfo) => + InstanceTitle( + identifyHashCode: nodeKey, + type: nodeInfo.value?.type, + nodeKind: nodeInfo.value?.nodeKind, + label: nodeInfo.value?.identify, + ), + }; + } +} diff --git a/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart b/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart index 7f029af9..1c07e62d 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; import 'package:reactter_devtools_extension/src/bases/tree_node.dart'; +import 'package:reactter_devtools_extension/src/utils/color_palette.dart'; class TreeNodeTileBuilder extends StatelessWidget { final TreeNode treeNode; @@ -27,7 +28,7 @@ class TreeNodeTileBuilder extends StatelessWidget { contentPadding: EdgeInsets.zero, minTileHeight: 24, selected: isSelected, - selectedTileColor: Theme.of(context).focusColor, + selectedTileColor: ColorPalette.of(context).selected, onTap: onTap, title: Row( mainAxisAlignment: MainAxisAlignment.start, From 9e4ad05387af958078b1f4a3ec5b5706017ee293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 27 Dec 2024 01:36:53 -0600 Subject: [PATCH 062/141] refactor(devtools): Remove unused `markToLoadNode` method. --- .../lib/src/nodes/instance/instance_node.dart | 5 ----- .../lib/src/nodes/sentinel_node.dart | 6 ------ .../lib/src/nodes/slot_node.dart | 5 ----- 3 files changed, 16 deletions(-) diff --git a/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart index a8d76fb9..4d694d78 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart @@ -38,9 +38,4 @@ base class InstanceNode extends Node { Future>> getDetails() async => [ await getDependency(), ].whereType().toList(); - - @override - void markToLoadNode(covariant Function? onUpdate) { - onUpdate?.call(); - } } diff --git a/packages/reactter_devtools_extension/lib/src/nodes/sentinel_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/sentinel_node.dart index 7522ee0f..58ffe9ca 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/sentinel_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/sentinel_node.dart @@ -22,10 +22,4 @@ final class SentinelNode extends Node { assert(false, 'SentinelNode should not be expanded'); throw UnimplementedError(); } - - @override - void markToLoadNode(covariant Function? onUpdate) { - assert(false, 'SentinelNode should not be updated'); - throw UnimplementedError(); - } } diff --git a/packages/reactter_devtools_extension/lib/src/nodes/slot_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/slot_node.dart index b8c3ccd5..62f10d3d 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/slot_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/slot_node.dart @@ -21,9 +21,4 @@ final class SlotNode extends Node { // TODO: implement getDetails throw UnimplementedError(); } - - @override - void markToLoadNode(covariant Function? onUpdate) { - // TODO: implement updateNode - } } From a81e663ed2cdd692cd0a994a5836b1e3c69f10ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 27 Dec 2024 01:56:19 -0600 Subject: [PATCH 063/141] refactor(logger): update exports to improve clarity and remove hidden types. --- packages/reactter/lib/reactter.dart | 6 +- packages/reactter/lib/src/logger.dart | 162 +++++++++++++------------- packages/reactter/lib/src/types.dart | 5 +- 3 files changed, 88 insertions(+), 85 deletions(-) diff --git a/packages/reactter/lib/reactter.dart b/packages/reactter/lib/reactter.dart index 6f4faf88..294fd19f 100644 --- a/packages/reactter/lib/reactter.dart +++ b/packages/reactter/lib/reactter.dart @@ -1,11 +1,13 @@ library reactter; export 'src/args.dart'; -export 'src/devtools.dart' hide RtDevTools; +export 'src/devtools.dart' + hide RtDevTools, NodeKind, RtDevToolsInitializeAssertionError; export 'src/framework.dart'; export 'src/hooks/hooks.dart' hide UseAsyncStateBase; export 'src/memo/memo.dart'; export 'src/signal.dart'; -export 'src/logger.dart' hide RtLogger; +export 'src/logger.dart' + hide RtLogger, RtLoggerInitializeAssertionError, prettyFormat; export 'src/types.dart'; diff --git a/packages/reactter/lib/src/logger.dart b/packages/reactter/lib/src/logger.dart index cc775f17..8c4ef9dd 100644 --- a/packages/reactter/lib/src/logger.dart +++ b/packages/reactter/lib/src/logger.dart @@ -31,16 +31,16 @@ extension RtLoggerExt on RtInterface { @internal class RtLogger with RtStateObserver, RtDependencyObserver { - static RtLogger? _instance; + static RtLogger? instance; static void initialize({ String name = 'REACTTER', LogOutput output = dev.log, }) { - assert(_instance == null, 'The logger has already been initialized.'); + assert(instance == null, 'The logger has already been initialized.'); if (kDebugMode) { - _instance ??= RtLogger._(name: name, output: output); + instance ??= RtLogger._(name: name, output: output); } } @@ -63,43 +63,10 @@ class RtLogger with RtStateObserver, RtDependencyObserver { ); } - String _prettyFormat(Object? instance) { - if (instance is DependencyRef) { - final type = instance.type.toString().replaceAll('?', ''); - final id = instance.id; - final idStr = id != null ? "id: '$id'" : null; - final mode = instance is DependencyRegister - ? instance.mode.label - : Rt.getDependencyRegisterByRef(instance)?.mode.label; - final modeStr = mode != null ? "mode: '$mode'" : null; - final params = [ - if (idStr != null) idStr, - if (modeStr != null) modeStr, - ].join(', '); - final paramsStr = params.isNotEmpty ? '($params)' : ''; - - return '[DEPENDENCY | $type$paramsStr]'; - } - - if (instance is RtState) { - final type = instance.runtimeType.toString(); - final label = instance.debugLabel; - final labelStr = label != null ? "(label: '$label')" : ''; - - if (instance is RtHook) { - return '[HOOK | $type$labelStr | #${instance.hashCode}]'; - } - - return '[STATE | $type$labelStr | #${instance.hashCode}]'; - } - - return '[UNKNOWN | ${instance.runtimeType} | #${instance.hashCode}]'; - } - @override void onStateCreated(RtState state) { log( - '${_prettyFormat(state)} created.', + '${prettyFormat(state)} created.', level: LogLevel.finer, stackTrace: StackTrace.current, ); @@ -108,7 +75,7 @@ class RtLogger with RtStateObserver, RtDependencyObserver { @override void onStateBound(RtState state, Object instance) { log( - '${_prettyFormat(state)} bound to ${_prettyFormat(instance)}.', + '${prettyFormat(state)} bound to ${prettyFormat(instance)}.', level: LogLevel.finer, stackTrace: StackTrace.current, ); @@ -117,14 +84,14 @@ class RtLogger with RtStateObserver, RtDependencyObserver { final T = instance.runtimeType; log( - 'The bound instance(${_prettyFormat(instance)}) to state(${_prettyFormat(state)}) is not in Reactter\'s context and cannot be disposed automatically.\n' - 'You can solve this problem in one of the following ways:\n' - '\t- Call `dispose` method manually when state is no longer needed:\n' - '\t\t`state.dispose();`\n' - '\t- Create bound instance using the dependency injection methods:\n' - '\t\t`Rt.register<$T>(() => $T(...));`\n' - '\t\t`Rt.create<$T>(() => $T(...));`\n' - '**Ignore this message if you are sure that it will be disposed.**', + "The bound instance(${prettyFormat(instance)}) to state(${prettyFormat(state)}) is not in Reactter's context and cannot be disposed automatically.\n" + "You can solve this problem in one of the following ways:\n" + "\t- Call `dispose` method manually when state is no longer needed:\n" + "\t\t`state.dispose();`\n" + "\t- Create bound instance using the dependency injection methods:\n" + "\t\t`Rt.register<$T>(() => $T(...));`\n" + "\t\t`Rt.create<$T>(() => $T(...));`\n" + "**Ignore this message if you are sure that it will be disposed.**", level: LogLevel.warning, stackTrace: StackTrace.current, ); @@ -134,7 +101,7 @@ class RtLogger with RtStateObserver, RtDependencyObserver { @override void onStateUnbound(RtState state, Object instance) { log( - '${_prettyFormat(state)} unbound from ${_prettyFormat(instance)}.', + '${prettyFormat(state)} unbound from ${prettyFormat(instance)}.', level: LogLevel.finer, stackTrace: StackTrace.current, ); @@ -143,7 +110,7 @@ class RtLogger with RtStateObserver, RtDependencyObserver { @override void onStateUpdated(RtState state) { log( - '${_prettyFormat(state)} updated.', + '${prettyFormat(state)} updated.', level: LogLevel.finer, stackTrace: StackTrace.current, ); @@ -152,7 +119,7 @@ class RtLogger with RtStateObserver, RtDependencyObserver { @override void onStateDisposed(RtState state) { log( - '${_prettyFormat(state)} disposed.', + '${prettyFormat(state)} disposed.', level: LogLevel.finer, stackTrace: StackTrace.current, ); @@ -161,7 +128,7 @@ class RtLogger with RtStateObserver, RtDependencyObserver { @override void onDependencyRegistered(DependencyRef dependency) { log( - '${_prettyFormat(dependency)} registered.', + '${prettyFormat(dependency)} registered.', level: LogLevel.fine, stackTrace: StackTrace.current, ); @@ -170,7 +137,7 @@ class RtLogger with RtStateObserver, RtDependencyObserver { @override void onDependencyCreated(DependencyRef dependency, Object? instance) { log( - '${_prettyFormat(dependency)} created. Its instance: ${_prettyFormat(instance)}.', + '${prettyFormat(dependency)} created. Its instance: ${prettyFormat(instance)}.', level: LogLevel.fine, stackTrace: StackTrace.current, ); @@ -179,7 +146,7 @@ class RtLogger with RtStateObserver, RtDependencyObserver { @override void onDependencyMounted(DependencyRef dependency, Object? instance) { log( - '${_prettyFormat(dependency)} mounted. Its instance: ${_prettyFormat(instance)}.', + '${prettyFormat(dependency)} mounted. Its instance: ${prettyFormat(instance)}.', level: LogLevel.fine, stackTrace: StackTrace.current, ); @@ -188,7 +155,7 @@ class RtLogger with RtStateObserver, RtDependencyObserver { @override void onDependencyUnmounted(DependencyRef dependency, Object? instance) { log( - '${_prettyFormat(dependency)} unmounted. Its instance: ${_prettyFormat(instance)}.', + '${prettyFormat(dependency)} unmounted. Its instance: ${prettyFormat(instance)}.', level: LogLevel.fine, stackTrace: StackTrace.current, ); @@ -197,7 +164,7 @@ class RtLogger with RtStateObserver, RtDependencyObserver { @override void onDependencyDeleted(DependencyRef dependency, Object? instance) { log( - '${_prettyFormat(dependency)} deleted. Its instance: ${_prettyFormat(instance)}.', + '${prettyFormat(dependency)} deleted. Its instance: ${prettyFormat(instance)}.', level: LogLevel.fine, stackTrace: StackTrace.current, ); @@ -206,7 +173,7 @@ class RtLogger with RtStateObserver, RtDependencyObserver { @override void onDependencyUnregistered(DependencyRef dependency) { log( - '${_prettyFormat(dependency)} unregistered.', + '${prettyFormat(dependency)} unregistered.', level: LogLevel.fine, stackTrace: StackTrace.current, ); @@ -224,67 +191,63 @@ class RtLogger with RtStateObserver, RtDependencyObserver { switch (fail) { case DependencyFail.alreadyRegistered: log( - '${_prettyFormat(dependency)} already registered.', + '${prettyFormat(dependency)} already registered.', level: LogLevel.info, stackTrace: StackTrace.current, ); break; case DependencyFail.alreadyCreated: log( - '${_prettyFormat(dependency)} already created.', + '${prettyFormat(dependency)} already created.', level: LogLevel.info, stackTrace: StackTrace.current, ); break; case DependencyFail.alreadyDeleted: log( - '${_prettyFormat(dependency)} already deleted.', + '${prettyFormat(dependency)} already deleted.', level: LogLevel.info, stackTrace: StackTrace.current, ); break; case DependencyFail.alreadyUnregistered: log( - '${_prettyFormat(dependency)} already unregistered.', + '${prettyFormat(dependency)} already unregistered.', level: LogLevel.info, stackTrace: StackTrace.current, ); break; case DependencyFail.builderRetainedAsFactory: log( - '${_prettyFormat(dependency)}\'s instance retained because it\'s factory mode.', + "${prettyFormat(dependency)}'s instance retained because it's factory mode.", level: LogLevel.info, stackTrace: StackTrace.current, ); break; case DependencyFail.dependencyRetainedAsSingleton: log( - '${_prettyFormat(dependency)} retained because it\'s singleton mode.', + "${prettyFormat(dependency)} retained because it's singleton mode.", level: LogLevel.info, stackTrace: StackTrace.current, ); break; case DependencyFail.missingInstanceBuilder: log( - '${_prettyFormat(dependency)} couldn\'t register.\n' - 'You should register the instance build with: \n' - '\t`Rt.register<$T>(() => $T(...)$idParam);` \n' - '\t`Rt.create<$T>(() => $T(...)$idParam);`', + "${prettyFormat(dependency)} couldn't register.\n" + "You should register the instance build with: \n" + "\t`Rt.register<$T>(() => $T(...)$idParam);`\n" + "\t`Rt.create<$T>(() => $T(...)$idParam);`", level: LogLevel.warning, stackTrace: StackTrace.current, ); break; case DependencyFail.cannotUnregisterActiveInstance: - final instance = dependency is DependencyRegister - ? dependency.instance - : Rt.getDependencyRegisterByRef(dependency)?.instance; - log( - '${_prettyFormat(dependency)} couldn\'t unregister ' - 'because ${_prettyFormat(instance)} is active.\n' - 'You should delete the instance before with:\n' - '\t`Rt.delete<$T>(${id ?? ''});`\n' - '\t`Rt.destroy<$T>($idParam, onlyInstance: true);`\n', + "${prettyFormat(dependency)} couldn't unregister " + "because ${prettyFormat(instance)} is active.\n" + "You should delete the instance before with:\n" + "\t`Rt.delete<$T>(${id ?? ''});`\n" + "\t`Rt.destroy<$T>($idParam, onlyInstance: true);`\n", level: LogLevel.severe, stackTrace: StackTrace.current, ); @@ -293,16 +256,53 @@ class RtLogger with RtStateObserver, RtDependencyObserver { } } +@internal +String prettyFormat(Object? instance) { + if (instance is DependencyRef) { + final type = instance.type.toString().replaceAll('?', ''); + final id = instance.id; + final idStr = id != null ? "id: '$id'" : null; + final mode = instance is DependencyRegister + ? instance.mode.label + : Rt.getDependencyRegisterByRef(instance)?.mode.label; + final modeStr = mode != null ? "mode: '$mode'" : null; + final params = [ + if (idStr != null) idStr, + if (modeStr != null) modeStr, + ].join(', '); + final paramsStr = params.isNotEmpty ? '($params)' : ''; + + return '[DEPENDENCY | $type$paramsStr]'; + } + + if (instance is RtState) { + final type = instance.runtimeType.toString(); + final label = instance.debugLabel; + final labelStr = label != null ? "(debugLabel: '$label')" : ''; + + if (instance is RtHook) { + return '[HOOK | $type$labelStr | #${instance.hashCode}]'; + } + + return '[STATE | $type$labelStr | #${instance.hashCode}]'; + } + + return '[UNKNOWN | ${instance.runtimeType} | #${instance.hashCode}]'; +} + +@internal +typedef RtLoggerInitializeAssertionError = AssertionError; + /// Copy from `package:logging`. -/// [Level]s to control logging output. Logging can be enabled to include all -/// levels above certain [Level]. The predefined [Level] constants below are sorted as -/// follows (in descending order): [Level.shout], [Level.severe], -/// [Level.warning], [Level.info], [Level.config], [Level.fine], [Level.finer], -/// [Level.finest], and [Level.all]. +/// [LogLevel]s to control logging output. Logging can be enabled to include all +/// levels above certain [LogLevel]. The predefined [LogLevel] constants below are sorted as +/// follows (in descending order): [LogLevel.shout], [LogLevel.severe], +/// [LogLevel.warning], [LogLevel.info], [LogLevel.config], [LogLevel.fine], [LogLevel.finer], +/// [LogLevel.finest], and [LogLevel.all]. /// /// We recommend using one of the predefined logging levels. If you define your -/// own level, make sure you use a value between those used in [Level.all] and -/// [Level.off]. +/// own level, make sure you use a value between those used in [LogLevel.all] and +/// [LogLevel.off]. class LogLevel { /// Special key to turn on logging for all levels (0). static const int all = 0; diff --git a/packages/reactter/lib/src/types.dart b/packages/reactter/lib/src/types.dart index 8366b237..e57db0e5 100644 --- a/packages/reactter/lib/src/types.dart +++ b/packages/reactter/lib/src/types.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'args.dart'; import 'hooks/hooks.dart'; import 'memo/memo.dart' show Memo; +import 'logger.dart' show LogLevel; /// {@template log_output} /// An function to specify the log output destination. @@ -14,8 +15,8 @@ import 'memo/memo.dart' show Memo; /// {@endtemplate} typedef LogOutput = void Function( String message, { - String name, - int level, + required String name, + required int level, StackTrace? stackTrace, }); From 08c946447010f6ea03cb5df49074c039fc3ae39a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 27 Dec 2024 02:16:06 -0600 Subject: [PATCH 064/141] refactor(devtools): improve devtools initialization and internal class structure; enhance code clarity and maintainability. --- packages/reactter/lib/src/devtools.dart | 41 +++++++++++++++---- packages/reactter/lib/src/framework.dart | 9 ++-- .../reactter/lib/src/hooks/use_compute.dart | 4 +- .../reactter/lib/src/interfaces/context.dart | 3 ++ 4 files changed, 42 insertions(+), 15 deletions(-) diff --git a/packages/reactter/lib/src/devtools.dart b/packages/reactter/lib/src/devtools.dart index 01733069..4d8cecf6 100644 --- a/packages/reactter/lib/src/devtools.dart +++ b/packages/reactter/lib/src/devtools.dart @@ -1,3 +1,4 @@ +// coverage:ignore-file import 'dart:collection'; import 'dart:developer' as dev; @@ -8,23 +9,41 @@ import 'framework.dart'; import 'internals.dart'; extension RtDevToolsExt on RtInterface { + /// Initialize the devtools for observing the states and dependencies. + /// This will enable the devtools extension in the `Reactter` tab. void initializeDevTools() { RtDevTools.initialize(); } } +/// This class is used internally by the devtools extension only +/// and should not be used directly by the users. +/// +/// To enable it, call the [Rt.initializeDevTools] method. +/// This will initialize the devtools and start observing changes. +/// +/// To access the Reactter devtools, open the devtools extension +/// and navigate to the Reactter tab. @internal class RtDevTools with RtStateObserver, RtDependencyObserver { - static RtDevTools? _instance; + static RtDevTools? instance; final LinkedList<_Node> _nodes = LinkedList(); final LinkedHashMap _nodesByKey = LinkedHashMap(); static void initialize() { - assert(_instance == null, 'The devtools has already been initialized.'); + assert(() { + if (kDebugMode && instance != null) { + throw RtDevToolsInitializeAssertionError( + 'The devtools has already been initialized.', + ); + } + + return true; + }()); if (kDebugMode) { - _instance ??= RtDevTools._(); + instance ??= RtDevTools._(); } } @@ -339,7 +358,8 @@ class RtDevTools with RtStateObserver, RtDependencyObserver { } } -abstract class _NodeKind { +@internal +abstract class NodeKind { static const String state = 'state'; static const String hook = 'hook'; static const String signal = 'signal'; @@ -456,7 +476,7 @@ class _InstanceNode extends _Node { final type = instance.runtimeType.toString(); return { - 'kind': _NodeKind.instance, + 'kind': NodeKind.instance, 'key': instance.hashCode.toString(), 'type': type, 'value': value.startsWith("Instance of") || value == type ? null : value, @@ -487,9 +507,9 @@ class _StateNode extends _Node { _StateNode({required RtState instance}) : super(instance: instance); static String resolveKind(RtState instance) { - if (instance is Signal) return _NodeKind.signal; - if (instance is RtHook) return _NodeKind.hook; - return _NodeKind.state; + if (instance is Signal) return NodeKind.signal; + if (instance is RtHook) return NodeKind.hook; + return NodeKind.state; } static Map getInstanceInfo(RtState instance) { @@ -530,7 +550,7 @@ class _DependencyNode extends _Node { final dependencyRef = Rt.getDependencyRegisterByRef(instance); return { - 'kind': _NodeKind.dependency, + 'kind': NodeKind.dependency, 'key': instance.hashCode.toString(), 'type': instance.type.toString(), 'id': instance.id, @@ -547,3 +567,6 @@ class _DependencyNode extends _Node { }; } } + +@internal +typedef RtDevToolsInitializeAssertionError = AssertionError; diff --git a/packages/reactter/lib/src/framework.dart b/packages/reactter/lib/src/framework.dart index 7f7d18f2..51018938 100644 --- a/packages/reactter/lib/src/framework.dart +++ b/packages/reactter/lib/src/framework.dart @@ -3,14 +3,15 @@ import 'internals.dart'; export 'internals.dart' show + DependencyMode, Lifecycle, LifecycleObserver, - DependencyMode, - RtInterface, - RtStateObserver, + RtDependencyObserver, RtHook, + RtInterface, RtState, - RtStateBase; + RtStateBase, + RtStateObserver; part 'framework/rt_context.dart'; part 'framework/rt_dependency.dart'; diff --git a/packages/reactter/lib/src/hooks/use_compute.dart b/packages/reactter/lib/src/hooks/use_compute.dart index 42501539..c2eaf60e 100644 --- a/packages/reactter/lib/src/hooks/use_compute.dart +++ b/packages/reactter/lib/src/hooks/use_compute.dart @@ -60,14 +60,14 @@ class UseCompute extends RtHook { String? debugLabel, }) : _debugLabel = debugLabel, _valueComputed = compute() { - for (var dependency in dependencies.toList(growable: false)) { + for (final dependency in dependencies.toList(growable: false)) { Rt.on(dependency, Lifecycle.didUpdate, _onDependencyChanged); } } @override void dispose() { - for (var dependency in dependencies.toList(growable: false)) { + for (final dependency in dependencies.toList(growable: false)) { Rt.off(dependency, Lifecycle.didUpdate, _onDependencyChanged); } diff --git a/packages/reactter/lib/src/interfaces/context.dart b/packages/reactter/lib/src/interfaces/context.dart index b1d5b78f..674deee2 100644 --- a/packages/reactter/lib/src/interfaces/context.dart +++ b/packages/reactter/lib/src/interfaces/context.dart @@ -10,11 +10,14 @@ part of '../internals.dart'; /// - [logger]: An instance of the [Logger] class that provides logging functionality. abstract class IContext { @internal + @protected DependencyInjection get dependencyInjection; @internal + @protected StateManagement get stateManagement; @internal + @protected EventHandler get eventHandler; } From 2baadff7172969b709124eb76eeaa165f5ea29b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 27 Dec 2024 02:17:53 -0600 Subject: [PATCH 065/141] refactor(hooks): Rename status from 'standby' to 'idle'; enhance state management and error handling. --- .../lib/src/hooks/use_async_state.dart | 115 +++++++++++++----- 1 file changed, 85 insertions(+), 30 deletions(-) diff --git a/packages/reactter/lib/src/hooks/use_async_state.dart b/packages/reactter/lib/src/hooks/use_async_state.dart index 565b0b95..4745cc2a 100644 --- a/packages/reactter/lib/src/hooks/use_async_state.dart +++ b/packages/reactter/lib/src/hooks/use_async_state.dart @@ -1,7 +1,7 @@ part of 'hooks.dart'; enum UseAsyncStateStatus { - standby, + idle, loading, done, error, @@ -13,6 +13,16 @@ abstract class UseAsyncStateBase extends RtHook { @override final $ = RtHook.$register; + final String? _debugLabel; + @override + String? get debugLabel => _debugLabel ?? super.debugLabel; + @override + Map get debugInfo => { + 'value': value, + 'error': error, + 'status': status, + }; + /// Stores the initial value. final T _initialValue; @@ -20,26 +30,49 @@ abstract class UseAsyncStateBase extends RtHook { /// Need to call [resolve] to execute. final Function _asyncFunction; - final UseState _value; - final _error = UseState(null); - final _status = UseState(UseAsyncStateStatus.standby); + final UseState _uValue; + late final uValue = Rt.lazyState( + () => UseCompute(() => _uValue.value, [_uValue]), + this, + ); + T get value => _uValue.value; + + final _uError = UseState(null); + late final uError = Rt.lazyState( + () => UseCompute(() => _uError.value, [_uError]), + this, + ); + Object? get error => _uError.value; + + final _uStatus = UseState(UseAsyncStateStatus.idle); + late final uStatus = Rt.lazyState( + () => UseCompute(() => _uStatus.value, [_uStatus]), + this, + ); + UseAsyncStateStatus get status => _uStatus.value; + + late final uIsLoading = Rt.lazyState( + () => UseCompute(() => status == UseAsyncStateStatus.loading, [uStatus]), + this, + ); + bool get isLoading => uIsLoading.value; + + late final uIsDone = Rt.lazyState( + () => UseCompute(() => status == UseAsyncStateStatus.done, [uStatus]), + this, + ); + bool get isDone => uIsDone.value; - T get value => _value.value; - Object? get error => _error.value; - UseAsyncStateStatus get status => _status.value; + late final uIsError = Rt.lazyState( + () => UseCompute(() => status == UseAsyncStateStatus.error, [uStatus]), + this, + ); + bool get isError => uIsError.value; Future _future = Completer().future; Future get future => _future; - final String? _debugLabel; - @override - String? get debugLabel => _debugLabel ?? super.debugLabel; - @override - Map get debugInfo => { - 'value': value, - 'error': error, - 'status': status, - }; + bool _isCanceled = false; UseAsyncStateBase( Function asyncFunction, @@ -48,28 +81,48 @@ abstract class UseAsyncStateBase extends RtHook { }) : _initialValue = initialValue, _asyncFunction = asyncFunction, _debugLabel = debugLabel, - _value = UseState(initialValue); + _uValue = UseState(initialValue); /// Execute [asyncFunction] to resolve [value]. FutureOr _resolve([A? arg]) async { try { - _status.value = UseAsyncStateStatus.loading; + _uStatus.value = UseAsyncStateStatus.loading; final asyncFunctionExecuting = arg == null ? _asyncFunction() : _asyncFunction(arg); - _future = Future.value(asyncFunctionExecuting); + _future = asyncFunctionExecuting is Future + ? asyncFunctionExecuting + : Future.value(asyncFunctionExecuting); - _value.value = await _future; + final response = await _future; - _status.value = UseAsyncStateStatus.done; + if (_isCanceled) return null; - return _value.value; + Rt.batch(() { + _uValue.value = response; + _uStatus.value = UseAsyncStateStatus.done; + }); + + return _uValue.value; } catch (e) { - _error.value = e; - _status.value = UseAsyncStateStatus.error; + Rt.batch(() { + _uError.value = e; + _uStatus.value = UseAsyncStateStatus.error; + }); + return null; - } finally {} + } finally { + if (_isCanceled) { + _uStatus.value = UseAsyncStateStatus.done; + _isCanceled = false; + } + } + } + + /// Cancels the async function execution. + void cancel() { + if (status == UseAsyncStateStatus.loading) _isCanceled = true; } /// Returns a new value of [R] depending on the state of the hook: @@ -90,7 +143,7 @@ abstract class UseAsyncStateBase extends RtHook { /// ) /// ``` R? when({ - WhenValueReturn? standby, + WhenValueReturn? idle, WhenValueReturn? loading, WhenValueReturn? done, WhenErrorReturn? error, @@ -107,14 +160,16 @@ abstract class UseAsyncStateBase extends RtHook { return done?.call(value); } - return standby?.call(value); + return idle?.call(value); } /// Reset [value], [status] and [error] to its [initial] state. void reset() { - _value.value = _initialValue; - _error.value = null; - _status.value = UseAsyncStateStatus.standby; + Rt.batch(() { + _uValue.value = _initialValue; + _uError.value = null; + _uStatus.value = UseAsyncStateStatus.idle; + }); } } From 19e27efabf493e92714ee46d06a71f51e2d0de57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 27 Dec 2024 02:19:18 -0600 Subject: [PATCH 066/141] refactor(core, framework): Enhance `autoBinding` method to accept an optional bound instance; improve state binding logic. --- packages/reactter/lib/src/core/binding_zone.dart | 12 +++++++++--- packages/reactter/lib/src/core/dependency_ref.dart | 6 +++--- packages/reactter/lib/src/core/state_management.dart | 7 +------ .../reactter/lib/src/framework/rt_state_base.dart | 3 +-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/reactter/lib/src/core/binding_zone.dart b/packages/reactter/lib/src/core/binding_zone.dart index 3ac3fee7..0e5b62ff 100644 --- a/packages/reactter/lib/src/core/binding_zone.dart +++ b/packages/reactter/lib/src/core/binding_zone.dart @@ -36,13 +36,19 @@ class BindingZone { } /// {@template reactter.binding_zone.auto_binding} - /// It's used to bind the instance to the stored states([IState]). + /// Automatically binds the instance to the stored states([IState]). /// If the instance is null, it adds the stored states to the [BindingZone] parent. /// {@endtemplate} - static T autoBinding(T Function() getInstance) { + static T autoBinding( + T Function() getInstance, [ + Object? boundInstance, + ]) { final zone = BindingZone(); final instance = getInstance(); - zone.bindInstanceToStates(instance); + + final instanceToBind = boundInstance ?? instance; + + zone.bindInstanceToStates(instanceToBind); return instance; } diff --git a/packages/reactter/lib/src/core/dependency_ref.dart b/packages/reactter/lib/src/core/dependency_ref.dart index e6be791b..13416812 100644 --- a/packages/reactter/lib/src/core/dependency_ref.dart +++ b/packages/reactter/lib/src/core/dependency_ref.dart @@ -8,14 +8,14 @@ part of '../internals.dart'; class DependencyRef { final String? id; - Type get type => T; + Type get type => getType(); const DependencyRef([this.id]); - int _getTypeHashCode() => TT.hashCode; + Type getType() => TT; @override - int get hashCode => Object.hash(_getTypeHashCode(), id.hashCode); + int get hashCode => Object.hash(type, id); @override bool operator ==(Object other) { diff --git a/packages/reactter/lib/src/core/state_management.dart b/packages/reactter/lib/src/core/state_management.dart index e5b31849..e54d03f4 100644 --- a/packages/reactter/lib/src/core/state_management.dart +++ b/packages/reactter/lib/src/core/state_management.dart @@ -61,12 +61,7 @@ abstract class StateManagement implements IContext { /// ``` /// {@endtemplate} T lazyState(T Function() buildState, Object instance) { - final zone = BindingZone(); - try { - return createState(buildState); - } finally { - zone.bindInstanceToStates(instance); - } + return BindingZone.autoBinding(buildState, instance); } /// {@template reactter.untracked} diff --git a/packages/reactter/lib/src/framework/rt_state_base.dart b/packages/reactter/lib/src/framework/rt_state_base.dart index 00ea9c24..6350747b 100644 --- a/packages/reactter/lib/src/framework/rt_state_base.dart +++ b/packages/reactter/lib/src/framework/rt_state_base.dart @@ -62,10 +62,9 @@ abstract class RtStateBase> implements RtState { @protected @mustCallSuper void _register() { - BindingZone.recollectState(this); - if (_isRegistered) return; + BindingZone.recollectState(this); _notifyCreated(); _isRegistered = true; } From 1555ad2ca05679a181f79b98570ccd5077937235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 27 Dec 2024 02:20:26 -0600 Subject: [PATCH 067/141] refactor(tests): Enhance test coverage by adding error handling, debug label, and debug info tests for various components. --- .../test/core/dependency_injection_test.dart | 4 +- .../test/core/event_handler_test.dart | 23 ++ packages/reactter/test/devtools_test.dart | 70 +++++ .../rt_dependency_observer_test.dart | 217 +++++++++++++++ .../test/framework/rt_state_base_test.dart | 83 ++++++ .../test/hooks/use_async_state_test.dart | 188 ++++++++++++- .../reactter/test/hooks/use_compute_test.dart | 29 ++ .../test/hooks/use_dependency_test.dart | 31 +++ .../reactter/test/hooks/use_effect_test.dart | 64 ++++- .../reactter/test/hooks/use_reducer_test.dart | 24 +- .../reactter/test/hooks/use_state_test.dart | 34 ++- packages/reactter/test/logger_test.dart | 253 ++++++++++++++++++ packages/reactter/test/memo_test.dart | 18 +- .../test/shareds/test_controllers.dart | 42 ++- packages/reactter/test/signal_test.dart | 26 ++ 15 files changed, 1060 insertions(+), 46 deletions(-) create mode 100644 packages/reactter/test/devtools_test.dart create mode 100644 packages/reactter/test/framework/rt_dependency_observer_test.dart create mode 100644 packages/reactter/test/framework/rt_state_base_test.dart create mode 100644 packages/reactter/test/logger_test.dart diff --git a/packages/reactter/test/core/dependency_injection_test.dart b/packages/reactter/test/core/dependency_injection_test.dart index 639e80e6..2643e0ed 100644 --- a/packages/reactter/test/core/dependency_injection_test.dart +++ b/packages/reactter/test/core/dependency_injection_test.dart @@ -246,7 +246,7 @@ void main() { }); test("should check if an instance is registered", () { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); bool isActive = Rt.isActive(testController); expect(isActive, false); @@ -260,7 +260,7 @@ void main() { }); test("should check if a dependency is registered", () { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); bool hasRegister = Rt.hasRegister(); expect(hasRegister, false); diff --git a/packages/reactter/test/core/event_handler_test.dart b/packages/reactter/test/core/event_handler_test.dart index d27b69bc..81b6778b 100644 --- a/packages/reactter/test/core/event_handler_test.dart +++ b/packages/reactter/test/core/event_handler_test.dart @@ -229,6 +229,29 @@ void main() { "should unlisten event with id", () => _testUnlistenEvent(withId: true), ); + + test('should catch error while emit event', () { + final testController = Rt.create(() => TestController()); + + Rt.on( + testController, + Events.TestEvent, + (inst, param) { + throw Error(); + }, + ); + + expect( + () => Rt.emit( + testController, + Events.TestEvent, + TEST_EVENT_PARAM_NAME, + ), + throwsA(isA()), + ); + + Rt.offAll(testController); + }); }); } diff --git a/packages/reactter/test/devtools_test.dart b/packages/reactter/test/devtools_test.dart new file mode 100644 index 00000000..82f381ee --- /dev/null +++ b/packages/reactter/test/devtools_test.dart @@ -0,0 +1,70 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:reactter/reactter.dart'; +import 'package:reactter/src/devtools.dart'; + +import 'shareds/test_controllers.dart'; + +void main() { + group("Devtools", () { + test('should be initialized', () { + try { + Rt.initializeDevTools(); + } catch (e) { + expect(e, isInstanceOf()); + } + + expect(RtDevTools.instance, isNotNull); + }); + + test('should be register states and dependencies', () { + try { + Rt.initializeDevTools(); + } catch (e) { + expect(e, isInstanceOf()); + } + + final devtools = RtDevTools.instance; + + final Type dependencyType = TestController; + Rt.create(() => TestController()); + + final response = devtools?.getNodes(0, 100); + + expect(response, isNotNull); + + final nodes = response?['nodes']; + + bool isDependencyRegistered = false; + void checkIsDependencyRegistered({ + required String kind, + required String type, + }) { + isDependencyRegistered = isDependencyRegistered || + kind == NodeKind.dependency.toString() && + type.startsWith(dependencyType.toString()); + } + + bool isDependencyCreated = false; + void checkIsDependencyCreated({ + required String kind, + required String type, + }) { + isDependencyCreated = isDependencyCreated || + kind == NodeKind.instance.toString() && + type.startsWith(dependencyType.toString()); + } + + for (final node in nodes) { + final kind = node['kind']; + final type = node['type']; + + checkIsDependencyRegistered(kind: kind, type: type); + checkIsDependencyCreated(kind: kind, type: type); + } + + expect(nodes, isNotNull); + expect(isDependencyRegistered, isTrue); + expect(isDependencyCreated, isTrue); + }); + }); +} diff --git a/packages/reactter/test/framework/rt_dependency_observer_test.dart b/packages/reactter/test/framework/rt_dependency_observer_test.dart new file mode 100644 index 00000000..bd3b824d --- /dev/null +++ b/packages/reactter/test/framework/rt_dependency_observer_test.dart @@ -0,0 +1,217 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:reactter/reactter.dart'; +import 'package:reactter/src/internals.dart'; + +import '../shareds/test_controllers.dart'; + +class RtDependencyObserverTest extends RtDependencyObserver { + int onDependencyRegisteredCalledCount = 0; + + int onDependencyCreatedCalledCount = 0; + Object? lastInstanceCreated; + + int onDependencyMountedCalledCount = 0; + Object? lastInstanceMounted; + + int onDependencyUnmountedCalledCount = 0; + Object? lastInstanceUnmounted; + + int onDependencyDeletedCalledCount = 0; + Object? lastInstanceDeleted; + + int onDependencyUnregisteredCalledCount = 0; + + int onDependencyFailedCalledCount = 0; + DependencyFail? lastFail; + + @override + void onDependencyRegistered(covariant DependencyRef dependency) { + onDependencyRegisteredCalledCount++; + } + + @override + void onDependencyCreated( + covariant DependencyRef dependency, + Object? instance, + ) { + onDependencyCreatedCalledCount++; + lastInstanceCreated = instance; + } + + @override + void onDependencyMounted( + covariant DependencyRef dependency, + Object? instance, + ) { + onDependencyMountedCalledCount++; + lastInstanceMounted = instance; + } + + @override + void onDependencyUnmounted( + covariant DependencyRef dependency, + Object? instance, + ) { + onDependencyUnmountedCalledCount++; + lastInstanceUnmounted = instance; + } + + @override + void onDependencyDeleted( + covariant DependencyRef dependency, + Object? instance, + ) { + onDependencyDeletedCalledCount++; + lastInstanceDeleted = instance; + } + + @override + void onDependencyUnregistered(covariant DependencyRef dependency) { + onDependencyUnregisteredCalledCount++; + } + + @override + void onDependencyFailed( + covariant DependencyRef dependency, + DependencyFail fail, + ) { + onDependencyFailedCalledCount++; + lastFail = fail; + } +} + +void main() { + group("RtDependencyObserver", () { + test("should be observed when a dependency is registered", () { + final observer = RtDependencyObserverTest(); + Rt.addObserver(observer); + + Rt.register(() => TestController()); + + expect(observer.onDependencyRegisteredCalledCount, 1); + + Rt.addObserver(observer); + Rt.unregister(); + }); + + test("should be observed when a dependency is created", () { + final observer = RtDependencyObserverTest(); + Rt.addObserver(observer); + + final instance = Rt.create(() => TestController()); + + expect(observer.onDependencyCreatedCalledCount, 1); + expect(observer.lastInstanceCreated, instance); + + Rt.removeObserver(observer); + Rt.destroy(); + }); + + test("should be observed when a dependency is mounted", () { + final observer = RtDependencyObserverTest(); + Rt.addObserver(observer); + + final instance = Rt.create(() => TestController()); + Rt.emit(RtDependency(), Lifecycle.didMount, instance); + + expect(observer.onDependencyMountedCalledCount, 1); + expect(observer.lastInstanceMounted, instance); + + Rt.removeObserver(observer); + Rt.destroy(); + }); + + test("should be observed when a dependency is unmounted", () { + final observer = RtDependencyObserverTest(); + Rt.addObserver(observer); + + final instance = Rt.create(() => TestController()); + Rt.emit(RtDependency(), Lifecycle.didUnmount, instance); + + expect(observer.onDependencyUnmountedCalledCount, 1); + expect(observer.lastInstanceUnmounted, instance); + + Rt.removeObserver(observer); + Rt.destroy(); + }); + + test("should be observed when a dependency is deleted", () { + final observer = RtDependencyObserverTest(); + Rt.addObserver(observer); + + final instance = Rt.create(() => TestController()); + Rt.delete(); + + expect(observer.onDependencyDeletedCalledCount, 1); + expect(observer.lastInstanceDeleted, instance); + + Rt.removeObserver(observer); + }); + + test("should be observed when a dependency is unregistered", () { + final observer = RtDependencyObserverTest(); + Rt.addObserver(observer); + + Rt.register(() => TestController()); + Rt.unregister(); + + expect(observer.onDependencyUnregisteredCalledCount, 1); + + Rt.removeObserver(observer); + }); + + test("should be observed when a dependency is failed", () { + final observer = RtDependencyObserverTest(); + Rt.addObserver(observer); + + Rt.create(() => TestController()); + Rt.register(() => TestController()); + + expect(observer.onDependencyFailedCalledCount, 1); + expect(observer.lastFail, DependencyFail.alreadyRegistered); + + Rt.create(() => TestController()); + + expect(observer.onDependencyFailedCalledCount, 3); + // before the last fail, it should be `DependencyFail.alreadyRegistered` again. + expect(observer.lastFail, DependencyFail.alreadyCreated); + + Rt.delete(); + Rt.delete(); + + expect(observer.onDependencyFailedCalledCount, 4); + expect(observer.lastFail, DependencyFail.alreadyDeleted); + + Rt.unregister(); + + expect(observer.onDependencyFailedCalledCount, 5); + expect(observer.lastFail, DependencyFail.alreadyUnregistered); + + Rt.get(); + + expect(observer.onDependencyFailedCalledCount, 6); + expect(observer.lastFail, DependencyFail.missingInstanceBuilder); + + Rt.create(() => TestController(), mode: DependencyMode.factory); + Rt.delete(); + + expect(observer.onDependencyFailedCalledCount, 7); + expect(observer.lastFail, DependencyFail.builderRetainedAsFactory); + + Rt.destroy(); + Rt.create(() => TestController(), mode: DependencyMode.singleton); + Rt.delete(); + + expect(observer.onDependencyFailedCalledCount, 8); + expect(observer.lastFail, DependencyFail.dependencyRetainedAsSingleton); + + Rt.unregister(); + + expect(observer.onDependencyFailedCalledCount, 9); + expect(observer.lastFail, DependencyFail.cannotUnregisterActiveInstance); + + Rt.removeObserver(observer); + Rt.destroy(); + }); + }); +} diff --git a/packages/reactter/test/framework/rt_state_base_test.dart b/packages/reactter/test/framework/rt_state_base_test.dart new file mode 100644 index 00000000..aa96c881 --- /dev/null +++ b/packages/reactter/test/framework/rt_state_base_test.dart @@ -0,0 +1,83 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:reactter/reactter.dart'; + +class CountTest with RtContext, RtStateBase { + int _count = 0; + int get count => _count; + set count(int value) { + if (_count != value) { + update(() { + _count = value; + }); + } + } + + @override + String? get debugLabel => 'CountTest'; + + @override + Map get debugInfo => { + "count": _count, + }; +} + +class StateTest with RtContext, RtStateBase { + StateTest._() { + assert(dependencyInjection == Rt); + assert(stateManagement == Rt); + assert(eventHandler == Rt); + } + + factory StateTest() { + return Rt.createState(() => StateTest._()); + } + + @override + String? get debugLabel => 'State2'; +} + +void main() { + group('RtStateBase', () { + test('should create a state object within the BindingZone', () { + expect(() => CountTest(), throwsA(isA())); + + final countState = Rt.createState(() => CountTest()); + expect(countState.debugLabel, 'CountTest'); + + final state = StateTest(); + expect(state.debugLabel, 'State2'); + }); + + test('should create a state using Dependency Injection', () { + final countState = Rt.create(() => CountTest()); + + expect(countState?.debugLabel, 'CountTest'); + + Rt.delete(); + + final state = Rt.create(() => StateTest()); + expect(state?.debugLabel, 'State2'); + + Rt.delete(); + }); + + test('should update the state', () { + final countState = Rt.createState(() => CountTest()); + expect(countState.count, 0); + + countState.count = 1; + expect(countState.count, 1); + }); + + test('should have debug info', () { + final countState = Rt.createState(() => CountTest()); + expect(countState.debugInfo, {"count": 0}); + + countState.count = 1; + expect(countState.debugInfo, {"count": 1}); + + final state = StateTest(); + expect(state.debugInfo, {}); + }); + }); +} diff --git a/packages/reactter/test/hooks/use_async_state_test.dart b/packages/reactter/test/hooks/use_async_state_test.dart index 0bd57905..5b95afd8 100644 --- a/packages/reactter/test/hooks/use_async_state_test.dart +++ b/packages/reactter/test/hooks/use_async_state_test.dart @@ -6,62 +6,161 @@ import '../shareds/test_controllers.dart'; void main() { group("UseAsyncState", () { test("should resolve state", () async { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); final stateAsync = testController.stateAsync; expect(stateAsync.value, "initial"); + expect(stateAsync.status, UseAsyncStateStatus.idle); + expect(stateAsync.error, null); + expect(stateAsync.isLoading, false); + expect(stateAsync.isDone, false); + expect(stateAsync.isError, false); - await stateAsync.resolve(); + final executing = stateAsync.resolve(); + + expect(stateAsync.value, "initial"); + expect(stateAsync.status, UseAsyncStateStatus.loading); + expect(stateAsync.error, null); + expect(stateAsync.isLoading, true); + expect(stateAsync.isDone, false); + expect(stateAsync.isError, false); + + final response = await executing; + expect(response, "resolved"); expect(stateAsync.value, "resolved"); + expect(stateAsync.status, UseAsyncStateStatus.done); + expect(stateAsync.error, null); + expect(stateAsync.isLoading, false); + expect(stateAsync.isDone, true); + expect(stateAsync.isError, false); }); test("should catch error", () async { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); final stateAsync = testController.stateAsyncWithError; - await stateAsync.resolve(); + expect(stateAsync.value, "initial"); + expect(stateAsync.status, UseAsyncStateStatus.idle); + expect(stateAsync.error, null); + expect(stateAsync.isLoading, false); + expect(stateAsync.isDone, false); + expect(stateAsync.isError, false); + + final executing = stateAsync.resolve(); + expect(stateAsync.value, "initial"); + expect(stateAsync.status, UseAsyncStateStatus.loading); + expect(stateAsync.error, null); + expect(stateAsync.isLoading, true); + expect(stateAsync.isDone, false); + expect(stateAsync.isError, false); + + final response = await executing; + + expect(response, null); expect(stateAsync.value, "initial"); expect(stateAsync.status, UseAsyncStateStatus.error); expect(stateAsync.error.toString(), "Exception: has a error"); + expect(stateAsync.isLoading, false); + expect(stateAsync.isDone, false); + expect(stateAsync.isError, true); }); test("should reset state", () async { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); final stateAsync = testController.stateAsync; - await stateAsync.resolve(); + expect(stateAsync.value, "initial"); + expect(stateAsync.status, UseAsyncStateStatus.idle); + expect(stateAsync.error, null); + expect(stateAsync.isLoading, false); + expect(stateAsync.isDone, false); + expect(stateAsync.isError, false); + + final executing = stateAsync.resolve(); + expect(stateAsync.value, "initial"); + expect(stateAsync.status, UseAsyncStateStatus.loading); + expect(stateAsync.error, null); + expect(stateAsync.isLoading, true); + expect(stateAsync.isDone, false); + expect(stateAsync.isError, false); + + final response = await executing; + + expect(response, "resolved"); expect(stateAsync.value, "resolved"); + expect(stateAsync.status, UseAsyncStateStatus.done); + expect(stateAsync.error, null); + expect(stateAsync.isLoading, false); + expect(stateAsync.isDone, true); + expect(stateAsync.isError, false); stateAsync.reset(); expect(stateAsync.value, "initial"); + expect(stateAsync.status, UseAsyncStateStatus.idle); + expect(stateAsync.error, null); + expect(stateAsync.isLoading, false); + expect(stateAsync.isDone, false); + expect(stateAsync.isError, false); }); test("should resolve state with arguments", () async { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); final stateAsync = testController.stateAsyncWithArg; - await stateAsync.resolve(Args1('arg1')); + expect(stateAsync.value, "initial"); + expect(stateAsync.status, UseAsyncStateStatus.idle); + expect(stateAsync.error, null); + expect(stateAsync.isLoading, false); + expect(stateAsync.isDone, false); + expect(stateAsync.isError, false); + + final executing = stateAsync.resolve(Args1('arg1')); + + expect(stateAsync.value, "initial"); + expect(stateAsync.status, UseAsyncStateStatus.loading); + expect(stateAsync.error, null); + expect(stateAsync.isLoading, true); + expect(stateAsync.isDone, false); + expect(stateAsync.isError, false); + + final response = await executing; + expect(response, "resolved with args: arg1"); expect(stateAsync.value, "resolved with args: arg1"); + expect(stateAsync.status, UseAsyncStateStatus.done); + expect(stateAsync.error, null); + expect(stateAsync.isLoading, false); + expect(stateAsync.isDone, true); + expect(stateAsync.isError, false); await stateAsync.resolve(Args2('arg1', 'arg2')); expect(stateAsync.value, "resolved with args: arg1,arg2"); + expect(stateAsync.status, UseAsyncStateStatus.done); + expect(stateAsync.error, null); + expect(stateAsync.isLoading, false); + expect(stateAsync.isDone, true); + expect(stateAsync.isError, false); await stateAsync.resolve(Args3('arg1', 'arg2', 'arg3')); expect(stateAsync.value, "resolved with args: arg1,arg2,arg3"); + expect(stateAsync.status, UseAsyncStateStatus.done); + expect(stateAsync.error, null); + expect(stateAsync.isLoading, false); + expect(stateAsync.isDone, true); + expect(stateAsync.isError, false); }); test("should get value when", () async { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); final stateAsync = testController.stateAsyncWithArg; - final s1 = stateAsync.when(standby: (value) => value); + final s1 = stateAsync.when(idle: (value) => value); expect(s1, "initial"); final futureResolve = stateAsync.resolve(Args1(null)); @@ -81,8 +180,75 @@ void main() { stateAsync.reset(); - final s5 = stateAsync.when(standby: (value) => value); + final s5 = stateAsync.when(idle: (value) => value); expect(s5, "initial"); }); + + test("should get the future", () async { + final stateAsync = UseAsyncState( + () async => Future.value("resolved"), + "initial", + ); + + expect(stateAsync.future, doesNotComplete); + + await stateAsync.resolve(); + + expect(stateAsync.future, completes); + expect(await stateAsync.future, "resolved"); + }); + + test("should execute a sync method", () async { + final stateAsync = UseAsyncState( + () => "RESOLVED", + "initial", + ); + + await stateAsync.resolve(); + + expect(stateAsync.value, "RESOLVED"); + expect(stateAsync.status, UseAsyncStateStatus.done); + }); + + test('should cancel the async function', () async { + final stateAsync = UseAsyncState( + () async { + await Future.delayed(Duration(milliseconds: 100)); + return "resolved"; + }, + "initial", + ); + + final executing = stateAsync.resolve(); + stateAsync.cancel(); + await executing; + + expect(stateAsync.value, "initial"); + expect(stateAsync.status, UseAsyncStateStatus.done); + + stateAsync.cancel(); + await stateAsync.resolve(); + + expect(stateAsync.value, "resolved"); + expect(stateAsync.status, UseAsyncStateStatus.done); + }); + + test("should get debugLabel", () { + final testController = TestController(); + final stateAsync = testController.stateAsync; + + expect(stateAsync.debugLabel, "stateAsync"); + }); + + test('should get debugInfo', () { + final testController = TestController(); + final stateAsync = testController.stateAsync; + + expect(stateAsync.debugInfo, { + 'value': "initial", + 'error': null, + 'status': UseAsyncStateStatus.idle, + }); + }); }); } diff --git a/packages/reactter/test/hooks/use_compute_test.dart b/packages/reactter/test/hooks/use_compute_test.dart index 3cf1a94c..9c4e55ff 100644 --- a/packages/reactter/test/hooks/use_compute_test.dart +++ b/packages/reactter/test/hooks/use_compute_test.dart @@ -55,6 +55,35 @@ void main() { Rt.delete(); }, ); + + test('should get debug label', () { + final testController = Rt.create( + () => TestController(), + )!; + + expect(testController.stateCompute.debugLabel, 'stateCompute'); + + Rt.delete(); + }); + + test('should get debug info', () { + final testController = Rt.create( + () => TestController(), + )!; + + expect( + testController.stateCompute.debugInfo, + { + 'value': 5, + 'dependencies': [ + testController.stateInt, + testController.stateDouble, + ], + }, + ); + + Rt.delete(); + }); }, ); } diff --git a/packages/reactter/test/hooks/use_dependency_test.dart b/packages/reactter/test/hooks/use_dependency_test.dart index e72843fe..6405010d 100644 --- a/packages/reactter/test/hooks/use_dependency_test.dart +++ b/packages/reactter/test/hooks/use_dependency_test.dart @@ -269,6 +269,37 @@ void main() { Rt.destroy(id: ID); }); + + test('should get debug label', () { + final useDependency = UseDependency.lazySingleton( + () => TestController(), + debugLabel: 'useDependency', + ); + + expect(useDependency.debugLabel, 'useDependency'); + + Rt.destroy(); + }); + + test('should get debug info', () { + final testController = Rt.create(() => TestController(), id: 'TEST')!; + + final useDependency = UseDependency.create( + () => testController, + id: 'TEST', + debugLabel: 'useDependency', + ); + + expect( + useDependency.debugInfo, + { + 'id': 'TEST', + 'instance': testController, + }, + ); + + Rt.destroy(id: 'TEST'); + }); }); } diff --git a/packages/reactter/test/hooks/use_effect_test.dart b/packages/reactter/test/hooks/use_effect_test.dart index d55d1235..4a05b73c 100644 --- a/packages/reactter/test/hooks/use_effect_test.dart +++ b/packages/reactter/test/hooks/use_effect_test.dart @@ -151,7 +151,7 @@ void main() { }); test("shouldn't be called by instance that was not registered", () { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); int nCalls = 0; UseEffect( @@ -171,6 +171,16 @@ void main() { expect(nCalls, 2); }); + test("should catch error", () { + expect( + () { + UseEffect.runOnInit(() { + throw Exception("Error"); + }, []); + }, + throwsA(isA()), + ); + }); }); group("UseEffect's cleaup", () { @@ -244,7 +254,7 @@ void main() { }); test("should be called with dispatchEffect", () { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); final stateA = testController.stateBool; final stateB = testController.stateInt; final stateC = testController.signalString; @@ -328,13 +338,61 @@ void main() { Rt.unregister(); }); + + test("should catch error", () { + expect( + () { + UseEffect.runOnInit(() { + () => throw Exception("Error"); + }, []); + }, + isNot(throwsA(isA())), + ); + + expect( + () { + final stateA = UseState('initial'); + + UseEffect.runOnInit(() { + return () => throw Exception(stateA.value); + }, [stateA]); + + stateA.value = 'throw error'; + }, + throwsA(isA()), + ); + }); + }); + + test("UseEffect should get debug label", () { + final uEffect = UseEffect(() {}, [], debugLabel: "uEffect"); + + expect(uEffect.debugLabel, "uEffect"); + }); + + test("UseEffect should get debug info", () { + final testController = Rt.create(() => UseEffectTestController())!; + final stateA = testController.stateBool; + final stateB = testController.stateInt; + final stateC = testController.signalString; + + final uEffect = UseEffect(() {}, [stateA, stateB, stateC]); + + expect( + uEffect.debugInfo, + { + "dependencies": [stateA, stateB, stateC], + }, + ); + + Rt.delete(); }); } class UseEffectDispatchController extends DispatchEffect {} class UseEffectTestController extends TestController { - final testControllerInner = Rt.createState(() => TestController()); + final testControllerInner = TestController(); int nCalls1 = 0; int nCalls2 = 0; diff --git a/packages/reactter/test/hooks/use_reducer_test.dart b/packages/reactter/test/hooks/use_reducer_test.dart index c1c1596f..2c7da00a 100644 --- a/packages/reactter/test/hooks/use_reducer_test.dart +++ b/packages/reactter/test/hooks/use_reducer_test.dart @@ -6,13 +6,13 @@ import '../shareds/test_controllers.dart'; void main() { group("UseReducer", () { test("should have a initial value", () { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); expect(testController.stateReduce.value.count, 0); }); test("should dispatch a action", () { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); testController.stateReduce.dispatch(IncrementAction()); expect(testController.stateReduce.value.count, 1); @@ -32,7 +32,7 @@ void main() { }); test("should dispatch a action callable", () { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); testController.stateReduce.dispatch(IncrementActionCallable(quantity: 2)); expect(testController.stateReduce.value.count, 2); @@ -42,7 +42,7 @@ void main() { }); test("should update state when dispatch action", () { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); late bool? isStateChanged; UseEffect(() { @@ -53,5 +53,21 @@ void main() { expectLater(isStateChanged, true); }); + + test('should get debug label', () { + final testController = TestController(); + + expect(testController.stateReduce.debugLabel, 'stateReduce'); + }); + + test('should get debug info', () { + final testController = TestController(); + + expect(testController.stateReduce.debugInfo['value'].count, 0); + + testController.stateReduce.dispatch(IncrementAction()); + + expect(testController.stateReduce.debugInfo['value'].count, 1); + }); }); } diff --git a/packages/reactter/test/hooks/use_state_test.dart b/packages/reactter/test/hooks/use_state_test.dart index df0c5e1d..76b3c9bc 100644 --- a/packages/reactter/test/hooks/use_state_test.dart +++ b/packages/reactter/test/hooks/use_state_test.dart @@ -90,11 +90,9 @@ void main() { }, ); - testController.update( - () => testController.stateInt.update(() { - testController.stateInt.value = 3; - }), - ); + testController.stateInt.update(() { + testController.stateInt.value = 3; + }); expectLater(testController.stateInt.value, 3); @@ -132,5 +130,31 @@ void main() { Rt.delete(); }); + + test("should get debug label", () { + final testController = Rt.create(() => TestController())!; + + expect(testController.stateInt.debugLabel, 'stateInt'); + + Rt.delete(); + }); + + test("should get debug info", () { + final testController = Rt.create(() => TestController())!; + + expect( + testController.stateInt.debugInfo, + {'value': 0}, + ); + + testController.stateInt.value = 1; + + expect( + testController.stateInt.debugInfo, + {'value': 1}, + ); + + Rt.delete(); + }); }); } diff --git a/packages/reactter/test/logger_test.dart b/packages/reactter/test/logger_test.dart new file mode 100644 index 00000000..886a0ea0 --- /dev/null +++ b/packages/reactter/test/logger_test.dart @@ -0,0 +1,253 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:reactter/reactter.dart'; +import 'package:reactter/src/logger.dart'; + +class StateTest with RtContext, RtStateBase { + StateTest._(); + + factory StateTest() { + return Rt.createState(() => StateTest._()); + } + + @override + String? get debugLabel => 'StateTest'; + + @override + Map get debugInfo => {}; +} + +void main() { + final List> logs = []; + + void output( + String message, { + required String name, + required int level, + StackTrace? stackTrace, + }) { + logs.add({ + 'message': message, + 'name': name, + 'level': level, + 'stackTrace': stackTrace, + }); + } + + List> initialized() { + try { + Rt.initializeLogger(name: 'Reactter Test', output: output); + } catch (e) { + expect(e, isInstanceOf()); + } + + expect(RtLogger.instance, isNotNull); + + return logs; + } + + group('Logger', () { + test('should be initialized', () { + initialized(); + }); + + test('should be log', () { + initialized(); + + final logger = RtLogger.instance; + + logger?.log('test'); + + expect(logs.last['name'], 'Reactter Test'); + expect(logs.last['level'], LogLevel.all); + expect(logs.last['message'], 'test'); + expect(logs.last['stackTrace'], isNotNull); + }); + + test('should be state log', () { + initialized(); + + final uState = UseState('test'); + + expect(logs.last['level'], LogLevel.finer); + expect(logs.last['message'], '${prettyFormat(uState)} created.'); + + final stateTest = StateTest(); + + expect(logs.last['level'], LogLevel.finer); + expect(logs.last['message'], '${prettyFormat(stateTest)} created.'); + + final instanceToBind = Symbol('test'); + + uState.bind(instanceToBind); + + expect(logs.last['level'], LogLevel.warning); + expect( + logs.last['message'], + startsWith( + 'The bound instance(${prettyFormat(instanceToBind)}) to state(${prettyFormat(uState)})', + ), + ); + + uState.unbind(); + + expect(logs.last['level'], LogLevel.finer); + expect( + logs.last['message'], + '${prettyFormat(uState)} unbound from ${prettyFormat(instanceToBind)}.', + ); + + uState.value = 'new value'; + + expect(logs.last['level'], LogLevel.finer); + expect(logs.last['message'], '${prettyFormat(uState)} updated.'); + + uState.dispose(); + + expect(logs.last['level'], LogLevel.finer); + expect(logs.last['message'], '${prettyFormat(uState)} disposed.'); + }); + + test('should be dependency log', () { + initialized(); + + final instance = StateTest(); + final id = 'test'; + + Rt.register(() => instance, id: id); + + expect(logs.last['level'], LogLevel.fine); + expect( + logs.last['message'], + '${prettyFormat(RtDependency(id))} registered.', + ); + + final dependency = Rt.getDependencyRegisterByRef( + RtDependency(id), + ); + + Rt.create(() => instance, id: id); + + expect(logs.last['level'], LogLevel.fine); + expect( + logs.last['message'], + '${prettyFormat(dependency)} created. Its instance: ${prettyFormat(instance)}.', + ); + + Rt.emit(dependency, Lifecycle.didMount, instance); + + expect(logs.last['level'], LogLevel.fine); + expect( + logs.last['message'], + '${prettyFormat(dependency)} mounted. Its instance: ${prettyFormat(instance)}.', + ); + + Rt.emit(dependency, Lifecycle.didUnmount, instance); + + expect(logs.last['level'], LogLevel.fine); + expect( + logs.last['message'], + '${prettyFormat(dependency)} unmounted. Its instance: ${prettyFormat(instance)}.', + ); + + instance.dispose(); + + Rt.destroy(id: id, onlyInstance: true); + + expect(logs.last['level'], LogLevel.fine); + expect( + logs.last['message'], + '${prettyFormat(dependency)} deleted. Its instance: ${prettyFormat(instance)}.', + ); + + Rt.unregister(id); + + expect(logs.last['level'], LogLevel.fine); + expect( + logs.last['message'], + '${prettyFormat(dependency)} unregistered.', + ); + + Rt.register(() => instance, id: id); + Rt.register(() => instance, id: id); + + expect(logs.last['level'], LogLevel.info); + expect( + logs.last['message'], + '${prettyFormat(dependency)} already registered.', + ); + + Rt.create(() => instance, id: id); + Rt.create(() => instance, id: id); + + expect(logs.last['level'], LogLevel.info); + expect( + logs.last['message'], + '${prettyFormat(dependency)} already created.', + ); + + Rt.delete(id); + Rt.delete(id); + + expect(logs.last['level'], LogLevel.info); + expect( + logs.last['message'], + '${prettyFormat(RtDependency(id))} already deleted.', + ); + + Rt.unregister(id); + Rt.unregister(id); + + expect(logs.last['level'], LogLevel.info); + expect( + logs.last['message'], + '${prettyFormat(RtDependency(id))} already unregistered.', + ); + + Rt.create(() => StateTest(), id: id, mode: DependencyMode.factory); + final dependencyFactoryRef = Rt.getDependencyRegisterByRef( + RtDependency(id), + ); + Rt.delete(id); + + expect(logs.last['level'], LogLevel.info); + expect( + logs.last['message'], + "${prettyFormat(dependencyFactoryRef)}'s instance retained because it's factory mode.", + ); + + Rt.destroy(id: id); + Rt.create(() => StateTest(), id: id, mode: DependencyMode.singleton); + final dependencySingletonRef = Rt.getDependencyRegisterByRef( + RtDependency(id), + ); + Rt.delete(id); + + expect(logs.last['level'], LogLevel.info); + expect( + logs.last['message'], + "${prettyFormat(dependencySingletonRef)} retained because it's singleton mode.", + ); + + Rt.destroy(id: id); + Rt.get(id); + + expect(logs.last['level'], LogLevel.warning); + expect( + logs.last['message'], + startsWith( + "${prettyFormat(RtDependency(id))} couldn't register."), + ); + + Rt.create(() => StateTest(), id: id); + Rt.unregister(id); + + expect(logs.last['level'], LogLevel.severe); + expect( + logs.last['message'], + startsWith( + "${prettyFormat(RtDependency(id))} couldn't unregister", + ), + ); + }); + }); +} diff --git a/packages/reactter/test/memo_test.dart b/packages/reactter/test/memo_test.dart index f03ce24f..48a43306 100644 --- a/packages/reactter/test/memo_test.dart +++ b/packages/reactter/test/memo_test.dart @@ -8,13 +8,13 @@ import 'shareds/test_controllers.dart'; void main() { group("Memo", () { test("should be a class callable with arguments", () { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); expect(testController.memo.call, isA()); }); test("should memoize the returned value by the calculate function", () { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); final value0 = testController.memo(null); final value1 = testController.memo(Args1(1)); @@ -32,7 +32,7 @@ void main() { }); test("should override the cached value", () { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); final value0 = testController.memo(null); final value1 = testController.memo(Args1(1)); @@ -50,7 +50,7 @@ void main() { }); test("should remove all cached data", () { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); final value0 = testController.memo(null); final value1 = testController.memo(Args1(1)); @@ -71,7 +71,7 @@ void main() { }); test("should removed the cached data", () { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); final value0 = testController.memo(null); final value1 = testController.memo(Args1(1)); @@ -93,7 +93,7 @@ void main() { }); test("shouldn't memoize when an error occurs", () { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); expect( () => testController.memo(Args1(ArgumentError())), @@ -257,13 +257,13 @@ void main() { group("Memo.inline", () { test("should be a class callable with arguments", () { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); expect(testController.inlineMemo.call, isA()); }); test("should memoize the returned value by the calculate function", () { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); final value0 = testController.inlineMemo(null); final value1 = testController.inlineMemo(Args1(1)); @@ -281,7 +281,7 @@ void main() { }); test("should override the cached value", () { - final testController = Rt.createState(() => TestController()); + final testController = TestController(); final value0 = testController.inlineMemo(null); final value1 = testController.inlineMemo(Args1(1)); diff --git a/packages/reactter/test/shareds/test_controllers.dart b/packages/reactter/test/shareds/test_controllers.dart index d522fd0a..dcad7dfa 100644 --- a/packages/reactter/test/shareds/test_controllers.dart +++ b/packages/reactter/test/shareds/test_controllers.dart @@ -75,32 +75,44 @@ TestStore _reducer(TestStore state, RtAction action) { } } -class TestController with RtContext, RtStateBase { - final signalString = Signal("initial"); - final stateBool = UseState(false); - final stateString = UseState("initial"); - final stateInt = UseState(0); - final stateDouble = UseState(0.0); - final stateList = UseState([]); - final stateMap = UseState({}); - final stateClass = UseState(null); - final stateAsync = UseAsyncState(_resolveStateAsync, "initial"); +class TestController { + final signalString = Signal("initial", debugLabel: "signalString"); + final stateBool = UseState(false, debugLabel: "stateBool"); + final stateString = UseState("initial", debugLabel: "stateString"); + final stateInt = UseState(0, debugLabel: "stateInt"); + final stateDouble = UseState(0.0, debugLabel: "stateDouble"); + final stateList = UseState([], debugLabel: "stateList"); + final stateMap = UseState({}, debugLabel: "stateMap"); + final stateClass = UseState(null, debugLabel: "stateClass"); + final stateAsync = UseAsyncState( + _resolveStateAsync, + "initial", + debugLabel: "stateAsync", + ); final stateAsyncWithArg = UseAsyncState.withArg( _resolveStateAsync, "initial", + debugLabel: "stateAsyncWithArg", ); final stateAsyncWithError = UseAsyncState( - () { + () async { + await Future.delayed(Duration(milliseconds: 1)); throw Exception("has a error"); }, "initial", + debugLabel: "stateAsyncWithError", + ); + final stateReduce = UseReducer( + _reducer, + TestStore(count: 0), + debugLabel: "stateReduce", ); - final stateReduce = UseReducer(_reducer, TestStore(count: 0)); late final stateCompute = Rt.lazyState( () => UseCompute( () => (stateInt.value + stateDouble.value).clamp(5, 10), [stateInt, stateDouble], + debugLabel: "stateCompute", ), this, ); @@ -131,6 +143,12 @@ class TestController with RtContext, RtStateBase { final inlineMemo = Memo.inline((Args? args) { return args?.arguments ?? []; }); + + // TestController() { + // assert(dependencyInjection == Rt); + // assert(stateManagement == Rt); + // assert(eventHandler == Rt); + // } } class TestLifecycleController extends LifecycleObserver { diff --git a/packages/reactter/test/signal_test.dart b/packages/reactter/test/signal_test.dart index 1a782e39..4ca2f78f 100644 --- a/packages/reactter/test/signal_test.dart +++ b/packages/reactter/test/signal_test.dart @@ -142,5 +142,31 @@ void main() { throwsA(isA()), ); }); + + test("should get debug label", () { + final signal = Signal("initial", debugLabel: "signal"); + + expect(signal.debugLabel, "signal"); + }); + + test("should get debug info", () { + final signal = Signal("initial"); + + expect(signal.debugInfo, {"value": "initial"}); + + signal("change value"); + + expect(signal.debugInfo, {"value": "change value"}); + }); + + test("should get value as String", () { + final signal = Signal("initial"); + + expect(signal.toString(), "initial"); + + signal("change value"); + + expect("$signal", "change value"); + }); }); } From 86af5d5ed20875f8367a2425cfe9178d3266dbe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 27 Dec 2024 02:22:30 -0600 Subject: [PATCH 068/141] refactor(framework): Enhance provider and scope implementations with improved type safety and instance management. --- .../lib/src/framework/nested.dart | 1 - .../lib/src/framework/provider_impl.dart | 51 +++++++++++++------ .../src/framework/scope_element_mixin.dart | 11 ++-- .../lib/src/widgets/rt_provider.dart | 51 ++++++++++++++----- .../lib/src/widgets/rt_providers.dart | 6 +-- .../lib/src/widgets/rt_scope.dart | 2 + 6 files changed, 82 insertions(+), 40 deletions(-) diff --git a/packages/flutter_reactter/lib/src/framework/nested.dart b/packages/flutter_reactter/lib/src/framework/nested.dart index eb1eb02c..fba07dff 100644 --- a/packages/flutter_reactter/lib/src/framework/nested.dart +++ b/packages/flutter_reactter/lib/src/framework/nested.dart @@ -16,7 +16,6 @@ class NestedWidget extends StatelessWidget { @override NestedElement createElement() => NestedElement(this); - // coverage:ignore-start @override Widget build(BuildContext context) => throw StateError('handled internally'); diff --git a/packages/flutter_reactter/lib/src/framework/provider_impl.dart b/packages/flutter_reactter/lib/src/framework/provider_impl.dart index 19e0b971..962e91b3 100644 --- a/packages/flutter_reactter/lib/src/framework/provider_impl.dart +++ b/packages/flutter_reactter/lib/src/framework/provider_impl.dart @@ -3,12 +3,23 @@ part of '../framework.dart'; @internal -abstract class ProviderRef {} +abstract class ProviderRef { + @protected + void registerInstance(); + @protected + T? findInstance(); + @protected + T? getInstance(); + @protected + T? createInstance(); + @protected + void disposeInstance(); +} @internal class ProvideImpl extends ProviderBase implements InheritedWidget { - final ProviderRef ref; + final ProviderRef ref; const ProvideImpl( InstanceBuilder instanceBuilder, { @@ -37,22 +48,21 @@ class ProvideImpl extends ProviderBase Widget get child { assert( super.child != null || - (!super.isLazy && super.builder != null || - super.isLazy && super.lazyBuilder != null), + (!isLazy && builder != null || isLazy && lazyBuilder != null), ); - if (super.isLazy && super.lazyBuilder != null) { + if (isLazy && lazyBuilder != null) { return Builder( builder: (context) { - return super.lazyBuilder!(context, super.child); + return lazyBuilder!(context, super.child); }, ); } - if (super.builder != null) { + if (builder != null) { return Builder( builder: (context) { - return super.builder!(context, context.use(id), super.child); + return builder!(context, context.use(id), super.child); }, ); } @@ -157,14 +167,14 @@ class ProviderElement extends InheritedElement T? get instance { if (!_isLazyInstanceObtained && widget.isLazy) { - final instance = Rt.get(widget.id, widget); + final instance = widget.ref.getInstance(); _isLazyInstanceObtained = instance != null; return instance; } - return Rt.find(widget.id); + return widget.ref.findInstance(); } /// Creates an element that uses the given widget as its configuration. @@ -174,11 +184,7 @@ class ProviderElement extends InheritedElement }) : super(widget) { if (widget.init && !widget.isLazy) return; - Rt.register( - widget.instanceBuilder, - id: widget.id, - mode: widget.mode, - ); + widget.ref.registerInstance(); } @override @@ -189,7 +195,7 @@ class ProviderElement extends InheritedElement final shouldNotifyMount = count == 1; if (!widget.init && !widget.isLazy) { - Rt.get(widget.id, widget.ref); + widget.ref.getInstance(); } _updateInheritedElementWithId(parent); @@ -205,6 +211,19 @@ class ProviderElement extends InheritedElement } } + @override + void update(covariant InheritedWidget newWidget) { + final ref = widget.ref; + + if (newWidget is ProvideImpl) { + newWidget.ref.createInstance(); + } + + super.update(newWidget); + + if (ref is RtProvider) ref.disposeInstance(); + } + @override Widget build() { if (hasDirtyDependencies) { diff --git a/packages/flutter_reactter/lib/src/framework/scope_element_mixin.dart b/packages/flutter_reactter/lib/src/framework/scope_element_mixin.dart index ded223a4..317a05bd 100644 --- a/packages/flutter_reactter/lib/src/framework/scope_element_mixin.dart +++ b/packages/flutter_reactter/lib/src/framework/scope_element_mixin.dart @@ -29,8 +29,8 @@ mixin ScopeElementMixin on InheritedElement { notifyClients(oldWidget); return; } - // coverage:ignore-line - super.updated(oldWidget); + + super.updated(oldWidget); // coverage:ignore-line } @override @@ -64,7 +64,6 @@ mixin ScopeElementMixin on InheritedElement { // If the aspect is not a Dependency or if the widget tree is marked as // needing build, we can skip the update of the dependencies. if (aspect is! Dependency || _isMarkNeedsBuild) { - // coverage:ignore-line return super.updateDependencies(dependent, aspect); } @@ -72,8 +71,8 @@ mixin ScopeElementMixin on InheritedElement { // If no MasterDependency is stored, we can skip the update of the dependencies. if (dependency != null && dependency is! MasterDependency) { - // coverage:ignore-line - return super.updateDependencies(dependent, aspect); + return super + .updateDependencies(dependent, aspect); // coverage:ignore-line } assert(dependency is MasterDependency?); @@ -92,7 +91,7 @@ mixin ScopeElementMixin on InheritedElement { masterDependency ??= aspect.toMaster(); if (masterDependency.isDirty) { - markNeedsBuild(); + markNeedsBuild(); // coverage:ignore-line } else { masterDependency.listen(markNeedsBuild); } diff --git a/packages/flutter_reactter/lib/src/widgets/rt_provider.dart b/packages/flutter_reactter/lib/src/widgets/rt_provider.dart index b82bb45d..4e736796 100644 --- a/packages/flutter_reactter/lib/src/widgets/rt_provider.dart +++ b/packages/flutter_reactter/lib/src/widgets/rt_provider.dart @@ -124,7 +124,7 @@ part of '../widgets.dart'; /// * [RtMultiProvider], a widget that allows to use multiple [RtProvider]. /// {@endtemplate} class RtProvider extends ProviderBase - implements ProviderWrapper, ProviderRef { + implements ProviderWrapper, ProviderRef { /// Creates an instance of [T] dependency and provides it to tree widget. const RtProvider( InstanceBuilder instanceBuilder, { @@ -179,19 +179,7 @@ class RtProvider extends ProviderBase lazyBuilder: builder, ); - void createInstance() { - Rt.create(instanceBuilder, id: id, mode: mode, ref: this); - } - - void disposeInstance() { - Rt.delete(id, this); - } - - @override - void dispose() { - disposeInstance(); - } - + @protected Widget buildWithChild(Widget? child) { if (id != null) { return ProvideImpl( @@ -224,6 +212,41 @@ class RtProvider extends ProviderBase ); } + @protected + @override + void registerInstance() { + Rt.register(instanceBuilder, id: id, mode: mode); + } + + @override + T? findInstance() { + return Rt.find(id); + } + + @protected + @override + T? getInstance() { + return Rt.get(id, this); + } + + @protected + @override + T? createInstance() { + return Rt.create(instanceBuilder, id: id, mode: mode, ref: this); + } + + @protected + @override + void disposeInstance() { + Rt.delete(id, this); + } + + @protected + @override + void dispose() { + disposeInstance(); + } + @override RtProviderElement createElement() => RtProviderElement(this); diff --git a/packages/flutter_reactter/lib/src/widgets/rt_providers.dart b/packages/flutter_reactter/lib/src/widgets/rt_providers.dart index 89fd7322..24309c04 100644 --- a/packages/flutter_reactter/lib/src/widgets/rt_providers.dart +++ b/packages/flutter_reactter/lib/src/widgets/rt_providers.dart @@ -90,10 +90,10 @@ class RtMultiProvider extends StatelessWidget implements ProviderWrapper { return RtMultiProviderElement(this); } + // coverage:ignore-start @override - void dispose() { - // TODO: implement dispose - } + void dispose() {} + // coverage:ignore-end } class RtMultiProviderElement extends StatelessElement diff --git a/packages/flutter_reactter/lib/src/widgets/rt_scope.dart b/packages/flutter_reactter/lib/src/widgets/rt_scope.dart index 900b4935..9d01f2a4 100644 --- a/packages/flutter_reactter/lib/src/widgets/rt_scope.dart +++ b/packages/flutter_reactter/lib/src/widgets/rt_scope.dart @@ -10,8 +10,10 @@ class RtScope extends InheritedWidget { required Widget child, }) : super(key: key, child: child); + // coverage:ignore-start @override bool updateShouldNotify(covariant InheritedWidget oldWidget) => false; + // coverage:ignore-end @override RtScopeElement createElement() => RtScopeElement(this); From 80db3537ed128c54116bc3887103868f55253a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 27 Dec 2024 02:26:01 -0600 Subject: [PATCH 069/141] chore: Update dependencies and version for reactter package; improve test command for better coverage. --- packages/reactter/pubspec.lock | 82 ++++++++++++++++++++++++++++++---- packages/reactter/pubspec.yaml | 6 ++- 2 files changed, 77 insertions(+), 11 deletions(-) diff --git a/packages/reactter/pubspec.lock b/packages/reactter/pubspec.lock index 9037a8fa..c465e635 100644 --- a/packages/reactter/pubspec.lock +++ b/packages/reactter/pubspec.lock @@ -41,6 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" clock: dependency: transitive description: @@ -97,6 +105,16 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" + flutter: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" frontend_server_client: dependency: transitive description: @@ -145,6 +163,30 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + url: "https://pub.dev" + source: hosted + version: "10.0.5" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + url: "https://pub.dev" + source: hosted + version: "3.0.5" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" lints: dependency: "direct dev" description: @@ -169,6 +211,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.12.16+1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" meta: dependency: "direct main" description: @@ -257,6 +307,11 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" source_map_stack_trace: dependency: transitive description: @@ -317,26 +372,26 @@ packages: dependency: "direct dev" description: name: test - sha256: d11b55850c68c1f6c0cf00eabded4e66c4043feaf6c0d7ce4a36785137df6331 + sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e" url: "https://pub.dev" source: hosted - version: "1.25.5" + version: "1.25.7" test_api: dependency: transitive description: name: test_api - sha256: "2419f20b0c8677b2d67c8ac4d1ac7372d862dc6c460cdbb052b40155408cd794" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.1" + version: "0.7.2" test_core: dependency: transitive description: name: test_core - sha256: "4d070a6bc36c1c4e89f20d353bfd71dc30cdf2bd0e14349090af360a029ab292" + sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696" url: "https://pub.dev" source: hosted - version: "0.6.2" + version: "0.6.4" typed_data: dependency: transitive description: @@ -345,14 +400,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" vm_service: dependency: transitive description: name: vm_service - sha256: a2662fb1f114f4296cf3f5a50786a2d888268d7776cf681aa17d660ffa23b246 + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.0.0" + version: "14.2.5" watcher: dependency: transitive description: @@ -386,4 +449,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/packages/reactter/pubspec.yaml b/packages/reactter/pubspec.yaml index 692efea3..dedef68c 100644 --- a/packages/reactter/pubspec.yaml +++ b/packages/reactter/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://2devs-team.github.io/reactter/ documentation: https://2devs-team.github.io/reactter/ repository: https://github.com/2devs-team/reactter/tree/master/packages/reactter license: MIT License -version: 7.3.0 +version: 8.0.0-dev.1 environment: sdk: ">=2.14.0 <4.0.0" @@ -16,9 +16,11 @@ dev_dependencies: fake_async: ^1.3.1 lints: ^2.0.1 test: ^1.25.2 + flutter_test: + sdk: flutter scripts: - test: "dart run test --coverage=./coverage " + test: "flutter test --coverage" analyze: "dart analyze ." public-dry-run: "dart pub publish --dry-run" public: "dart pub publish" \ No newline at end of file From cf5c3bf60ca4041f62ecf5f1e39ec04aefce7dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 27 Dec 2024 02:51:48 -0600 Subject: [PATCH 070/141] refactor(examples): Update example structure and enhance functionality; rename and remove outdated files, add new components. --- packages/flutter_reactter/example/README.md | 21 ++- .../lib/examples/1_counter/counter_page.dart | 9 +- .../2_calculator/calculator_page.dart | 123 +++++++++------ .../controllers/calculator_controller.dart | 107 +++++++------ .../examples/2_calculator/widgets/button.dart | 17 ++- .../widgets/calculator_action_button.dart | 12 +- .../widgets/calculator_number_button.dart | 10 +- .../widgets/calculator_result.dart | 34 +++-- .../controllers/cart_controller.dart | 119 +++++++++++++++ .../controllers/product_controller.dart | 40 +++++ .../controllers/products_controller.dart | 24 +++ .../3_shopping_cart/data/data_source.dart | 119 +++++++++++++++ .../examples/3_shopping_cart/models/cart.dart | 20 +++ .../models/cart_item.dart | 9 +- .../models/product.dart | 7 +- .../repositories/cart_repository.dart | 37 +++++ .../repositories/product_repository.dart | 20 +++ .../shopping_cart_page.dart | 7 +- .../utils/format_currency.dart | 0 .../utils/lorem_gen.dart | 0 .../3_shopping_cart/views/cart_view.dart | 119 +++++++++++++++ .../3_shopping_cart/views/products_view.dart | 83 +++++++++++ .../widgets/cart_action.dart | 7 +- .../widgets/cart_item_card.dart | 31 ++-- .../widgets/custom_icon_button.dart | 0 .../widgets/product_buttons.dart | 40 +++++ .../3_shopping_cart/widgets/product_card.dart | 103 +++++++++++++ .../widgets/product_cover.dart | 27 ++++ .../widgets/quantity.dart | 19 +-- .../3_tree/controllers/tree_controller.dart | 13 -- .../lib/examples/3_tree/states/tree_list.dart | 26 ---- .../controllers/github_search_controller.dart | 44 ++++++ .../controllers/search_input_controller.dart | 29 ++++ .../4_github_search/github_search_page.dart | 41 +++++ .../models/repository.dart | 2 +- .../models/user.dart | 0 .../providers/github_provider.dart | 39 +++++ .../repositories/github_repository.dart | 30 ++++ .../4_github_search/utils/http_response.dart | 20 +++ .../widgets/badget.dart | 0 .../4_github_search/widgets/info_card.dart | 60 ++++++++ .../widgets/repository_info.dart} | 6 +- .../4_github_search/widgets/search_bar.dart | 64 ++++++++ .../widgets/user_info.dart} | 8 +- .../controllers/cart_controller.dart | 48 ------ .../controllers/products_controller.dart | 11 -- .../examples/4_shopping_cart/models/cart.dart | 13 -- .../repositories/store_repository.dart | 105 ------------- .../4_shopping_cart/views/cart_view.dart | 97 ------------ .../4_shopping_cart/views/products_view.dart | 67 --------- .../4_shopping_cart/widgets/cart_bottom.dart | 88 ----------- .../widgets/product_buttons.dart | 41 ----- .../4_shopping_cart/widgets/product_card.dart | 98 ------------ .../example/lib/examples/5_api/api_page.dart | 88 ----------- .../5_api/controllers/api_controller.dart | 39 ----- .../examples/5_api/services/api_service.dart | 40 ----- .../examples/5_api/widgets/search_bar.dart | 84 ----------- .../actions/add_todo_action.dart | 4 +- .../actions/clear_completed_action.dart | 2 +- .../actions/filter_action.dart | 2 +- .../actions/remove_todo_action.dart | 4 +- .../actions/toggle_todo_action.dart | 4 +- .../controllers/todo_controller.dart | 23 +-- .../{6_todo => 5_todo}/models/todo.dart | 0 .../{6_todo => 5_todo}/stores/todo_store.dart | 2 +- .../{6_todo => 5_todo}/todo_page.dart | 9 +- .../{6_todo => 5_todo}/widgets/input_bar.dart | 5 +- .../widgets/radio_with_label.dart | 0 .../examples/5_todo/widgets/todo_filter.dart | 94 ++++++++++++ .../{6_todo => 5_todo}/widgets/todo_item.dart | 2 +- .../{6_todo => 5_todo}/widgets/todo_list.dart | 4 +- .../examples/6_todo/widgets/todo_filter.dart | 97 ------------ .../6_tree/controllers/tree_controller.dart | 11 ++ .../lib/examples/6_tree/states/tree_list.dart | 65 ++++++++ .../{3_tree => 6_tree}/states/tree_node.dart | 141 +++++++++++------- .../{3_tree => 6_tree}/tree_page.dart | 14 +- .../widgets/custom_icon_button.dart | 3 - .../{3_tree => 6_tree}/widgets/tree_item.dart | 44 +++--- .../examples/7_animation/animation_page.dart | 20 +-- .../7_animation/hooks/use_animation.dart | 16 +- .../example/lib/examples_page.dart | 66 ++++---- .../flutter_reactter/example/lib/main.dart | 2 +- .../flutter_reactter/example/pubspec.lock | 47 +++--- .../flutter_reactter/example/pubspec.yaml | 11 +- 84 files changed, 1737 insertions(+), 1320 deletions(-) create mode 100644 packages/flutter_reactter/example/lib/examples/3_shopping_cart/controllers/cart_controller.dart create mode 100644 packages/flutter_reactter/example/lib/examples/3_shopping_cart/controllers/product_controller.dart create mode 100644 packages/flutter_reactter/example/lib/examples/3_shopping_cart/controllers/products_controller.dart create mode 100644 packages/flutter_reactter/example/lib/examples/3_shopping_cart/data/data_source.dart create mode 100644 packages/flutter_reactter/example/lib/examples/3_shopping_cart/models/cart.dart rename packages/flutter_reactter/example/lib/examples/{4_shopping_cart => 3_shopping_cart}/models/cart_item.dart (52%) rename packages/flutter_reactter/example/lib/examples/{4_shopping_cart => 3_shopping_cart}/models/product.dart (82%) create mode 100644 packages/flutter_reactter/example/lib/examples/3_shopping_cart/repositories/cart_repository.dart create mode 100644 packages/flutter_reactter/example/lib/examples/3_shopping_cart/repositories/product_repository.dart rename packages/flutter_reactter/example/lib/examples/{4_shopping_cart => 3_shopping_cart}/shopping_cart_page.dart (73%) rename packages/flutter_reactter/example/lib/examples/{4_shopping_cart => 3_shopping_cart}/utils/format_currency.dart (100%) rename packages/flutter_reactter/example/lib/examples/{4_shopping_cart => 3_shopping_cart}/utils/lorem_gen.dart (100%) create mode 100644 packages/flutter_reactter/example/lib/examples/3_shopping_cart/views/cart_view.dart create mode 100644 packages/flutter_reactter/example/lib/examples/3_shopping_cart/views/products_view.dart rename packages/flutter_reactter/example/lib/examples/{4_shopping_cart => 3_shopping_cart}/widgets/cart_action.dart (86%) rename packages/flutter_reactter/example/lib/examples/{4_shopping_cart => 3_shopping_cart}/widgets/cart_item_card.dart (76%) rename packages/flutter_reactter/example/lib/examples/{4_shopping_cart => 3_shopping_cart}/widgets/custom_icon_button.dart (100%) create mode 100644 packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/product_buttons.dart create mode 100644 packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/product_card.dart create mode 100644 packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/product_cover.dart rename packages/flutter_reactter/example/lib/examples/{4_shopping_cart => 3_shopping_cart}/widgets/quantity.dart (71%) delete mode 100644 packages/flutter_reactter/example/lib/examples/3_tree/controllers/tree_controller.dart delete mode 100644 packages/flutter_reactter/example/lib/examples/3_tree/states/tree_list.dart create mode 100644 packages/flutter_reactter/example/lib/examples/4_github_search/controllers/github_search_controller.dart create mode 100644 packages/flutter_reactter/example/lib/examples/4_github_search/controllers/search_input_controller.dart create mode 100644 packages/flutter_reactter/example/lib/examples/4_github_search/github_search_page.dart rename packages/flutter_reactter/example/lib/examples/{5_api => 4_github_search}/models/repository.dart (92%) rename packages/flutter_reactter/example/lib/examples/{5_api => 4_github_search}/models/user.dart (100%) create mode 100644 packages/flutter_reactter/example/lib/examples/4_github_search/providers/github_provider.dart create mode 100644 packages/flutter_reactter/example/lib/examples/4_github_search/repositories/github_repository.dart create mode 100644 packages/flutter_reactter/example/lib/examples/4_github_search/utils/http_response.dart rename packages/flutter_reactter/example/lib/examples/{5_api => 4_github_search}/widgets/badget.dart (100%) create mode 100644 packages/flutter_reactter/example/lib/examples/4_github_search/widgets/info_card.dart rename packages/flutter_reactter/example/lib/examples/{5_api/widgets/repository_item.dart => 4_github_search/widgets/repository_info.dart} (94%) create mode 100644 packages/flutter_reactter/example/lib/examples/4_github_search/widgets/search_bar.dart rename packages/flutter_reactter/example/lib/examples/{5_api/widgets/user_item.dart => 4_github_search/widgets/user_info.dart} (93%) delete mode 100644 packages/flutter_reactter/example/lib/examples/4_shopping_cart/controllers/cart_controller.dart delete mode 100644 packages/flutter_reactter/example/lib/examples/4_shopping_cart/controllers/products_controller.dart delete mode 100644 packages/flutter_reactter/example/lib/examples/4_shopping_cart/models/cart.dart delete mode 100644 packages/flutter_reactter/example/lib/examples/4_shopping_cart/repositories/store_repository.dart delete mode 100644 packages/flutter_reactter/example/lib/examples/4_shopping_cart/views/cart_view.dart delete mode 100644 packages/flutter_reactter/example/lib/examples/4_shopping_cart/views/products_view.dart delete mode 100644 packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/cart_bottom.dart delete mode 100644 packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/product_buttons.dart delete mode 100644 packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/product_card.dart delete mode 100644 packages/flutter_reactter/example/lib/examples/5_api/api_page.dart delete mode 100644 packages/flutter_reactter/example/lib/examples/5_api/controllers/api_controller.dart delete mode 100644 packages/flutter_reactter/example/lib/examples/5_api/services/api_service.dart delete mode 100644 packages/flutter_reactter/example/lib/examples/5_api/widgets/search_bar.dart rename packages/flutter_reactter/example/lib/examples/{6_todo => 5_todo}/actions/add_todo_action.dart (76%) rename packages/flutter_reactter/example/lib/examples/{6_todo => 5_todo}/actions/clear_completed_action.dart (87%) rename packages/flutter_reactter/example/lib/examples/{6_todo => 5_todo}/actions/filter_action.dart (86%) rename packages/flutter_reactter/example/lib/examples/{6_todo => 5_todo}/actions/remove_todo_action.dart (81%) rename packages/flutter_reactter/example/lib/examples/{6_todo => 5_todo}/actions/toggle_todo_action.dart (83%) rename packages/flutter_reactter/example/lib/examples/{6_todo => 5_todo}/controllers/todo_controller.dart (65%) rename packages/flutter_reactter/example/lib/examples/{6_todo => 5_todo}/models/todo.dart (100%) rename packages/flutter_reactter/example/lib/examples/{6_todo => 5_todo}/stores/todo_store.dart (92%) rename packages/flutter_reactter/example/lib/examples/{6_todo => 5_todo}/todo_page.dart (80%) rename packages/flutter_reactter/example/lib/examples/{6_todo => 5_todo}/widgets/input_bar.dart (94%) rename packages/flutter_reactter/example/lib/examples/{6_todo => 5_todo}/widgets/radio_with_label.dart (100%) create mode 100644 packages/flutter_reactter/example/lib/examples/5_todo/widgets/todo_filter.dart rename packages/flutter_reactter/example/lib/examples/{6_todo => 5_todo}/widgets/todo_item.dart (94%) rename packages/flutter_reactter/example/lib/examples/{6_todo => 5_todo}/widgets/todo_list.dart (85%) delete mode 100644 packages/flutter_reactter/example/lib/examples/6_todo/widgets/todo_filter.dart create mode 100644 packages/flutter_reactter/example/lib/examples/6_tree/controllers/tree_controller.dart create mode 100644 packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart rename packages/flutter_reactter/example/lib/examples/{3_tree => 6_tree}/states/tree_node.dart (54%) rename packages/flutter_reactter/example/lib/examples/{3_tree => 6_tree}/tree_page.dart (73%) rename packages/flutter_reactter/example/lib/examples/{3_tree => 6_tree}/widgets/custom_icon_button.dart (88%) rename packages/flutter_reactter/example/lib/examples/{3_tree => 6_tree}/widgets/tree_item.dart (89%) diff --git a/packages/flutter_reactter/example/README.md b/packages/flutter_reactter/example/README.md index 91ce5452..4c88b336 100644 --- a/packages/flutter_reactter/example/README.md +++ b/packages/flutter_reactter/example/README.md @@ -13,20 +13,17 @@ flutter create . flutter run ``` -for [Github search example](#github-search), add http permission: - -- [Android](https://docs.flutter.dev/development/data-and-backend/networking#android) -- [IOS](https://guides.codepath.com/ios/Internet-Permissions) +for [Github search example](#github-search), add http permission(This is the documented [here](https://docs.flutter.dev/data-and-backend/networking)) ## Counter Increase and decrease the counter. -> Learn how to use reactive state using signal. +> Learn how to use reactive state using UseState. [![Counter example](https://raw.githubusercontent.com/2devs-team/reactter_assets/main/examples/counter_example.gif)](https://github.com/2devs-team/reactter/tree/master/packages/flutter_reactter/example/lib/counter) -Implements: `ReactterWatcher`, `Signal`. +Implements: `RtWatcher`, `UseState`. ## Calculator @@ -36,7 +33,7 @@ Performs simple arithmetic operations on numbers [![Counter example](https://raw.githubusercontent.com/2devs-team/reactter_assets/main/examples/calculator_example.png)](https://github.com/2devs-team/reactter/tree/master/packages/flutter_reactter/example/lib/calculator) -Implements: `BuildContext.use`, `ReactterConsumer`, `ReactterProvider`, `ReactterSelector`, `Signal`. +Implements: `BuildContext.use`, `RtProvider`, `RtSelector`, `RtWatcher`, `UseState`. ## Shopping cart @@ -46,7 +43,7 @@ Add, remove product to cart and checkout. [![Shopping cart example](https://raw.githubusercontent.com/2devs-team/reactter_assets/main/examples/cart_example.gif)](https://github.com/2devs-team/reactter/tree/master/packages/flutter_reactter/example/lib/shopping_cart) -Implements: `ReactterComponent`, `ReactterConsumer`, `ReactterProvider`, `ReactterProviders`, `ReactterSelector`, `UseDependencycy`, `UseState`. +Implements: `ReactterComponent`, `RtConsumer`, `RtProvider`, `RtProviders`, `RtSelector`, `UseDependencycy`, `UseState`. ## Tree widget @@ -56,7 +53,7 @@ Add, remove and hide child widget with counter. [![Tree widget example](https://raw.githubusercontent.com/2devs-team/reactter_assets/main/examples/tree_example.gif)](https://github.com/2devs-team/reactter/tree/master/packages/flutter_reactter/example/lib/tree) -Implements: `BuilContext.use`, `BuilContext.watchId`, `Reactter.lazyState`, `ReactterComponent`, `ReactterProvider`, `UseEffect`, `UseState`. +Implements: `BuilContext.use`, `BuilContext.watchId`, `Reactter.lazyState`, `ReactterComponent`, `RtProvider`, `UseEffect`, `UseState`. ## Github search @@ -66,7 +63,7 @@ Search user or repository and show info about it. [![Github search example](https://raw.githubusercontent.com/2devs-team/reactter_assets/main/examples/api_example.png)](https://github.com/2devs-team/reactter/tree/master/packages/flutter_reactter/example/lib/api) -Implements: `Memo`, `ReactterConsumer`, `ReactterProvider`, `UseAsyncState`. +Implements: `Memo`, `RtConsumer`, `RtProvider`, `UseAsyncState`. ## To-Do List @@ -76,7 +73,7 @@ Add and remove to-do, mark and unmark to-do as done and filter to-do list. [![To-Do List example](https://raw.githubusercontent.com/2devs-team/reactter_assets/main/examples/todos_example.png)](https://github.com/2devs-team/reactter/tree/master/packages/flutter_reactter/example/lib/todo) -Implements: `Reactter.lazyState`, `ReactterActionCallable`, `ReactterCompontent`, `ReactterConsumer`, `ReactterProvider`, `ReactterSelector`, `UseCompute`, `UseReducer`. +Implements: `Reactter.lazyState`, `ReactterActionCallable`, `ReactterCompontent`, `RtConsumer`, `RtProvider`, `RtSelector`, `UseCompute`, `UseReducer`. ## Animate widget @@ -86,4 +83,4 @@ Change size, shape and color using animations. [![Animate widget example](https://raw.githubusercontent.com/2devs-team/reactter_assets/main/examples/animation_example.gif)](https://github.com/2devs-team/reactter/tree/master/packages/flutter_reactter/example/lib/animation) -Implements: `Reactter.lazyState`, `ReactterConsumer`, `ReactterHook`, `ReactterProvider`, `ReactterSelector`, `UseCompute`, `UseEffect`, `UseState`. +Implements: `Reactter.lazyState`, `RtConsumer`, `ReactterHook`, `RtProvider`, `RtSelector`, `UseCompute`, `UseEffect`, `UseState`. diff --git a/packages/flutter_reactter/example/lib/examples/1_counter/counter_page.dart b/packages/flutter_reactter/example/lib/examples/1_counter/counter_page.dart index d7babd84..e901d346 100644 --- a/packages/flutter_reactter/example/lib/examples/1_counter/counter_page.dart +++ b/packages/flutter_reactter/example/lib/examples/1_counter/counter_page.dart @@ -1,14 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; -final count = UseState(0); +final uCount = UseState(0, debugLabel: "uCount"); class CounterPage extends StatelessWidget { const CounterPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - return Scaffold( appBar: AppBar( title: const Text("Counter"), @@ -21,7 +20,7 @@ class CounterPage extends StatelessWidget { runSpacing: 8, children: [ ElevatedButton( - onPressed: () => count.value--, + onPressed: () => uCount.value--, style: ElevatedButton.styleFrom( shape: const CircleBorder(), backgroundColor: Colors.red, @@ -40,7 +39,7 @@ class CounterPage extends StatelessWidget { child: FittedBox( child: RtWatcher((context, watch) { return Text( - "${watch(count).value}", + "${watch(uCount).value}", style: Theme.of(context).textTheme.displaySmall, textAlign: TextAlign.center, ); @@ -48,7 +47,7 @@ class CounterPage extends StatelessWidget { ), ), ElevatedButton( - onPressed: () => count.value++, + onPressed: () => uCount.value++, style: ElevatedButton.styleFrom( shape: const CircleBorder(), backgroundColor: Colors.green, diff --git a/packages/flutter_reactter/example/lib/examples/2_calculator/calculator_page.dart b/packages/flutter_reactter/example/lib/examples/2_calculator/calculator_page.dart index d3afca04..52da67c1 100644 --- a/packages/flutter_reactter/example/lib/examples/2_calculator/calculator_page.dart +++ b/packages/flutter_reactter/example/lib/examples/2_calculator/calculator_page.dart @@ -8,6 +8,8 @@ import 'package:examples/examples/2_calculator/widgets/calculator_action_button. import 'package:examples/examples/2_calculator/widgets/calculator_number_button.dart'; import 'package:examples/examples/2_calculator/widgets/calculator_result.dart'; +const kCalculatorSize = Size(230, 315); + class CalculatorPage extends StatelessWidget { const CalculatorPage({Key? key}) : super(key: key); @@ -20,50 +22,87 @@ class CalculatorPage extends StatelessWidget { appBar: AppBar( title: const Text('Calculator'), ), - body: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - CalculatorResult(), - Row( - children: [ - CalculatorActionButton(action: ActionCalculator.clear), - CalculatorActionButton(action: ActionCalculator.sign), - CalculatorActionButton(action: ActionCalculator.porcentage), - CalculatorActionButton(action: ActionCalculator.divide), - ], - ), - Row( - children: [ - CalculatorNumberButton(number: 7), - CalculatorNumberButton(number: 8), - CalculatorNumberButton(number: 9), - CalculatorActionButton(action: ActionCalculator.multiply), - ], - ), - Row( - children: [ - CalculatorNumberButton(number: 4), - CalculatorNumberButton(number: 5), - CalculatorNumberButton(number: 6), - CalculatorActionButton(action: ActionCalculator.subtract), - ], - ), - Row( - children: [ - CalculatorNumberButton(number: 1), - CalculatorNumberButton(number: 2), - CalculatorNumberButton(number: 3), - CalculatorActionButton(action: ActionCalculator.add), - ], + body: Center( + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: kCalculatorSize.width, + maxHeight: kCalculatorSize.height, ), - Row( - children: [ - CalculatorNumberButton(number: 0), - CalculatorActionButton(action: ActionCalculator.point), - CalculatorActionButton(action: ActionCalculator.equal), - ], + child: AspectRatio( + aspectRatio: kCalculatorSize.width / kCalculatorSize.height, + child: FittedBox( + fit: BoxFit.fitWidth, + child: Container( + color: Colors.grey.shade900, + width: kCalculatorSize.width, + height: kCalculatorSize.height, + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + CalculatorResult(), + Row( + children: [ + CalculatorActionButton( + action: ActionCalculator.clear, + ), + CalculatorActionButton( + action: ActionCalculator.sign, + ), + CalculatorActionButton( + action: ActionCalculator.porcentage, + ), + CalculatorActionButton( + action: ActionCalculator.divide, + ), + ], + ), + Row( + children: [ + CalculatorNumberButton(number: 7), + CalculatorNumberButton(number: 8), + CalculatorNumberButton(number: 9), + CalculatorActionButton( + action: ActionCalculator.multiply, + ), + ], + ), + Row( + children: [ + CalculatorNumberButton(number: 4), + CalculatorNumberButton(number: 5), + CalculatorNumberButton(number: 6), + CalculatorActionButton( + action: ActionCalculator.subtract, + ), + ], + ), + Row( + children: [ + CalculatorNumberButton(number: 1), + CalculatorNumberButton(number: 2), + CalculatorNumberButton(number: 3), + CalculatorActionButton( + action: ActionCalculator.add, + ), + ], + ), + Row( + children: [ + CalculatorNumberButton(number: 0, flex: 2), + CalculatorActionButton( + action: ActionCalculator.point, + ), + CalculatorActionButton( + action: ActionCalculator.equal, + ), + ], + ), + ], + ), + ), + ), ), - ], + ), ), ); }, diff --git a/packages/flutter_reactter/example/lib/examples/2_calculator/controllers/calculator_controller.dart b/packages/flutter_reactter/example/lib/examples/2_calculator/controllers/calculator_controller.dart index 8f47c627..51ea6c61 100644 --- a/packages/flutter_reactter/example/lib/examples/2_calculator/controllers/calculator_controller.dart +++ b/packages/flutter_reactter/example/lib/examples/2_calculator/controllers/calculator_controller.dart @@ -21,76 +21,91 @@ final mathOperationMethods = { }; class CalculatorController { - final result = Signal("0"); - final mathOperation = Signal(null); + final uResult = UseState("0", debugLabel: "uResult"); + final uMathOperation = UseState( + null, + debugLabel: "uMathOperation", + ); ActionCalculator? _lastAction; double? _numberMemorized; - late final _actionMethods = { - ActionCalculator.number: _insertNumber, - ActionCalculator.add: _resolveMathOperation, - ActionCalculator.subtract: _resolveMathOperation, - ActionCalculator.multiply: _resolveMathOperation, - ActionCalculator.divide: _resolveMathOperation, - ActionCalculator.equal: _equal, - ActionCalculator.sign: _changeSign, - ActionCalculator.porcentage: _calculatePercentage, - ActionCalculator.point: _addPoint, - ActionCalculator.clear: _clear, - }; - void executeAction(ActionCalculator action, [int? value]) { - final actionMethod = _actionMethods[action]; - - () { - if (actionMethod is Function(int) && value != null) { - return actionMethod(value); - } - - if (actionMethod is Function(ActionCalculator)) { - return actionMethod(action); - } - - actionMethod?.call(); - }(); + switch (action) { + case ActionCalculator.number: + if (value != null) _insertNumber(value); + break; + case ActionCalculator.add: + case ActionCalculator.subtract: + case ActionCalculator.multiply: + case ActionCalculator.divide: + _resolveMathOperation(action); + break; + case ActionCalculator.equal: + _equal(); + break; + case ActionCalculator.sign: + _changeSign(); + break; + case ActionCalculator.porcentage: + _calculatePercentage(); + break; + case ActionCalculator.point: + _addPoint(); + break; + case ActionCalculator.clear: + _clear(); + break; + } _lastAction = action; } void _insertNumber(int value) { - if (_lastAction != ActionCalculator.number) _resetResult(); + if (_lastAction + case != ActionCalculator.number && + != ActionCalculator.point && + != ActionCalculator.sign) { + _resetResult(); + } + _concatValue(value); } void _resolveMathOperation(ActionCalculator action) { - mathOperation.value = action; + uMathOperation.value = action; _resolve(); - _numberMemorized = double.tryParse(result.value); + _numberMemorized = double.tryParse(uResult.value); } void _equal() { - final resultNumber = double.tryParse(result.value); + final resultNumber = double.tryParse(uResult.value); + _resolve(); - if (_lastAction != ActionCalculator.equal) _numberMemorized = resultNumber; + + if (_lastAction != ActionCalculator.equal) { + _numberMemorized = resultNumber; + } } void _changeSign() { - final resultNumber = double.tryParse(result.value) ?? 0; - result(_formatValue(resultNumber * -1)); + final resultNumber = double.tryParse(uResult.value) ?? 0; + uResult.value = _formatValue(resultNumber * -1); } void _calculatePercentage() { - final resultNumber = double.tryParse(result.value) ?? 0; - result(_formatValue(resultNumber / 100)); + final resultNumber = double.tryParse(uResult.value) ?? 0; + uResult.value = _formatValue(resultNumber / 100); } void _addPoint() { - if (!result.value.contains(".")) result("$result."); + if (uResult.value.contains(".")) return; + + uResult.value = "${uResult.value}."; } void _clear() { - mathOperation.value = null; + uMathOperation.value = null; _numberMemorized = null; _resetResult(); } @@ -98,21 +113,23 @@ class CalculatorController { void _resolve() { if (_numberMemorized == null) return; - final resultNumber = double.tryParse(result.value) ?? 0; - final mathOperationMethod = mathOperationMethods[mathOperation.value]; + final resultNumber = double.tryParse(uResult.value) ?? 0; + final mathOperationMethod = mathOperationMethods[uMathOperation.value]; final resultOfOperation = mathOperationMethod?.call( _numberMemorized ?? resultNumber, resultNumber, ); - result(_formatValue(resultOfOperation ?? 0)); + uResult.value = _formatValue(resultOfOperation ?? 0); } - void _resetResult() => result(_formatValue(0.0)); + void _resetResult() { + uResult.value = _formatValue(0.0); + } void _concatValue(int? value) { - final numConcatenated = double.tryParse("$result$value") ?? 0; - result(_formatValue(numConcatenated)); + final numConcatenated = double.tryParse("${uResult.value}$value") ?? 0; + uResult.value = _formatValue(numConcatenated); } String _formatValue(double value) { diff --git a/packages/flutter_reactter/example/lib/examples/2_calculator/widgets/button.dart b/packages/flutter_reactter/example/lib/examples/2_calculator/widgets/button.dart index c807f4e3..63a7c398 100644 --- a/packages/flutter_reactter/example/lib/examples/2_calculator/widgets/button.dart +++ b/packages/flutter_reactter/example/lib/examples/2_calculator/widgets/button.dart @@ -42,18 +42,23 @@ class Button extends StatelessWidget { onPressed: onPressed ?? () {}, style: ElevatedButton.styleFrom( backgroundColor: color ?? Colors.grey.shade700, + padding: const EdgeInsets.symmetric( + horizontal: 16, + ), side: BorderSide( width: isSelected ? 4 : 1, color: Theme.of(context).scaffoldBackgroundColor, ), shape: const ContinuousRectangleBorder(side: BorderSide.none), ), - child: Text( - label, - style: Theme.of(context).textTheme.headlineSmall?.copyWith( - color: Colors.white, - fontSize: isSmall ? 16 : null, - ), + child: FittedBox( + child: Text( + label, + style: Theme.of(context).textTheme.headlineSmall?.copyWith( + color: Colors.white, + fontSize: isSmall ? 16 : null, + ), + ), ), ), ); diff --git a/packages/flutter_reactter/example/lib/examples/2_calculator/widgets/calculator_action_button.dart b/packages/flutter_reactter/example/lib/examples/2_calculator/widgets/calculator_action_button.dart index b025646b..53db474c 100644 --- a/packages/flutter_reactter/example/lib/examples/2_calculator/widgets/calculator_action_button.dart +++ b/packages/flutter_reactter/example/lib/examples/2_calculator/widgets/calculator_action_button.dart @@ -1,9 +1,8 @@ +import 'package:examples/examples/2_calculator/controllers/calculator_controller.dart'; +import 'package:examples/examples/2_calculator/widgets/button.dart'; import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:examples/examples/2_calculator/widgets/button.dart'; -import 'package:examples/examples/2_calculator/controllers/calculator_controller.dart'; - final actionLabel = { ActionCalculator.add: '+', ActionCalculator.clear: 'C', @@ -12,7 +11,7 @@ final actionLabel = { ActionCalculator.multiply: '×', ActionCalculator.point: '.', ActionCalculator.porcentage: '%', - ActionCalculator.sign: '+/–', + ActionCalculator.sign: 'ᐩ⁄-', ActionCalculator.subtract: '–', }; @@ -29,14 +28,13 @@ class CalculatorActionButton extends StatelessWidget { final calculatorController = context.use(); final isMathOperation = mathOperationMethods.keys.contains(action); final label = actionLabel[action] ?? 'N/A'; - void onPressed() => calculatorController.executeAction(action); if (isMathOperation) { return Expanded( child: RtSelector( - selector: (inst, $) { - return $(calculatorController.mathOperation).value == action; + selector: (inst, watch) { + return watch(calculatorController.uMathOperation).value == action; }, builder: (_, __, isSelected, ___) { return Button.primary( diff --git a/packages/flutter_reactter/example/lib/examples/2_calculator/widgets/calculator_number_button.dart b/packages/flutter_reactter/example/lib/examples/2_calculator/widgets/calculator_number_button.dart index eb9f020f..93bb656f 100644 --- a/packages/flutter_reactter/example/lib/examples/2_calculator/widgets/calculator_number_button.dart +++ b/packages/flutter_reactter/example/lib/examples/2_calculator/widgets/calculator_number_button.dart @@ -1,21 +1,21 @@ +import 'package:examples/examples/2_calculator/controllers/calculator_controller.dart'; +import 'package:examples/examples/2_calculator/widgets/button.dart'; import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:examples/examples/2_calculator/widgets/button.dart'; -import 'package:examples/examples/2_calculator/controllers/calculator_controller.dart'; - class CalculatorNumberButton extends StatelessWidget { - const CalculatorNumberButton({Key? key, required this.number}) + const CalculatorNumberButton({Key? key, required this.number, this.flex = 1}) : super(key: key); final int number; + final int flex; @override Widget build(BuildContext context) { final calculatorController = context.use(); return Expanded( - flex: number == 0 ? 2 : 1, + flex: flex, child: Button.tertiary( label: "$number", onPressed: () => calculatorController.executeAction( diff --git a/packages/flutter_reactter/example/lib/examples/2_calculator/widgets/calculator_result.dart b/packages/flutter_reactter/example/lib/examples/2_calculator/widgets/calculator_result.dart index d2fb4849..2e708cab 100644 --- a/packages/flutter_reactter/example/lib/examples/2_calculator/widgets/calculator_result.dart +++ b/packages/flutter_reactter/example/lib/examples/2_calculator/widgets/calculator_result.dart @@ -1,27 +1,33 @@ +import 'package:examples/examples/2_calculator/controllers/calculator_controller.dart'; import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:examples/examples/2_calculator/controllers/calculator_controller.dart'; - class CalculatorResult extends StatelessWidget { const CalculatorResult({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - final calculatorController = context.use(); - return Container( - padding: const EdgeInsets.all(10.0), alignment: Alignment.bottomRight, - child: RtWatcher((context, watch) { - return Text( - "${watch(calculatorController.result)}", - style: const TextStyle( - fontSize: 48, - fontWeight: FontWeight.w200, - ), - ); - }), + height: 75, + padding: const EdgeInsets.symmetric( + horizontal: 16, + ), + child: FittedBox( + fit: BoxFit.fitWidth, + child: RtWatcher((context, watch) { + final calculatorController = context.use(); + final result = watch(calculatorController.uResult).value; + + return Text( + result, + style: const TextStyle( + fontSize: 48, + fontWeight: FontWeight.w200, + ), + ); + }), + ), ); } } diff --git a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/controllers/cart_controller.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/controllers/cart_controller.dart new file mode 100644 index 00000000..8f41ba2c --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/controllers/cart_controller.dart @@ -0,0 +1,119 @@ +import 'package:examples/examples/3_shopping_cart/models/cart_item.dart'; +import 'package:examples/examples/3_shopping_cart/repositories/cart_repository.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; + +class CartController { + final _recentlyUpdatedCartItems = {}; + + final uCartRepository = UseDependency.create( + CartRepository.new, + debugLabel: 'uCartRepository', + ); + + late final uAsyncCart = Rt.lazyState(() { + assert(uCartRepository.instance != null); + + return UseAsyncState( + uCartRepository.instance!.getCart, + null, + debugLabel: 'uAsyncCart', + ); + }, this); + + late final uCartItems = Rt.lazyState(() { + return UseCompute( + () => uAsyncCart.value?.items.toList() ?? [], + [uAsyncCart.uValue], + debugLabel: 'uCartItems', + ); + }, this); + + late final uTotal = Rt.lazyState(() { + return UseCompute( + () => uAsyncCart.value?.total ?? 0, + [uAsyncCart.uValue], + debugLabel: 'uTotal', + ); + }, this); + + CartController() { + _loadCartItems(); + } + + Future incrementCartItem(CartItem cartItem) async { + final newCartItem = cartItem.copyWith(quantity: cartItem.quantity + 1); + await _updateCartItem(newCartItem); + } + + Future decrementCartItem(CartItem cartItem) async { + final newCartItem = cartItem.copyWith(quantity: cartItem.quantity - 1); + await _updateCartItem(newCartItem); + } + + Future removeCartItem(CartItem cartItem) async { + await _updateCartItem(cartItem.copyWith(quantity: 0)); + } + + Future checkout() async { + await uCartRepository.instance?.checkout(); + await _loadCartItems(); + } + + Future _loadCartItems() async { + uAsyncCart.cancel(); + await uAsyncCart.resolve(); + } + + bool _updateCartItemsState(CartItem cartItem) { + final index = uCartItems.value.indexOf(cartItem); + final product = cartItem.product; + + if (cartItem.quantity > product.stock) { + return false; + } + + uCartItems.value.remove(cartItem); + + if (cartItem.quantity > 0) { + final indexToInsert = index < 0 ? uCartItems.value.length : index; + uCartItems.value.insert(indexToInsert, cartItem); + } + + uCartItems.notify(); + + return true; + } + + Future _updateCartItem(CartItem cartItem) async { + uAsyncCart.cancel(); + + final isStateUpdated = _updateCartItemsState(cartItem); + + if (!isStateUpdated) return null; + + final isUpdating = _recentlyUpdatedCartItems.remove(cartItem); + _recentlyUpdatedCartItems.add(cartItem); + + if (isUpdating) return cartItem; + + final cartItemUpdated = await uCartRepository.instance?.updateCartItem( + cartItem, + ); + + final mostRecentCartItem = _recentlyUpdatedCartItems.lookup(cartItem); + final isStillUpdating = mostRecentCartItem != null && + cartItem.quantity != mostRecentCartItem.quantity; + + _recentlyUpdatedCartItems.remove(mostRecentCartItem); + + if (isStillUpdating) { + return await _updateCartItem(mostRecentCartItem); + } + + if (_recentlyUpdatedCartItems.isEmpty) { + await _loadCartItems(); + } + + return cartItemUpdated; + } +} diff --git a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/controllers/product_controller.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/controllers/product_controller.dart new file mode 100644 index 00000000..7177f79d --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/controllers/product_controller.dart @@ -0,0 +1,40 @@ +import 'package:examples/examples/3_shopping_cart/controllers/cart_controller.dart'; +import 'package:examples/examples/3_shopping_cart/models/cart_item.dart'; +import 'package:examples/examples/3_shopping_cart/models/product.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; + +class ProductController { + final Product product; + + final uCartController = UseDependency.create(CartController.new); + + late final uQuantity = Rt.lazyState(() { + assert(uCartController.instance != null); + + return UseCompute( + () { + final cartItem = uCartController.instance!.uCartItems.value.firstWhere( + (item) => item.product == product, + orElse: () => CartItem(product, 0), + ); + + return cartItem.quantity; + }, + [uCartController.instance!.uCartItems], + ); + }, this); + + ProductController(this.product); + + Future addToCart() async { + await uCartController.instance?.incrementCartItem( + CartItem(product, uQuantity.value), + ); + } + + Future removeFromCart() async { + await uCartController.instance?.decrementCartItem( + CartItem(product, uQuantity.value), + ); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/controllers/products_controller.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/controllers/products_controller.dart new file mode 100644 index 00000000..e3910cc1 --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/controllers/products_controller.dart @@ -0,0 +1,24 @@ +import 'package:examples/examples/3_shopping_cart/models/product.dart'; +import 'package:examples/examples/3_shopping_cart/repositories/product_repository.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; + +class ProductsController { + final uProductRepository = UseDependency.create(ProductRepository.new); + + late final uAsyncProducts = Rt.lazyState( + () { + assert(uProductRepository.instance != null); + + return UseAsyncState>( + uProductRepository.instance!.getAllProducts, + [], + debugLabel: 'uAsyncProducts', + ); + }, + this, + ); + + ProductsController() { + uAsyncProducts.resolve(); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/data/data_source.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/data/data_source.dart new file mode 100644 index 00000000..96c5d111 --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/data/data_source.dart @@ -0,0 +1,119 @@ +import 'dart:math'; + +import 'package:examples/examples/3_shopping_cart/models/cart.dart'; +import 'package:examples/examples/3_shopping_cart/models/cart_item.dart'; +import 'package:examples/examples/3_shopping_cart/models/product.dart'; +import 'package:examples/examples/3_shopping_cart/utils/lorem_gen.dart'; +import 'package:flutter/material.dart'; + +const delay = Duration(milliseconds: 500); + +abstract class IDataSource { + Future> fetchAllProducts(); + Future fetchCart(); + Future fetchCartItemByProduct(Product product); + Future putCartItem(CartItem cartItem); + Future deleteCartItem(CartItem cartItem); + Future checkout(); +} + +class DataSource implements IDataSource { + static final _random = Random(); + + static final dataSource = DataSource._(); + + Cart _cart = Cart(); + final _products = _generateProducts(); + + DataSource._(); + + factory DataSource() { + return dataSource; + } + + @override + Future> fetchAllProducts() async { + await Future.delayed(delay); + return _products; + } + + @override + Future fetchCart() async { + await Future.delayed(delay); + return _cart; + } + + @override + Future fetchCartItemByProduct(Product product) async { + await Future.delayed(delay); + return _cart.items.lookup(CartItem(product, 0)); + } + + @override + Future putCartItem(CartItem cartItem) async { + await Future.delayed(delay); + + final newItems = { + ..._cart.items.map((item) => item == cartItem ? cartItem : item), + cartItem, + }; + final cartItemPrev = _cart.items.lookup(cartItem); + final cartItemPrevTotal = cartItemPrev != null + ? cartItemPrev.product.price * cartItemPrev.quantity + : 0; + final cartItemTotal = cartItem.product.price * cartItem.quantity; + + _cart = _cart.copyWith( + items: newItems, + total: _cart.total - cartItemPrevTotal + cartItemTotal, + ); + + return cartItem; + } + + @override + Future deleteCartItem(CartItem cartItem) async { + await Future.delayed(delay); + + final cartItemPrev = _cart.items.lookup(cartItem); + final cartItemPrevTotal = cartItemPrev != null + ? cartItemPrev.product.price * cartItemPrev.quantity + : 0; + + _cart.items.remove(cartItem); + _cart = _cart.copyWith( + items: _cart.items, + total: _cart.total - cartItemPrevTotal, + ); + } + + @override + Future checkout() async { + await Future.delayed(delay); + + for (final item in _cart.items) { + final product = item.product; + final newStock = product.stock - item.quantity; + final index = _products.indexOf(product); + + _products.remove(product); + _products.insert(index, product.copyWith(stock: newStock)); + } + + _cart = Cart(); + } + + static List _generateProducts() { + return List.generate( + 26, + (index) { + return Product( + id: index, + name: generateLoremText(_random.nextInt(8) + 3), + price: (_random.nextInt(3000) + 100) + _random.nextDouble(), + stock: _random.nextInt(20), + ); + }, + ); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/models/cart.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/models/cart.dart new file mode 100644 index 00000000..c7fc150d --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/models/cart.dart @@ -0,0 +1,20 @@ +import 'package:examples/examples/3_shopping_cart/models/cart_item.dart'; + +class Cart { + final Set items; + final double total; + + Cart({ + this.items = const {}, + this.total = 0.0, + }); + + Cart copyWith({ + Set? items, + double? total, + }) => + Cart( + items: items ?? this.items, + total: total ?? this.total, + ); +} diff --git a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/models/cart_item.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/models/cart_item.dart similarity index 52% rename from packages/flutter_reactter/example/lib/examples/4_shopping_cart/models/cart_item.dart rename to packages/flutter_reactter/example/lib/examples/3_shopping_cart/models/cart_item.dart index db3f4476..019fecbf 100644 --- a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/models/cart_item.dart +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/models/cart_item.dart @@ -1,4 +1,4 @@ -import 'package:examples/examples/4_shopping_cart/models/product.dart'; +import 'package:examples/examples/3_shopping_cart/models/product.dart'; class CartItem { final Product product; @@ -6,6 +6,13 @@ class CartItem { CartItem(this.product, this.quantity); + @override + bool operator ==(Object other) => + identical(this, other) || other is CartItem && product == other.product; + + @override + int get hashCode => Object.hashAll([product]); + CartItem copyWith({ Product? product, int? quantity, diff --git a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/models/product.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/models/product.dart similarity index 82% rename from packages/flutter_reactter/example/lib/examples/4_shopping_cart/models/product.dart rename to packages/flutter_reactter/example/lib/examples/3_shopping_cart/models/product.dart index c6a21f3f..5759e261 100644 --- a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/models/product.dart +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/models/product.dart @@ -1,25 +1,26 @@ class Product { + final int id; final String name; - final String image; final double price; final int stock; Product({ + required this.id, required this.name, - required this.image, required this.price, required this.stock, }); Product copyWith({ + int? id, String? name, String? image, double? price, int? stock, }) => Product( + id: id ?? this.id, name: name ?? this.name, - image: image ?? this.image, price: price ?? this.price, stock: stock ?? this.stock, ); diff --git a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/repositories/cart_repository.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/repositories/cart_repository.dart new file mode 100644 index 00000000..7016c18e --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/repositories/cart_repository.dart @@ -0,0 +1,37 @@ +import 'package:examples/examples/3_shopping_cart/data/data_source.dart'; +import 'package:examples/examples/3_shopping_cart/models/cart.dart'; +import 'package:examples/examples/3_shopping_cart/models/cart_item.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; + +abstract class ICartRepository { + Future getCart(); + Future updateCartItem(CartItem cartItem); + Future checkout(); +} + +class CartRepository implements ICartRepository { + final _uDataSource = UseDependency.create(DataSource.new); + + DataSource get _dataSource { + return _uDataSource.instance ?? (throw Exception('DataSource not found')); + } + + @override + Future getCart() async => _dataSource.fetchCart(); + + @override + Future updateCartItem(CartItem cartItem) async { + if (cartItem.quantity <= 0) { + await _dataSource.deleteCartItem(cartItem); + + return null; + } + + return await _dataSource.putCartItem(cartItem); + } + + @override + Future checkout() async { + await _dataSource.checkout(); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/repositories/product_repository.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/repositories/product_repository.dart new file mode 100644 index 00000000..9cb7831b --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/repositories/product_repository.dart @@ -0,0 +1,20 @@ +import 'package:examples/examples/3_shopping_cart/data/data_source.dart'; +import 'package:examples/examples/3_shopping_cart/models/product.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; + +abstract class IProductRepository { + Future> getAllProducts(); +} + +class ProductRepository implements IProductRepository { + final _uDataSource = UseDependency.create(DataSource.new); + + DataSource get _dataSource { + return _uDataSource.instance ?? (throw Exception('DataSource not found')); + } + + @override + Future> getAllProducts() async { + return _dataSource.fetchAllProducts(); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/shopping_cart_page.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/shopping_cart_page.dart similarity index 73% rename from packages/flutter_reactter/example/lib/examples/4_shopping_cart/shopping_cart_page.dart rename to packages/flutter_reactter/example/lib/examples/3_shopping_cart/shopping_cart_page.dart index 1d7b7786..9a419834 100644 --- a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/shopping_cart_page.dart +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/shopping_cart_page.dart @@ -1,10 +1,9 @@ +import 'package:examples/examples/3_shopping_cart/controllers/cart_controller.dart'; +import 'package:examples/examples/3_shopping_cart/controllers/products_controller.dart'; +import 'package:examples/examples/3_shopping_cart/views/products_view.dart'; import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:examples/examples/4_shopping_cart/controllers/cart_controller.dart'; -import 'package:examples/examples/4_shopping_cart/controllers/products_controller.dart'; -import 'package:examples/examples/4_shopping_cart/views/products_view.dart'; - class ShoppingCartPage extends StatelessWidget { const ShoppingCartPage({Key? key}) : super(key: key); diff --git a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/utils/format_currency.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/utils/format_currency.dart similarity index 100% rename from packages/flutter_reactter/example/lib/examples/4_shopping_cart/utils/format_currency.dart rename to packages/flutter_reactter/example/lib/examples/3_shopping_cart/utils/format_currency.dart diff --git a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/utils/lorem_gen.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/utils/lorem_gen.dart similarity index 100% rename from packages/flutter_reactter/example/lib/examples/4_shopping_cart/utils/lorem_gen.dart rename to packages/flutter_reactter/example/lib/examples/3_shopping_cart/utils/lorem_gen.dart diff --git a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/views/cart_view.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/views/cart_view.dart new file mode 100644 index 00000000..1f104b0e --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/views/cart_view.dart @@ -0,0 +1,119 @@ +import 'package:examples/examples/3_shopping_cart/controllers/cart_controller.dart'; +import 'package:examples/examples/3_shopping_cart/utils/format_currency.dart'; +import 'package:examples/examples/3_shopping_cart/widgets/cart_item_card.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; + +class CartView extends RtComponent { + const CartView({ + Key? key, + this.onCheckout, + }) : super(key: key); + + final VoidCallback? onCheckout; + + @override + get builder => CartController.new; + + @override + Widget render(BuildContext context, CartController cartController) { + return Scaffold( + appBar: AppBar( + title: const Text('Shopping Cart'), + ), + body: RtSelector( + selector: (inst, watch) => watch(inst.uCartItems).value.length, + builder: (_, __, itemCount, ____) { + if (itemCount == 0) { + return Center( + child: Text( + "Your cart is empty!", + style: Theme.of(context).textTheme.titleMedium, + ), + ); + } + + return ListView.builder( + itemCount: itemCount, + cacheExtent: 70, + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 4, + ), + itemBuilder: (context, index) { + return RtSelector( + selector: (inst, watch) { + return watch(inst.uCartItems) + .value + .elementAtOrNull(index) + ?.quantity ?? + 0; + }, + builder: (_, cartController, __, ___) { + final item = cartController.uCartItems.value[index]; + + return SizedBox( + height: 80, + child: CartItemCard( + key: ObjectKey(item), + cartItem: item, + onIncrement: cartController.incrementCartItem, + onDecrement: cartController.decrementCartItem, + onRemove: cartController.removeCartItem, + ), + ); + }, + ); + }, + ); + }, + ), + bottomNavigationBar: RtWatcher( + (context, watch) { + final total = watch(cartController.uTotal).value; + final isLoading = watch(cartController.uAsyncCart.uIsLoading).value; + + return Padding( + padding: const EdgeInsets.all(8.0), + child: ElevatedButton( + onPressed: isLoading || total == 0 + ? null + : () { + cartController.checkout(); + onCheckout?.call(); + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Builder(builder: (context) { + return Row( + children: [ + Text( + "Checkout", + style: Theme.of(context) + .textTheme + .titleLarge! + .copyWith(color: Colors.white), + ), + const Spacer(), + if (isLoading) + const SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator(), + ) + else + Text( + formatCurrency(total), + style: Theme.of(context).textTheme.titleLarge, + ), + ], + ); + }), + ), + ), + ); + }, + ), + ); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/views/products_view.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/views/products_view.dart new file mode 100644 index 00000000..fd2d200b --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/views/products_view.dart @@ -0,0 +1,83 @@ +// ignore_for_file: avoid_renaming_method_parameters + +import 'package:examples/examples/3_shopping_cart/controllers/products_controller.dart'; +import 'package:examples/examples/3_shopping_cart/views/cart_view.dart'; +import 'package:examples/examples/3_shopping_cart/widgets/cart_action.dart'; +import 'package:examples/examples/3_shopping_cart/widgets/product_card.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; + +class ProductsView extends RtComponent { + const ProductsView({Key? key}) : super(key: key); + + @override + get builder => ProductsController.new; + + @override + Widget render(BuildContext context, ProductsController productsController) { + return Scaffold( + appBar: AppBar( + title: const Text('Shopping Cart'), + actions: [ + RtConsumer( + builder: (_, __, ___) { + return CartAction( + onPressed: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => CartView( + onCheckout: productsController.uAsyncProducts.resolve, + ), + ), + ); + }, + ); + }, + ), + ], + ), + body: RtConsumer( + listenStates: (inst) => [inst.uAsyncProducts], + builder: (_, __, ___) { + return productsController.uAsyncProducts.when( + idle: (_) { + return const Center( + child: CircularProgressIndicator(), + ); + }, + loading: (_) { + return const Center( + child: CircularProgressIndicator(), + ); + }, + done: (products) { + return LayoutBuilder( + builder: (context, constraints) { + final crossAxisCount = (constraints.maxWidth / 140).floor(); + + return GridView.count( + padding: const EdgeInsets.all(8), + crossAxisCount: crossAxisCount.clamp(1, crossAxisCount), + childAspectRatio: 9 / 16, + children: [ + for (final product in products) + ProductCard( + key: ObjectKey(product), + product: product, + ), + ], + ); + }, + ); + }, + error: (error) { + return Center( + child: Text(error.toString()), + ); + }, + ) as Widget; + }, + ), + ); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/cart_action.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/cart_action.dart similarity index 86% rename from packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/cart_action.dart rename to packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/cart_action.dart index 3e5a58a7..e53210c9 100644 --- a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/cart_action.dart +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/cart_action.dart @@ -1,8 +1,7 @@ +import 'package:examples/examples/3_shopping_cart/controllers/cart_controller.dart'; import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:examples/examples/4_shopping_cart/controllers/cart_controller.dart'; - class CartAction extends StatelessWidget { const CartAction({ Key? key, @@ -28,7 +27,9 @@ class CartAction extends StatelessWidget { backgroundColor: Colors.amber, radius: 8, child: RtSelector( - selector: (inst, $) => $(inst.uCartItems).value.length, + selector: (inst, watch) { + return watch(inst.uCartItems).value.length; + }, builder: (_, cartController, count, __) { return Text( "$count", diff --git a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/cart_item_card.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/cart_item_card.dart similarity index 76% rename from packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/cart_item_card.dart rename to packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/cart_item_card.dart index d29dd957..3d5c8843 100644 --- a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/cart_item_card.dart +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/cart_item_card.dart @@ -1,23 +1,24 @@ import 'package:flutter/material.dart'; -import 'package:examples/examples/4_shopping_cart/models/cart_item.dart'; -import 'package:examples/examples/4_shopping_cart/models/product.dart'; -import 'package:examples/examples/4_shopping_cart/utils/format_currency.dart'; -import 'package:examples/examples/4_shopping_cart/widgets/custom_icon_button.dart'; -import 'package:examples/examples/4_shopping_cart/widgets/quantity.dart'; +import 'package:examples/examples/3_shopping_cart/models/cart_item.dart'; +import 'package:examples/examples/3_shopping_cart/models/product.dart'; +import 'package:examples/examples/3_shopping_cart/utils/format_currency.dart'; +import 'package:examples/examples/3_shopping_cart/widgets/custom_icon_button.dart'; +import 'package:examples/examples/3_shopping_cart/widgets/product_cover.dart'; +import 'package:examples/examples/3_shopping_cart/widgets/quantity.dart'; class CartItemCard extends StatelessWidget { final CartItem cartItem; - final void Function(Product product)? onAdd; - final void Function(Product product)? onRemove; - final void Function(Product product)? onDelete; + final void Function(CartItem cartItem)? onIncrement; + final void Function(CartItem cartItem)? onDecrement; + final void Function(CartItem cartItem)? onRemove; const CartItemCard({ Key? key, required this.cartItem, - this.onAdd, + this.onIncrement, + this.onDecrement, this.onRemove, - this.onDelete, }) : super(key: key); Product get product => cartItem.product; @@ -33,7 +34,7 @@ class CartItemCard extends StatelessWidget { children: [ AspectRatio( aspectRatio: 1, - child: Image.network(product.image), + child: ProductCover(index: product.id), ), Expanded( child: Padding( @@ -65,14 +66,16 @@ class CartItemCard extends StatelessWidget { Quantity( quantity: quantity, maxQuantity: product.stock, - onRemove: () => onRemove?.call(product), - onAdd: () => onAdd?.call(product), + onDecrement: quantity > 1 + ? () => onDecrement?.call(cartItem) + : null, + onIncrement: () => onIncrement?.call(cartItem), ), const SizedBox(width: 8), CustomIconButton( icon: Icons.delete, color: Colors.red, - onPressed: () => onDelete?.call(product), + onPressed: () => onRemove?.call(cartItem), ), ], ), diff --git a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/custom_icon_button.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/custom_icon_button.dart similarity index 100% rename from packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/custom_icon_button.dart rename to packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/custom_icon_button.dart diff --git a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/product_buttons.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/product_buttons.dart new file mode 100644 index 00000000..02d192c4 --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/product_buttons.dart @@ -0,0 +1,40 @@ +import 'package:examples/examples/3_shopping_cart/controllers/product_controller.dart'; +import 'package:examples/examples/3_shopping_cart/models/product.dart'; +import 'package:examples/examples/3_shopping_cart/widgets/custom_icon_button.dart'; +import 'package:examples/examples/3_shopping_cart/widgets/quantity.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; + +class ProductButtons extends StatelessWidget { + const ProductButtons({ + Key? key, + required this.product, + }) : super(key: key); + + final Product product; + + @override + Widget build(BuildContext context) { + return RtWatcher((context, watch) { + final productController = context.use("${product.id}"); + final quantity = watch(productController.uQuantity).value; + + if (quantity == 0) { + return CustomIconButton( + icon: Icons.add, + color: Colors.green, + onPressed: quantity < product.stock + ? () => productController.addToCart() + : null, + ); + } + + return Quantity( + quantity: quantity, + maxQuantity: product.stock, + onDecrement: () => productController.removeFromCart(), + onIncrement: () => productController.addToCart(), + ); + }); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/product_card.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/product_card.dart new file mode 100644 index 00000000..707fd45d --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/product_card.dart @@ -0,0 +1,103 @@ +import 'package:examples/examples/3_shopping_cart/controllers/product_controller.dart'; +import 'package:examples/examples/3_shopping_cart/models/product.dart'; +import 'package:examples/examples/3_shopping_cart/utils/format_currency.dart'; +import 'package:examples/examples/3_shopping_cart/widgets/product_buttons.dart'; +import 'package:examples/examples/3_shopping_cart/widgets/product_cover.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; + +class ProductCard extends StatelessWidget { + final Product product; + + const ProductCard({ + Key? key, + required this.product, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return RtProvider( + () => ProductController(product), + id: "${product.id}", + builder: (context, controller, child) { + return Card( + clipBehavior: Clip.hardEdge, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AspectRatio( + aspectRatio: 1, + child: Container( + color: Colors.black45, + child: Stack( + children: [ + SizedBox.expand( + child: ProductCover(index: product.id), + ), + Positioned( + top: 4, + right: 4, + child: ProductButtons( + product: product, + ), + ) + ], + ), + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.all(8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + formatCurrency(product.price), + style: Theme.of(context).textTheme.titleMedium, + ), + Text( + product.name, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + const Spacer(), + _buildStock(context), + ], + ), + ), + ), + ], + ), + ); + }, + ); + } + + Widget _buildStock(BuildContext context) { + if (product.stock == 0) { + return Text( + "Sold out", + style: Theme.of(context).textTheme.labelSmall, + ); + } + + return Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + 'In stock: ', + style: Theme.of(context).textTheme.labelSmall, + ), + Text( + "${product.stock}", + style: Theme.of(context).textTheme.labelSmall?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + ], + ); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/product_cover.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/product_cover.dart new file mode 100644 index 00000000..eb25b23a --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/product_cover.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; + +const kCoverColors = [...Colors.primaries, ...Colors.accents]; + +class ProductCover extends StatelessWidget { + final int index; + + const ProductCover({ + Key? key, + required this.index, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + color: kCoverColors[index % kCoverColors.length], + child: FittedBox( + child: Text( + String.fromCharCode(index + 65), + style: Theme.of(context).textTheme.titleLarge?.copyWith( + color: Colors.white, + ), + ), + ), + ); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/quantity.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/quantity.dart similarity index 71% rename from packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/quantity.dart rename to packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/quantity.dart index 890bd55c..762dd10e 100644 --- a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/quantity.dart +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/quantity.dart @@ -1,20 +1,19 @@ +import 'package:examples/examples/3_shopping_cart/widgets/custom_icon_button.dart'; import 'package:flutter/material.dart'; -import 'package:examples/examples/4_shopping_cart/widgets/custom_icon_button.dart'; - class Quantity extends StatelessWidget { const Quantity({ Key? key, required this.quantity, required this.maxQuantity, - this.onAdd, - this.onRemove, + this.onIncrement, + this.onDecrement, }) : super(key: key); final int quantity; final int maxQuantity; - final VoidCallback? onAdd; - final VoidCallback? onRemove; + final VoidCallback? onIncrement; + final VoidCallback? onDecrement; @override Widget build(BuildContext context) { @@ -27,9 +26,11 @@ class Quantity extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ CustomIconButton( - icon: quantity == 1 ? Icons.delete : Icons.remove, + icon: quantity == 1 && onDecrement != null + ? Icons.delete + : Icons.remove, color: Colors.red, - onPressed: quantity > 0 ? onRemove : null, + onPressed: quantity > 0 ? onDecrement : null, ), Padding( padding: const EdgeInsets.symmetric(horizontal: 8), @@ -44,7 +45,7 @@ class Quantity extends StatelessWidget { CustomIconButton( icon: Icons.add, color: Colors.green, - onPressed: quantity < maxQuantity ? onAdd : null, + onPressed: quantity < maxQuantity ? onIncrement : null, ), ], ), diff --git a/packages/flutter_reactter/example/lib/examples/3_tree/controllers/tree_controller.dart b/packages/flutter_reactter/example/lib/examples/3_tree/controllers/tree_controller.dart deleted file mode 100644 index dff26bf6..00000000 --- a/packages/flutter_reactter/example/lib/examples/3_tree/controllers/tree_controller.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:examples/examples/3_tree/states/tree_list.dart'; -import 'package:examples/examples/3_tree/states/tree_node.dart'; -import 'package:flutter_reactter/reactter.dart'; - -class TreeController { - final root = TreeNode(null); - final treeList = Rt.createState(() => TreeList()); - - TreeController() { - treeList.add(root); - root.bind(treeList); - } -} diff --git a/packages/flutter_reactter/example/lib/examples/3_tree/states/tree_list.dart b/packages/flutter_reactter/example/lib/examples/3_tree/states/tree_list.dart deleted file mode 100644 index 82eb37e4..00000000 --- a/packages/flutter_reactter/example/lib/examples/3_tree/states/tree_list.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'dart:collection'; - -import 'package:flutter_reactter/reactter.dart'; - -class TreeList> extends LinkedList - with RtContext, RtStateBase> { - @override - void add(E entry) => update(() => super.add(entry)); - - @override - void addFirst(E entry) => update(() => super.addFirst(entry)); - - @override - void addAll(Iterable entries) => update(() => super.addAll(entries)); - - @override - bool remove(E entry) { - final res = super.remove(entry); - if (res) notify(); - - return res; - } - - @override - void clear() => update(() => super.clear()); -} diff --git a/packages/flutter_reactter/example/lib/examples/4_github_search/controllers/github_search_controller.dart b/packages/flutter_reactter/example/lib/examples/4_github_search/controllers/github_search_controller.dart new file mode 100644 index 00000000..31114d82 --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/4_github_search/controllers/github_search_controller.dart @@ -0,0 +1,44 @@ +import 'package:flutter_reactter/flutter_reactter.dart'; + +import 'package:examples/examples/4_github_search/repositories/github_repository.dart'; + +class GithubSearchController { + String _query = ''; + String get query => _query; + + final uGithubRepository = UseDependency.create(GithubRepository.new); + + GithubRepository get githubRepository => + uGithubRepository.instance ?? + (throw Exception('GithubRepository not found')); + + late final uEntity = Rt.lazyState(() { + final getEntityMemo = Memo.inline, String>( + _getEntity, + const MemoMultiInterceptor([ + MemoSafeAsyncInterceptor(), + MemoTemporaryCacheInterceptor(Duration(seconds: 30)), + ]), + ); + + return UseAsyncState.withArg(getEntityMemo, null); + }, this); + + Future _getEntity(String query) async { + final queryPath = query.split("/"); + + if (queryPath.length > 1) { + final owner = queryPath.first; + final repo = queryPath.last; + + return await githubRepository.getRepository(owner, repo); + } + + return await githubRepository.getUser(query); + } + + void onSearch(String query) { + _query = query; + uEntity.resolve(query); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/4_github_search/controllers/search_input_controller.dart b/packages/flutter_reactter/example/lib/examples/4_github_search/controllers/search_input_controller.dart new file mode 100644 index 00000000..f0270573 --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/4_github_search/controllers/search_input_controller.dart @@ -0,0 +1,29 @@ +import 'package:flutter/widgets.dart'; + +class SearchInputController { + final void Function(String query) onSearch; + + SearchInputController({required this.onSearch}); + + final searchKey = GlobalKey>(); + final focusNode = FocusNode(); + + String? validator(String? value) { + if ((value?.isEmpty ?? true)) { + return "Can't be empty"; + } + + return null; + } + + void onFieldSubmitted(String query) { + if (!(searchKey.currentState?.validate() ?? false)) return; + + onSearch.call(query); + } + + void onButtonPressed() { + final query = searchKey.currentState?.value ?? ''; + onFieldSubmitted(query); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/4_github_search/github_search_page.dart b/packages/flutter_reactter/example/lib/examples/4_github_search/github_search_page.dart new file mode 100644 index 00000000..46802038 --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/4_github_search/github_search_page.dart @@ -0,0 +1,41 @@ +import 'package:examples/examples/4_github_search/widgets/info_card.dart'; +import 'package:flutter/material.dart' hide SearchBar; +import 'package:flutter_reactter/flutter_reactter.dart'; + +import 'package:examples/examples/4_github_search/controllers/github_search_controller.dart'; +import 'package:examples/examples/4_github_search/widgets/search_bar.dart'; + +class GithubSearchPage extends StatelessWidget { + const GithubSearchPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return RtProvider( + GithubSearchController.new, + builder: (context, githubSearchController, child) { + return Scaffold( + appBar: AppBar( + title: const Text("Github search"), + bottom: PreferredSize( + preferredSize: const Size.fromHeight(40), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: SearchBar( + onSearch: githubSearchController.onSearch, + ), + ), + ), + ), + body: const SingleChildScrollView( + padding: EdgeInsets.all(8), + child: Center( + child: FittedBox( + child: InfoCard(), + ), + ), + ), + ); + }, + ); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/5_api/models/repository.dart b/packages/flutter_reactter/example/lib/examples/4_github_search/models/repository.dart similarity index 92% rename from packages/flutter_reactter/example/lib/examples/5_api/models/repository.dart rename to packages/flutter_reactter/example/lib/examples/4_github_search/models/repository.dart index 41a3c4bf..e2273663 100644 --- a/packages/flutter_reactter/example/lib/examples/5_api/models/repository.dart +++ b/packages/flutter_reactter/example/lib/examples/4_github_search/models/repository.dart @@ -1,4 +1,4 @@ -import 'package:examples/examples/5_api/models/user.dart'; +import 'package:examples/examples/4_github_search/models/user.dart'; class Repository { final int id; diff --git a/packages/flutter_reactter/example/lib/examples/5_api/models/user.dart b/packages/flutter_reactter/example/lib/examples/4_github_search/models/user.dart similarity index 100% rename from packages/flutter_reactter/example/lib/examples/5_api/models/user.dart rename to packages/flutter_reactter/example/lib/examples/4_github_search/models/user.dart diff --git a/packages/flutter_reactter/example/lib/examples/4_github_search/providers/github_provider.dart b/packages/flutter_reactter/example/lib/examples/4_github_search/providers/github_provider.dart new file mode 100644 index 00000000..30049de6 --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/4_github_search/providers/github_provider.dart @@ -0,0 +1,39 @@ +import 'package:http/http.dart' as http; +import 'package:http/http.dart'; + +class GithubAPI { + static const String _apiBaseUrl = 'api.github.com'; + + Uri user(String username) { + return _buildUri(endpoint: '/users/$username'); + } + + Uri repository(String owner, String repo) { + return _buildUri(endpoint: '/repos/$owner/$repo'); + } + + Uri _buildUri({ + required String endpoint, + Map Function()? parametersBuilder, + }) { + return Uri( + scheme: "https", + host: _apiBaseUrl, + path: endpoint, + queryParameters: parametersBuilder?.call(), + ); + } +} + +class GithubProvider { + final GithubAPI _api = GithubAPI(); + final Client _client = http.Client(); + + Future getUser(String username) { + return _client.get(_api.user(username)); + } + + Future getRepository(String owner, String repo) { + return _client.get(_api.repository(owner, repo)); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/4_github_search/repositories/github_repository.dart b/packages/flutter_reactter/example/lib/examples/4_github_search/repositories/github_repository.dart new file mode 100644 index 00000000..2cde65cb --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/4_github_search/repositories/github_repository.dart @@ -0,0 +1,30 @@ +import 'dart:convert'; + +import 'package:examples/examples/4_github_search/providers/github_provider.dart'; +import 'package:examples/examples/4_github_search/utils/http_response.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; + +import 'package:examples/examples/4_github_search/models/repository.dart'; +import 'package:examples/examples/4_github_search/models/user.dart'; + +class GithubRepository { + final uGithubProvider = UseDependency.create(GithubProvider.new); + + GithubProvider get _githubProvider { + return uGithubProvider.instance ?? (throw Exception('ApiProvider not found')); + } + + Future getUser(String query) async { + return getResponse( + request: _githubProvider.getUser(query), + onSuccess: (response) => User.fromJson(jsonDecode(response.body)), + ); + } + + Future getRepository(String owner, String repo) async { + return getResponse( + request: _githubProvider.getRepository(owner, repo), + onSuccess: (response) => Repository.fromJson(jsonDecode(response.body)), + ); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/4_github_search/utils/http_response.dart b/packages/flutter_reactter/example/lib/examples/4_github_search/utils/http_response.dart new file mode 100644 index 00000000..4d16483d --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/4_github_search/utils/http_response.dart @@ -0,0 +1,20 @@ +import 'package:http/http.dart'; + +typedef NotFoundException = Exception; + +Future getResponse({ + required Future request, + required T Function(Response) onSuccess, +}) { + return request.then((response) { + if (response.statusCode == 200) { + return onSuccess(response); + } + + if (response.statusCode == 404) { + throw NotFoundException('Not found'); + } + + throw Exception('Failed to load data'); + }); +} diff --git a/packages/flutter_reactter/example/lib/examples/5_api/widgets/badget.dart b/packages/flutter_reactter/example/lib/examples/4_github_search/widgets/badget.dart similarity index 100% rename from packages/flutter_reactter/example/lib/examples/5_api/widgets/badget.dart rename to packages/flutter_reactter/example/lib/examples/4_github_search/widgets/badget.dart diff --git a/packages/flutter_reactter/example/lib/examples/4_github_search/widgets/info_card.dart b/packages/flutter_reactter/example/lib/examples/4_github_search/widgets/info_card.dart new file mode 100644 index 00000000..93147581 --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/4_github_search/widgets/info_card.dart @@ -0,0 +1,60 @@ +import 'package:examples/examples/4_github_search/controllers/github_search_controller.dart'; +import 'package:examples/examples/4_github_search/models/repository.dart'; +import 'package:examples/examples/4_github_search/models/user.dart'; +import 'package:examples/examples/4_github_search/utils/http_response.dart'; +import 'package:examples/examples/4_github_search/widgets/repository_info.dart'; +import 'package:examples/examples/4_github_search/widgets/user_info.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; + +class InfoCard extends StatelessWidget { + const InfoCard({super.key}); + + @override + Widget build(BuildContext context) { + return Card( + child: Container( + alignment: Alignment.center, + padding: const EdgeInsets.all(16), + width: 400, + child: RtConsumer( + listenStates: (inst) => [inst.uEntity], + builder: (_, githubSearchController, ___) { + return githubSearchController.uEntity.when( + idle: (_) { + return const Text( + 'Search a user or repository(like "flutter/flutter")', + ); + }, + loading: (_) { + return const Center( + child: CircularProgressIndicator(), + ); + }, + done: (entity) { + if (entity is User) { + return UserInfo(user: entity); + } + + if (entity is Repository) { + return RepositoryInfo(repository: entity); + } + + return const Text("Not found"); + }, + error: (error) { + if (error is NotFoundException) { + return Text( + 'Not found "${githubSearchController.query}"', + ); + } + + return Text(error.toString()); + }, + ) as Widget; + }, + ), + ), + ); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/5_api/widgets/repository_item.dart b/packages/flutter_reactter/example/lib/examples/4_github_search/widgets/repository_info.dart similarity index 94% rename from packages/flutter_reactter/example/lib/examples/5_api/widgets/repository_item.dart rename to packages/flutter_reactter/example/lib/examples/4_github_search/widgets/repository_info.dart index d83e4d73..c0777170 100644 --- a/packages/flutter_reactter/example/lib/examples/5_api/widgets/repository_item.dart +++ b/packages/flutter_reactter/example/lib/examples/4_github_search/widgets/repository_info.dart @@ -2,12 +2,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; import 'badget.dart'; -import 'package:examples/examples/5_api/models/repository.dart'; +import 'package:examples/examples/4_github_search/models/repository.dart'; -class RepositoryItem extends StatelessWidget { +class RepositoryInfo extends StatelessWidget { final Repository repository; - const RepositoryItem({Key? key, required this.repository}) : super(key: key); + const RepositoryInfo({Key? key, required this.repository}) : super(key: key); @override Widget build(BuildContext context) { diff --git a/packages/flutter_reactter/example/lib/examples/4_github_search/widgets/search_bar.dart b/packages/flutter_reactter/example/lib/examples/4_github_search/widgets/search_bar.dart new file mode 100644 index 00000000..174a5fde --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/4_github_search/widgets/search_bar.dart @@ -0,0 +1,64 @@ +import 'package:examples/examples/4_github_search/controllers/search_input_controller.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; + +class SearchBar extends StatelessWidget { + final void Function(String query) onSearch; + + const SearchBar({ + super.key, + required this.onSearch, + }); + + @override + Widget build(BuildContext context) { + return RtProvider( + () => SearchInputController(onSearch: onSearch), + builder: (context, searchInputController, child) { + return TextFormField( + key: searchInputController.searchKey, + maxLength: 150, + autofocus: true, + scrollPadding: EdgeInsets.zero, + textCapitalization: TextCapitalization.sentences, + autovalidateMode: AutovalidateMode.onUserInteraction, + validator: searchInputController.validator, + focusNode: searchInputController.focusNode, + onFieldSubmitted: searchInputController.onFieldSubmitted, + decoration: InputDecoration( + isDense: true, + filled: true, + hintText: 'Type a username or repository (like "flutter/flutter")', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(30), + borderSide: const BorderSide( + strokeAlign: BorderSide.strokeAlignOutside, + ), + ), + counter: const SizedBox(), + prefixIcon: const Icon(Icons.search), + suffixIcon: Padding( + padding: const EdgeInsets.all(4), + child: OutlinedButton( + style: OutlinedButton.styleFrom( + backgroundColor: Theme.of(context).colorScheme.primary, + foregroundColor: Theme.of(context).colorScheme.inversePrimary, + side: BorderSide( + strokeAlign: BorderSide.strokeAlignOutside, + width: 0.5, + color: Theme.of(context).colorScheme.primary, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30.0), + ), + ), + onPressed: searchInputController.onButtonPressed, + child: const Text('Search'), + ), + ), + ), + ); + }, + ); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/5_api/widgets/user_item.dart b/packages/flutter_reactter/example/lib/examples/4_github_search/widgets/user_info.dart similarity index 93% rename from packages/flutter_reactter/example/lib/examples/5_api/widgets/user_item.dart rename to packages/flutter_reactter/example/lib/examples/4_github_search/widgets/user_info.dart index 7fe28cb9..9a4e3735 100644 --- a/packages/flutter_reactter/example/lib/examples/5_api/widgets/user_item.dart +++ b/packages/flutter_reactter/example/lib/examples/4_github_search/widgets/user_info.dart @@ -2,13 +2,13 @@ import 'package:intl/intl.dart'; import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; -import 'package:examples/examples/5_api/models/user.dart'; -import 'package:examples/examples/5_api/widgets/badget.dart'; +import 'package:examples/examples/4_github_search/models/user.dart'; +import 'package:examples/examples/4_github_search/widgets/badget.dart'; -class UserItem extends StatelessWidget { +class UserInfo extends StatelessWidget { final User user; - const UserItem({Key? key, required this.user}) : super(key: key); + const UserInfo({Key? key, required this.user}) : super(key: key); @override Widget build(BuildContext context) { diff --git a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/controllers/cart_controller.dart b/packages/flutter_reactter/example/lib/examples/4_shopping_cart/controllers/cart_controller.dart deleted file mode 100644 index 6553d9c7..00000000 --- a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/controllers/cart_controller.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:examples/examples/4_shopping_cart/models/product.dart'; -import 'package:flutter_reactter/flutter_reactter.dart'; - -import 'package:examples/examples/4_shopping_cart/models/cart.dart'; -import 'package:examples/examples/4_shopping_cart/models/cart_item.dart'; -import 'package:examples/examples/4_shopping_cart/repositories/store_repository.dart'; - -class CartController { - final uStoreRepository = UseDependency.create(StoreRepository.new); - final uCartItems = UseState>([]); - final uCartItemsCount = UseState(0); - final uTotal = UseState(0.0); - - int getProductQuantity(Product product) { - return uCartItems.value - .firstWhere( - (item) => item.product == product, - orElse: () => CartItem(product, 0), - ) - .quantity; - } - - void addProduct(Product product) { - final cart = uStoreRepository.instance?.addProductToCart(product); - if (cart != null) _setState(cart); - } - - void removeProduct(Product product) { - final cart = uStoreRepository.instance?.removeProductFromCart(product); - if (cart != null) _setState(cart); - } - - void deleteProduct(Product product) { - final cart = uStoreRepository.instance?.deleteProductFromCart(product); - if (cart != null) _setState(cart); - } - - void checkout() { - uStoreRepository.instance?.checkout(uCartItems.value); - _setState(Cart()); - } - - void _setState(Cart cart) { - uCartItems.value = cart.items; - uCartItemsCount.value = cart.itemsCount; - uTotal.value = cart.total; - } -} diff --git a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/controllers/products_controller.dart b/packages/flutter_reactter/example/lib/examples/4_shopping_cart/controllers/products_controller.dart deleted file mode 100644 index 5be53310..00000000 --- a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/controllers/products_controller.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:flutter_reactter/flutter_reactter.dart'; - -import 'package:examples/examples/4_shopping_cart/repositories/store_repository.dart'; - -class ProductsController { - final uStoreRepository = UseDependency.create(StoreRepository.new); - late final uProducts = Rt.lazyState( - () => UseState(uStoreRepository.instance?.getProducts() ?? []), - this, - ); -} diff --git a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/models/cart.dart b/packages/flutter_reactter/example/lib/examples/4_shopping_cart/models/cart.dart deleted file mode 100644 index e848c5f0..00000000 --- a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/models/cart.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:examples/examples/4_shopping_cart/models/cart_item.dart'; - -class Cart { - final List items; - final int itemsCount; - final double total; - - Cart({ - this.items = const [], - this.itemsCount = 0, - this.total = 0.0, - }); -} diff --git a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/repositories/store_repository.dart b/packages/flutter_reactter/example/lib/examples/4_shopping_cart/repositories/store_repository.dart deleted file mode 100644 index 2dcd3c35..00000000 --- a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/repositories/store_repository.dart +++ /dev/null @@ -1,105 +0,0 @@ -import 'dart:math'; - -import 'package:examples/examples/4_shopping_cart/models/cart.dart'; -import 'package:examples/examples/4_shopping_cart/models/cart_item.dart'; -import 'package:flutter/material.dart'; - -import 'package:examples/examples/4_shopping_cart/models/product.dart'; -import 'package:examples/examples/4_shopping_cart/utils/lorem_gen.dart'; - -class StoreRepository { - static final _random = Random(); - final _products = _generateProducts(); - final _cartItems = {}; - int _cartItemsCount = 0; - double _total = 0.0; - - List getProducts() => _products; - - Cart addProductToCart(Product product, [int quantity = 1]) { - final item = _cartItems[product] ?? CartItem(product, 0); - - _cartItems[product] = item.copyWith(quantity: item.quantity + quantity); - _cartItemsCount += quantity; - _total += product.price * quantity; - - return Cart( - items: _cartItems.values.toList(), - itemsCount: _cartItemsCount, - total: _total, - ); - } - - Cart removeProductFromCart(Product product, [int quantity = 1]) { - final item = _cartItems[product] ?? CartItem(product, 0); - - if (item.quantity <= quantity) { - _cartItems.remove(product); - } else { - _cartItems[product] = item.copyWith(quantity: item.quantity - quantity); - } - - _cartItemsCount -= quantity; - _total -= product.price * quantity; - - return Cart( - items: _cartItems.values.toList(), - itemsCount: _cartItemsCount, - total: _total, - ); - } - - Cart deleteProductFromCart(Product product) { - final item = _cartItems[product] ?? CartItem(product, 0); - - _cartItems.remove(product); - _cartItemsCount -= item.quantity; - _total -= item.product.price * item.quantity; - - return Cart( - items: _cartItems.values.toList(), - itemsCount: _cartItemsCount, - total: _total, - ); - } - - void checkout(List items) { - final productsChanged = _products.map((product) { - final item = items.firstWhere( - (element) => element.product == product, - orElse: () => CartItem(product, 0), - ); - final quantity = item.quantity; - - return product.copyWith(stock: product.stock - quantity); - }).toList(); - - _products.clear(); - _products.addAll(productsChanged); - _cartItems.clear(); - _cartItemsCount = 0; - _total = 0.0; - } - - static List _generateProducts() { - final colors = [...Colors.primaries, ...Colors.accents]; - - return List.generate( - 26, - (index) { - final letter = String.fromCharCode(index + 65); - final iColor = _random.nextInt(colors.length); - final color = colors[iColor][100]!.value.toRadixString(16).substring(2); - final color2 = - colors[iColor][700]!.value.toRadixString(16).substring(2); - - return Product( - name: generateLoremText(_random.nextInt(8) + 3), - image: 'https://placehold.co/200/$color/$color2/png?text=$letter', - price: (_random.nextInt(3000) + 100) + _random.nextDouble(), - stock: _random.nextInt(20), - ); - }, - ); - } -} diff --git a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/views/cart_view.dart b/packages/flutter_reactter/example/lib/examples/4_shopping_cart/views/cart_view.dart deleted file mode 100644 index 7b2ad7b8..00000000 --- a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/views/cart_view.dart +++ /dev/null @@ -1,97 +0,0 @@ -// ignore_for_file: avoid_renaming_method_parameters - -import 'package:flutter/material.dart'; -import 'package:flutter_reactter/flutter_reactter.dart'; - -import 'package:examples/examples/4_shopping_cart/widgets/cart_bottom.dart'; -import 'package:examples/examples/4_shopping_cart/widgets/cart_item_card.dart'; -import 'package:examples/examples/4_shopping_cart/controllers/cart_controller.dart'; - -class CartView extends RtComponent { - const CartView({ - Key? key, - this.onCheckout, - }) : super(key: key); - - final VoidCallback? onCheckout; - - @override - get builder => CartController.new; - - @override - Widget render(BuildContext context, CartController cartController) { - return Scaffold( - appBar: AppBar( - title: const Text('Shopping Cart'), - ), - body: RtSelector( - selector: (inst, $) => $(inst.uCartItems).value.length, - builder: (_, __, itemCount, ____) { - if (itemCount == 0) { - return Center( - child: Text( - "Your cart is empty!", - style: Theme.of(context).textTheme.titleMedium, - ), - ); - } - - return ListView.builder( - itemCount: itemCount, - cacheExtent: 70, - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 4, - ), - itemBuilder: (context, index) { - return RtSelector( - selector: (inst, $) { - try { - return $(inst.uCartItems).value[index].quantity; - } catch (e) { - return 0; - } - }, - builder: (_, cartController, __, ___) { - final item = cartController.uCartItems.value[index]; - final product = item.product; - - return SizedBox( - height: 80, - child: CartItemCard( - key: ObjectKey(product), - cartItem: item, - onAdd: cartController.addProduct, - onRemove: cartController.removeProduct, - onDelete: cartController.deleteProduct, - ), - ); - }, - ); - }, - ); - }, - ), - bottomNavigationBar: RtConsumer( - listenStates: (inst) => [inst.uCartItems], - builder: (_, cartController, __) { - final items = cartController.uCartItems.value; - final itemsCount = cartController.uCartItemsCount.value; - final total = cartController.uTotal.value; - - return CartBottom( - productsCount: items.length, - itemsCount: itemsCount, - total: total, - onCheckout: items.isNotEmpty - ? () { - cartController.checkout(); - onCheckout?.call(); - } - : null, - ); - }, - ), - ); - } -} diff --git a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/views/products_view.dart b/packages/flutter_reactter/example/lib/examples/4_shopping_cart/views/products_view.dart deleted file mode 100644 index 9875252d..00000000 --- a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/views/products_view.dart +++ /dev/null @@ -1,67 +0,0 @@ -// ignore_for_file: avoid_renaming_method_parameters - -import 'package:flutter/material.dart'; -import 'package:flutter_reactter/flutter_reactter.dart'; - -import 'package:examples/examples/4_shopping_cart/controllers/products_controller.dart'; -import 'package:examples/examples/4_shopping_cart/views/cart_view.dart'; -import 'package:examples/examples/4_shopping_cart/widgets/cart_action.dart'; -import 'package:examples/examples/4_shopping_cart/widgets/product_card.dart'; - -class ProductsView extends RtComponent { - const ProductsView({Key? key}) : super(key: key); - - @override - get builder => ProductsController.new; - - @override - Widget render(BuildContext context, ProductsController productsController) { - return Scaffold( - appBar: AppBar( - title: const Text('Shopping Cart'), - actions: [ - RtConsumer( - builder: (_, __, ___) { - return CartAction( - onPressed: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => CartView( - onCheckout: productsController.uProducts.refresh, - ), - ), - ); - }, - ); - }, - ), - ], - ), - body: LayoutBuilder( - builder: (context, constraints) { - final crossAxisCount = (constraints.maxWidth / 140).floor(); - - return RtConsumer( - listenStates: (inst) => [inst.uProducts], - builder: (_, __, ___) { - final products = productsController.uProducts.value; - - return GridView.count( - padding: const EdgeInsets.all(8), - crossAxisCount: crossAxisCount.clamp(1, crossAxisCount), - childAspectRatio: 9 / 16, - children: [ - for (final product in products) - ProductCard( - key: ObjectKey(product), - product: product, - ) - ], - ); - }, - ); - }, - ), - ); - } -} diff --git a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/cart_bottom.dart b/packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/cart_bottom.dart deleted file mode 100644 index f971b158..00000000 --- a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/cart_bottom.dart +++ /dev/null @@ -1,88 +0,0 @@ -// ignore_for_file: avoid_renaming_method_parameters - -import 'package:flutter/material.dart'; - -import 'package:examples/examples/4_shopping_cart/utils/format_currency.dart'; - -class CartBottom extends StatelessWidget { - final int productsCount; - final int itemsCount; - final double total; - final void Function()? onCheckout; - - const CartBottom({ - Key? key, - this.productsCount = 0, - this.itemsCount = 0, - this.total = 0, - required this.onCheckout, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return BottomSheet( - onClosing: () {}, - elevation: 16, - enableDrag: false, - builder: (context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.all(8), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Expanded( - child: Wrap( - children: [ - Text( - "$itemsCount " - "${itemsCount == 1 ? 'item' : 'items'} ", - style: Theme.of(context).textTheme.titleSmall, - ), - Text( - "of $productsCount " - "${productsCount == 1 ? 'product' : 'products'}", - style: Theme.of(context).textTheme.titleSmall, - ), - ], - ), - ), - Row( - children: [ - Text( - "Total: ", - style: Theme.of(context).textTheme.titleMedium, - ), - Text( - formatCurrency(total), - style: Theme.of(context).textTheme.titleLarge, - ), - ], - ), - ], - ), - ), - SizedBox( - width: double.infinity, - child: ElevatedButton( - onPressed: onCheckout, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 16), - child: Text( - "Checkout", - style: Theme.of(context) - .textTheme - .titleLarge! - .copyWith(color: Colors.white), - ), - ), - ), - ), - ], - ); - }, - ); - } -} diff --git a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/product_buttons.dart b/packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/product_buttons.dart deleted file mode 100644 index 767ee9ab..00000000 --- a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/product_buttons.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_reactter/flutter_reactter.dart'; - -import 'package:examples/examples/4_shopping_cart/controllers/cart_controller.dart'; -import 'package:examples/examples/4_shopping_cart/models/product.dart'; -import 'package:examples/examples/4_shopping_cart/widgets/custom_icon_button.dart'; -import 'package:examples/examples/4_shopping_cart/widgets/quantity.dart'; - -class ProductButtons extends StatelessWidget { - const ProductButtons({ - Key? key, - required this.product, - }) : super(key: key); - - final Product product; - - @override - Widget build(BuildContext context) { - final cartController = context.use(); - final quantity = context.select( - (inst, $) => inst.getProductQuantity(product), - ); - - if (quantity == 0) { - return CustomIconButton( - icon: Icons.add, - color: Colors.green, - onPressed: quantity < product.stock - ? () => cartController.addProduct(product) - : null, - ); - } - - return Quantity( - quantity: quantity, - maxQuantity: product.stock, - onRemove: () => cartController.removeProduct(product), - onAdd: () => cartController.addProduct(product), - ); - } -} diff --git a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/product_card.dart b/packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/product_card.dart deleted file mode 100644 index 1d1e09d0..00000000 --- a/packages/flutter_reactter/example/lib/examples/4_shopping_cart/widgets/product_card.dart +++ /dev/null @@ -1,98 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:examples/examples/4_shopping_cart/models/product.dart'; -import 'package:examples/examples/4_shopping_cart/utils/format_currency.dart'; -import 'package:examples/examples/4_shopping_cart/widgets/product_buttons.dart'; - -class ProductCard extends StatelessWidget { - final Product product; - - const ProductCard({ - Key? key, - required this.product, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return Card( - clipBehavior: Clip.hardEdge, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - AspectRatio( - aspectRatio: 1, - child: Container( - color: Colors.black45, - child: Stack( - children: [ - SizedBox.expand( - child: Image.network( - product.image, - fit: BoxFit.contain, - ), - ), - Positioned( - top: 4, - right: 4, - child: ProductButtons( - product: product, - ), - ) - ], - ), - ), - ), - Expanded( - child: Padding( - padding: const EdgeInsets.all(8), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - formatCurrency(product.price), - style: Theme.of(context).textTheme.titleMedium, - ), - Text( - product.name, - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - const Spacer(), - _buildStock(context), - ], - ), - ), - ), - ], - ), - ); - } - - Widget _buildStock(BuildContext context) { - if (product.stock == 0) { - return Text( - "Sold out", - style: Theme.of(context).textTheme.labelSmall, - ); - } - - return Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - 'In stock: ', - style: Theme.of(context).textTheme.labelSmall, - ), - Text( - "${product.stock}", - style: Theme.of(context).textTheme.labelSmall?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - ], - ); - } -} diff --git a/packages/flutter_reactter/example/lib/examples/5_api/api_page.dart b/packages/flutter_reactter/example/lib/examples/5_api/api_page.dart deleted file mode 100644 index 3dcb1746..00000000 --- a/packages/flutter_reactter/example/lib/examples/5_api/api_page.dart +++ /dev/null @@ -1,88 +0,0 @@ -import 'package:flutter/material.dart' hide SearchBar; -import 'package:flutter_reactter/flutter_reactter.dart'; - -import 'package:examples/examples/5_api/controllers/api_controller.dart'; -import 'package:examples/examples/5_api/models/repository.dart'; -import 'package:examples/examples/5_api/models/user.dart'; -import 'package:examples/examples/5_api/services/api_service.dart'; -import 'package:examples/examples/5_api/widgets/repository_item.dart'; -import 'package:examples/examples/5_api/widgets/search_bar.dart'; -import 'package:examples/examples/5_api/widgets/user_item.dart'; - -class ApiPage extends StatelessWidget { - const ApiPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return RtProvider( - ApiController.new, - builder: (context, apiController, child) { - return Scaffold( - appBar: AppBar( - title: const Text("Github search"), - bottom: PreferredSize( - preferredSize: const Size.fromHeight(56), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8) - .copyWith(bottom: 2), - child: SearchBar( - onSearch: apiController.search, - ), - ), - ), - ), - body: SingleChildScrollView( - padding: const EdgeInsets.all(8), - child: Center( - child: RtConsumer( - listenStates: (inst) => [inst.uEntity], - builder: (_, __, ___) { - return FittedBox( - child: SizedBox( - width: 400, - child: Card( - margin: EdgeInsets.zero, - child: Container( - alignment: Alignment.center, - padding: const EdgeInsets.all(16), - child: apiController.uEntity.when( - standby: (_) => const Text( - 'Search a user or repository(like "flutter/flutter")', - ), - loading: (_) => const Center( - child: CircularProgressIndicator(), - ), - done: (entity) { - if (entity is User) { - return UserItem(user: entity); - } - - if (entity is Repository) { - return RepositoryItem(repository: entity); - } - - return const Text("Not found"); - }, - error: (error) { - if (error is NotFoundException) { - return Text( - 'Not found "${apiController.query}"', - ); - } - - return Text(error.toString()); - }, - ), - ), - ), - ), - ); - }, - ), - ), - ), - ); - }, - ); - } -} diff --git a/packages/flutter_reactter/example/lib/examples/5_api/controllers/api_controller.dart b/packages/flutter_reactter/example/lib/examples/5_api/controllers/api_controller.dart deleted file mode 100644 index 0c1929ec..00000000 --- a/packages/flutter_reactter/example/lib/examples/5_api/controllers/api_controller.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter_reactter/flutter_reactter.dart'; - -import 'package:examples/examples/5_api/services/api_service.dart'; - -class ApiController { - final apiService = ApiService(); - - String _query = ''; - String get query => _query; - - late final uEntity = Rt.lazyState( - () => UseAsyncState.withArg( - Memo.inline, String>( - getEntity, - const MemoMultiInterceptor([ - MemoSafeAsyncInterceptor(), - MemoTemporaryCacheInterceptor(Duration(seconds: 30)), - ]), - ), - null, - ), - this, - ); - - Future getEntity(String query) async { - final queryPath = query.split("/"); - - if (queryPath.length > 1) { - return await apiService.getRepository(queryPath[0], queryPath[1]); - } - - return await apiService.getUser(query); - } - - void search(String query) { - _query = query; - uEntity.resolve(query); - } -} diff --git a/packages/flutter_reactter/example/lib/examples/5_api/services/api_service.dart b/packages/flutter_reactter/example/lib/examples/5_api/services/api_service.dart deleted file mode 100644 index f65c9ee2..00000000 --- a/packages/flutter_reactter/example/lib/examples/5_api/services/api_service.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'dart:convert'; - -import 'package:http/http.dart' as http; - -import 'package:examples/examples/5_api/models/repository.dart'; -import 'package:examples/examples/5_api/models/user.dart'; - -typedef NotFoundException = Exception; - -class ApiService { - Future getUser(String query) async { - final response = - await http.get(Uri.parse('https://api.github.com/users/$query')); - - if (response.statusCode == 200) { - return User.fromJson(jsonDecode(response.body)); - } - - if (response.statusCode == 404) { - throw NotFoundException('User not found'); - } - - throw Exception('Failed to load user'); - } - - Future getRepository(String owner, String repo) async { - final response = - await http.get(Uri.parse('https://api.github.com/repos/$owner/$repo')); - - if (response.statusCode == 200) { - return Repository.fromJson(jsonDecode(response.body)); - } - - if (response.statusCode == 404) { - throw NotFoundException('Repository not found'); - } - - throw Exception('Failed to load repository'); - } -} diff --git a/packages/flutter_reactter/example/lib/examples/5_api/widgets/search_bar.dart b/packages/flutter_reactter/example/lib/examples/5_api/widgets/search_bar.dart deleted file mode 100644 index a5f0d310..00000000 --- a/packages/flutter_reactter/example/lib/examples/5_api/widgets/search_bar.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'package:flutter/material.dart'; - -class SearchBar extends StatelessWidget { - const SearchBar({ - super.key, - this.onSearch, - }); - - final void Function(String query)? onSearch; - - @override - Widget build(BuildContext context) { - final searchKey = GlobalKey>(); - final focusNode = FocusNode(); - - void search() { - final isValid = searchKey.currentState?.validate() ?? false; - - if (!isValid) return; - - final query = searchKey.currentState?.value ?? ''; - - onSearch?.call(query); - } - - String? validator(String? value) { - if (value == null || value.isEmpty) { - return "Can't be empty"; - } - - return null; - } - - return TextFormField( - key: searchKey, - maxLength: 150, - autofocus: true, - scrollPadding: EdgeInsets.zero, - textCapitalization: TextCapitalization.sentences, - autovalidateMode: AutovalidateMode.onUserInteraction, - validator: validator, - focusNode: focusNode, - onFieldSubmitted: (_) { - focusNode.requestFocus(); - search(); - }, - decoration: InputDecoration( - isDense: true, - filled: true, - hintText: 'Type a username or repository (like "flutter/flutter")', - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(30), - borderSide: const BorderSide( - strokeAlign: BorderSide.strokeAlignOutside, - ), - ), - counter: const SizedBox(), - prefixIcon: const Icon(Icons.search), - suffixIcon: Padding( - padding: const EdgeInsets.symmetric( - vertical: 2, - horizontal: 4, - ).copyWith(left: 0), - child: OutlinedButton( - style: OutlinedButton.styleFrom( - backgroundColor: Theme.of(context).colorScheme.primary, - foregroundColor: Theme.of(context).colorScheme.inversePrimary, - side: BorderSide( - strokeAlign: BorderSide.strokeAlignOutside, - width: 0.5, - color: Theme.of(context).colorScheme.primary, - ), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30.0), - ), - ), - child: const Text('Search'), - onPressed: () => search(), - ), - ), - ), - ); - } -} diff --git a/packages/flutter_reactter/example/lib/examples/6_todo/actions/add_todo_action.dart b/packages/flutter_reactter/example/lib/examples/5_todo/actions/add_todo_action.dart similarity index 76% rename from packages/flutter_reactter/example/lib/examples/6_todo/actions/add_todo_action.dart rename to packages/flutter_reactter/example/lib/examples/5_todo/actions/add_todo_action.dart index 38c35179..14fb5369 100644 --- a/packages/flutter_reactter/example/lib/examples/6_todo/actions/add_todo_action.dart +++ b/packages/flutter_reactter/example/lib/examples/5_todo/actions/add_todo_action.dart @@ -1,7 +1,7 @@ import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:examples/examples/6_todo/models/todo.dart'; -import 'package:examples/examples/6_todo/stores/todo_store.dart'; +import 'package:examples/examples/5_todo/models/todo.dart'; +import 'package:examples/examples/5_todo/stores/todo_store.dart'; class AddTodoAction extends RtActionCallable { final Todo todo; diff --git a/packages/flutter_reactter/example/lib/examples/6_todo/actions/clear_completed_action.dart b/packages/flutter_reactter/example/lib/examples/5_todo/actions/clear_completed_action.dart similarity index 87% rename from packages/flutter_reactter/example/lib/examples/6_todo/actions/clear_completed_action.dart rename to packages/flutter_reactter/example/lib/examples/5_todo/actions/clear_completed_action.dart index ead48afa..1e3341e4 100644 --- a/packages/flutter_reactter/example/lib/examples/6_todo/actions/clear_completed_action.dart +++ b/packages/flutter_reactter/example/lib/examples/5_todo/actions/clear_completed_action.dart @@ -1,6 +1,6 @@ import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:examples/examples/6_todo/stores/todo_store.dart'; +import 'package:examples/examples/5_todo/stores/todo_store.dart'; class ClearCompletedAction extends RtActionCallable { const ClearCompletedAction() diff --git a/packages/flutter_reactter/example/lib/examples/6_todo/actions/filter_action.dart b/packages/flutter_reactter/example/lib/examples/5_todo/actions/filter_action.dart similarity index 86% rename from packages/flutter_reactter/example/lib/examples/6_todo/actions/filter_action.dart rename to packages/flutter_reactter/example/lib/examples/5_todo/actions/filter_action.dart index 0bd7f987..ef8f7ff4 100644 --- a/packages/flutter_reactter/example/lib/examples/6_todo/actions/filter_action.dart +++ b/packages/flutter_reactter/example/lib/examples/5_todo/actions/filter_action.dart @@ -1,6 +1,6 @@ import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:examples/examples/6_todo/stores/todo_store.dart'; +import 'package:examples/examples/5_todo/stores/todo_store.dart'; class FilterAction extends RtActionCallable { final TodoListType todoListType; diff --git a/packages/flutter_reactter/example/lib/examples/6_todo/actions/remove_todo_action.dart b/packages/flutter_reactter/example/lib/examples/5_todo/actions/remove_todo_action.dart similarity index 81% rename from packages/flutter_reactter/example/lib/examples/6_todo/actions/remove_todo_action.dart rename to packages/flutter_reactter/example/lib/examples/5_todo/actions/remove_todo_action.dart index 56c12682..1cb0a940 100644 --- a/packages/flutter_reactter/example/lib/examples/6_todo/actions/remove_todo_action.dart +++ b/packages/flutter_reactter/example/lib/examples/5_todo/actions/remove_todo_action.dart @@ -1,7 +1,7 @@ import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:examples/examples/6_todo/models/todo.dart'; -import 'package:examples/examples/6_todo/stores/todo_store.dart'; +import 'package:examples/examples/5_todo/models/todo.dart'; +import 'package:examples/examples/5_todo/stores/todo_store.dart'; class RemoveTodoAction extends RtActionCallable { final Todo todo; diff --git a/packages/flutter_reactter/example/lib/examples/6_todo/actions/toggle_todo_action.dart b/packages/flutter_reactter/example/lib/examples/5_todo/actions/toggle_todo_action.dart similarity index 83% rename from packages/flutter_reactter/example/lib/examples/6_todo/actions/toggle_todo_action.dart rename to packages/flutter_reactter/example/lib/examples/5_todo/actions/toggle_todo_action.dart index 993dd24b..42f691d5 100644 --- a/packages/flutter_reactter/example/lib/examples/6_todo/actions/toggle_todo_action.dart +++ b/packages/flutter_reactter/example/lib/examples/5_todo/actions/toggle_todo_action.dart @@ -1,7 +1,7 @@ import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:examples/examples/6_todo/models/todo.dart'; -import 'package:examples/examples/6_todo/stores/todo_store.dart'; +import 'package:examples/examples/5_todo/models/todo.dart'; +import 'package:examples/examples/5_todo/stores/todo_store.dart'; class ToggleTodoAction extends RtActionCallable { final Todo todo; diff --git a/packages/flutter_reactter/example/lib/examples/6_todo/controllers/todo_controller.dart b/packages/flutter_reactter/example/lib/examples/5_todo/controllers/todo_controller.dart similarity index 65% rename from packages/flutter_reactter/example/lib/examples/6_todo/controllers/todo_controller.dart rename to packages/flutter_reactter/example/lib/examples/5_todo/controllers/todo_controller.dart index 555f3d02..e7591eb7 100644 --- a/packages/flutter_reactter/example/lib/examples/6_todo/controllers/todo_controller.dart +++ b/packages/flutter_reactter/example/lib/examples/5_todo/controllers/todo_controller.dart @@ -1,12 +1,12 @@ import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:examples/examples/6_todo/actions/add_todo_action.dart'; -import 'package:examples/examples/6_todo/actions/clear_completed_action.dart'; -import 'package:examples/examples/6_todo/actions/filter_action.dart'; -import 'package:examples/examples/6_todo/actions/remove_todo_action.dart'; -import 'package:examples/examples/6_todo/actions/toggle_todo_action.dart'; -import 'package:examples/examples/6_todo/models/todo.dart'; -import 'package:examples/examples/6_todo/stores/todo_store.dart'; +import 'package:examples/examples/5_todo/actions/add_todo_action.dart'; +import 'package:examples/examples/5_todo/actions/clear_completed_action.dart'; +import 'package:examples/examples/5_todo/actions/filter_action.dart'; +import 'package:examples/examples/5_todo/actions/remove_todo_action.dart'; +import 'package:examples/examples/5_todo/actions/toggle_todo_action.dart'; +import 'package:examples/examples/5_todo/models/todo.dart'; +import 'package:examples/examples/5_todo/stores/todo_store.dart'; TodoStore _reducer(TodoStore state, RtAction action) { return action is RtActionCallable ? action(state) : UnimplementedError(); @@ -25,11 +25,12 @@ class TodoController { List getTodosBy(TodoListType todoListType) { if (todoListType == TodoListType.all) return uReduce.value.todoList; - final isDone = todoListType == TodoListType.done; + final isShowTodoDone = todoListType == TodoListType.done; - return uReduce.value.todoList - .where((todo) => todo.isDone == isDone) - .toList(); + return [ + for (final todo in uReduce.value.todoList) + if (todo.isDone == isShowTodoDone) todo + ]; } void filterBy(TodoListType? todoListType) { diff --git a/packages/flutter_reactter/example/lib/examples/6_todo/models/todo.dart b/packages/flutter_reactter/example/lib/examples/5_todo/models/todo.dart similarity index 100% rename from packages/flutter_reactter/example/lib/examples/6_todo/models/todo.dart rename to packages/flutter_reactter/example/lib/examples/5_todo/models/todo.dart diff --git a/packages/flutter_reactter/example/lib/examples/6_todo/stores/todo_store.dart b/packages/flutter_reactter/example/lib/examples/5_todo/stores/todo_store.dart similarity index 92% rename from packages/flutter_reactter/example/lib/examples/6_todo/stores/todo_store.dart rename to packages/flutter_reactter/example/lib/examples/5_todo/stores/todo_store.dart index 033e4bb2..8cb13845 100644 --- a/packages/flutter_reactter/example/lib/examples/6_todo/stores/todo_store.dart +++ b/packages/flutter_reactter/example/lib/examples/5_todo/stores/todo_store.dart @@ -1,4 +1,4 @@ -import 'package:examples/examples/6_todo/models/todo.dart'; +import 'package:examples/examples/5_todo/models/todo.dart'; enum TodoListType { all, done, todo } diff --git a/packages/flutter_reactter/example/lib/examples/6_todo/todo_page.dart b/packages/flutter_reactter/example/lib/examples/5_todo/todo_page.dart similarity index 80% rename from packages/flutter_reactter/example/lib/examples/6_todo/todo_page.dart rename to packages/flutter_reactter/example/lib/examples/5_todo/todo_page.dart index f9f869ab..75d874b3 100644 --- a/packages/flutter_reactter/example/lib/examples/6_todo/todo_page.dart +++ b/packages/flutter_reactter/example/lib/examples/5_todo/todo_page.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:examples/examples/6_todo/controllers/todo_controller.dart'; -import 'package:examples/examples/6_todo/widgets/input_bar.dart'; -import 'package:examples/examples/6_todo/widgets/todo_filter.dart'; -import 'package:examples/examples/6_todo/widgets/todo_list.dart'; +import 'package:examples/examples/5_todo/controllers/todo_controller.dart'; +import 'package:examples/examples/5_todo/widgets/input_bar.dart'; +import 'package:examples/examples/5_todo/widgets/todo_filter.dart'; +import 'package:examples/examples/5_todo/widgets/todo_list.dart'; class TodoPage extends StatelessWidget { const TodoPage({Key? key}) : super(key: key); @@ -30,6 +30,7 @@ class TodoPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ InputBar(onAdd: todoController.addTodo), + const SizedBox(height: 4), const TodoFilter(), ], ), diff --git a/packages/flutter_reactter/example/lib/examples/6_todo/widgets/input_bar.dart b/packages/flutter_reactter/example/lib/examples/5_todo/widgets/input_bar.dart similarity index 94% rename from packages/flutter_reactter/example/lib/examples/6_todo/widgets/input_bar.dart rename to packages/flutter_reactter/example/lib/examples/5_todo/widgets/input_bar.dart index 04d07a00..2f968d49 100644 --- a/packages/flutter_reactter/example/lib/examples/6_todo/widgets/input_bar.dart +++ b/packages/flutter_reactter/example/lib/examples/5_todo/widgets/input_bar.dart @@ -60,10 +60,7 @@ class InputBar extends StatelessWidget { ), counter: const SizedBox(), suffixIcon: Padding( - padding: const EdgeInsets.symmetric( - vertical: 2, - horizontal: 4, - ).copyWith(left: 0), + padding: const EdgeInsets.all(4), child: OutlinedButton( style: OutlinedButton.styleFrom( tapTargetSize: MaterialTapTargetSize.shrinkWrap, diff --git a/packages/flutter_reactter/example/lib/examples/6_todo/widgets/radio_with_label.dart b/packages/flutter_reactter/example/lib/examples/5_todo/widgets/radio_with_label.dart similarity index 100% rename from packages/flutter_reactter/example/lib/examples/6_todo/widgets/radio_with_label.dart rename to packages/flutter_reactter/example/lib/examples/5_todo/widgets/radio_with_label.dart diff --git a/packages/flutter_reactter/example/lib/examples/5_todo/widgets/todo_filter.dart b/packages/flutter_reactter/example/lib/examples/5_todo/widgets/todo_filter.dart new file mode 100644 index 00000000..3ed56ad9 --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/5_todo/widgets/todo_filter.dart @@ -0,0 +1,94 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; + +import 'package:examples/examples/5_todo/controllers/todo_controller.dart'; +import 'package:examples/examples/5_todo/stores/todo_store.dart'; +import 'package:examples/examples/5_todo/widgets/radio_with_label.dart'; + +class TodoFilter extends StatelessWidget { + const TodoFilter({super.key}); + + @override + Widget build(BuildContext context) { + return Wrap( + alignment: WrapAlignment.end, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + FittedBox( + child: SizedBox( + height: 30, + child: RtSelector( + selector: (inst, watch) => watch(inst.uReduce).value.filteredBy, + builder: (_, __, filteredBy, ___) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Text('Filter by:'), + RtSelector( + selector: (inst, watch) { + return watch(inst.uReduce).value.todoList.length; + }, + builder: (_, todoController, allCount, __) { + return RadioWithLabel( + label: 'All($allCount)', + value: TodoListType.all, + groupValue: filteredBy, + onChanged: todoController.filterBy, + ); + }, + ), + RtSelector( + selector: (inst, watch) { + final uReduce = watch(inst.uReduce).value; + final allCount = uReduce.todoList.length; + final doneCount = uReduce.doneCount; + + return allCount - doneCount; + }, + builder: (_, todoController, activeCount, __) { + return RadioWithLabel( + label: 'Active($activeCount)', + value: TodoListType.todo, + groupValue: filteredBy, + onChanged: todoController.filterBy, + ); + }, + ), + RtSelector( + selector: (inst, watch) { + return watch(inst.uReduce).value.doneCount; + }, + builder: (_, todoController, doneCount, __) { + return RadioWithLabel( + label: 'Completed($doneCount)', + value: TodoListType.done, + groupValue: filteredBy, + onChanged: todoController.filterBy, + ); + }, + ), + ], + ); + }, + ), + ), + ), + const SizedBox(width: 8), + RtSelector( + selector: (inst, watch) { + return watch(inst.uReduce).value.doneCount > 0; + }, + builder: (_, todoController, hasCount, __) { + return OutlinedButton( + style: OutlinedButton.styleFrom( + foregroundColor: Colors.red, + ), + onPressed: hasCount ? todoController.clearCompleted : null, + child: const Text('Clear completed'), + ); + }, + ), + ], + ); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/6_todo/widgets/todo_item.dart b/packages/flutter_reactter/example/lib/examples/5_todo/widgets/todo_item.dart similarity index 94% rename from packages/flutter_reactter/example/lib/examples/6_todo/widgets/todo_item.dart rename to packages/flutter_reactter/example/lib/examples/5_todo/widgets/todo_item.dart index 7e002684..4a111802 100644 --- a/packages/flutter_reactter/example/lib/examples/6_todo/widgets/todo_item.dart +++ b/packages/flutter_reactter/example/lib/examples/5_todo/widgets/todo_item.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:examples/examples/6_todo/models/todo.dart'; +import 'package:examples/examples/5_todo/models/todo.dart'; class TodoItem extends StatelessWidget { final Todo todo; diff --git a/packages/flutter_reactter/example/lib/examples/6_todo/widgets/todo_list.dart b/packages/flutter_reactter/example/lib/examples/5_todo/widgets/todo_list.dart similarity index 85% rename from packages/flutter_reactter/example/lib/examples/6_todo/widgets/todo_list.dart rename to packages/flutter_reactter/example/lib/examples/5_todo/widgets/todo_list.dart index ca7cdd7a..dfefff21 100644 --- a/packages/flutter_reactter/example/lib/examples/6_todo/widgets/todo_list.dart +++ b/packages/flutter_reactter/example/lib/examples/5_todo/widgets/todo_list.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:examples/examples/6_todo/controllers/todo_controller.dart'; -import 'package:examples/examples/6_todo/widgets/todo_item.dart'; +import 'package:examples/examples/5_todo/controllers/todo_controller.dart'; +import 'package:examples/examples/5_todo/widgets/todo_item.dart'; class TodoList extends RtComponent { const TodoList({super.key}); diff --git a/packages/flutter_reactter/example/lib/examples/6_todo/widgets/todo_filter.dart b/packages/flutter_reactter/example/lib/examples/6_todo/widgets/todo_filter.dart deleted file mode 100644 index fcafa092..00000000 --- a/packages/flutter_reactter/example/lib/examples/6_todo/widgets/todo_filter.dart +++ /dev/null @@ -1,97 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_reactter/flutter_reactter.dart'; - -import 'package:examples/examples/6_todo/controllers/todo_controller.dart'; -import 'package:examples/examples/6_todo/stores/todo_store.dart'; -import 'package:examples/examples/6_todo/widgets/radio_with_label.dart'; - -class TodoFilter extends StatelessWidget { - const TodoFilter({super.key}); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(bottom: 4), - child: Wrap( - alignment: WrapAlignment.end, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - FittedBox( - child: SizedBox( - height: 30, - child: RtSelector( - selector: (inst, $) => $(inst.uReduce).value.filteredBy, - builder: (_, __, filteredBy, ___) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Text('Filter by:'), - RtSelector( - selector: (inst, $) { - return $(inst.uReduce).value.todoList.length; - }, - builder: (_, todoController, allCount, __) { - return RadioWithLabel( - label: 'All($allCount)', - value: TodoListType.all, - groupValue: filteredBy, - onChanged: todoController.filterBy, - ); - }, - ), - RtSelector( - selector: (inst, $) { - final uReduce = $(inst.uReduce).value; - final allCount = uReduce.todoList.length; - final doneCount = uReduce.doneCount; - - return allCount - doneCount; - }, - builder: (_, todoController, activeCount, __) { - return RadioWithLabel( - label: 'Active($activeCount)', - value: TodoListType.todo, - groupValue: filteredBy, - onChanged: todoController.filterBy, - ); - }, - ), - RtSelector( - selector: (inst, $) { - return $(inst.uReduce).value.doneCount; - }, - builder: (_, todoController, doneCount, __) { - return RadioWithLabel( - label: 'Completed($doneCount)', - value: TodoListType.done, - groupValue: filteredBy, - onChanged: todoController.filterBy, - ); - }, - ), - ], - ); - }, - ), - ), - ), - const SizedBox(width: 8), - RtSelector( - selector: (inst, $) { - return $(inst.uReduce).value.doneCount > 0; - }, - builder: (_, todoController, hasCount, __) { - return OutlinedButton( - style: OutlinedButton.styleFrom( - foregroundColor: Colors.red, - ), - onPressed: hasCount ? todoController.clearCompleted : null, - child: const Text('Clear completed'), - ); - }, - ), - ], - ), - ); - } -} diff --git a/packages/flutter_reactter/example/lib/examples/6_tree/controllers/tree_controller.dart b/packages/flutter_reactter/example/lib/examples/6_tree/controllers/tree_controller.dart new file mode 100644 index 00000000..2243fb5b --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/6_tree/controllers/tree_controller.dart @@ -0,0 +1,11 @@ +import 'package:examples/examples/6_tree/states/tree_list.dart'; +import 'package:examples/examples/6_tree/states/tree_node.dart'; + +class TreeController { + final root = TreeNode(); + final treeList = TreeList(); + + TreeController() { + treeList.add(root); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart new file mode 100644 index 00000000..37e2e87a --- /dev/null +++ b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart @@ -0,0 +1,65 @@ +import 'dart:collection'; + +import 'package:examples/examples/6_tree/states/tree_node.dart'; +import 'package:flutter_reactter/reactter.dart'; + +base class TreeList extends LinkedList + with RtContext, RtStateBase { + TreeList._(); + + factory TreeList() { + return Rt.createState(() => TreeList._()); + } + + @override + void add(TreeNode entry) { + update(() { + super.add(entry); + + entry.bind(this); + }); + } + + @override + void addFirst(TreeNode entry) { + update(() { + super.addFirst(entry); + + entry.bind(this); + }); + } + + @override + void addAll(Iterable entries) { + update(() { + super.addAll(entries); + + for (final entry in entries) { + entry.bind(this); + } + }); + } + + @override + bool remove(TreeNode entry) { + Rt.destroy(id: entry.id); + + final res = super.remove(entry); + + if (res) notify(); + + return res; + } + + @override + void dispose() { + clear(); + super.dispose(); + } + + @override + void clear() { + super.clear(); + if (!isDisposed) notify(); + } +} diff --git a/packages/flutter_reactter/example/lib/examples/3_tree/states/tree_node.dart b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart similarity index 54% rename from packages/flutter_reactter/example/lib/examples/3_tree/states/tree_node.dart rename to packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart index a5df694c..cef2b91f 100644 --- a/packages/flutter_reactter/example/lib/examples/3_tree/states/tree_node.dart +++ b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart @@ -4,37 +4,66 @@ import 'dart:collection'; import 'package:flutter_reactter/flutter_reactter.dart'; -class TreeNode extends LinkedListEntry - with RtContext, RtStateBase { +base class TreeNode extends LinkedListEntry + with RtStateBase, RtContext { /// A unique identifier for each instance of [TreeNode]. static int _lastId = 0; static String _getId() => (_lastId++).toString(); - final TreeNode? parent; final String id = _getId(); + final TreeNode? parent; - late final String path = parent != null ? "${parent?.path} > $id" : id; + late final String path = parent != null ? "${parent!.path} > $id" : id; late final int depth = parent != null ? parent!.depth + 1 : 0; - final uChildren = UseState({}); - final uIsExpanded = UseState(false); - final uHasNext = UseState(false); - final uCount = UseState(0); - final uDescendantsTotal = UseState(0); + @override + String? get debugLabel => id; + + @override + Map get debugInfo => { + 'id': id, + 'path': path, + 'depth': depth, + 'isExpanded': uIsExpanded.value, + 'count': uCount.value, + 'descendantsTotal': uDescendantsTotal.value, + 'total': uTotal.value, + 'parent': parent?.id, + 'children': uChildren.value, + }; + + final uChildren = UseState({}, debugLabel: 'uChildren'); + final uIsExpanded = UseState(true, debugLabel: 'uIsExpanded'); + final uCount = UseState(0, debugLabel: 'uCount'); + final uDescendantsTotal = UseState(0, debugLabel: 'uDescendantsTotal'); late final uTotal = Rt.lazyState( () => UseCompute( () => uCount.value + uDescendantsTotal.value, [uCount, uDescendantsTotal], + debugLabel: 'uTotal', ), this, ); TreeNode._(this.parent) { - UseEffect(_onIsExpandedChanged, [uIsExpanded]); - UseEffect(_onChildrenChanged, [uChildren]); + UseEffect( + _onIsExpandedChanged, + [uIsExpanded], + debugLabel: 'onIsExpandedChanged', + ); + + UseEffect( + _onChildrenChanged, + [uChildren], + debugLabel: 'onChildrenChanged', + ); if (parent != null) { - UseEffect(parent!._calculateDescendantsTotal, [uTotal]); + UseEffect( + _onTotalChanged, + [uTotal], + debugLabel: 'onTotalChanged', + ); } /// Bind the instance to the parent or list @@ -50,29 +79,49 @@ class TreeNode extends LinkedListEntry /// Create a new instance of [TreeNode] and register as a singleton. /// This way, the instance can be accessed from anywhere in the application /// and keep it in memory until it is destroyed. - factory TreeNode(TreeNode? parent) { + factory TreeNode([TreeNode? parent]) { return Rt.singleton( - () => TreeNode._(parent), + () => Rt.createState(() { + return TreeNode._(parent); + }), id: _lastId.toString(), )!; } @override - unlink() { + void dispose() { + super.dispose(); + + /// It's need to destroy the instance because it's a singleton + /// and it's not automatically destroyed when it's removed from the parent. + Rt.destroy(id: id); + } + + @override + void unlink() { + if (list == null) return; + final rList = list; super.unlink(); + if (rList is RtState) (rList as RtState).notify(); } @override void insertAfter(TreeNode entry) { + if (list == null) return; + super.insertAfter(entry); + if (list is RtState) (list as RtState).notify(); } @override void insertBefore(TreeNode entry) { + if (list == null) return; + super.insertBefore(entry); + if (list is RtState) (list as RtState).notify(); } @@ -83,34 +132,26 @@ class TreeNode extends LinkedListEntry uChildren.value.add(node); uChildren.notify(); - if (uChildren.value.length > 1) { - node.uHasNext.value = true; - } - uIsExpanded.value = true; - _showChildren(); + uIsExpanded.notify(); }); } - void remove() { + bool remove() { Rt.batch(() { - final children = uChildren.value.toList(); - - for (final node in children) { + for (final node in uChildren.value.toList()) { node.remove(); } - if (list != null) { - unlink(); - } + unlink(); parent?.uChildren.value.remove(this); parent?.uChildren.notify(); - /// It's need to destroy the instance because it's a singleton - /// and it's not automatically destroyed when it's removed from the parent. - Rt.destroy(id: id); + if (!isDisposed) dispose(); }); + + return list == null; } void toggleExpansion() => uIsExpanded.value = !uIsExpanded.value; @@ -120,33 +161,32 @@ class TreeNode extends LinkedListEntry void decrease() => uCount.value--; void _onIsExpandedChanged() { - if (uIsExpanded.value) { - return _showChildren(); - } + Rt.batch(() { + if (uIsExpanded.value) return _showChildren(); - _hideChildren(); + _hideChildren(); + }); } - void _onChildrenChanged() { - if (uChildren.value.isNotEmpty) { - uChildren.value.first.uHasNext.value = false; - } + void _onChildrenChanged() => _calculateDescendantsTotal(); + + void _onTotalChanged() => parent?._calculateDescendantsTotal(); - _calculateDescendantsTotal(); + void _calculateDescendantsTotal() { + uDescendantsTotal.value = uChildren.value.fold( + 0, + (acc, child) => acc + child.uTotal.value, + ); } void _showChildren() { Rt.batch(() { for (final node in uChildren.value) { - if (node.list != null) { - continue; - } + if (node.list != null) continue; insertAfter(node); - if (node.uIsExpanded.value) { - node._showChildren(); - } + if (node.uIsExpanded.value) node._showChildren(); } }); } @@ -154,20 +194,11 @@ class TreeNode extends LinkedListEntry void _hideChildren() { Rt.batch(() { for (final node in uChildren.value) { - if (node.list == null) { - continue; - } + if (node.list == null) continue; node._hideChildren(); node.unlink(); } }); } - - void _calculateDescendantsTotal() { - uDescendantsTotal.value = uChildren.value.fold( - 0, - (acc, child) => acc + child.uTotal.value, - ); - } } diff --git a/packages/flutter_reactter/example/lib/examples/3_tree/tree_page.dart b/packages/flutter_reactter/example/lib/examples/6_tree/tree_page.dart similarity index 73% rename from packages/flutter_reactter/example/lib/examples/3_tree/tree_page.dart rename to packages/flutter_reactter/example/lib/examples/6_tree/tree_page.dart index 0d51f77f..1c39be2d 100644 --- a/packages/flutter_reactter/example/lib/examples/3_tree/tree_page.dart +++ b/packages/flutter_reactter/example/lib/examples/6_tree/tree_page.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:examples/examples/3_tree/controllers/tree_controller.dart'; -import 'package:examples/examples/3_tree/widgets/tree_item.dart'; +import 'package:examples/examples/6_tree/controllers/tree_controller.dart'; +import 'package:examples/examples/6_tree/widgets/tree_item.dart'; class Test extends ChangeNotifier { final int value; @@ -15,6 +15,7 @@ class TreePage extends StatelessWidget { @override Widget build(BuildContext context) { + final listKey = GlobalKey(); return RtProvider( TreeController.new, builder: (context, treeController, _) { @@ -24,10 +25,13 @@ class TreePage extends StatelessWidget { ), body: CustomScrollView( slivers: [ - RtConsumer( - listenAll: true, - builder: (context, treeController, _) { + RtSelector( + selector: (treeController, watch) { + return watch(treeController.treeList).length; + }, + builder: (context, treeController, length, _) { return SliverList( + key: listKey, delegate: SliverChildBuilderDelegate( (context, index) { final treeNode = diff --git a/packages/flutter_reactter/example/lib/examples/3_tree/widgets/custom_icon_button.dart b/packages/flutter_reactter/example/lib/examples/6_tree/widgets/custom_icon_button.dart similarity index 88% rename from packages/flutter_reactter/example/lib/examples/3_tree/widgets/custom_icon_button.dart rename to packages/flutter_reactter/example/lib/examples/6_tree/widgets/custom_icon_button.dart index 6e44f386..c1bf995e 100644 --- a/packages/flutter_reactter/example/lib/examples/3_tree/widgets/custom_icon_button.dart +++ b/packages/flutter_reactter/example/lib/examples/6_tree/widgets/custom_icon_button.dart @@ -4,12 +4,10 @@ class CustomIconButton extends StatelessWidget { const CustomIconButton({ Key? key, required this.icon, - this.tooltip, required this.onPressed, }) : super(key: key); final Widget icon; - final String? tooltip; final void Function()? onPressed; @override @@ -20,7 +18,6 @@ class CustomIconButton extends StatelessWidget { iconSize: 24, constraints: const BoxConstraints.tightForFinite(), icon: icon, - tooltip: tooltip, onPressed: onPressed, ); } diff --git a/packages/flutter_reactter/example/lib/examples/3_tree/widgets/tree_item.dart b/packages/flutter_reactter/example/lib/examples/6_tree/widgets/tree_item.dart similarity index 89% rename from packages/flutter_reactter/example/lib/examples/3_tree/widgets/tree_item.dart rename to packages/flutter_reactter/example/lib/examples/6_tree/widgets/tree_item.dart index d4b29fd0..177aff9f 100644 --- a/packages/flutter_reactter/example/lib/examples/3_tree/widgets/tree_item.dart +++ b/packages/flutter_reactter/example/lib/examples/6_tree/widgets/tree_item.dart @@ -6,8 +6,8 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; -import 'package:examples/examples/3_tree/states/tree_node.dart'; -import 'package:examples/examples/3_tree/widgets/custom_icon_button.dart'; +import 'package:examples/examples/6_tree/states/tree_node.dart'; +import 'package:examples/examples/6_tree/widgets/custom_icon_button.dart'; class TreeItem extends RtComponent { final TreeNode treeNode; @@ -40,7 +40,7 @@ class TreeItem extends RtComponent { Row( mainAxisSize: MainAxisSize.min, children: [ - SizedBox(width: 24.0 * treeNode.depth + 8), + SizedBox(width: 22.0 * treeNode.depth), _buildNode(), const Expanded( child: Divider( @@ -65,7 +65,7 @@ class TreeItem extends RtComponent { context: context, builder: (context) { return AlertDialog( - title: Text("About item[$idStr]"), + title: Text("About the node($idStr)"), content: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -152,7 +152,6 @@ class TreeItem extends RtComponent { children: [ if (children.isNotEmpty) CustomIconButton( - tooltip: isExpanded ? "Collapse" : "Expand", icon: Transform.rotate( angle: isExpanded ? 0 : -pi / 2, child: const Icon(Icons.expand_circle_down_rounded), @@ -160,7 +159,6 @@ class TreeItem extends RtComponent { onPressed: treeNode.toggleExpansion, ), CustomIconButton( - tooltip: "Add child", icon: const Icon(Icons.add_circle), onPressed: treeNode.addChild, ), @@ -175,7 +173,6 @@ class TreeItem extends RtComponent { ), if (treeNode.parent != null) CustomIconButton( - tooltip: "Remove", icon: const Icon(Icons.cancel), onPressed: treeNode.remove, ), @@ -200,7 +197,6 @@ class TreeItem extends RtComponent { child: Row( children: [ CustomIconButton( - tooltip: "Decrease", icon: const Icon(Icons.indeterminate_check_box_rounded), onPressed: treeNode.decrease, ), @@ -211,7 +207,6 @@ class TreeItem extends RtComponent { ); }), CustomIconButton( - tooltip: "Increase", icon: const Icon(Icons.add_box), onPressed: treeNode.increase, ), @@ -223,9 +218,14 @@ class TreeItem extends RtComponent { } Widget _buildTreeLine() { + if (treeNode.parent == null) { + return const SizedBox(); + } + return Positioned( - top: -4, + top: -2, bottom: -4, + left: -6, child: Align( alignment: Alignment.topLeft, child: Row( @@ -234,10 +234,11 @@ class TreeItem extends RtComponent { ..._buildParentLines(treeNode, []), RtWatcher((context, watch) { return SizedBox( - height: - treeNode.parent == null || !watch(treeNode.uHasNext).value - ? 23 - : null, + height: treeNode.parent != null && + watch(treeNode.parent!.uChildren).value.first == + treeNode + ? 22 + : null, child: const VerticalDivider( width: 2, thickness: 2, @@ -246,8 +247,8 @@ class TreeItem extends RtComponent { }), RtWatcher((context, watch) { return SizedBox( - width: watch(treeNode.uChildren).value.isEmpty ? 36 : 6, - height: 48, + width: watch(treeNode.uChildren).value.isEmpty ? 32 : 4, + height: 46, child: const Divider( height: 2, thickness: 2, @@ -268,12 +269,17 @@ class TreeItem extends RtComponent { parentLines.insert( 0, RtWatcher((context, watch) { - if (!watch(node.parent!.uHasNext).value) { - return const SizedBox(width: 24); + final parentFromParent = node.parent?.parent; + final hasParentParent = parentFromParent != null; + final isParentFirst = hasParentParent && + watch(parentFromParent.uChildren).value.first == node.parent; + + if (isParentFirst) { + return const SizedBox(width: 22); } return Container( - width: 24, + width: 22, alignment: Alignment.centerLeft, child: const VerticalDivider( width: 2, diff --git a/packages/flutter_reactter/example/lib/examples/7_animation/animation_page.dart b/packages/flutter_reactter/example/lib/examples/7_animation/animation_page.dart index 365a54a1..f3ad33d3 100644 --- a/packages/flutter_reactter/example/lib/examples/7_animation/animation_page.dart +++ b/packages/flutter_reactter/example/lib/examples/7_animation/animation_page.dart @@ -24,9 +24,9 @@ class AnimationPage extends StatelessWidget { direction: Axis.horizontal, children: [ RtSelector( - selector: (inst, $) { + selector: (inst, watch) { return inst.checkIfPlaying( - $(inst.uBorderRadiusAnimation.uControl).value, + watch(inst.uBorderRadiusAnimation.uControl).value, ); }, child: RtConsumer( @@ -51,9 +51,9 @@ class AnimationPage extends StatelessWidget { }, ), RtSelector( - selector: (inst, $) { + selector: (inst, watch) { return inst.checkIfPlaying( - $(inst.uSizeAnimation.uControl).value, + watch(inst.uSizeAnimation.uControl).value, ); }, child: RtConsumer( @@ -77,9 +77,9 @@ class AnimationPage extends StatelessWidget { }, ), RtSelector( - selector: (inst, $) { + selector: (inst, watch) { return inst.checkIfPlaying( - $(inst.uColorAnimation.uControl).value, + watch(inst.uColorAnimation.uControl).value, ); }, child: RtConsumer( @@ -101,11 +101,11 @@ class AnimationPage extends StatelessWidget { }, ), RtSelector( - selector: (inst, $) { + selector: (inst, watch) { return [ - $(inst.uSizeAnimation.uControl).value, - $(inst.uBorderRadiusAnimation.uControl).value, - $(inst.uColorAnimation.uControl).value, + watch(inst.uSizeAnimation.uControl).value, + watch(inst.uBorderRadiusAnimation.uControl).value, + watch(inst.uColorAnimation.uControl).value, ].every(inst.checkIfPlaying); }, child: RtConsumer( diff --git a/packages/flutter_reactter/example/lib/examples/7_animation/hooks/use_animation.dart b/packages/flutter_reactter/example/lib/examples/7_animation/hooks/use_animation.dart index 4672e6e0..f989e7ff 100644 --- a/packages/flutter_reactter/example/lib/examples/7_animation/hooks/use_animation.dart +++ b/packages/flutter_reactter/example/lib/examples/7_animation/hooks/use_animation.dart @@ -4,6 +4,13 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; +class UseX extends RtHook { + @override + final $ = RtHook.$register; + + late final uCounter = Rt.lazyState(() => UseState(0), this); +} + /// Defines different control options for playing an animation. Each option /// represents a specific behavior for the animation playback: enum AnimationControl { @@ -62,7 +69,6 @@ class AnimationOptions { class UseAnimation extends RtHook implements TickerProvider { @override - @protected final $ = RtHook.$register; late final uTween = Rt.lazyState( @@ -93,6 +99,14 @@ class UseAnimation extends RtHook implements TickerProvider { T get value => _animation.value; + @override + Map get debugInfo => { + 'value': value, + 'duration': uDuration.value, + 'control': uControl.value, + 'curve': uCurve.value, + }; + final AnimationOptions options; UseAnimation(this.options) { diff --git a/packages/flutter_reactter/example/lib/examples_page.dart b/packages/flutter_reactter/example/lib/examples_page.dart index 6ee1546b..ab587833 100644 --- a/packages/flutter_reactter/example/lib/examples_page.dart +++ b/packages/flutter_reactter/example/lib/examples_page.dart @@ -3,10 +3,10 @@ import 'package:examples/custom_list.dart'; import 'examples/1_counter/counter_page.dart'; import 'examples/2_calculator/calculator_page.dart'; -import 'examples/4_shopping_cart/shopping_cart_page.dart'; -import 'examples/3_tree/tree_page.dart'; -import 'examples/5_api/api_page.dart'; -import 'examples/6_todo/todo_page.dart'; +import 'examples/3_shopping_cart/shopping_cart_page.dart'; +import 'examples/6_tree/tree_page.dart'; +import 'examples/4_github_search/github_search_page.dart'; +import 'examples/5_todo/todo_page.dart'; import 'examples/7_animation/animation_page.dart'; final examples = [ @@ -16,7 +16,7 @@ final examples = [ "Increase and decrease the counter", [ "RtWatcher", - "Signal", + "UseState", ], () => const CounterPage(), ), @@ -26,38 +26,16 @@ final examples = [ "Performs simple arithmetic operations on numbers", [ "BuilContext.use", - "Rt.batch", - "RtConsumer", "RtProvider", "RtSelector", "RtWatcher", - "Signal", - ], - () => const CalculatorPage(), - ), - ExampleItem( - "/tree", - "3. Tree widget", - "Add, remove and hide child widget with counter.", - [ - "Rt.batch", - "Rt.createSate", - "Rt.lazyState", - "RtContext", - "RtComponent", - "RtConsumer", - "RtProvider", - "RtState", - "RtWatcher", - "UseCompute", - "UseEffect", "UseState", ], - () => const TreePage(), + () => const CalculatorPage(), ), ExampleItem( "/shopping-cart", - "4. Shopping Cart", + "3. Shopping Cart", "Add, remove product to cart and checkout", [ "Rt.lazyState", @@ -66,14 +44,15 @@ final examples = [ "RtProvider", "RtMultiProvider", "RtSelector", + "RtWatcher", "UseDependency", "UseState", ], () => const ShoppingCartPage(), ), ExampleItem( - "/api", - "5. Github Search", + "/github-search", + "4. Github Search", "Search user or repository and show info about it.", [ "Memo", @@ -81,12 +60,13 @@ final examples = [ "RtConsumer", "RtProvider", "UseAsyncState", + "UseDependency", ], - () => const ApiPage(), + () => const GithubSearchPage(), ), ExampleItem( "/todo", - "6. To-Do List", + "5. To-Do List", "Add and remove to-do, mark and unmark to-do as done and filter to-do list", [ "Rt.lazyState", @@ -99,6 +79,26 @@ final examples = [ ], () => const TodoPage(), ), + ExampleItem( + "/tree", + "6. Tree widget", + "Add, remove and hide child widget with counter.", + [ + "Rt.batch", + "Rt.createSate", + "Rt.lazyState", + "RtContext", + "RtComponent", + "RtConsumer", + "RtProvider", + "RtState", + "RtWatcher", + "UseCompute", + "UseEffect", + "UseState", + ], + () => const TreePage(), + ), ExampleItem( "/animation", "7. Animate widget", diff --git a/packages/flutter_reactter/example/lib/main.dart b/packages/flutter_reactter/example/lib/main.dart index 86a5ca06..406369bd 100644 --- a/packages/flutter_reactter/example/lib/main.dart +++ b/packages/flutter_reactter/example/lib/main.dart @@ -3,6 +3,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; Future main() async { - Rt.initializeDebugging(); + Rt.initializeDevTools(); runApp(const MyApp()); } diff --git a/packages/flutter_reactter/example/pubspec.lock b/packages/flutter_reactter/example/pubspec.lock index 9dc1440c..793582e5 100644 --- a/packages/flutter_reactter/example/pubspec.lock +++ b/packages/flutter_reactter/example/pubspec.lock @@ -114,29 +114,29 @@ packages: source: hosted version: "3.0.3" custom_lint: - dependency: transitive + dependency: "direct dev" description: name: custom_lint - sha256: b09a939c3bb062fdf072aa4c8eb5447231338854c4fefb23e4c9f7cef41cb3ff + sha256: "22bd87a362f433ba6aae127a7bac2838645270737f3721b180916d7c5946cb5d" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.5.11" custom_lint_builder: dependency: transitive description: name: custom_lint_builder - sha256: f1430048ccddcd82cbf7722fce7701530fd535b82e91f45b0c81ed07814143bf + sha256: "0d48e002438950f9582e574ef806b2bea5719d8d14c0f9f754fbad729bcf3b19" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.5.14" custom_lint_core: dependency: transitive description: name: custom_lint_core - sha256: a39e98cba1d96076e2e5c96aad582a776909c78b4d7480d0efdcc3791b225c1b + sha256: "2952837953022de610dacb464f045594854ced6506ac7f76af28d4a6490e189b" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.5.14" dart_style: dependency: transitive description: @@ -169,10 +169,9 @@ packages: flutter_reactter: dependency: "direct main" description: - name: flutter_reactter - sha256: ade4971b87071de16f81b18f2dd021eec76daa1d6734d50b59266b381fb647d0 - url: "https://pub.dev" - source: hosted + path: ".." + relative: true + source: path version: "7.3.1" flutter_web_plugins: dependency: transitive @@ -263,18 +262,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.15.0" package_config: dependency: transitive description: @@ -318,19 +317,17 @@ packages: reactter: dependency: transitive description: - name: reactter - sha256: ad773608411600531cbc3513ae05a4ce91610ccb93701f24e79d26999b663f04 - url: "https://pub.dev" - source: hosted + path: "../../reactter" + relative: true + source: path version: "7.3.0" reactter_lint: dependency: "direct dev" description: - name: reactter_lint - sha256: "14e1d927b7e4f9e079a480582d1f34d9b0fd54d121e6f64c29d034e35c745f39" - url: "https://pub.dev" - source: hosted - version: "0.1.0" + path: "../../reactter_lint" + relative: true + source: path + version: "1.0.0" rxdart: dependency: transitive description: @@ -513,5 +510,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.2.0-0 <4.0.0" + dart: ">=3.3.0-0 <4.0.0" flutter: ">=3.7.0" diff --git a/packages/flutter_reactter/example/pubspec.yaml b/packages/flutter_reactter/example/pubspec.yaml index d2cd002e..7256ae2b 100644 --- a/packages/flutter_reactter/example/pubspec.yaml +++ b/packages/flutter_reactter/example/pubspec.yaml @@ -1,10 +1,10 @@ name: examples description: Reactter examples -version: 2.3.0+1 +version: 3.0.0+1 publish_to: "none" environment: - sdk: ">=2.19.0 <4.0.0" + sdk: ">3.2.0 <4.0.0" dependencies: flutter: @@ -12,11 +12,14 @@ dependencies: http: ^0.13.6 url_launcher: ^6.1.2 intl: ^0.17.0 - flutter_reactter: ^7.3.1 + flutter_reactter: + path: ../ dev_dependencies: + custom_lint: ^0.5.11 flutter_lints: ^2.0.2 - reactter_lint: ^0.1.0 + reactter_lint: + path: ../../reactter_lint flutter: uses-material-design: true From dbc71fb895c73f0a045ba6271145c112bc66cead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Mon, 30 Dec 2024 22:41:19 -0600 Subject: [PATCH 071/141] refactor(core, framework): Rename `RtContext` to `RtContextMixin` for consistency across state management classes. --- packages/reactter/lib/src/core/state_management.dart | 2 +- packages/reactter/lib/src/framework/rt_context.dart | 2 +- packages/reactter/lib/src/framework/rt_hook.dart | 4 +++- packages/reactter/lib/src/signal.dart | 2 +- packages/reactter/test/framework/rt_state_base_test.dart | 4 ++-- packages/reactter/test/logger_test.dart | 3 ++- .../reactter_devtools_extension/lib/src/bases/tree_list.dart | 2 +- .../reactter_devtools_extension/lib/src/bases/tree_node.dart | 2 +- packages/reactter_lint/README.md | 4 ++-- 9 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/reactter/lib/src/core/state_management.dart b/packages/reactter/lib/src/core/state_management.dart index e54d03f4..fdf90802 100644 --- a/packages/reactter/lib/src/core/state_management.dart +++ b/packages/reactter/lib/src/core/state_management.dart @@ -17,7 +17,7 @@ abstract class StateManagement implements IContext { /// /// Example usage: /// ```dart - /// class MyState with RtContext, RtStateBase { + /// class MyState with RtContextMixin, RtStateBase { /// int _value = 0; /// int get value => value; /// set value(int n) { diff --git a/packages/reactter/lib/src/framework/rt_context.dart b/packages/reactter/lib/src/framework/rt_context.dart index ab63fd09..a4377644 100644 --- a/packages/reactter/lib/src/framework/rt_context.dart +++ b/packages/reactter/lib/src/framework/rt_context.dart @@ -8,7 +8,7 @@ part of '../framework.dart'; /// {@endtemplate} final Rt = RtInterface(); -mixin RtContext implements IContext { +mixin RtContextMixin implements IContext { @override @internal DependencyInjection get dependencyInjection => Rt; diff --git a/packages/reactter/lib/src/framework/rt_hook.dart b/packages/reactter/lib/src/framework/rt_hook.dart index 31ac73f7..1942b4d4 100644 --- a/packages/reactter/lib/src/framework/rt_hook.dart +++ b/packages/reactter/lib/src/framework/rt_hook.dart @@ -40,7 +40,9 @@ part of '../internals.dart'; /// See also: /// * [RtState], adds state management features to [RtHook]. /// {@endtemplate} -abstract class RtHook with RtContext, RtStateBase implements IHook { +abstract class RtHook + with RtContextMixin, RtStateBase + implements IHook { /// {@template reactter.rt_hook.register} /// This getter allows access to the [HookBindingZone] instance /// which is responsible for registering a [RtHook] diff --git a/packages/reactter/lib/src/signal.dart b/packages/reactter/lib/src/signal.dart index f5cbf444..12ea3145 100644 --- a/packages/reactter/lib/src/signal.dart +++ b/packages/reactter/lib/src/signal.dart @@ -73,7 +73,7 @@ enum SignalEvent { onGetValue, onSetValue } /// package on your dependencies and use its Widgets. /// /// {@endtemplate} -class Signal with RtContext, RtStateBase> { +class Signal with RtContextMixin, RtStateBase> { /// {@macro reactter.signal} Signal._( T value, { diff --git a/packages/reactter/test/framework/rt_state_base_test.dart b/packages/reactter/test/framework/rt_state_base_test.dart index aa96c881..84a428cb 100644 --- a/packages/reactter/test/framework/rt_state_base_test.dart +++ b/packages/reactter/test/framework/rt_state_base_test.dart @@ -1,7 +1,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:reactter/reactter.dart'; -class CountTest with RtContext, RtStateBase { +class CountTest with RtContextMixin, RtStateBase { int _count = 0; int get count => _count; set count(int value) { @@ -21,7 +21,7 @@ class CountTest with RtContext, RtStateBase { }; } -class StateTest with RtContext, RtStateBase { +class StateTest with RtContextMixin, RtStateBase { StateTest._() { assert(dependencyInjection == Rt); assert(stateManagement == Rt); diff --git a/packages/reactter/test/logger_test.dart b/packages/reactter/test/logger_test.dart index 886a0ea0..847380a5 100644 --- a/packages/reactter/test/logger_test.dart +++ b/packages/reactter/test/logger_test.dart @@ -2,7 +2,8 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:reactter/reactter.dart'; import 'package:reactter/src/logger.dart'; -class StateTest with RtContext, RtStateBase { +class StateTest + with RtContextMixin, RtStateBase { StateTest._(); factory StateTest() { diff --git a/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart b/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart index 8a23e6e8..6dd8e9e6 100644 --- a/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart +++ b/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart @@ -4,7 +4,7 @@ import 'package:flutter_reactter/reactter.dart'; import 'package:reactter_devtools_extension/src/bases/tree_node.dart'; base class TreeList> extends LinkedList - with RtContext, RtStateBase> { + with RtContextMixin, RtStateBase> { final uMaxDepth = UseState(0); TreeList._(); diff --git a/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart b/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart index 8aa83ea6..643ccb76 100644 --- a/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart +++ b/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart @@ -3,7 +3,7 @@ import 'package:flutter_reactter/reactter.dart'; import 'package:reactter_devtools_extension/src/bases/tree_list.dart'; abstract base class TreeNode> extends LinkedListEntry - with RtStateBase, RtContext { + with RtStateBase, RtContextMixin { final uChildren = UseState(LinkedHashSet()); final uIsExpanded = UseState(false); final uDepth = UseState(0); diff --git a/packages/reactter_lint/README.md b/packages/reactter_lint/README.md index 9f856f96..86371894 100644 --- a/packages/reactter_lint/README.md +++ b/packages/reactter_lint/README.md @@ -172,7 +172,7 @@ The state must be create under the Reactter context. Cause: The state cannot be created outside Reactter context. ```dart -class MyState with RtStateBase, RtContext {...} +class MyState with RtStateBase, RtContextMixin {...} > final myState = MyState(); ``` @@ -182,7 +182,7 @@ class MyState with RtStateBase, RtContext {...} Fix: Use `Rt.createState` method for creating the state under the Reactter context. ```dart -class MyState with RtStateBase, RtContext {...} +class MyState with RtStateBase, RtContextMixin {...} final myState = Rt.createState(() => MyState()); ``` From a9d99b4d174d7594261827f3411d07390fd21da4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Mon, 30 Dec 2024 22:55:26 -0600 Subject: [PATCH 072/141] refactor(examples): Rename `RtContext` to `RtContextMixin` for consistency in tree structure classes. --- .../example/lib/examples/6_tree/states/tree_list.dart | 2 +- .../example/lib/examples/6_tree/states/tree_node.dart | 2 +- packages/flutter_reactter/example/lib/examples_page.dart | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart index 37e2e87a..fdef7708 100644 --- a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart +++ b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart @@ -4,7 +4,7 @@ import 'package:examples/examples/6_tree/states/tree_node.dart'; import 'package:flutter_reactter/reactter.dart'; base class TreeList extends LinkedList - with RtContext, RtStateBase { + with RtContextMixin, RtStateBase { TreeList._(); factory TreeList() { diff --git a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart index cef2b91f..1fb68c95 100644 --- a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart +++ b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart @@ -5,7 +5,7 @@ import 'dart:collection'; import 'package:flutter_reactter/flutter_reactter.dart'; base class TreeNode extends LinkedListEntry - with RtStateBase, RtContext { + with RtStateBase, RtContextMixin { /// A unique identifier for each instance of [TreeNode]. static int _lastId = 0; static String _getId() => (_lastId++).toString(); diff --git a/packages/flutter_reactter/example/lib/examples_page.dart b/packages/flutter_reactter/example/lib/examples_page.dart index ab587833..48134d05 100644 --- a/packages/flutter_reactter/example/lib/examples_page.dart +++ b/packages/flutter_reactter/example/lib/examples_page.dart @@ -87,7 +87,7 @@ final examples = [ "Rt.batch", "Rt.createSate", "Rt.lazyState", - "RtContext", + "RtContextMixin", "RtComponent", "RtConsumer", "RtProvider", From 25384d3355c578dce2994bd5b36eb9543913adbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Mon, 30 Dec 2024 23:50:32 -0600 Subject: [PATCH 073/141] fix(devtools): Update references from `_instance` to `instance` to use the correct one in eval calls. --- .../lib/src/nodes/dart/plain_instance_node.dart | 2 +- .../lib/src/nodes/instance/instance_node.dart | 2 +- .../lib/src/nodes/state/state_node.dart | 4 ++-- .../lib/src/services/devtools_service.dart | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/reactter_devtools_extension/lib/src/nodes/dart/plain_instance_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/dart/plain_instance_node.dart index 875fc5b5..f6880a21 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/dart/plain_instance_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/dart/plain_instance_node.dart @@ -33,7 +33,7 @@ base class PlainInstanceNode extends AsyncNode { final eval = await EvalService.devtoolsEval; final instanceInfo = await EvalService.evalsQueue.add( () => eval.evalInstance( - 'RtDevTools._instance?.getPlainInstanceInfo(instance)', + 'RtDevTools.instance?.getPlainInstanceInfo(instance)', isAlive: isAlive, scope: {'instance': instanceRef.id!}, ), diff --git a/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart index 4d694d78..19bfffb5 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart @@ -22,7 +22,7 @@ base class InstanceNode extends Node { final dependencyKey = uInfo.value?.dependencyKey; final dependencyRef = await EvalService.evalsQueue.add( () => eval.safeEval( - 'RtDevTools._instance?.getDependencyRef("$dependencyKey")', + 'RtDevTools.instance?.getDependencyRef("$dependencyKey")', isAlive: isAlive, ), ); diff --git a/packages/reactter_devtools_extension/lib/src/nodes/state/state_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/state/state_node.dart index cfcd4eda..7533c762 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/state/state_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/state/state_node.dart @@ -31,7 +31,7 @@ final class StateNode extends InstanceNode { try { final eval = await EvalService.devtoolsEval; final boundInstanceRef = await eval.evalInstance( - 'RtDevTools._instance?.getBoundInstance("$key")', + 'RtDevTools.instance?.getBoundInstance("$key")', isAlive: isAlive, ); return boundInstanceRef.getNode('boundInstance'); @@ -45,7 +45,7 @@ final class StateNode extends InstanceNode { try { final eval = await EvalService.devtoolsEval; final debugInfoRef = await eval.evalInstance( - 'RtDevTools._instance?.getDebugInfo("$key")', + 'RtDevTools.instance?.getDebugInfo("$key")', isAlive: isAlive, ); return debugInfoRef.getNode('debugInfo'); diff --git a/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart b/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart index 9bef28ea..f8af4439 100644 --- a/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart +++ b/packages/reactter_devtools_extension/lib/src/services/devtools_service.dart @@ -15,7 +15,7 @@ class DevtoolsService { final pageNodes = await EvalService.evalsQueue.add( () => eval.evalInstance( - 'RtDevTools._instance?.getNodes($page, $pageSize)', + 'RtDevTools.instance?.getNodes($page, $pageSize)', isAlive: isAlive, ), ); @@ -41,7 +41,7 @@ class DevtoolsService { final nodeInst = await EvalService.evalsQueue.add( () => eval.evalInstance( - 'RtDevTools._instance?._nodesByKey["$nodeKey"]?.toJson()', + 'RtDevTools.instance?._nodesByKey["$nodeKey"]?.toJson()', isAlive: isAlive, ), ); From fc25d6719ad97b87484c519082c655de86ab2689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Mon, 30 Dec 2024 23:51:37 -0600 Subject: [PATCH 074/141] doc: Update README.md. --- README.md | 1785 +---------------------------------------------------- 1 file changed, 17 insertions(+), 1768 deletions(-) diff --git a/README.md b/README.md index 9e3d6c78..b4943e7f 100644 --- a/README.md +++ b/README.md @@ -12,1793 +12,43 @@ ____ [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/2devs-team/reactter/dart.yml?branch=master)](https://github.com/2devs-team/reactter/actions) [![Codecov](https://img.shields.io/codecov/c/github/2devs-team/reactter?logo=codecov)](https://app.codecov.io/gh/2devs-team/reactter) -**A light, powerful and quick Reactive State Management, Dependency Injection and Event Handler.** - -> Now documentation on the official web site: -> +# A lightweight, powerful, and reactive State Management, Dependency Injection and Event Handler for Dart/Flutter ## Features - ⚡️ Engineered for **Speed**. -- ⚖️ Super **Lightweight**([🥇 See benchmarks](https://github.com/CarLeonDev/state_managements#memory-size)). -- 📏 **Reduce Boilerplate Code** significantly([🥇 See benchmarks](https://github.com/CarLeonDev/state_managements#lines-number)). +- ⚖️ Super **Lightweight**. +- 📏 **Reduce Boilerplate Code** significantly. - ✏️ Improve **Code Readability**. - 💧 **Flexible** and **Adaptable** to any architecture. -- ☢️ **Reactive States** using [Signal](#signal) and Hooks. -- ♻️ **Reusable States and Logic** with [Custom hooks](#custom-hooks). -- 🎮 Fully **[Rendering Control](#rendering-control)**. +- ☢️ **Reactive States** using [State](https://2devs-team.github.io/reactter/classes/rt_state_base) and [Hooks](https://2devs-team.github.io/reactter/core_concepts/hooks). +- ♻️ **Reusable States and Logic** with [Custom hooks]([#custom-hooks](https://2devs-team.github.io/reactter/core_concepts/hooks/#custom-hook)). +- 🎮 Fully **[Rendering Control]([#rendering-control](https://2devs-team.github.io/reactter/core_concepts/rendering_control))**. - 🧪 Fully **Testable**, 100% code coverage. -- 🪄 **Zero Configuration** and **No Code Generation** necessary. -- 💙 **Compatible with Dart and Flutter**, supports the latest version of Dart. - -Let's see a small and simple example: - -```dart -// Create a reactive state using `Signal` -final count = Signal(0); - -void main() { - // Change the `value` in any time(e.g., each 1 second). - Timer.periodic( - Duration(seconds: 1), - (timer) => count.value++, - ); - - // Put on listen `didUpdate` event, whitout use `Stream` - Rt.on( - count, - Lifecycle.didUpdate, - (inst, state) => print('Count: $count'), - ); - - // And you can use in flutter, e.g: - runApp( - MaterialApp( - home: Scaffold( - body: RtSignalWatcher( - // Just use it, and puts it in listening mode - // for further rendering automatically. - builder: (context, child) => Text("Count: $count"), - ), - ), - ), - ); -} -``` - -Clean and easy! - -See more examples [here](https://zapp.run/pub/flutter_reactter)! - -## Contents - -- [Features](#features) -- [Contents](#contents) -- [Quickstart](#quickstart) -- [About Reactter](#about-reactter) -- [State management](#state-management) - - [Signal](#signal) - - [UseState](#usestate) - - [UseAsyncState](#useasyncstate) - - [UseReducer](#usereducer) - - [UseCompute](#usecompute) -- [Dependency injection](#dependency-injection) - - [Builder](#builder) - - [Factory](#factory) - - [Singleton](#singleton) - - [Shortcuts to manage instances](#shortcuts-to-manage-instances) - - [UseDependency](#usedependency) -- [Event handler](#event-handler) - - [Lifecycles](#lifecycles) - - [Shortcuts to manage events](#shortcuts-to-manage-events) - - [UseEffect](#useeffect) -- [Rendering control](#rendering-control) - - [RtProvider](#rtprovider) - - [RtMultiProvider](#rtmultiprovider) - - [RtComponent](#rtcomponent) - - [RtConsumer](#rtconsumer) - - [RtSelector](#rtselector) - - [RtSignalWatcher](#rtsignalwatcher) - - [BuildContext.use](#buildcontextuse) - - [BuildContext.watch](#buildcontextwatch) - - [BuildContext.select](#buildcontextselect) -- [Custom hooks](#custom-hooks) -- [Lazy state](#lazy-state) -- [Batch](#batch) -- [Untracked](#untracked) -- [Generic arguments](#generic-arguments) -- [Memo](#memo) -- [Difference between Signal and UseState](#difference-between-signal-and-usestate) -- [Resources](#resources) -- [Contribute](#contribute) -- [Authors](#authors) - -## Quickstart - -Before anything, you need to be aware that Reactter is distributed on two packages, with slightly different usage. - -The package of Reactter that you will want to install depends on the type of project you are working on. - -Select one of the following options to know how to install it: - - - -

- -

- - Flutter  - - Flutter Reactter - - - -Add the package on your project. - -- Using command: - - ```shell - flutter pub add flutter_reactter - ``` - -- Or put directly into `pubspec.yaml` file: - - ```yaml - dependencies: - flutter_reactter: #add version here - ``` - - and run `flutter pub get`. - -Now in your Dart code, you can use: - -```dart -import 'package:flutter_reactter/flutter_reactter.dart'; -``` - -
- -And it is recommended to use -[![Reactter Lint](https://img.shields.io/pub/v/reactter_lint?color=1d7fac&labelColor=29b6f6&label=reactter_lint&logo=dart)](https://pub.dev/packages/reactter_lint) -which will help to encourage good coding practices and prevent frequent problems using the Reactter convensions. - -If you use Visual Studio Code, it is a good idea to use [Reactter Snippets](https://marketplace.visualstudio.com/items?itemName=CarLeonDev.reacttersnippets) for improving productivity. - -## About Reactter - -Reactter is a light and powerful solution for Dart and Flutter. It is composed of three main concepts that can be used together to create maintainable and scalable applications, which are: - -- [State management](#state-management) -- [Dependency injection](#dependency-injection) -- [Event handler](#event-handler) - -Moreover, Reactter offers an extensive collection of widgets and extensions, granting advanced [rendering control](#rendering-control) through the `flutter_reactter` package. - -## State management - -In Reactter, state is understood as any object that extends [`RtState`](https://pub.dev/documentation/reactter/latest/reactter/RtState-class.html), endowing it with capabilities such as the ability to store one or more values and to broadcast notifications of its changes. - -Reactter offers the following several state managers: - -- [Signal](#signal) -- [UseState](#usestate) -- [UseAsyncState](#useasyncstate) -- [UseReducer](#usereducer) -- [UseCompute](#usecompute) - -> **NOTE:** -> The hooks (also known as [`RtHook`](https://pub.dev/documentation/reactter/latest/reactter/RtHook-class.html)) are named with the prefix `Use` according to convention. - -> **RECOMMENDED:** -> See also [difference between Signal and UseState](#difference-between-signal-and-usestate) and about [custom hooks](#custom-hooks). - -### Signal - -[`Signal`](https://pub.dev/documentation/reactter/latest/reactter/Signal-class.html) is an object (that extends [`RtState`](https://pub.dev/documentation/reactter/latest/reactter/RtState-class.html)) which has a `value` and notifies about its changes. - -It can be initialized using the constructor class `Signal(T initialValue)`: - -```dart -final intSignal = Signal(0); -final strSignal = Signal("initial value"); -final userSignal = Signal(User()); -``` - -`Signal` has a `value` property that allows to read and write its state: - -```dart -intSignal.value = 10; -print("Current state: ${intSignal.value}"); -``` - -or also can use the callable function: - -```dart -intSignal(10); -print("Current state: ${intSignal()}"); -``` - -or simply use `.toString()` implicit to get its `value` as String: - -```dart -print("Current state: $intSignal"); -``` - -> **NOTE:** -> `Signal` notifies that its `value` has changed when the previous `value` is different from the current `value`. -> If its `value` is an `Object`, it does not detect internal changes, only when `value` is setted to another `Object`. - -Use `update` method to notify changes after run a set of instructions: - -```dart -userSignal.update((user) { - user.firstname = "Firstname"; - user.lastname = "Lastname"; -}); -``` - -Use `refresh` method to force to notify changes. - -```dart -userSignal.refresh(); -``` - -When `value` has changed, the `Signal` will emit the following events(learn about it [here](#lifecycle-and-event-management)): - -- `Lifecycle.willUpdate` event is triggered before the `value` change or `update`, `refresh` methods have been invoked. -- `Lifecycle.didUpdate` event is triggered after the `value` change or `update`, `refresh` methods have been invoked. - -> **NOTE:** -> When you do any arithmetic operation between two `Signal`s, it returns an `Obj`, for example: `signal(1) + Signal(2)` returns `Obj(3)`. -> An [`Obj`](https://pub.dev/documentation/reactter/latest/reactter/Obj-class.html) is like a `Signal` without reactive functionality, but you can convert it to `Signal` using `.toSignal`. - -> **NOTE:** -> In flutter, using [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/latest/flutter_reactter/RtSignalWatcher-class.html), is a way to keep the widgets automatically updates, accessing the value of signal reactively. - -### UseState - -[`UseState`](https://pub.dev/documentation/reactter/latest/reactter/UseState-class.html) is a hook([`RtHook`](https://pub.dev/documentation/reactter/latest/reactter/RtHook-class.html)) that allows to declare state variables and manipulate its `value`, which in turn notifies about its changes. - -```dart -UseState(T initialValue) -``` - -`UseState` accepts a property: - -- `initialValue`: is a unique value of any type that you use to initialize the state. - -It can be declared inside a class, like this: - -```dart -class CounterController { - final count = UseState(0); -} -``` - -> **NOTE:** -> if your variable hook is `late` use `Rt.lazyState`. Learn about it [here](#lazy-state). - -`UseState` has a `value` property that allows to read and write its state: - -```dart -class CounterController { - final count = UseState(0); - - CounterController() { - print("Prev state: ${count.value}"); - count.value = 10; - print("Current state: ${count.value}"); - } -} -``` - -> **NOTE:** -> `UseState` notifies that its `value` has changed when the previous `value` is different from the current `value`. -> If its `value` is an `Object`, it does not detect internal changes, only when `value` is setted to another `Object`. - -Use `update` method to notify changes after run a set of instructions: - -```dart -userState.update((user) { - user.firstname = "Firstname"; - user.lastname = "Lastname"; -}); -``` - -Use `refresh` method to force to notify changes. - -```dart -userState.refresh(); -``` - -When `value` has changed, the `UseState` will emitted the following events(learn about it [here](#lifecycle-and-event-management)): - -- `Lifecycle.willUpdate` event is triggered before the `value` change or `update`, `refresh` methods have been invoked. -- `Lifecycle.didUpdate` event is triggered after the `value` change or `update`, `refresh` methods have been invoked. - -### UseAsyncState - -[`UseAsyncState`](https://pub.dev/documentation/reactter/latest/reactter/UseAsyncState-class.html) is a hook ([`RtHook`](https://pub.dev/documentation/reactter/latest/reactter/RtHook-class.html)) with the same feature as [`UseState`](#usestate) but its value will be lazily resolved by a function(`asyncFunction`). - -```dart -UseAsyncState( - T initialValue, - Future asyncFunction(), -); -``` - -`UseAsyncState` accepts these properties: - -- `initialValue`: is a unique value of any type that you use to initialize the state. -- `asyncFunction`: is a function that will be called by the `resolved` method and sets the value of the state. - -Use `UseAsyncState.withArg` to pass a argument to the `asyncFunction`. - -```dart -UseAsyncState.withArg( - T initialValue, - Future asyncFunction(A) , -) -``` - -> **NOTE:** -> if your variable hook is `late` use `Rt.lazyState`. Learn about it [here](#lazy-state). - -This is a translate example: - -```dart -class TranslateController { - final translateState = UseAsyncStates.withArg( - null, - (ArgsX3 args) async { - final text = args.arg; - final from = args.arg2; - final to = args.arg3; - // this is fake code, which simulates a request to API - return await api.translate(text, from, to); - } - ); - - TranslateController() { - translateState.resolve( - Args3('Hello world', 'EN','ES'), - ).then((_) { - print("'Hello world' translated to Spanish: '${translateState.value}'"); - }); - } -} -``` - -> **RECOMMENDED:** -> If you wish to optimize the state resolution, the best option is to use the memoization technique. Reactter provides this using `Memo`(Learn about it [here](#memo)), e.g: -> -> ```dart -> [...] -> final translateState = UseAsyncState.withArg>( -> null, -> /// `Memo` stores the value resolved in cache, -> /// and retrieving that same value from the cache the next time -> /// it's needed instead of resolving it again. -> Memo.inline( -> (ArgsX3 args) async { -> final text = args.arg; -> final from = args.arg2; -> final to = args.arg3; -> // this is fake code, which simulates a request to API -> return await api.translate(text, from, to); -> }, -> AsyncMemoSafe(), // avoid to save in cache when throw a error -> ), -> ); -> [...] -> ``` - -> **RECOMMENDED:** -> In the above example uses `Args`([generic arguments](#generic-arguments)), but using [Record](https://dart.dev/language/records#record-types) instead is recommended if your project supports it. - -Use the `when` method to return a computed value depending on it's state: - -```dart -final computedValue = asyncState.when( - standby: (value) => "🔵 Standby: $value", - loading: (value) => "⏳ Loading...", - done: (value) => "✅ Resolved: $value", - error: (error) => "❌ Error: $error", -); -``` - -When `value` has changed, the `UseAsyncState` will emit the following events (learn about it [here](#lifecycle-and-event-management)): - -- `Lifecycle.willUpdate` event is triggered before the `value` change or `update`, `refresh` methods have been invoked. -- `Lifecycle.didUpdate` event is triggered after the `value` change or `update`, `refresh` methods have been invoked. - -### UseReducer - -[`UseReducer`](https://pub.dev/documentation/reactter/latest/reactter/UseReducer-class.html) is a hook([`RtHook`](https://pub.dev/documentation/reactter/latest/reactter/RtHook-class.html)) that manages state using reducer method. An alternative to [`UseState`](#usestate). - -> **RECOMMENDED:** -> `UseReducer` is usually preferable over `UseState` when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. - - ```dart - UseReducer( - T reducer(T state, RtAction action), - T initialState, - ); - ``` - -`UseReducer` accepts two properties: - -- `reducer`: is a method contains your custom state logic that calculates the new state using current state, and actions. -- `initialState`: is a unique value of any type that you use to initialize the state. - -`UseReducer` exposes a `dispatch` method that allows you to invoke the `reducer` method sending a `RtAction`. - -The current state can be accessed through the `value` property. - -Here's the counter example using `UseReducer`: - - ```dart -class Store { - final int count; - - Store({this.count = 0}); -} - -Store reducer(Store state, RtAction action) { - switch (action.type) { - case 'INCREMENT': - return Store(count: state.count + (action.payload ?? 1)); - case 'DECREMENT': - return Store(count: state.count + (action.payload ?? 1)); - default: - throw UnimplementedError(); - } -} - -class CounterController { - final useCounter = UseReducer(reducer, Store(count: 0)); - - CounterController() { - print("count: ${useCounter.value.count}"); // count: 0; - useCounter.dispatch(RtAction(type: 'INCREMENT', payload: 2)); - print("count: ${useCounter.value.count}"); // count: 2; - useCounter.dispatch(RtAction(type: 'DECREMENT')); - print("count: ${useCounter.value.count}"); // count: 1; - } -} - ``` - -The actions can be created as a callable class, extending from [`RtActionCallable`](https://pub.dev/documentation/reactter/latest/reactter/RtActionCallable-class.html) and used as follows: - -```dart -class IncrementAction extends RtActionCallable { - IncrementAction([int quantity = 1]) : super( - type: 'INCREEMNT', payload: quantity - ); - - @override - Store call(Store state) => Store(count: state.count + payload); -} - -class DecrementAction extends RtActionCallable { - DecrementAction([int quantity = 1]) : super( - type: 'DECREMENT', payload: quantity - ); - - @override - Store call(Store state) => Store(count: state.count - payload); -} - -Store reducer(Store state, RtAction action) { - if (action is RtActionCallable) return action(state); - - return UnimplementedError(); -} - -class CounterController { - final useCounter = UseReducer(reducer , Store(count: 0)); - - CounterController() { - print("count: ${useCounter.value.count}"); // count: 0; - useCounter.dispatch(IncrementAction(2)); - print("count: ${useCounter.value.count}"); // count: 2; - useCounter.dispatch(DecrementAction()); - print("count: ${useCounter.value.count}"); // count: 1; - } -} -``` - -When `value` has changed, the `UseReducer` will emit the following events (learn about it [here](#lifecycle-and-event-management)): - -- `Lifecycle.willUpdate` event is triggered before the `value` change or `update`, `refresh` methods have been invoked. -- `Lifecycle.didUpdate` event is triggered after the `value` change or `update`, `refresh` methods have been invoked. - -### UseCompute - -[`UseCompute`](https://pub.dev/documentation/reactter/latest/reactter/UseCompute-class.html) is a hook([`RtHook`](https://pub.dev/documentation/reactter/latest/reactter/RtHook-class.html)) that keeps listening for state `dependencies` changes, to return a computed value(`T`) from a defined method(`computeValue`). - -```dart -UseCompute( - T computeValue(), - List dependencies, -) -``` - -`UseCompute` accepts two arguments: - -- `computeValue`: is a method is called whenever there is a change in any of the `dependencies`, and it is responsible for calculating and setting the computed value. -- `dependencies`: is a list of states that `UseCompute` keeps an active watch on, listening for any changes that may occur for calling the `computeValue` function. - -so, here is an example: - -```dart -class MyController { - final stateA = UseState(1); - final stateB = UseState(7); - - late final computeState = Rt.lazyState( - () => UseCompute( - // The `clamp` is a method that returns this num clamped - // to be in the range lowerLimit-upperLimit(e.g., 10-15). - () => addAB().clamp(10, 15), - [stateA, stateB], - ), - ); - - int addAB() => stateA.value + stateB.value; - void printResult() => print("${addAB()} -> ${computeState.value}"); - - MyController() { - printResult(); // 8 -> 10 - stateA.value += 1; // Will not notify change - printResult(); // 9 -> 10 - stateB.value += 2; // Will notify change - printResult(); // 11 -> 11 - stateA.value += 6; // Will notify change - printResult(); // 17 -> 15 - stateB.value -= 1; // Will not notify change - printResult(); // 16 -> 15 - stateA.value -= 8; // Will notify change - printResult(); // 8 -> 10 - } -} -``` - -`UseCompute` has a `value` property which represents the computed value. - -> **NOTE:** -> `UseCompute` notifies that its `value` has changed when the previous `value` is different from the current `value`. - -When `value` has changed, the `UseState` will emit the following events (learn about it [here](#lifecycle-and-event-management)): - -- `Lifecycle.willUpdate` event is triggered before the `value` change or `update`, `refresh` methods have been invoked. -- `Lifecycle.didUpdate` event is triggered after the `value` change or `update`, `refresh` methods have been invoked. - -> **NOTE:** -> `UseCompute` is read-only, meaning that its value cannot be changed, except by invoking the `computeValue` method. - -> **RECOMENDED:** -> `UseCompute` does not cache the computed value, meaning it recalculates the value when its depenencies has changes, potentially impacting performance, especially if the computation is expensive. In these cases, you should consider using `Memo`(learn about it [here](#memo)) in the following manner: - -```dart - late final myUseComputeMemo = Rt.lazyState((){ - final addAB = Memo( - (Args2 args) => args.arg1 + args.arg2, - ); - - return UseCompute( - () => addAB( - Args2(stateA.value, stateB.value), - ), - [stateA, stateB], - ), - }, this); -``` - -## Dependency injection - -With Reactter, you can create, delete and access the desired object from a single location, and you can do it from anywhere in the code, thanks to reactter's dependency injection system. - -Dependency injection offers several benefits. It promotes the principle of inversion of control, where the control over object creation and management is delegated to Reactter. This improves code modularity, reusability, and testability. It also simplifies the code by removing the responsibility of creating dependencies from individual classes, making them more focused on their core functionality. - -Reactter has three ways to manage an instance, which are: - -- [Builder](#builder) -- [Factory](#factory) -- [Singleton](#singleton) - -Reactter offers the following several instance managers: - -- [Shorcuts to manage instances](#shortcuts-to-manage-instances) -- [UseDependency](#usedependency) - -by `flutter_reactter`: - -- [RtProvider](#rtprovider) -- [RtProviders](#rtproviders) -- [RtComponent](#rtcomponent) -- [BuildContext.use](#buildcontextuse) - -### Builder - -Builder is a ways to manage an instance, which registers a builder function and creates the instance, unless it has already done so. - -In builder mode, when the dependency tree no longer needs it, it is completely deleted, including deregistration (deleting the builder function). - -Reactter identifies the builder mode as [`DependencyMode.builder`](https://pub.dev/documentation/reactter/latest/DependencyMode/DependencyMode.builder.html) and it's using for default. - -> **NOTE:** -> **Builder** uses less RAM than [Factory](#factory) and [Singleton](#singleton), but it consumes more CPU than the other modes. - -### Factory - -Factory is a ways to manage an instance, which registers a builder function only once and creates the instance if not already done. - -In factory mode, when the dependency tree no longer needs it, the instance is deleted and the builder function is kept in the register. - -Reactter identifies the factory mode as [`DependencyMode.factory`](https://pub.dev/documentation/reactter/latest/DependencyMode/DependencyMode.factory.html) and to active it, set it in the `mode` argument of `Rt.register` and `Rt.create`, or use `Rt.lazyFactory`, `Rt.factory`. - -> **NOTE:** -> **Factory** uses more RAM than [Builder](#builder) but not more than [Singleton](#singleton), and consumes more CPU than [Singleton](#singleton) but not more than [Builder](#builder). - -### Singleton - -Singleton is a ways to manage an instance, which registers a builder function and creates the instance only once. - -The singleton mode preserves the instance and its states, even if the dependency tree stops using it. - -Reactter identifies the singleton mode as [`DependencyMode.singleton`](https://pub.dev/documentation/reactter/latest/DependencyMode/DependencyMode.singleton.html) and to active it, set it in the `mode` argument of `Rt.register` and `Rt.create`, or use `Rt.lazySingleton`, `Rt.singleton`. - -> **NOTE:** -> Use `Rt.destroy` if you want to force destroy the instance and its register. - -> **NOTE:** -> **Singleton** consumes less CPU than [Builder](#builder) and [Factory](#factory), but uses more RAM than the other modes. - -### Shortcuts to manage instances - -Reactter offers several convenient shortcuts for managing instances: - -- [`Rt.register`](https://pub.dev/documentation/reactter/latest/reactter/RtInterface/register.html): Registers a builder function, for creating a new instance using `[Rt|UseDependency].[get|create|builder|factory|singleton]`. -- [`Rt.lazyBuilder`](https://pub.dev/documentation/reactter/latest/reactter/RtInterface/lazyBuilder.html): Registers a builder function, for creating a new instance as [Builder](#builder) mode using `[Rt|UseDependency].[get|create|builder]`. -- [`Rt.lazyFactory`](https://pub.dev/documentation/reactter/latest/reactter/RtInterface/lazyFactory.html): Registers a builder function, for creating a new instance as [Factory](#factory) mode using `[Rt|UseDependency].[get|create|factory]`. -- [`Rt.lazySingleton`](https://pub.dev/documentation/reactter/latest/reactter/RtInterface/lazySingleton.html): Registers a builder function, for creating a new instance as [Singleton](#singleton) mode using `[Rt|UseDependency].[get|create|singleton]`. -- [`Rt.create`](https://pub.dev/documentation/reactter/latest/reactter/RtInterface/create.html): Registers, creates and returns the instance directly. -- [`Rt.builder`](https://pub.dev/documentation/reactter/latest/reactter/RtInterface/builder.html): Registers, creates and returns the instance as [Builder](#builder) directly. -- [`Rt.factory`](https://pub.dev/documentation/reactter/latest/reactter/RtInterface/factory.html): Registers, creates and returns the instance as [Factory](#factory) directly. -- [`Rt.singleton`](https://pub.dev/documentation/reactter/latest/reactter/RtInterface/singleton.html): Registers, creates and returns the instance as [Singleton](#singleton) directly. -- [`Rt.get`](https://pub.dev/documentation/reactter/latest/reactter/RtInterface/get.html): Returns a previously created instance or creates a new instance from the builder function registered by `[Rt|UseDependency].[register|lazyBuilder|lazyFactory|lazySingleton]`. -- [`Rt.delete`](https://pub.dev/documentation/reactter/latest/reactter/RtInterface/delete.html): Deletes the instance but keeps the builder function. -- [`Rt.unregister`](https://pub.dev/documentation/reactter/latest/reactter/RtInterface/factory.html): Removes the builder function, preventing the creation of the instance. -- [`Rt.destroy`](https://pub.dev/documentation/reactter/latest/reactter/RtInterface/destroy.html): Destroys the instance and the builder function. -- [`Rt.find`](https://pub.dev/documentation/reactter/latest/reactter/RtInterface/find.html): Gets the instance. -- [`Rt.isRegistered`](https://pub.dev/documentation/reactter/latest/reactter/RtInterface/isRegistered.html): Checks if an instance is registered in Reactter. -- [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/latest/reactter/RtInterface/getDependencyMode.html): Returns the `DependencyMode` of the instance. - -In each of the events methods shown above (except `Rt.isRegister` and `Rt.getDependencyMode`), it provides the `id` argument for managing the instances of the same type by a unique identity. - -> **NOTE:** -> The scope of the registered instances is global. -> This indicates that using the [shortcuts to manage instance](#shortcuts-to-manage-events) or [`UseDependency`](#usedependency) will allow you to access them from anywhere in the project. - -### UseDependency - -[`UseDependency`](https://pub.dev/documentation/reactter/latest/reactter/UseDependency-class.html) is a hook([`RtHook`](https://pub.dev/documentation/reactter/latest/reactter/RtHook-class.html)) that allows to manage an instance. - -```dart -UseDependency([String? id]); -``` - -The default constructor uses `Rt.find` to get the instance of the `T` type with or without `id` that is available. - -> **NOTE:** -> The instance that you need to get, must be created by [`Dependency injection`](#dependency-injection) before. - -Use `instance` getter to get the instance. - -Here is an example using `UseIntance`: - -```dart -class MyController { - final useAuthController = UseDependency(); - // final useOtherControllerWithId = UseDependency("UniqueId"); - - AuthController? authController = useAuthController.instance; - - MyController() { - UseEffect(() { - authController = useAuthController.instance; - }, [useAuthController], - ); - } -} -``` - -> **NOTE:** -> In the example above uses [`UseEffect`](#useeffect) hook, to wait for the `instance` to become available. - -`UseDependency` provides some constructors and factories for managing an instance, which are: - -- [`UseDependency.register`](https://pub.dev/documentation/reactter/latest/reactter/UseDependency/register.html): Registers a builder function, for creating a new instance using `[Rt|UseDependency].[get|create|builder|factory|singleton]`. -- [`UseDependency.lazyBuilder`](https://pub.dev/documentation/reactter/latest/reactter/UseDependency/lazyBuilder.html): Registers a builder function, for creating a new instance as [Builder](#builder) mode using `[Rt|UseDependency].[get|create|builder]`. -- [`UseDependency.lazyFactory`](https://pub.dev/documentation/reactter/latest/reactter/UseDependency/lazyFactory.html): Registers a builder function, for creating a new instance as [Factory](#factory) mode using `[Rt|UseDependency].[get|create|factory]`. -- [`UseDependency.lazySingleton`](https://pub.dev/documentation/reactter/latest/reactter/UseDependency/lazySingleton.html): Registers a builder function, for creating a new instance as [Singleton](#singleton) mode using `[Rt|UseDependency].[get|create|singleton]`. -- [`UseDependency.create`](https://pub.dev/documentation/reactter/latest/reactter/UseDependency/create.html): Registers, creates and returns the instance directly. -- [`UseDependency.builder`](https://pub.dev/documentation/reactter/latest/reactter/UseDependency/builder.html): Registers, creates and returns the instance as [Builder](#builder) directly. -- [`UseDependency.factory`](https://pub.dev/documentation/reactter/latest/reactter/UseDependency/factory.html): Registers, creates and returns the instance as [Factory](#factory) directly. -- [`UseDependency.singleton`](https://pub.dev/documentation/reactter/latest/reactter/UseDependency/singleton.html): Registers, creates and returns the instance as [Singleton](#singleton) directly. -- [`UseDependency.get`](https://pub.dev/documentation/reactter/latest/reactter/UseDependency/get.html): Returns a previously created instance or creates a new instance from the builder function registered by `[Rt|UseDependency].[register|lazyBuilder|lazyFactory|lazySingleton]`. - -In each of the contructors or factories above shown, it provides the `id` property for managing the instances of the same type by a unique identity. - -> **NOTE:** -> The scope of the registered instances is global. -> This indicates that using the [shortcuts to manage instance](#shortcuts-to-manage-events) or [`UseDependency`](#usedependency) will allow you to access them from anywhere in the project. - -## Event handler - -In Reactter, event handler plays a pivotal role in facilitating seamless communication and coordination between various components within the application. -The event handler system is designed to ensure efficient handling of states and instances, fostering a cohesive ecosystem where different parts of the application can interact harmoniously. - -One of the key aspects of event handler in Reactter is the introduction of [lifecycles](#lifecycles) linked to events. -These lifecycles define the different stages through which a state or instance passes, offering a structured flow and effective handling of changes. - -Additionally, Reactter offers the following event managers: - -- [Shortcuts to manage events](#shortcuts-to-manage-instances) -- [UseEffect](#useeffect) - -by `flutter_reactter`: - -- [RtConsumer](#rtconsumer) -- [RtSelector](#rtselector) -- [RtSignalWatcher](#rtsignalwatcher) -- [BuildContext.watch](#buildcontextwatch) -- [BuildContext.select](#buildcontextselect) - -### Lifecycles - -In Reactter, both the states ([`RtState`](#state-management)) and the instances (managed by the [`dependency injection`](#dependency-injection)) contain different stages, also known as [`Lifecycle`](https://pub.dev/documentation/reactter/latest/reactter/Lifecycle.html). -This lifecycles linked events, which are: - -- `Lifecycle.registered`: is triggered when the dependency has been registered. -- `Lifecycle.created`: is triggered when the dependency instance has been created. -- `Lifecycle.willMount` (exclusive of `flutter_reactter`): is triggered when the dependency is going to be mounted in the widget tree. -- `Lifecycle.didMount` (exclusive of `flutter_reactter`): is triggered after the dependency has been successfully mounted in the widget tree. -- `Lifecycle.willUpdate`: is triggered anytime the dependency's state is about to be updated. The event parameter is a `RtState`. -- `Lifecycle.didUpdate`: is triggered anytime the dependency's state has been updated. The event parameter is a `RtState`. -- `Lifecycle.willUnmount`(exclusive of `flutter_reactter`): is triggered when the dependency is about to be unmounted from the widget tree. -- `Lifecycle.didUnmount`(exclusive of `flutter_reactter`): is triggered when the dependency has been successfully unmounted from the widget tree. -- `Lifecycle.deleted`: is triggered when the dependency instance has been deleted. -- `Lifecycle.unregistered`: is triggered when the dependency is no longer registered. - -You can extend your instances with [`LifecycleObserver`](https://pub.dev/documentation/reactter/latest/reactter/LifecycleObserver-class.html) mixin for observing and reacting to the various lifecycle events. e.g: - -```dart -class MyController with LifecycleObserver { - final state = UseState('initial'); - - @override - void onInitialized() { - print("MyController has been initialized"); - } - - @override - void onDidUpdate(RtState? state) { - print("$state has been changed"); - } -} - -final myController = Rt.create(() => MyController()); -// MyController has been initialized -myController.state.value = "value changed"; -// state has been changed -``` - -### Shortcuts to manage events - -Reactter offers several convenient shortcuts for managing events: - -- [`Rt.on`](https://pub.dev/documentation/reactter/latest/reactter/RtEventHandler/on.html): turns on the listen event. When the `event` of `instance` is emitted, the `callback` is called: - - ```dart - Rt.on(Object instance, Enum event, callback(T inst, P params)); - ``` - -- [`Rt.one`](https://pub.dev/documentation/reactter/latest/reactter/RtEventHandler/one.html): turns on the listen event for only once. When the `event` of `instance` is emitted, the `callback` is called and then removed. - - ```dart - Rt.one(Object instance, Enum event, callback(T inst, P param)); - ``` - -- [`Rt.off`](https://pub.dev/documentation/reactter/latest/reactter/RtEventHandler/off.html): removes the `callback` from `event` of `instance`. +- 🔬 Fully **debuggable** using the **[Reactter DevTools extension](https://2devs-team.github.io/reactter/devtools_extension)** +- 🪄 **Zero Dependencies**, **Zero Configuration** and **No Code Generation**. - ```dart - Rt.off(Object instance, Enum event, callback(T instance, P param)); - ``` - -- [`Rt.offAll`](https://pub.dev/documentation/reactter/latest/reactter/RtEventHandler/offAll.html): removes all events of `instance`. - - ```dart - Rt.offAll(Object instance); - ``` - - > **IMPORTANT**: - > Don't use it, if you're not sure. Because it will remove all events, even those events that Reactter needs to work properly. Instead, use `Rt.off` to remove the specific events. - -- [`Rt.emit`](https://pub.dev/documentation/reactter/latest/reactter/RtEventHandler/emit.html): triggers an `event` of `instance` with or without the `param` given. - - ```dart - Rt.emit(Object instance, Enum event, [dynamic param]); - ``` - -In each of the methods it receives as first parameter an `instance` that can be directly the instance object or use `RtInstance` instead: - -```dart -void onDidUpdate(inst, state) => print("Instance: $inst, state: $state"); - -final myController = Rt.get(); -// or using `RtDependency` -final myController = RtDependency(); - -Rt.on(myController, Lifecycle.didUpdate, onDidUpdate); -Rt.emit(myController, Lifecycle.didUpdate, 'test param'); -``` - -> **RECOMMENDED:** -> Use the instance object directly on event methods for optimal performance. - -> **NOTE:** -> The `RtDependency` helps to find the instance for event, if the instance not exists, put it on wait. It's a good option if you're not sure that the instance has been created yet. - -### UseEffect - -[`UseEffect`](https://pub.dev/documentation/reactter/latest/reactter/UseEffect-class.html) is a hook([`RtHook`](https://pub.dev/documentation/reactter/latest/reactter/RtHook-class.html)) that allows to manage side-effect. - -```dart -UseEffect( - Function() callback, - List dependencies, -) -``` - -The side-effect logic into the `callback` function is executed when the `dependencies` argument changes or the `instance` trigger `Lifecycle.didMount` event. - -If the `callback` returns a function, then `UseEffect` considers this as an effect `cleanup`. -The `cleanup` callback is executed, before `callback` is called or `instance` trigger `Lifecycle.willUnmount` event: - -Let's see an example with a counter that increments every second: - -```dart -class MyController { - final count = UseState(0); - - MyController() { - UseEffect((){ - // Execute by count state changed or 'Lifecycle.didMount' event - print("Count: ${count.value}"); - Future.delayed(const Duration(seconds: 1), () => count.value += 1); - - return () { - // Cleanup - Execute before count state changed or 'Lifecycle.willUnmount' event - print("Cleanup executed"); - }; - }, [count]); - } -} -``` - -Use `UseEffect.runOnInit` to execute the callback effect on initialization. - -```dart -UseEffect.runOnInit( - () => print("Excute immediately and by hook changes"), - [someState], -); -``` - -## Rendering control - -Rendering control provides the capability to observe specific instances or states, triggering re-renders of the widget tree as required. This methodology ensures a unified and responsive user interface, facilitating efficient updates based on changes in the application's state. - -In this context, the [`flutter_reactter`](https://pub.dev/packages/flutter_reactter) package provides the following purpose-built widgets and certain `BuildContext` extension for rendering control: - -- [RtProvider](#rtprovider) -- [RtProviders](#rtproviders) -- [RtComponent](#rtcomponent) -- [RtConsumer](#rtconsumer) -- [RtSignalWatcher](#rtsignalwatcher) -- [RtSelector](#rtselector) -- [BuildContext.use](#buildcontextuse) -- [BuildContext.watch](#buildcontextwatch) -- [BuildContext.select](#buildcontextselect) - -### RtProvider - -[`RtProvider`](https://pub.dev/documentation/flutter_reactter/latest/flutter_reactter/RtProvider-class.html) is a Widget (exclusive of `flutter_reactter`) that hydrates from an instance of `T` type to the Widget tree. - -```dart -RtProvider( - T instanceBuilder(), { - String? id, - DependencyMode type = DependencyMode.builder, - Widget? child, - required Widget builder(BuilderContext context, T instance, Widget? child), -}) -``` - -`RtProvider` accepts these properties: - -- `instanceBuilder`: to define a method for the creation of a new instance of `T` type. - - > **RECOMMENDED:** - > Don't use Object with constructor parameters to prevent conflicts. - -- `id`: to uniquely identify the instance. -- `mode`: to determine the instance manage mode([Builder](#builder), [Factory](#factory) or [Singleton](#singleton)). -- `child`: to pass a `Widget` through the `builder` method that it will be built only once. -- `builder`: to define a method that contains the builder logic of the widget that will be embedded in the widget tree. This method exposes the `instance`(`T`) created, a new `context`(`BuildContext`) and a `child`(`Widget`) defined in the `child` property. - -Here is an example: - -```dart -RtProvider( - () => CounterController(), - child: const Text('This widget is rendered once'), - builder: (context, counterController, child) { - // `context.watch` listens any CounterController changes for rebuild this widget tree. - context.watch(); - - // Change the `value` each 1 second. - Future.delayed(Duration(seconds: 1), (_) => counterController.count.value++); - - return Column( - children: [ - child!, // The child widget has already been built in `child` property. - Text("count: ${counterController.count.value}"), - ], - ); - }, -) -``` - -Use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/latest/flutter_reactter/RtProvider/RtProvider.init.html) to initialize the dependency instance before that it's mounted. - -Use [`RtProvider.lazy`](https://pub.dev/documentation/flutter_reactter/latest/flutter_reactter/RtProvider/RtProvider.lazy.html) to enable lazy-loading of the instance, ensuring it is only instantiated when necessary. While this feature enhances performance by deferring instantiation until required, it's important to note that it may result in the loss of lifecycle tracing. - -> **NOTE:** -> `RtProvider` is "scoped". So, the `builder` method will be rebuild when the instance or any `RtState` specified in [`BuildContext.watch`](#buildcontextwatch) or [`BuildContext.select`](#buildcontextselect) changes. - -### RtMultiProvider - -[`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/latest/flutter_reactter/RtMultiProvider-class.html) is a Widget (exclusive of `flutter_reactter`) that allows to use multiple [`RtProvider`](#rtprovider) in a nested way. - -```dart -RtMultiProvider( - [ - RtProvider( - () => MyController(), - ), - RtProvider( - () => ConfigController(), - id: 'App', - ), - RtProvider( - () => ConfigController(), - id: 'Dashboard' - ), - ], - builder: (context, child) { - final myController = context.use(); - final appConfigController = context.use('App'); - final dashboardConfigController = context.use('Dashboard'); - ... - }, -) -``` - -> **RECOMMENDED:** -> Don't use Object with constructor parameters to prevent conflicts. - -> **NOTE:** -> `RtProvider` is "scoped". So, the `builder` method will be rebuild when the instance or any `RtState` specified in [`BuildContext.watch`](#buildcontextwatch) or [`BuildContext.select`](#buildcontextselect) changes. - -### RtComponent - -[`RtComponent`](https://pub.dev/documentation/flutter_reactter/latest/flutter_reactter/RtComponent-class.html) is a abstract `StatelessWidget` (exclusive of `flutter_reactter`) that provides [`RtProvider`](#rtprovider) features, whose instance of `T` type is exposed trough `render` method. - -```dart -class CounterComponent extends RtComponent { - const CounterComponent({Key? key}) : super(key: key); - - @override - get builder => () => CounterController(); - - @override - void listenStates(counterController) => [counterController.count]; - - @override - Widget render(context, counterController) { - return Text("Count: ${counterController.count.value}"); - } -} -``` - -Use `builder` getter to define the instance builder function. - -> **RECOMMENDED:** -> Don't use Object with constructor parameters to prevent conflicts. - -> **NOTE:** -> If you don't use `builder` getter, the instance will not be created. Instead, an attempt will be made to locate it within the closest ancestor where it was initially created. - -Use the `id` getter to identify the instance of `T`: - -Use the `listenStates` getter to define the states that will rebuild the tree of the widget defined in the `render` method whenever it changes. - -Use the `listenAll` getter as `true` to listen to all the instance changes to rebuild the Widget tree defined in the `render` method. - -### RtConsumer - -[`RtConsumer`](https://pub.dev/documentation/flutter_reactter/latest/flutter_reactter/RtConsumer-class.html) is a Widget (exclusive of `flutter_reactter`) that allows to access the instance of `T` type from `RtProvider`'s nearest ancestor and can listen all or specified states to rebuild the Widget when these changes occur: - -```dart -RtConsumer({ - String? id, - bool listenAll = false, - List listenStates(T instance)?, - Widget? child, - required Widget builder(BuildContext context, T instance, Widget? child), -}); -``` - -`RtConsumer` accepts these properties: - -- `id`: to uniquely identify the instance. -- `listenAll`: to listen to all events emitted by the instance or its states(`RtState`). -- `listenStates`: to listen to states(`RtState`) defined in it. -- `child`: to pass a `Widget` through the `builder` method that it will be built only once. -- `builder`: to define a method that contains the builder logic of the widget that will be embedded in the widget tree. This method exposes the `instance`(`T`) created, a new `context`(`BuildContext`) and a `child`(`Widget`) defined in the `child` property. - -Here is an example: - -```dart -class ExampleWidget extends StatelessWidget { - ... - Widget build(context) { - return RtConsumer( - listenStates: (inst) => [inst.stateA, inst.stateB], - child: const Text('This widget is rendered once'), - builder: (context, myController, child) { - // This is built when stateA or stateB has changed. - return Column( - children: [ - Text("My instance: $d"), - Text("StateA: ${d.stateA.value}"), - Text("StateB: ${d.stateB.value}"), - child!, // The child widget has already been built in `child` property. - ], - ); - } - ); - } -} -``` - -> **NOTE:** -> `ReactteConsumer` is "scoped". So, the `builder` method will be rebuild when the instance or any `RtState` specified get change. - -> **NOTE:** -> Use [`RtSelector`](#rtselector) for more specific conditional state when you want the widget tree to be re-rendered. - -### RtSelector - -[`RtSelector`](https://pub.dev/documentation/flutter_reactter/latest/flutter_reactter/RtSelector-class.html) is a Widget (exclusive of `flutter_reactter`) that allows to control the rebuilding of widget tree by selecting the states([`RtState`](https://pub.dev/documentation/reactter/latest/reactter/RtState-class.html)) and a computed value. - -```dart -RtSelector( - V selector( - T inst, - RtState $(RtState state), - ), - String? id, - Widget? child, - Widget builder( - BuildContext context, - T inst, - V value, - Widget? child - ), -) -``` - -`RtSelector` accepts four properties: - -- `selector`: to define a method that contains the computed value logic and determined when to be rebuilding the widget tree which defined in `build` property. It returns a value of `V` type and exposes the following arguments: - - `inst`: the found instance of `T` type and by `id` if specified it. - - `$`: a method that allows to wrap to the state(`RtState`) to put it in listening. -- `id`: to uniquely identify the instance. -- `child`: to pass a `Widget` through the `builder` method that it will be built only once. -- `builder`: to define a method that contains the builder logic of the widget that will be embedded in the widget tree. It exposes the following arguments: - - `context`: a new `BuilContext`. - - `inst`: the found instance of `T` type and by `id` if specified it. - - `value`: the computed value of `V` type. It is computed by `selector` method. - - `child`: a `Widget` defined in the `child` property. - -`RtSelector` determines if the widget tree of `builder` needs to be rebuild again by comparing the previous and new result of `selector`. -This evaluation only occurs if one of the selected states(`RtState`) gets updated, or by the instance if the `selector` does not have any selected states(`RtState`). e.g.: - -```dart -class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - - @override - Widget? build(BuildContext context) { - return RtProvider( - () => MyController(), - builder: (context, inst, child) { - return OtherWidget(); - } - ); - } -} - -class OtherWidget extends StatelessWidget { - const OtherWidget({Key? key}) : super(key: key); - - @override - Widget? build(BuildContext context) { - return RtSelector( - selector: (inst, $) => $(inst.stateA).value % $(inst.stateB).value, - builder: (context, inst, value, child) { - // This is rebuilt every time that the result of selector is different to previous result. - return Text("${inst.stateA.value} mod ${inst.stateB.value}: ${value}"); - }, - ); - } -} -``` - -`RtSelector` typing can be ommited, but the app must be wrapper by `RtScope`. e.g.: - -```dart -[...] -RtScope( - child: MyApp(), -) -[...] - -class OtherWidget extends StatelessWidget { - const OtherWidget({Key? key}) : super(key: key); - - @override - Widget? build(BuildContext context) { - final myController = context.use(); - - return RtSelector( - selector: (_, $) => $(myController.stateA).value % $(myController.stateB).value, - builder: (context, _, value, child) { - // This is rebuilt every time that the result of selector is different to previous result. - return Text("${myController.stateA.value} mod ${myController.stateB.value}: ${value}"); - }, - ); -} -``` - -### RtSignalWatcher - -[`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/latest/flutter_reactter/RtSignalWatcher-class.html) is a Widget (exclusive of `flutter_reactter`) that allows to listen all `Signal`s contained in `builder` property and rebuilt the Widget when it changes: - -```dart -RtSignalWatcher({ - Widget? child, - required Widget builder(BuildContext context, Widget? child), -}) -``` - -`RtSignalWatcher` accepts two properties: - -- `child`: to pass a `Widget` through the `builder` method that it will be built only once. -- `builder`: to define a method that contains the builder logic of the widget that will be embedded in the widget tree. It exposes the following arguments: - - `context`: a new `BuilContext`. - - `child`: a `Widget` defined in the `child` property. - -```dart -final count = Signal(0); -final flag = Signal(false); - -void increase() => count.value += 1; -void toggle() => flag(!flag.value); - -class App extends StatelessWidget { - ... - Widget build(context) { - return RtSignalWatcher( - // This widget is rendered once only and passed through the `builder` method. - child: Row( - children: const [ - ElevatedButton( - onPressed: increase, - child: Text("Increase"), - ), - ElevatedButton( - onPressed: toggle, - child: Text("Toogle"), - ), - ], - ), - builder: (context, child) { - // Rebuilds the Widget tree returned when `count` or `flag` are updated. - return Column( - children: [ - Text("Count: $count"), - Text("Flag is: $flag"), - child!, // Takes the Widget from the `child` property in each rebuild. - ], - ); - }, - ); - } -} -``` - -### BuildContext.use - -[`BuildContext.use`](https://pub.dev/documentation/flutter_reactter/latest/flutter_reactter/RtBuildContextExtension/use.html) is an extension method of the `BuildContext`, that allows to access to instance of `T` type from the closest ancestor [`RtProvider`](https://pub.dev/documentation/flutter_reactter/latest/flutter_reactter/RtProvider-class.html). - -```dart -T context.use([String? id]) -``` - -Here is an example: - -```dart -class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - - @override - Widget? build(BuildContext context) { - return RtProvider( - () => MyController(), - builder: (context, inst, child) { - return OtherWidget(); - } - ); - } -} - -class OtherWidget extends StatelessWidget { - const OtherWidget({Key? key}) : super(key: key); - - @override - Widget? build(BuildContext context) { - final myController = context.use(); - - return Text("value: ${myController.stateA.value}"); - } -} -``` - -Use the first argument for obtaining the instance by `id`. e.g.: - -```dart - final myControllerById = context.use('uniqueId'); -``` - -Use the nullable type to safely get the instance, avoiding exceptions if the instance is not found, and get `null` instead. e.g.: - -```dart - final myController = context.use(); -``` - ->**NOTE:** -> If `T` is non-nullable and the instance is not found, it will throw `ProviderNullException`. - -### BuildContext.watch - -[`BuildContext.watch`](https://pub.dev/documentation/flutter_reactter/latest/flutter_reactter/RtBuildContextExtension/watch.html) is an extension method of the `BuildContext`, that allows to access to instance of `T` type from the closest ancestor [`RtProvider`](https://pub.dev/documentation/flutter_reactter/latest/flutter_reactter/RtProvider-class.html), and listen to the instance or [`RtState`](https://pub.dev/documentation/reactter/latest/reactter/RtState-class.html) list for rebuilding the widget tree in the scope of `BuildContext`. - -```dart -T context.watch( - List listenStates(T inst)?, -) -``` - -Here is an example, that shows how to listen an instance and react for rebuild: - -```dart -class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - - @override - Widget? build(BuildContext context) { - return RtProvider( - () => MyController(), - builder: (context, inst, child) { - return OtherWidget(); - } - ); - } -} - -class OtherWidget extends StatelessWidget { - const OtherWidget({Key? key}) : super(key: key); - - @override - Widget? build(BuildContext context) { - final myController = context.watch(); - // This is rebuilt every time any states in the instance are updated - return Text("value: ${myController.stateA.value}"); - } -} -``` - -Use the first argument(`listenStates`) to specify the states that are to be listen on for rebuild. e.g.: - -```dart -[...] - @override - Widget? build(BuildContext context) { - final myController = context.watch( - (inst) => [inst.stateA, inst.stateB], - ); - // This is rebuilt every time any defined states are updated - return Text("value: ${myController.stateA.value}"); - } -[...] -``` - -Use [`BuildContext.watchId`](https://pub.dev/documentation/flutter_reactter/latest/flutter_reactter/RtBuildContextExtension/watchId.html) for obtaining the instance of `T` type by `id`, and listens the instance or [`RtState`](https://pub.dev/documentation/reactter/latest/reactter/RtState-class.html) list for rebuilding the widget tree in the scope of `BuildContext`. - -```dart -T context.watchId( - String id, - List listenStates(T inst)?, -) -``` - -It is used as follows: - -```dart -// for listening the instance -final myControllerById = context.watchId('uniqueId'); -// for listening the states -final myControllerById = context.watchId( - 'uniqueId', - (inst) => [inst.stateA, inst.stateB], -); -``` - -### BuildContext.select - -[`BuildContext.select`](https://pub.dev/documentation/flutter_reactter/latest/flutter_reactter/RtBuildContextExtension/select.html) is an extension method of the `BuildContext`, that allows to control the rebuilding of widget tree by selecting the states([`RtState`](https://pub.dev/documentation/reactter/latest/reactter/RtState-class.html)) and a computed value. - -```dart -V context.select( - V selector( - T inst, - RtState $(RtState state), - ), - [String? id], -) -``` - -`BuildContext.select` accepts two argtuments: - -- `selector`: to define a method that computed value logic and determined when to be rebuilding the widget tree of the `BuildContext`. It returns a value of `V` type and exposes the following arguments: - - `inst`: the found instance of `T` type and by `id` if specified it. - - `$`: a method that allows to wrap to the state(`RtState`) to put it in listening. -- `id`: to uniquely identify the instance. - -`BuildContext.select` determines if the widget tree in scope of `BuildContext` needs to be rebuild again by comparing the previous and new result of `selector`. -This evaluation only occurs if one of the selected states(`RtState`) gets updated, or by the instance if the `selector` does not have any selected states(`RtState`). e.g.: - -```dart -class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - - @override - Widget? build(BuildContext context) { - return RtProvider( - () => MyController(), - builder: (context, inst, child) { - return OtherWidget(); - } - ); - } -} - -class OtherWidget extends StatelessWidget { - const OtherWidget({Key? key}) : super(key: key); - - @override - Widget? build(BuildContext context) { - final value = context.select( - (inst, $) => $(inst.stateA).value % $(inst.stateB).value, - ); - // This is rebuilt every time that the result of selector is different to previous result. - return Text("stateA mod stateB: ${value}"); - } -} -``` - -`BuildContext.select` typing can be ommited, but the app must be wrapper by `RtScope`. e.g.: - -```dart -[...] -RtScope( - child: MyApp(), -) -[...] - -class OtherWidget extends StatelessWidget { - const OtherWidget({Key? key}) : super(key: key); - - @override - Widget? build(BuildContext context) { - final myController = context.use(); - final value = context.select( - (_, $) => $(myController.stateA).value % $(myController.stateB).value, - ); - // This is rebuilt every time that the result of selector is different to previous result. - return Text("stateA mod stateB: ${value}"); - } -} -``` - -## Custom hooks - -Custom hooks are classes that extend [`RtHook`](https://pub.dev/documentation/reactter/latest/reactter/RtHook-class.html) that follow a special naming convention with the `use` prefix and can contain state logic, effects or any other custom code. - -There are several advantages to using Custom Hooks: - -- **Reusability**: to use the same hook again and again, without the need to write it twice. -- **Clean Code**: extracting part of code into a hook will provide a cleaner codebase. -- **Maintainability**: easier to maintain. if you need to change the logic of the hook, you only need to change it once. - -Here's the counter example: - -```dart -class UseCount extends RtHook { - final $ = RtHook.$register; - - int _count = 0; - int get value => _count; - - UseCount(int initial) : _count = initial; - - void increment() => update(() => _count += 1); - void decrement() => update(() => _count -= 1); -} -``` - -> **IMPORTANT:** -> To create a `RtHook`, you need to register it by adding the following line: -> `final $ = RtHook.$register;` - -> **NOTE:** -> `RtHook` provides an `update` method which notifies about its changes. - -You can then call that custom hook from anywhere in the code and get access to its shared logic: - -```dart -class MyController { - final count = UseCount(0); - - MyController() { - Timer.periodic(Duration(seconds: 1), (_) => count.increment()); - - // Print count value every second - Rt.on( - count, - Lifecycle.didUpdate, - (_, __) => print("Count: ${count.value}", - ); - } -} -``` - -## Lazy state - -A lazy state is a `RtState`([`Signal`](#signal) or [`RtHook`](https://pub.dev/documentation/reactter/latest/reactter/RtHook-class.html)) that is loaded lazily using `Rt.lazyState`. - -```dart -T Rt.lazyState(T stateFn(), Object instance); -``` - -`Rt.lazyState` is generally used in states declared with the `late` keyword. -> In dart, `late` keyword is used to declare a variable or field that will be initialized at a later time. It is used to declare a non-nullable variable that is not initialized at the time of declaration. - -For example, when the a state declared in a class requires some variable or methods immediately: - -```dart -class MyController { - final String initialValue = 'test'; - dynamic resolveValue() async => [...]; - - /// late final state = UseAsyncState( - /// initialValue, - /// resolveValue - /// ); <- to use `Rt.lazyState` is required, like: - - late final state = Rt.lazyState( - () => UseAsyncState(initialValue, resolveValue), - this, - ); - - ... -} -``` - -> **IMPORTANT**: -> A state(`RtState`) declared with the `late` keyword and not using `Rt.lazyState` is outside the context of the instance where it was declared, and therefore the instance does not notice about its changes. - -## Batch - -```dart -T Rt.batch(T Function() callback) -``` - -The [`batch`](https://pub.dev/documentation/reactter/latest/reactter/RtInterface/batch.html) function allows you to combine multiple state changes to be grouped together, ensuring that any associated side effects are only triggered once, improving performance and reducing unnecessary re-renders. e.g.: - -```dart -final stateA = UseState(0); -final stateB = UseState(0); -final computed = UseCompute( - () => stateA.value + stateB.value, - [stateA, stateB], -); - -final batchReturned = Rt.batch(() { - stateA.value = 1; - stateB.value = 2; - - print(computed.value); // 0 -> because the batch operation is not completed yet. - - return stateA.value + stateB.value; -}); - -print(batchReturned); // 3 -print(computed.value); // 3 -> because the batch operation is completed. -``` - -Batches can be nested and updates will be flushed when the outermost batch call completes. e.g.: - -```dart -final stateA = UseState(0); -final stateB = UseState(0); -final computed = UseCompute( - () => stateA.value + stateB.value, - [stateA, stateB], -); - -Rt.batch(() { - stateA.value = 1; - print(computed.value); // 0; - - Rt.batch(() { - stateB.value = 2; - print(computed.value); // 0; - }); - - print(computed.value); // 0; -}); - -print(computed.value); // 3; -``` - -## Untracked - -```dart -T Rt.untracked(T Function() callback) -``` - -The [`untracked`](https://pub.dev/documentation/reactter/latest/reactter/RtInterface/untracked.html) function helps you to execute the given `callback` function without tracking any state changes. This means that any state changes that occur inside the `callback` function will not trigger any side effects. e.g.: - -```dart -final state = UseState(0); -final computed = UseCompute(() => state.value + 1, [state]); - -Rt.untracked(() { - state.value = 2; - - print(computed.value); // 1 -> because the state change is not tracked -}); - -print(computed.value); // 1 -> because the state change is not tracked -``` - -## Generic arguments - -Generic arguments are objects of the `Args` class that represent the arguments of the specified types. -It is used to define the arguments that are passed through a `Function` and allows to type the `Function` appropriately. - -> **RECOMMENDED**: -> If your project supports [`Record`](https://dart.dev/language/records#record-types), it is recommended to use it instead of the generic arguments. - -Reactter provides these generic arguments classes: - -- [`Args`](https://pub.dev/documentation/reactter/latest/reactter/Args-class.html): represents one or more arguments of `A` type. -- [`Args1`](https://pub.dev/documentation/reactter/latest/reactter/Args1-class.html) : represents a argument of `A` type. -- [`Args2`](https://pub.dev/documentation/reactter/latest/reactter/Args2-class.html): represents two arguments of `A`, `A2` type consecutively. -- [`Args3`](https://pub.dev/documentation/reactter/latest/reactter/Args3-class.html): represents three arguments of `A`, `A2`, `A3` type consecutively. -- [`ArgsX2`](https://pub.dev/documentation/reactter/latest/reactter/ArgsX2.html): represents two arguments of `A` type. -- [`ArgsX3`](https://pub.dev/documentation/reactter/latest/reactter/ArgsX3.html): represents three arguments of `A` type. - -In each of the methods it provides these methods and properties: - -- `arguments`: gets the list of arguments. -- `toList()`: gets the list of arguments `T` type. -- `arg1`: gets the first argument. -- `arg2`(`Args2`, `Args3`, `ArgsX2`, `ArgsX3` only): gets the second argument. -- `arg3`(`Args3`, `ArgsX3` only): gets the third argument. - -> **NOTE:** -> If you need a generic argument class with more arguments, then create a new class following pattern: -> -> ```dart -> class Args+n extends Args+(n-1) { -> final A+n arg+n; -> -> const Args+n(A arg1, (...), A+(n-1) arg+(n-1), this.arg+n) : super(arg1, (...), arg+(n-1)); -> -> @override -> List get arguments => [...super.arguments, arg+n]; -> } -> -> typedef ArgX+n = Args+n; -> ``` -> -> e.g. 4 arguments: -> -> ```dart -> class Args4 extends Args3 { -> final A4 arg4; -> -> const Args4(A arg1, A2 arg2, A3 arg3, this.arg4) : super(arg1, arg2, arg3); -> -> @override -> List get arguments => [...super.arguments, arg4]; -> } -> -> typedef ArgX4 = Args4; -> ``` - -> **NOTE:** -> Use `ary` Function extention to convert any `Function` with positional arguments to `Function` with generic argument, e.g.: -> -> ```dart -> int addNum(int num1, int num2) => num1 + num2; -> // convert to `int Function(Args2(int, int))` -> final addNumAry = myFunction.ary; -> addNumAry(Arg2(1, 1)); -> // or call directly -> addNum.ary(ArgX2(2, 2)); -> ``` - -## Memo - -[`Memo`](https://pub.dev/documentation/reactter/latest/reactter/Memo-class.html) is a class callable with memoization logic which it stores computation results in cache, and retrieve that same information from the cache the next time it's needed instead of computing it again. - -> **NOTE:** -> Memoization is a powerful trick that can help speed up our code, especially when dealing with repetitive and heavy computing functions. - -```dart -Memo( - T computeValue(A arg), [ - MemoInterceptor? interceptor, -]); -``` - -`Memo` accepts these properties: - -- `computeValue`: represents a method that takes an argument of type `A` and returns a value of `T` type. This is the core function that will be memoized. -- `interceptor`: receives a [`MemoInterceptor`](https://pub.dev/documentation/reactter/latest/reactter/MemoInterceptor-class.html) that allows you to intercept the memoization function calls and modify the memoization process. - Reactter providers some interceptors: - - [`MemoMultiInterceptor`](https://pub.dev/documentation/reactter/latest/reactter/MemoMultiInterceptor-class.html): allows multiple memoization interceptors to be used together. - - [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/latest/reactter/MemoWrapperInterceptor-class.html): a wrapper for a memoized function that allows you to define callbacks for initialization, successful completion, error handling, and finishing. - - [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/latest/reactter/MemoSafeAsyncInterceptor-class.html): prevents saving in cache if the `Future` calculation function throws an error during execution. - - [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/latest/reactter/MemoTemporaryCacheInterceptor-class.html): removes memoized values from the cache after a specified duration. - -Here an factorial example using `Memo`: - -```dart -late final factorialMemo = Memo(calculateFactorial); - -/// A factorial(n!) represents the multiplication of all numbers between 1 and n. -/// So if you were to have 3!, for example, you'd compute 3 x 2 x 1 (which = 6). -BigInt calculateFactorial(int number) { - if (number == 0) return BigInt.one; - return BigInt.from(number) * factorialMemo(number - 1); -} - -void main() { - // Returns the result of multiplication of 1 to 50. - final f50 = factorialMemo(50); - // Returns the result immediately from cache - // because it was resolved in the previous line. - final f10 = factorialMemo(10); - // Returns the result of the multiplication of 51 to 100 - // and 50! which is obtained from the cache(as computed previously by f50). - final f100 = factorialMemo(100); - - print( - 'Results:\n' - '\t10!: $f10\n' - '\t50!: $f50\n' - '\t100!: $f100\n' - ); -} -``` - -> **NOTE**: -> The `computeValue` of `Memo` accepts one argument only. If you want to add more arguments, you can supply it using the `Record`(`if your proyect support`) or `generic arguments`(learn more [here](#generic-arguments)). - -> **NOTE:** -> Use [`Memo.inline`](https://pub.dev/documentation/reactter/latest/reactter/Memo/inline.html) in case there is a typing conflict, e.g. with the `UseAsynState` and `UseCompute` hooks which a `Function` type is required. - -`Memo` provides the following methods that will help you manipulate the cache as you wish: - -- `T? get(A arg)`: returns the cached value by `arg`. -- `T? remove(A arg)`: removes the cached value by `arg`. -- `clear`: removes all cached data. - -## Difference between Signal and UseState - -Both `UseState` and `Signal` represent a state (`RtState`). However, it possesses distinct features that set them apart. - -`UseState` is a `RtHook`, giving it the unique ability to be extended and enriched with new capabilities, which sets it apart from `Signal`. - -In the case of `UseState`, it necessitates the use of the `value` attribute whenever state is read or modified. On the other hand, `Signal` streamlines this process, eliminating the need for explicit `value` handling, thus enhancing code clarity and ease of understanding. - -In the context of Flutter, when implementing `UseState`, it is necessary to expose the parent class containing the state to the widget tree via a `RtProvider` or `RtComponent`, and subsequently access it through `BuildContext`. Conversely, with `Signal`, which is inherently reactive, you can conveniently employ `RtSignalWatcher`. - -It's important to note that while `Signal` offers distinct advantages, particularly for managing global states and enhancing code readability, it can introduce potential antipatterns and may complicate the debugging process. Nevertheless, these concerns are actively being addressed and improved in upcoming versions of the package. +- 💙 **Compatible with Dart and Flutter**, supports the latest version of Dart. -Ultimately, the choice between `UseState` and `Signal` lies in your hands. They can coexist seamlessly, and you have the flexibility to transition from `UseState` to `Signal`, or vice versa, as your project's requirements evolve. +**To start using Reactter, check out the full documentation on [The Official Documentation](https://2devs-team.github.io/reactter).** ## Resources - [Website Official](https://2devs-team.github.io/reactter) - [Github](https://github.com/2devs-team/reactter) - [Examples](https://github.com/2devs-team/reactter/tree/master/packages/flutter_reactter/example) -- [Examples in zapp](https://zapp.run/pub/flutter_reactter) +- [Examples in Zapp](https://zapp.run/pub/flutter_reactter) - [Reactter documentation](https://pub.dev/documentation/reactter/latest) - [Flutter Reactter documentation](https://pub.dev/documentation/flutter_reactter/latest) - [Reactter Lint](https://pub.dev/packages/reactter_lint) - [Reactter Snippets](https://marketplace.visualstudio.com/items?itemName=CarLeonDev.reacttersnippets) +## DevTools + +![Reactter DevTools](https://raw.githubusercontent.com/2devs-team/reactter_assets/refs/heads/main/devtools.png) + +You can debug your app using the **[Reactter DevTools extension](https://2devs-team.github.io/reactter/devtools_extension)**. + ## Contribute If you want to contribute don't hesitate to create an [issue](https://github.com/2devs-team/reactter/issues/new) or [pull-request](https://github.com/2devs-team/reactter/pulls) in [Reactter repository](https://github.com/2devs-team/reactter). @@ -1814,7 +64,6 @@ You can: - Add a new widget. - Add examples. - Translate documentation. -- Write articles or make videos teaching how to use [Reactter](https://github.com/2devs-team/reactter). Any idean is welcome! From 4a76157addd3790ade5618b9603620044b2f89f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Tue, 31 Dec 2024 00:47:57 -0600 Subject: [PATCH 075/141] fix(core): Update type handling and improve null safety. --- .../lib/src/core/dependency_injection.dart | 5 ++- .../reactter/lib/src/core/event_handler.dart | 4 +- packages/reactter/pubspec.lock | 44 +++++++++---------- packages/reactter/pubspec.yaml | 4 +- 4 files changed, 30 insertions(+), 27 deletions(-) diff --git a/packages/reactter/lib/src/core/dependency_injection.dart b/packages/reactter/lib/src/core/dependency_injection.dart index f4044fa5..baf43f2d 100644 --- a/packages/reactter/lib/src/core/dependency_injection.dart +++ b/packages/reactter/lib/src/core/dependency_injection.dart @@ -541,9 +541,10 @@ abstract class DependencyInjection implements IContext { /// Returns the [DependencyRegister] of [T] type with a given [dependencyRef]. DependencyRegister? getDependencyRegisterByRef( - DependencyRef? dependencyRef, + DependencyRef? dependencyRef, ) { - return _dependencyRegisters.lookup(dependencyRef) as DependencyRegister?; + return _dependencyRegisters.lookup(dependencyRef as Object) + as DependencyRegister?; } void _notifyDependencyFailed( diff --git a/packages/reactter/lib/src/core/event_handler.dart b/packages/reactter/lib/src/core/event_handler.dart index 97e0921e..b300fa27 100644 --- a/packages/reactter/lib/src/core/event_handler.dart +++ b/packages/reactter/lib/src/core/event_handler.dart @@ -87,7 +87,9 @@ abstract class EventHandler implements IContext { for (final notifier in notifiers.toList(growable: false)) { if (boundInstance != null && - notifier.isInstanceOrDependencyRef(boundInstance)) continue; + notifier.isInstanceOrDependencyRef(boundInstance)) { + continue; + } notifier.dispose(); _notifiers.remove(notifier); diff --git a/packages/reactter/pubspec.lock b/packages/reactter/pubspec.lock index c465e635..95d1d890 100644 --- a/packages/reactter/pubspec.lock +++ b/packages/reactter/pubspec.lock @@ -61,10 +61,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" convert: dependency: transitive description: @@ -167,18 +167,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" url: "https://pub.dev" source: hosted - version: "10.0.5" + version: "10.0.7" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.8" leak_tracker_testing: dependency: transitive description: @@ -191,10 +191,10 @@ packages: dependency: "direct dev" description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "5.1.1" logging: dependency: transitive description: @@ -311,7 +311,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_map_stack_trace: dependency: transitive description: @@ -340,10 +340,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.0" stream_channel: dependency: transitive description: @@ -356,10 +356,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" term_glyph: dependency: transitive description: @@ -372,26 +372,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e" + sha256: "713a8789d62f3233c46b4a90b174737b2c04cb6ae4500f2aa8b1be8f03f5e67f" url: "https://pub.dev" source: hosted - version: "1.25.7" + version: "1.25.8" test_api: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.7.3" test_core: dependency: transitive description: name: test_core - sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696" + sha256: "12391302411737c176b0b5d6491f466b0dd56d4763e347b6714efbaa74d7953d" url: "https://pub.dev" source: hosted - version: "0.6.4" + version: "0.6.5" typed_data: dependency: transitive description: @@ -412,10 +412,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "14.3.0" watcher: dependency: transitive description: @@ -449,5 +449,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.3.0 <4.0.0" + dart: ">=3.6.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/packages/reactter/pubspec.yaml b/packages/reactter/pubspec.yaml index dedef68c..6b5db853 100644 --- a/packages/reactter/pubspec.yaml +++ b/packages/reactter/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://2devs-team.github.io/reactter/ documentation: https://2devs-team.github.io/reactter/ repository: https://github.com/2devs-team/reactter/tree/master/packages/reactter license: MIT License -version: 8.0.0-dev.1 +version: 8.0.0-dev.2 environment: sdk: ">=2.14.0 <4.0.0" @@ -14,7 +14,7 @@ dependencies: dev_dependencies: fake_async: ^1.3.1 - lints: ^2.0.1 + lints: ^5.1.1 test: ^1.25.2 flutter_test: sdk: flutter From 2bdeac2bd41a74a3b0eba2e74e6510e2a55067f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Tue, 31 Dec 2024 00:49:27 -0600 Subject: [PATCH 076/141] chore: Bump version to 8.0.0-dev.3 and update Flutter version information in README.md. --- CHANGELOG.md | 111 +++++++++++++++++++++++++++++++++ README.md | 6 +- packages/reactter/pubspec.yaml | 2 +- 3 files changed, 117 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa473bed..84798617 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,116 @@ # Reactter +## 8.0.0-dev.3 + +### Enhancements + +- Add Reactter devtools. +- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtInterface/removeObserver.html) methods to manage the observers. +- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). +- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). +- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. +- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. +- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtStateBase/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtStateBase/debugInfo.html) to states and hooks to get info for debugging. +- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. +- Add [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtStateBase-class.html) abstract class to implement the base logic of the state. +- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtInterface/createState.html) method to create a new state. +- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. +- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. +- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. +- Enhance event handling and notifier logic for improved stability and performance. +- Enhance state management and error handling. + + #### flutter_reactter + +- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.3/flutter_reactter/RtComponent-class.html). +- Enhance dependency management to ensure to re-build when is detected changes while building. + +### Breakings + +- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/Rt.html) instead. +- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtStateBase-class.html) instead. +- Replace [`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html) to `UseAsyncStateStatus.idle`. +- Move `asyncFunction` to the first argument of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/UseAsyncState/withArg.html). +- Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. +- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtInterface/isActive.html) instead. +- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtInterface/getDependencyMode.html) instead. +- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/DependencyMode.html) instead. +- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/Lifecycle.html) instead. +- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/Lifecycle.html) instead. +- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`LifecycleObserver.onCreated`](https://pub.dev/documentation/reactter/8.0.0-dev.3//reactter/LifecycleObserver/onCreated.html) instead. +- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.3//reactter/RtDependency-class.html) instead. +- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0-dev.3//reactter/RtHook-class.html) instead. +- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0-dev.3//reactter/RtInterface-class.html) instead. +- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtState/notify.html) instead. +- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/UseDependency-class.html) instead. +- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtAction-class.html) instead. +- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtActionCallable-class.html) instead. +- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MemoMultiInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/MemoMultiInterceptor-class.html) instead. +- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/MemoSafeAsyncInterceptor-class.html) instead. +- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/MemoTemporaryCacheInterceptor-class.html) instead. +- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/MemoWrapperInterceptor-class.html) instead. +- Remove [`Obj`](https://pub.dev/documentation/reactter/7.3.0/reactter/Obj-class.html). + + #### flutter_reactter + +- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.3/flutter_reactter/RtProvider/RtProvider.init.html) instead. +- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.3/flutter_reactter/RtComponent-class.html) instead. +- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.3/flutter_reactter/RtConsumer-class.html) instead. +- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.3/flutter_reactter/RtProvider-class.html) instead. +- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.3/flutter_reactter/RtMultiProvider-class.html) instead. +- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.3/flutter_reactter/RtScope-class.html) instead. +- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.3/flutter_reactter/RtSignalWatcher-class.html) instead. +- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.3/flutter_reactter/RtDependencyNotFoundException-class.html) instead. +- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.3/flutter_reactter/RtScopeNotFoundException-class.html) instead. + +## Fixes + +- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/UseEffect-class.html) causing dependencies to not be unwatched. +- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtInterface/register.html) method. +- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtInterface/batch.html) method to work correctly. +- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/Notifier-class.html) class. +- Fix to propagate state param to `boundInstance`. +- Remove listeners correctly from single-use listeners and handle null cases. + +### Internal + +- Add `StateObserver` test. +- Rename some variables to use consistent naming convention. +- Update `UseDependency` to support id parameter in tests. +- Update annotated markdown references for clarity. +- Add Reactter devtools extension and configuration files. +- Rearrange arguments in `UseAsyncState` constructor. +- Simplify the `UseReducer` implementation. +- Add observer support for nested state updates. +- Allow communication between application and devtools extension. +- Add `IContext` interface class to represent the current context what the application is running in. +- Replace `StateBase` to `RtState` interface. +- Separate logic of `EventNotifier` to `Notifier` class. +- Restructure the framework and core classes. +- Update `RtWatcher` to extend `RtScope` and refactor code. +- Update examples to use new features. +- Improve dispose logic in `RtStateBase` and `UseDependency`. +- Update hook registration and state attachment logic. +- Improve `null` listener handling in `Notifier` class. +- Improve dispose, register and notify logic of state. +- Support asynchronous callbacks in untracked and batch methods and add test for it. +- Simplify dependency listener callbacks and improve state management checks. +- Clean up exports, remove deprecated code, and improve event handling. +- Add tests for dependency state watching and useEffect disposal. +- Update exports to improve clarity and remove hidden types. +- Enhance autoBinding method to accept an optional bound instance; improve state binding logic. +- Enhance test coverage by adding error handling, debug label, and debug info tests for various components. +- Update dependencies and version for reactter package. +- Improve test command for better coverage. + + #### flutter_reactter + +- Update dependency references to use objects instead of hashcodes. +- Update mount and unmount logic in `ProviderElement`. +- Add constants for release, profile, debug, web modes. +- Update dependency handling and improve widget lifecycle management. +- Enhance provider and scope implementations with improved type safety and instance management. + ## 7.3.1 ## Fixes diff --git a/README.md b/README.md index b4943e7f..d839d0d2 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,6 @@ ____ - 🧪 Fully **Testable**, 100% code coverage. - 🔬 Fully **debuggable** using the **[Reactter DevTools extension](https://2devs-team.github.io/reactter/devtools_extension)** - 🪄 **Zero Dependencies**, **Zero Configuration** and **No Code Generation**. - - 💙 **Compatible with Dart and Flutter**, supports the latest version of Dart. **To start using Reactter, check out the full documentation on [The Official Documentation](https://2devs-team.github.io/reactter).** @@ -71,3 +70,8 @@ Any idean is welcome! - **[Carlos León](https://twitter.com/CarLeonDev)** - - **[Leo Castellanos](https://twitter.com/leoocast10)** - + +Flutter 3.24.3 • channel stable • https://github.com/flutter/flutter.git +Framework • revision 2663184aa7 (4 months ago) • 2024-09-11 16:27:48 -0500 +Engine • revision 36335019a8 +Tools • Dart 3.5.3 • DevTools 2.37.3 \ No newline at end of file diff --git a/packages/reactter/pubspec.yaml b/packages/reactter/pubspec.yaml index 6b5db853..a827afb2 100644 --- a/packages/reactter/pubspec.yaml +++ b/packages/reactter/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://2devs-team.github.io/reactter/ documentation: https://2devs-team.github.io/reactter/ repository: https://github.com/2devs-team/reactter/tree/master/packages/reactter license: MIT License -version: 8.0.0-dev.2 +version: 8.0.0-dev.3 environment: sdk: ">=2.14.0 <4.0.0" From 3015458dcc9c3608c0eb4de9577760d9e871f49b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Tue, 31 Dec 2024 00:53:02 -0600 Subject: [PATCH 077/141] doc: Remove unnecessary information from README.md. --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index d839d0d2..5b888d91 100644 --- a/README.md +++ b/README.md @@ -70,8 +70,3 @@ Any idean is welcome! - **[Carlos León](https://twitter.com/CarLeonDev)** - - **[Leo Castellanos](https://twitter.com/leoocast10)** - - -Flutter 3.24.3 • channel stable • https://github.com/flutter/flutter.git -Framework • revision 2663184aa7 (4 months ago) • 2024-09-11 16:27:48 -0500 -Engine • revision 36335019a8 -Tools • Dart 3.5.3 • DevTools 2.37.3 \ No newline at end of file From e8a61159a9ea505915da9bab70b5cb5259bd565f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Tue, 31 Dec 2024 01:20:37 -0600 Subject: [PATCH 078/141] build: Bump version to 8.0.0-dev.4 and update README.md for improved clarity. --- CHANGELOG.md | 105 +++++++++++++++------------------ README.md | 4 +- packages/reactter/pubspec.yaml | 2 +- 3 files changed, 51 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84798617..e0a0d316 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,74 +1,68 @@ # Reactter -## 8.0.0-dev.3 +## 8.0.0-dev.4 ### Enhancements - Add Reactter devtools. -- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtInterface/removeObserver.html) methods to manage the observers. -- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). -- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). -- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. -- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. -- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtStateBase/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtStateBase/debugInfo.html) to states and hooks to get info for debugging. -- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. -- Add [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtStateBase-class.html) abstract class to implement the base logic of the state. -- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtInterface/createState.html) method to create a new state. -- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. -- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. -- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. +- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtInterface/removeObserver.html) methods to manage the observers. +- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). +- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). +- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. +- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. +- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtStateBase/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtStateBase/debugInfo.html) to states and hooks to get info for debugging. +- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. +- Add [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtStateBase-class.html) abstract class to implement the base logic of the state. +- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtInterface/createState.html) method to create a new state. +- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. +- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. +- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. - Enhance event handling and notifier logic for improved stability and performance. - Enhance state management and error handling. - - #### flutter_reactter - -- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.3/flutter_reactter/RtComponent-class.html). +- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.4/flutter_reactter/RtComponent-class.html). - Enhance dependency management to ensure to re-build when is detected changes while building. ### Breakings -- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/Rt.html) instead. -- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtStateBase-class.html) instead. +- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/Rt.html) instead. +- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtStateBase-class.html) instead. - Replace [`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html) to `UseAsyncStateStatus.idle`. -- Move `asyncFunction` to the first argument of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/UseAsyncState/withArg.html). -- Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. -- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtInterface/isActive.html) instead. -- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtInterface/getDependencyMode.html) instead. -- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/DependencyMode.html) instead. -- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/Lifecycle.html) instead. -- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/Lifecycle.html) instead. -- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`LifecycleObserver.onCreated`](https://pub.dev/documentation/reactter/8.0.0-dev.3//reactter/LifecycleObserver/onCreated.html) instead. -- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.3//reactter/RtDependency-class.html) instead. -- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0-dev.3//reactter/RtHook-class.html) instead. -- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0-dev.3//reactter/RtInterface-class.html) instead. -- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtState/notify.html) instead. -- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/UseDependency-class.html) instead. -- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtAction-class.html) instead. -- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtActionCallable-class.html) instead. -- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MemoMultiInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/MemoMultiInterceptor-class.html) instead. -- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/MemoSafeAsyncInterceptor-class.html) instead. -- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/MemoTemporaryCacheInterceptor-class.html) instead. -- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/MemoWrapperInterceptor-class.html) instead. +- Move `asyncFunction` to the first argument of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/UseAsyncState/withArg.html). +- Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. +- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtInterface/isActive.html) instead. +- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtInterface/getDependencyMode.html) instead. +- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/DependencyMode.html) instead. +- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/Lifecycle.html) instead. +- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/Lifecycle.html) instead. +- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`LifecycleObserver.onCreated`](https://pub.dev/documentation/reactter/8.0.0-dev.4//reactter/LifecycleObserver/onCreated.html) instead. +- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.4//reactter/RtDependency-class.html) instead. +- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0-dev.4//reactter/RtHook-class.html) instead. +- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0-dev.4//reactter/RtInterface-class.html) instead. +- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtState/notify.html) instead. +- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/UseDependency-class.html) instead. +- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtAction-class.html) instead. +- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtActionCallable-class.html) instead. +- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MemoMultiInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/MemoMultiInterceptor-class.html) instead. +- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/MemoSafeAsyncInterceptor-class.html) instead. +- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/MemoTemporaryCacheInterceptor-class.html) instead. +- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/MemoWrapperInterceptor-class.html) instead. - Remove [`Obj`](https://pub.dev/documentation/reactter/7.3.0/reactter/Obj-class.html). - - #### flutter_reactter - -- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.3/flutter_reactter/RtProvider/RtProvider.init.html) instead. -- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.3/flutter_reactter/RtComponent-class.html) instead. -- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.3/flutter_reactter/RtConsumer-class.html) instead. -- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.3/flutter_reactter/RtProvider-class.html) instead. -- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.3/flutter_reactter/RtMultiProvider-class.html) instead. -- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.3/flutter_reactter/RtScope-class.html) instead. -- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.3/flutter_reactter/RtSignalWatcher-class.html) instead. -- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.3/flutter_reactter/RtDependencyNotFoundException-class.html) instead. -- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.3/flutter_reactter/RtScopeNotFoundException-class.html) instead. +- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.4/flutter_reactter/RtProvider/RtProvider.init.html) instead. +- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.4/flutter_reactter/RtComponent-class.html) instead. +- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.4/flutter_reactter/RtConsumer-class.html) instead. +- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.4/flutter_reactter/RtProvider-class.html) instead. +- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.4/flutter_reactter/RtMultiProvider-class.html) instead. +- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.4/flutter_reactter/RtScope-class.html) instead. +- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.4/flutter_reactter/RtSignalWatcher-class.html) instead. +- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.4/flutter_reactter/RtDependencyNotFoundException-class.html) instead. +- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.4/flutter_reactter/RtScopeNotFoundException-class.html) instead. ## Fixes -- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/UseEffect-class.html) causing dependencies to not be unwatched. -- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtInterface/register.html) method. -- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/RtInterface/batch.html) method to work correctly. -- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0-dev.3/reactter/Notifier-class.html) class. +- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/UseEffect-class.html) causing dependencies to not be unwatched. +- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtInterface/register.html) method. +- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtInterface/batch.html) method to work correctly. +- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/Notifier-class.html) class. - Fix to propagate state param to `boundInstance`. - Remove listeners correctly from single-use listeners and handle null cases. @@ -102,9 +96,6 @@ - Enhance test coverage by adding error handling, debug label, and debug info tests for various components. - Update dependencies and version for reactter package. - Improve test command for better coverage. - - #### flutter_reactter - - Update dependency references to use objects instead of hashcodes. - Update mount and unmount logic in `ProviderElement`. - Add constants for release, profile, debug, web modes. diff --git a/README.md b/README.md index 5b888d91..079e60a7 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ ____ [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/2devs-team/reactter/dart.yml?branch=master)](https://github.com/2devs-team/reactter/actions) [![Codecov](https://img.shields.io/codecov/c/github/2devs-team/reactter?logo=codecov)](https://app.codecov.io/gh/2devs-team/reactter) -# A lightweight, powerful, and reactive State Management, Dependency Injection and Event Handler for Dart/Flutter +A lightweight, powerful, and reactive **State Management**, **Dependency Injection** and **Event Handler** for Dart/Flutter ## Features @@ -25,7 +25,7 @@ ____ - ♻️ **Reusable States and Logic** with [Custom hooks]([#custom-hooks](https://2devs-team.github.io/reactter/core_concepts/hooks/#custom-hook)). - 🎮 Fully **[Rendering Control]([#rendering-control](https://2devs-team.github.io/reactter/core_concepts/rendering_control))**. - 🧪 Fully **Testable**, 100% code coverage. -- 🔬 Fully **debuggable** using the **[Reactter DevTools extension](https://2devs-team.github.io/reactter/devtools_extension)** +- 🔬 Fully **Debuggable** using the **[Reactter DevTools extension](https://2devs-team.github.io/reactter/devtools_extension)** - 🪄 **Zero Dependencies**, **Zero Configuration** and **No Code Generation**. - 💙 **Compatible with Dart and Flutter**, supports the latest version of Dart. diff --git a/packages/reactter/pubspec.yaml b/packages/reactter/pubspec.yaml index a827afb2..e3422532 100644 --- a/packages/reactter/pubspec.yaml +++ b/packages/reactter/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://2devs-team.github.io/reactter/ documentation: https://2devs-team.github.io/reactter/ repository: https://github.com/2devs-team/reactter/tree/master/packages/reactter license: MIT License -version: 8.0.0-dev.3 +version: 8.0.0-dev.4 environment: sdk: ">=2.14.0 <4.0.0" From ceff34e54a2425b91ab9c5b4729d8a7815119583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Tue, 31 Dec 2024 02:14:23 -0600 Subject: [PATCH 079/141] refactor(signal): restructure signal imports and add extensions. --- packages/reactter/lib/reactter.dart | 3 +- packages/reactter/lib/src/devtools.dart | 2 +- .../src/signal/extensions/signal_bigint.dart | 684 ++++++++++ .../src/signal/extensions/signal_bool.dart | 19 + .../signal/extensions/signal_date_time.dart | 664 +++++++++ .../src/signal/extensions/signal_double.dart | 334 +++++ .../lib/src/signal/extensions/signal_int.dart | 476 +++++++ .../signal/extensions/signal_iterable.dart | 1070 +++++++++++++++ .../src/signal/extensions/signal_list.dart | 1210 +++++++++++++++++ .../lib/src/signal/extensions/signal_map.dart | 553 ++++++++ .../lib/src/signal/extensions/signal_num.dart | 783 +++++++++++ .../lib/src/signal/extensions/signal_set.dart | 462 +++++++ .../src/signal/extensions/signal_string.dart | 1057 ++++++++++++++ .../reactter/lib/src/{ => signal}/signal.dart | 13 + 14 files changed, 7327 insertions(+), 3 deletions(-) create mode 100644 packages/reactter/lib/src/signal/extensions/signal_bigint.dart create mode 100644 packages/reactter/lib/src/signal/extensions/signal_bool.dart create mode 100644 packages/reactter/lib/src/signal/extensions/signal_date_time.dart create mode 100644 packages/reactter/lib/src/signal/extensions/signal_double.dart create mode 100644 packages/reactter/lib/src/signal/extensions/signal_int.dart create mode 100644 packages/reactter/lib/src/signal/extensions/signal_iterable.dart create mode 100644 packages/reactter/lib/src/signal/extensions/signal_list.dart create mode 100644 packages/reactter/lib/src/signal/extensions/signal_map.dart create mode 100644 packages/reactter/lib/src/signal/extensions/signal_num.dart create mode 100644 packages/reactter/lib/src/signal/extensions/signal_set.dart create mode 100644 packages/reactter/lib/src/signal/extensions/signal_string.dart rename packages/reactter/lib/src/{ => signal}/signal.dart (91%) diff --git a/packages/reactter/lib/reactter.dart b/packages/reactter/lib/reactter.dart index 294fd19f..e342cc8a 100644 --- a/packages/reactter/lib/reactter.dart +++ b/packages/reactter/lib/reactter.dart @@ -6,8 +6,7 @@ export 'src/devtools.dart' export 'src/framework.dart'; export 'src/hooks/hooks.dart' hide UseAsyncStateBase; export 'src/memo/memo.dart'; - -export 'src/signal.dart'; +export 'src/signal/signal.dart'; export 'src/logger.dart' hide RtLogger, RtLoggerInitializeAssertionError, prettyFormat; export 'src/types.dart'; diff --git a/packages/reactter/lib/src/devtools.dart b/packages/reactter/lib/src/devtools.dart index 4d8cecf6..7a306e13 100644 --- a/packages/reactter/lib/src/devtools.dart +++ b/packages/reactter/lib/src/devtools.dart @@ -3,7 +3,7 @@ import 'dart:collection'; import 'dart:developer' as dev; import 'package:meta/meta.dart'; -import 'package:reactter/src/signal.dart'; +import 'package:reactter/src/signal/signal.dart'; import 'framework.dart'; import 'internals.dart'; diff --git a/packages/reactter/lib/src/signal/extensions/signal_bigint.dart b/packages/reactter/lib/src/signal/extensions/signal_bigint.dart new file mode 100644 index 00000000..feda689b --- /dev/null +++ b/packages/reactter/lib/src/signal/extensions/signal_bigint.dart @@ -0,0 +1,684 @@ +// coverage:ignore-file +part of '../signal.dart'; + +extension SignalBigIntExt on Signal { + /// Return the negative value of this integer. + /// + /// The result of negating an integer always has the opposite sign, except + /// for zero, which is its own negation. + BigInt operator -() => -value; + + /// Adds [other] to this big integer. + /// + /// The result is again a big integer. + BigInt operator +(BigInt other) => value + other; + + /// Subtracts [other] from this big integer. + /// + /// The result is again a big integer. + BigInt operator -(BigInt other) => value - other; + + /// Multiplies [other] by this big integer. + /// + /// The result is again a big integer. + BigInt operator *(BigInt other) => value * other; + + /// Double division operator. + /// + /// Matching the similar operator on [int], + /// this operation first performs [toDouble] on both this big integer + /// and [other], then does [double.operator/] on those values and + /// returns the result. + /// + /// **Note:** The initial [toDouble] conversion may lose precision. + /// + /// Example: + /// ```dart + /// print(Signal(BigInt.from(1)) / BigInt.from(2)); // 0.5 + /// print(Signal(BigInt.from(1.99999)) / BigInt.from(2)); // 0.5 + /// ``` + double operator /(BigInt other) => value / other; + + /// Truncating integer division operator. + /// + /// Performs a truncating integer division, where the remainder is discarded. + /// + /// The remainder can be computed using the [remainder] method. + /// + /// Examples: + /// ```dart + /// var seven = Signal(BigInt.from(7)); + /// var three = BigInt.from(3); + /// seven ~/ three; // => 2 + /// (-seven) ~/ three; // => -2 + /// seven ~/ -three; // => -2 + /// seven.remainder(three); // => 1 + /// (-seven).remainder(three); // => -1 + /// seven.remainder(-three); // => 1 + /// ``` + BigInt operator ~/(BigInt other) => value ~/ other; + + /// Euclidean modulo operator. + /// + /// Returns the remainder of the Euclidean division. The Euclidean division of + /// two integers `a` and `b` yields two integers `q` and `r` such that + /// `a == b * q + r` and `0 <= r < b.abs()`. + /// + /// The sign of the returned value `r` is always positive. + /// + /// See [remainder] for the remainder of the truncating division. + /// + /// Example: + /// ```dart + /// print(Signal(BigInt.from(5)) % BigInt.from(3)); // 2 + /// print(Signal(BigInt.from(-5)) % BigInt.from(3)); // 1 + /// print(Signal(BigInt.from(5)) % BigInt.from(-3)); // 2 + /// print(Signal(BigInt.from(-5)) % BigInt.from(-3)); // 1 + /// ``` + BigInt operator %(BigInt other) => value % other; + + /// Shift the bits of this integer to the left by [shiftAmount]. + /// + /// Shifting to the left makes the number larger, effectively multiplying + /// the number by `pow(2, shiftIndex)`. + /// + /// There is no limit on the size of the result. It may be relevant to + /// limit intermediate values by using the "and" operator with a suitable + /// mask. + /// + /// It is an error if [shiftAmount] is negative. + BigInt operator <<(int shiftAmount) => value << shiftAmount; + + /// Shift the bits of this integer to the right by [shiftAmount]. + /// + /// Shifting to the right makes the number smaller and drops the least + /// significant bits, effectively doing an integer division by + ///`pow(2, shiftIndex)`. + /// + /// It is an error if [shiftAmount] is negative. + BigInt operator >>(int shiftAmount) => value >> shiftAmount; + + /// Bit-wise and operator. + /// + /// Treating both `this` and [other] as sufficiently large two's component + /// integers, the result is a number with only the bits set that are set in + /// both `this` and [other] + /// + /// Of both operands are negative, the result is negative, otherwise + /// the result is non-negative. + BigInt operator &(BigInt other) => value & other; + + /// Bit-wise or operator. + /// + /// Treating both `this` and [other] as sufficiently large two's component + /// integers, the result is a number with the bits set that are set in either + /// of `this` and [other] + /// + /// If both operands are non-negative, the result is non-negative, + /// otherwise the result is negative. + BigInt operator |(BigInt other) => value | other; + + /// Bit-wise exclusive-or operator. + /// + /// Treating both `this` and [other] as sufficiently large two's component + /// integers, the result is a number with the bits set that are set in one, + /// but not both, of `this` and [other] + /// + /// If the operands have the same sign, the result is non-negative, + /// otherwise the result is negative. + BigInt operator ^(BigInt other) => value ^ other; + + /// The bit-wise negate operator. + /// + /// Treating `this` as a sufficiently large two's component integer, + /// the result is a number with the opposite bits set. + /// + /// This maps any integer `x` to `-x - 1`. + BigInt operator ~() => ~value; + + /// Whether this big integer is numerically smaller than [other]. + bool operator <(BigInt other) => value < other; + + /// Whether [other] is numerically greater than this big integer. + bool operator <=(BigInt other) => value <= other; + + /// Whether this big integer is numerically greater than [other]. + bool operator >(BigInt other) => value > other; + + /// Whether [other] is numerically smaller than this big integer. + bool operator >=(BigInt other) => value >= other; + + /// Returns the absolute value of this integer. + /// + /// For any integer `x`, the result is the same as `x < 0 ? -x : x`. + BigInt abs() => value.abs(); + + /// Returns the remainder of the truncating division of `this` by [other]. + /// + /// The result `r` of this operation satisfies: + /// `this == (this ~/ other) * other + r`. + /// As a consequence the remainder `r` has the same sign as the divider `this`. + /// + /// Example: + /// ```dart + /// print(Signal(BigInt.from(5)).remainder(BigInt.from(3))); // 2 + /// print(Signal(BigInt.from(-5)).remainder(BigInt.from(3))); // -2 + /// print(Signal(BigInt.from(5)).remainder(BigInt.from(-3))); // 2 + /// print(Signal(BigInt.from(-5)).remainder(BigInt.from(-3))); // -2 + /// ``` + BigInt remainder(BigInt other) => value.remainder(other); + + /// Compares this to `other`. + /// + /// Returns a negative number if `this` is less than `other`, zero if they are + /// equal, and a positive number if `this` is greater than `other`. + /// + /// Example: + /// ```dart + /// print(Signal(BigInt.from(1)).compareTo(BigInt.from(2))); // => -1 + /// print(Signal(BigInt.from(2)).compareTo(BigInt.from(1))); // => 1 + /// print(Signal(BigInt.from(1)).compareTo(BigInt.from(1))); // => 0 + /// ``` + int compareTo(BigInt other) => value.compareTo(other); + + /// Returns the minimum number of bits required to store this big integer. + /// + /// The number of bits excludes the sign bit, which gives the natural length + /// for non-negative (unsigned) values. Negative values are complemented to + /// return the bit position of the first bit that differs from the sign bit. + /// + /// To find the number of bits needed to store the value as a signed value, + /// add one, i.e. use `x.bitLength + 1`. + /// + /// ```dart + /// x.bitLength == (-x-1).bitLength; + /// + /// Signal(BigInt.from(3)).bitLength == 2; // 00000011 + /// Signal(BigInt.from(2)).bitLength == 2; // 00000010 + /// Signal(BigInt.from(1)).bitLength == 1; // 00000001 + /// Signal(BigInt.from(0)).bitLength == 0; // 00000000 + /// Signal(BigInt.from(-1)).bitLength == 0; // 11111111 + /// Signal(BigInt.from(-2)).bitLength == 1; // 11111110 + /// Signal(BigInt.from(-3)).bitLength == 2; // 11111101 + /// Signal(BigInt.from(-4)).bitLength == 2; // 11111100 + /// ``` + int get bitLength => value.bitLength; + + /// Returns the sign of this big integer. + /// + /// Returns 0 for zero, -1 for values less than zero and + /// +1 for values greater than zero. + int get sign => value.sign; + + /// Whether this big integer is even. + bool get isEven => value.isEven; + + /// Whether this big integer is odd. + bool get isOdd => value.isOdd; + + /// Whether this number is negative. + bool get isNegative => value.isNegative; + + /// Returns `this` to the power of [exponent]. + /// + /// Returns [one] if the [exponent] equals 0. + /// + /// The [exponent] must otherwise be positive. + /// + /// The result is always equal to the mathematical result of this to the power + /// [exponent], only limited by the available memory. + /// + /// Example: + /// ```dart + /// var value = Signal(BigInt.from(1000)); + /// print(value.pow(0)); // 1 + /// print(value.pow(1)); // 1000 + /// print(value.pow(2)); // 1000000 + /// print(value.pow(3)); // 1000000000 + /// print(value.pow(4)); // 1000000000000 + /// print(value.pow(5)); // 1000000000000000 + /// print(value.pow(6)); // 1000000000000000000 + /// print(value.pow(7)); // 1000000000000000000000 + /// print(value.pow(8)); // 1000000000000000000000000 + /// ``` + BigInt pow(int exponent) => value.pow(exponent); + + /// Returns this integer to the power of [exponent] modulo [modulus]. + /// + /// The [exponent] must be non-negative and [modulus] must be + /// positive. + BigInt modPow(BigInt exponent, BigInt modulus) => + value.modPow(exponent, modulus); + + /// Returns the modular multiplicative inverse of this big integer + /// modulo [modulus]. + /// + /// The [modulus] must be positive. + /// + /// It is an error if no modular inverse exists. + // Returns 1/this % modulus, with modulus > 0. + BigInt modInverse(BigInt modulus) => value.modInverse(modulus); + + /// Returns the greatest common divisor of this big integer and [other]. + /// + /// If either number is non-zero, the result is the numerically greatest + /// integer dividing both `this` and `other`. + /// + /// The greatest common divisor is independent of the order, + /// so `x.gcd(y)` is always the same as `y.gcd(x)`. + /// + /// For any integer `x`, `x.gcd(x)` is `x.abs()`. + /// + /// If both `this` and `other` is zero, the result is also zero. + /// + /// Example: + /// ```dart + /// print(Signal(BigInt.from(4)).gcd(BigInt.from(2))); // 2 + /// print(Signal(BigInt.from(8)).gcd(BigInt.from(4))); // 4 + /// print(Signal(BigInt.from(10)).gcd(BigInt.from(12))); // 2 + /// print(Signal(BigInt.from(10)).gcd(BigInt.from(10))); // 10 + /// print(Signal(BigInt.from(-2)).gcd(BigInt.from(-3))); // 1 + /// ``` + BigInt gcd(BigInt other) => value.gcd(other); + + /// Returns the least significant [width] bits of this big integer as a + /// non-negative number (i.e. unsigned representation). The returned value has + /// zeros in all bit positions higher than [width]. + /// + /// ```dart + /// Signal(BigInt.from(-1)).toUnsigned(5) == 31 // 11111111 -> 00011111 + /// ``` + /// + /// This operation can be used to simulate arithmetic from low level languages. + /// For example, to increment an 8 bit quantity: + /// + /// ```dart + /// q = (q + 1).toUnsigned(8); + /// ``` + /// + /// `q` will count from `0` up to `255` and then wrap around to `0`. + /// + /// If the input fits in [width] bits without truncation, the result is the + /// same as the input. The minimum width needed to avoid truncation of `x` is + /// given by `x.bitLength`, i.e. + /// + /// ```dart + /// x == x.toUnsigned(x.bitLength); + /// ``` + BigInt toUnsigned(int width) => value.toUnsigned(width); + + /// Returns the least significant [width] bits of this integer, extending the + /// highest retained bit to the sign. This is the same as truncating the value + /// to fit in [width] bits using an signed 2-s complement representation. The + /// returned value has the same bit value in all positions higher than [width]. + /// + /// ```dart + /// var big15 = Signal(BigInt.from(15)); + /// var big16 = Signal(BigInt.from(16)); + /// var big239 = Signal(BigInt.from(239)); + /// // V--sign bit-V + /// big16.toSigned(5) == -big16; // 00010000 -> 11110000 + /// big239.toSigned(5) == big15; // 11101111 -> 00001111 + /// // ^ ^ + /// ``` + /// + /// This operation can be used to simulate arithmetic from low level languages. + /// For example, to increment an 8 bit signed quantity: + /// + /// ```dart + /// q = (q + 1).toSigned(8); + /// ``` + /// + /// `q` will count from `0` up to `127`, wrap to `-128` and count back up to + /// `127`. + /// + /// If the input value fits in [width] bits without truncation, the result is + /// the same as the input. The minimum width needed to avoid truncation of `x` + /// is `x.bitLength + 1`, i.e. + /// + /// ```dart + /// x == x.toSigned(x.bitLength + 1); + /// ``` + BigInt toSigned(int width) => value.toSigned(width); + + /// Whether this big integer can be represented as an `int` without losing + /// precision. + /// + /// **Warning:** this function may give a different result on + /// dart2js, dev compiler, and the VM, due to the differences in + /// integer precision. + /// + /// Example: + /// ```dart + /// var bigNumber = BigInt.parse('100000000000000000000000'); + /// print(bigNumber.isValidInt); // false + /// + /// var value = BigInt.parse('0xFF'); // 255 + /// print(value.isValidInt); // true + /// ``` + bool get isValidInt => value.isValidInt; + + /// Returns this [BigInt] as an [int]. + /// + /// If the number does not fit, clamps to the max (or min) + /// integer. + /// + /// **Warning:** the clamping behaves differently between the web and + /// native platforms due to the differences in integer precision. + /// + /// Example: + /// ```dart + /// var bigNumber = BigInt.parse('100000000000000000000000'); + /// print(bigNumber.isValidInt); // false + /// print(bigNumber.toInt()); // 9223372036854775807 + /// ``` + int toInt() => value.toInt(); + + /// Returns this [BigInt] as a [double]. + /// + /// If the number is not representable as a [double], an + /// approximation is returned. For numerically large integers, the + /// approximation may be infinite. + /// + /// Example: + /// ```dart + /// var bigNumber = BigInt.parse('100000000000000000000000'); + /// print(bigNumber.toDouble()); // 1e+23 + /// ``` + double toDouble() => value.toDouble(); + + /// Converts [this] to a string representation in the given [radix]. + /// + /// In the string representation, lower-case letters are used for digits above + /// '9', with 'a' being 10 an 'z' being 35. + /// + /// The [radix] argument must be an integer in the range 2 to 36. + /// + /// Example: + /// ```dart + /// // Binary (base 2). + /// print(Signal(BigInt.from(12)).toRadixString(2)); // 1100 + /// print(Signal(BigInt.from(31)).toRadixString(2)); // 11111 + /// print(Signal(BigInt.from(2021)).toRadixString(2)); // 11111100101 + /// print(Signal(BigInt.from(-)12).toRadixString(2)); // -1100 + /// // Octal (base 8). + /// print(Signal(BigInt.from(12)).toRadixString(8)); // 14 + /// print(Signal(BigInt.from(31)).toRadixString(8)); // 37 + /// print(Signal(BigInt.from(2021)).toRadixString(8)); // 3745 + /// // Hexadecimal (base 16). + /// print(Signal(BigInt.from(12)).toRadixString(16)); // c + /// print(Signal(BigInt.from(31)).toRadixString(16)); // 1f + /// print(Signal(BigInt.from(2021)).toRadixString(16)); // 7e5 + /// // Base 36. + /// print(Signal(BigInt.from(35 * 36 + 1)).toRadixString(36)); // z1 + /// ``` + String toRadixString(int radix) => value.toRadixString(radix); +} + +extension SignalBigIntNullExt on Signal { + /// Returns the absolute value of this integer. + /// + /// For any integer `x`, the result is the same as `x < 0 ? -x : x`. + BigInt? abs() => value?.abs(); + + /// Returns the remainder of the truncating division of `this` by [other]. + /// + /// The result `r` of this operation satisfies: + /// `this == (this ~/ other) * other + r`. + /// As a consequence the remainder `r` has the same sign as the divider `this`. + /// + /// Example: + /// ```dart + /// print(Signal(BigInt.from(5)).remainder(BigInt.from(3))); // 2 + /// print(Signal(BigInt.from(-5)).remainder(BigInt.from(3))); // -2 + /// print(Signal(BigInt.from(5)).remainder(BigInt.from(-3))); // 2 + /// print(Signal(BigInt.from(-5)).remainder(BigInt.from(-3))); // -2 + /// ``` + BigInt? remainder(BigInt other) => value?.remainder(other); + + /// Compares this to `other`. + /// + /// Returns a negative number if `this` is less than `other`, zero if they are + /// equal, and a positive number if `this` is greater than `other`. + /// + /// Example: + /// ```dart + /// print(Signal(BigInt.from(1)).compareTo(BigInt.from(2))); // => -1 + /// print(Signal(BigInt.from(2)).compareTo(BigInt.from(1))); // => 1 + /// print(Signal(BigInt.from(1)).compareTo(BigInt.from(1))); // => 0 + /// ``` + int? compareTo(BigInt other) => value?.compareTo(other); + + /// Returns the minimum number of bits required to store this big integer. + /// + /// The number of bits excludes the sign bit, which gives the natural length + /// for non-negative (unsigned) values. Negative values are complemented to + /// return the bit position of the first bit that differs from the sign bit. + /// + /// To find the number of bits needed to store the value as a signed value, + /// add one, i.e. use `x.bitLength + 1`. + /// + /// ```dart + /// x.bitLength == (-x-1).bitLength; + /// + /// Signal(BigInt.from(3)).bitLength == 2; // 00000011 + /// Signal(BigInt.from(2)).bitLength == 2; // 00000010 + /// Signal(BigInt.from(1)).bitLength == 1; // 00000001 + /// Signal(BigInt.from(0)).bitLength == 0; // 00000000 + /// Signal(BigInt.from(-1)).bitLength == 0; // 11111111 + /// Signal(BigInt.from(-2)).bitLength == 1; // 11111110 + /// Signal(BigInt.from(-3)).bitLength == 2; // 11111101 + /// Signal(BigInt.from(-4)).bitLength == 2; // 11111100 + /// ``` + int? get bitLength => value?.bitLength; + + /// Returns the sign of this big integer. + /// + /// Returns 0 for zero, -1 for values less than zero and + /// +1 for values greater than zero. + int? get sign => value?.sign; + + /// Whether this big integer is even. + bool? get isEven => value?.isEven; + + /// Whether this big integer is odd. + bool? get isOdd => value?.isOdd; + + /// Whether this number is negative. + bool? get isNegative => value?.isNegative; + + /// Returns `this` to the power of [exponent]. + /// + /// Returns [one] if the [exponent] equals 0. + /// + /// The [exponent] must otherwise be positive. + /// + /// The result is always equal to the mathematical result of this to the power + /// [exponent], only limited by the available memory. + /// + /// Example: + /// ```dart + /// var value = Signal(BigInt.from(1000)); + /// print(value.pow(0)); // 1 + /// print(value.pow(1)); // 1000 + /// print(value.pow(2)); // 1000000 + /// print(value.pow(3)); // 1000000000 + /// print(value.pow(4)); // 1000000000000 + /// print(value.pow(5)); // 1000000000000000 + /// print(value.pow(6)); // 1000000000000000000 + /// print(value.pow(7)); // 1000000000000000000000 + /// print(value.pow(8)); // 1000000000000000000000000 + /// ``` + BigInt? pow(int exponent) => value?.pow(exponent); + + /// Returns this integer to the power of [exponent] modulo [modulus]. + /// + /// The [exponent] must be non-negative and [modulus] must be + /// positive. + BigInt? modPow(BigInt exponent, BigInt modulus) => + value?.modPow(exponent, modulus); + + /// Returns the modular multiplicative inverse of this big integer + /// modulo [modulus]. + /// + /// The [modulus] must be positive. + /// + /// It is an error if no modular inverse exists. + // Returns 1/this % modulus, with modulus > 0. + BigInt? modInverse(BigInt modulus) => value?.modInverse(modulus); + + /// Returns the greatest common divisor of this big integer and [other]. + /// + /// If either number is non-zero, the result is the numerically greatest + /// integer dividing both `this` and `other`. + /// + /// The greatest common divisor is independent of the order, + /// so `x.gcd(y)` is always the same as `y.gcd(x)`. + /// + /// For any integer `x`, `x.gcd(x)` is `x.abs()`. + /// + /// If both `this` and `other` is zero, the result is also zero. + /// + /// Example: + /// ```dart + /// print(Signal(BigInt.from(4)).gcd(BigInt.from(2))); // 2 + /// print(Signal(BigInt.from(8)).gcd(BigInt.from(4))); // 4 + /// print(Signal(BigInt.from(10)).gcd(BigInt.from(12))); // 2 + /// print(Signal(BigInt.from(10)).gcd(BigInt.from(10))); // 10 + /// print(Signal(BigInt.from(-2)).gcd(BigInt.from(-3))); // 1 + /// ``` + BigInt? gcd(BigInt other) => value?.gcd(other); + + /// Returns the least significant [width] bits of this big integer as a + /// non-negative number (i.e. unsigned representation). The returned value has + /// zeros in all bit positions higher than [width]. + /// + /// ```dart + /// Signal(BigInt.from(-1)).toUnsigned(5) == 31 // 11111111 -> 00011111 + /// ``` + /// + /// This operation can be used to simulate arithmetic from low level languages. + /// For example, to increment an 8 bit quantity: + /// + /// ```dart + /// q = (q + 1).toUnsigned(8); + /// ``` + /// + /// `q` will count from `0` up to `255` and then wrap around to `0`. + /// + /// If the input fits in [width] bits without truncation, the result is the + /// same as the input. The minimum width needed to avoid truncation of `x` is + /// given by `x.bitLength`, i.e. + /// + /// ```dart + /// x == x.toUnsigned(x.bitLength); + /// ``` + BigInt? toUnsigned(int width) => value?.toUnsigned(width); + + /// Returns the least significant [width] bits of this integer, extending the + /// highest retained bit to the sign. This is the same as truncating the value + /// to fit in [width] bits using an signed 2-s complement representation. The + /// returned value has the same bit value in all positions higher than [width]. + /// + /// ```dart + /// var big15 = Signal(BigInt.from(15)); + /// var big16 = Signal(BigInt.from(16)); + /// var big239 = Signal(BigInt.from(239)); + /// // V--sign bit-V + /// big16.toSigned(5) == -big16; // 00010000 -> 11110000 + /// big239.toSigned(5) == big15; // 11101111 -> 00001111 + /// // ^ ^ + /// ``` + /// + /// This operation can be used to simulate arithmetic from low level languages. + /// For example, to increment an 8 bit signed quantity: + /// + /// ```dart + /// q = (q + 1).toSigned(8); + /// ``` + /// + /// `q` will count from `0` up to `127`, wrap to `-128` and count back up to + /// `127`. + /// + /// If the input value fits in [width] bits without truncation, the result is + /// the same as the input. The minimum width needed to avoid truncation of `x` + /// is `x.bitLength + 1`, i.e. + /// + /// ```dart + /// x == x.toSigned(x.bitLength + 1); + /// ``` + BigInt? toSigned(int width) => value?.toSigned(width); + + /// Whether this big integer can be represented as an `int` without losing + /// precision. + /// + /// **Warning:** this function may give a different result on + /// dart2js, dev compiler, and the VM, due to the differences in + /// integer precision. + /// + /// Example: + /// ```dart + /// var bigNumber = BigInt.parse('100000000000000000000000'); + /// print(bigNumber.isValidInt); // false + /// + /// var value = BigInt.parse('0xFF'); // 255 + /// print(value.isValidInt); // true + /// ``` + bool? get isValidInt => value?.isValidInt; + + /// Returns this [BigInt] as an [int]. + /// + /// If the number does not fit, clamps to the max (or min) + /// integer. + /// + /// **Warning:** the clamping behaves differently between the web and + /// native platforms due to the differences in integer precision. + /// + /// Example: + /// ```dart + /// var bigNumber = BigInt.parse('100000000000000000000000'); + /// print(bigNumber.isValidInt); // false + /// print(bigNumber.toInt()); // 9223372036854775807 + /// ``` + int? toInt() => value?.toInt(); + + /// Returns this [BigInt] as a [double]. + /// + /// If the number is not representable as a [double], an + /// approximation is returned. For numerically large integers, the + /// approximation may be infinite. + /// + /// Example: + /// ```dart + /// var bigNumber = BigInt.parse('100000000000000000000000'); + /// print(bigNumber.toDouble()); // 1e+23 + /// ``` + double? toDouble() => value?.toDouble(); + + /// Converts [this] to a string representation in the given [radix]. + /// + /// In the string representation, lower-case letters are used for digits above + /// '9', with 'a' being 10 an 'z' being 35. + /// + /// The [radix] argument must be an integer in the range 2 to 36. + /// + /// Example: + /// ```dart + /// // Binary (base 2). + /// print(Signal(BigInt.from(12)).toRadixString(2)); // 1100 + /// print(Signal(BigInt.from(31)).toRadixString(2)); // 11111 + /// print(Signal(BigInt.from(2021)).toRadixString(2)); // 11111100101 + /// print(Signal(BigInt.from(-12)).toRadixString(2)); // -1100 + /// // Octal (base 8). + /// print(Signal(BigInt.from(12)).toRadixString(8)); // 14 + /// print(Signal(BigInt.from(31)).toRadixString(8)); // 37 + /// print(Signal(BigInt.from(2021)).toRadixString(8)); // 3745 + /// // Hexadecimal (base 16). + /// print(Signal(BigInt.from(12)).toRadixString(16)); // c + /// print(Signal(BigInt.from(31)).toRadixString(16)); // 1f + /// print(Signal(BigInt.from(2021)).toRadixString(16)); // 7e5 + /// // Base 36. + /// print(Signal(BigInt.from(35 * 36 + 1)).toRadixString(36)); // z1 + /// ``` + String? toRadixString(int radix) => value?.toRadixString(radix); +} diff --git a/packages/reactter/lib/src/signal/extensions/signal_bool.dart b/packages/reactter/lib/src/signal/extensions/signal_bool.dart new file mode 100644 index 00000000..cbed62ac --- /dev/null +++ b/packages/reactter/lib/src/signal/extensions/signal_bool.dart @@ -0,0 +1,19 @@ +// coverage:ignore-file +part of '../signal.dart'; + +extension SignalBoolExt on Signal { + /// The logical conjunction ("and") of this and [other]. + /// + /// Returns `true` if both this and [other] are `true`, and `false` otherwise. + bool operator &(bool other) => value && other; + + /// The logical disjunction ("inclusive or") of this and [other]. + /// + /// Returns `true` if either this or [other] is `true`, and `false` otherwise. + bool operator |(bool other) => value || other; + + /// The logical exclusive disjunction ("exclusive or") of this and [other]. + /// + /// Returns whether this and [other] are neither both `true` nor both `false`. + bool operator ^(bool other) => value ^ other; +} diff --git a/packages/reactter/lib/src/signal/extensions/signal_date_time.dart b/packages/reactter/lib/src/signal/extensions/signal_date_time.dart new file mode 100644 index 00000000..3727277f --- /dev/null +++ b/packages/reactter/lib/src/signal/extensions/signal_date_time.dart @@ -0,0 +1,664 @@ +// coverage:ignore-file +part of '../signal.dart'; + +extension SignalDateTimeExt on Signal { + /// Returns true if [this] occurs before [other]. + /// + /// The comparison is independent + /// of whether the time is in UTC or in the local time zone. + /// + /// ```dart + /// final now = DateTime.now(); + /// final earlier = now.subtract(const Duration(seconds: 5)); + /// print(earlier.isBefore(now)); // true + /// print(!now.isBefore(now)); // true + /// + /// // This relation stays the same, even when changing timezones. + /// print(earlier.isBefore(now.toUtc())); // true + /// print(earlier.toUtc().isBefore(now)); // true + /// + /// print(!now.toUtc().isBefore(now)); // true + /// print(!now.isBefore(now.toUtc())); // true + /// ``` + bool isBefore(DateTime other) => value.isBefore(other); + + /// Returns true if [this] occurs after [other]. + /// + /// The comparison is independent + /// of whether the time is in UTC or in the local time zone. + /// + /// ```dart + /// final now = DateTime.now(); + /// final later = now.add(const Duration(seconds: 5)); + /// print(later.isAfter(now)); // true + /// print(!now.isBefore(now)); // true + /// + /// // This relation stays the same, even when changing timezones. + /// print(later.isAfter(now.toUtc())); // true + /// print(later.toUtc().isAfter(now)); // true + /// + /// print(!now.toUtc().isAfter(now)); // true + /// print(!now.isAfter(now.toUtc())); // true + /// ``` + bool isAfter(DateTime other) => value.isAfter(other); + + /// Returns true if [this] occurs at the same moment as [other]. + /// + /// The comparison is independent of whether the time is in UTC or in the local + /// time zone. + /// + /// ```dart + /// final now = DateTime.now(); + /// final later = now.add(const Duration(seconds: 5)); + /// print(!later.isAtSameMomentAs(now)); // true + /// print(now.isAtSameMomentAs(now)); // true + /// + /// // This relation stays the same, even when changing timezones. + /// print(!later.isAtSameMomentAs(now.toUtc())); // true + /// print(!later.toUtc().isAtSameMomentAs(now)); // true + /// + /// print(now.toUtc().isAtSameMomentAs(now)); // true + /// print(now.isAtSameMomentAs(now.toUtc())); // true + /// ``` + bool isAtSameMomentAs(DateTime other) => value.isAtSameMomentAs(other); + + /// Compares this DateTime Object to [other], + /// returning zero if the values are equal. + /// + /// A [compareTo] function returns: + /// * a negative value if this DateTime [isBefore] [other]. + /// * `0` if this DateTime [isAtSameMomentAs] [other], and + /// * a positive value otherwise (when this DateTime [isAfter] [other]). + /// + /// ```dart + /// final now = DateTime.now(); + /// final future = now.add(const Duration(days: 2)); + /// final past = now.subtract(const Duration(days: 2)); + /// final newDate = now.toUtc(); + /// + /// print(now.compareTo(future)); // -1 + /// print(now.compareTo(past)); // 1 + /// print(now.compareTo(newDate)); // 0 + /// ``` + int compareTo(DateTime other) => value.compareTo(other); + + /// Returns this DateTime value in the local time zone. + /// + /// Returns [this] if it is already in the local time zone. + /// Otherwise this method is equivalent to: + /// + /// ```dart template:expression + /// DateTime.fromMicrosecondsSinceEpoch(microsecondsSinceEpoch, + /// isUtc: false) + /// ``` + DateTime toLocal() => value.toLocal(); + + /// Returns this DateTime value in the UTC time zone. + /// + /// Returns [this] if it is already in UTC. + /// Otherwise this method is equivalent to: + /// + /// ```dart template:expression + /// DateTime.fromMicrosecondsSinceEpoch(microsecondsSinceEpoch, + /// isUtc: true) + /// ``` + DateTime toUtc() => value.toUtc(); + + /// Returns an ISO-8601 full-precision extended format representation. + /// + /// The format is `yyyy-MM-ddTHH:mm:ss.mmmuuuZ` for UTC time, and + /// `yyyy-MM-ddTHH:mm:ss.mmmuuu` (no trailing "Z") for local/non-UTC time, + /// where: + /// + /// * `yyyy` is a, possibly negative, four digit representation of the year, + /// if the year is in the range -9999 to 9999, + /// otherwise it is a signed six digit representation of the year. + /// * `MM` is the month in the range 01 to 12, + /// * `dd` is the day of the month in the range 01 to 31, + /// * `HH` are hours in the range 00 to 23, + /// * `mm` are minutes in the range 00 to 59, + /// * `ss` are seconds in the range 00 to 59 (no leap seconds), + /// * `mmm` are milliseconds in the range 000 to 999, and + /// * `uuu` are microseconds in the range 001 to 999. If [microsecond] equals + /// 0, then this part is omitted. + /// + /// The resulting string can be parsed back using [parse]. + /// ```dart + /// final moonLanding = DateTime.utc(1969, 7, 20, 20, 18, 04); + /// final isoDate = moonLanding.toIso8601String(); + /// print(isoDate); // 1969-07-20T20:18:04.000Z + /// ``` + String toIso8601String() => value.toIso8601String(); + + /// Returns a new [DateTime] instance with [duration] added to [this]. + /// + /// ```dart + /// final today = DateTime.now(); + /// final fiftyDaysFromNow = today.add(const Duration(days: 50)); + /// ``` + /// + /// Notice that the duration being added is actually 50 * 24 * 60 * 60 + /// seconds. If the resulting `DateTime` has a different daylight saving offset + /// than `this`, then the result won't have the same time-of-day as `this`, and + /// may not even hit the calendar date 50 days later. + /// + /// Be careful when working with dates in local time. + DateTime add(Duration duration) => value.add(duration); + + /// Returns a new [DateTime] instance with [duration] subtracted from [this]. + /// + /// ```dart + /// final today = DateTime.now(); + /// final fiftyDaysAgo = today.subtract(const Duration(days: 50)); + /// ``` + /// + /// Notice that the duration being subtracted is actually 50 * 24 * 60 * 60 + /// seconds. If the resulting `DateTime` has a different daylight saving offset + /// than `this`, then the result won't have the same time-of-day as `this`, and + /// may not even hit the calendar date 50 days earlier. + /// + /// Be careful when working with dates in local time. + DateTime subtract(Duration duration) => value.subtract(duration); + + /// Returns a [Duration] with the difference when subtracting [other] from + /// [this]. + /// + /// The returned [Duration] will be negative if [other] occurs after [this]. + /// + /// ```dart + /// final berlinWallFell = DateTime.utc(1989, DateTime.november, 9); + /// final dDay = DateTime.utc(1944, DateTime.june, 6); + /// + /// final difference = berlinWallFell.difference(dDay); + /// print(difference.inDays); // 16592 + /// ``` + /// + /// The difference is measured in seconds and fractions of seconds. + /// The difference above counts the number of fractional seconds between + /// midnight at the beginning of those dates. + /// If the dates above had been in local time, not UTC, then the difference + /// between two midnights may not be a multiple of 24 hours due to daylight + /// saving differences. + /// + /// For example, in Australia, similar code using local time instead of UTC: + /// + /// ```dart + /// final berlinWallFell = DateTime(1989, DateTime.november, 9); + /// final dDay = DateTime(1944, DateTime.june, 6); + /// final difference = berlinWallFell.difference(dDay); + /// print(difference.inDays); // 16591 + /// assert(difference.inDays == 16592); + /// ``` + /// will fail because the difference is actually 16591 days and 23 hours, and + /// [Duration.inDays] only returns the number of whole days. + Duration difference(DateTime other) => value.difference(other); + + /// The number of milliseconds since + /// the "Unix epoch" 1970-01-01T00:00:00Z (UTC). + /// + /// This value is independent of the time zone. + /// + /// This value is at most + /// 8,640,000,000,000,000ms (100,000,000 days) from the Unix epoch. + /// In other words: `millisecondsSinceEpoch.abs() <= 8640000000000000`. + int get millisecondsSinceEpoch => value.millisecondsSinceEpoch; + + /// The number of microseconds since + /// the "Unix epoch" 1970-01-01T00:00:00Z (UTC). + /// + /// This value is independent of the time zone. + /// + /// This value is at most + /// 8,640,000,000,000,000,000us (100,000,000 days) from the Unix epoch. + /// In other words: `microsecondsSinceEpoch.abs() <= 8640000000000000000`. + /// + /// Note that this value does not fit into 53 bits (the size of a IEEE double). + /// A JavaScript number is not able to hold this value. + int get microsecondsSinceEpoch => value.microsecondsSinceEpoch; + + /// The time zone name. + /// + /// This value is provided by the operating system and may be an + /// abbreviation or a full name. + /// + /// In the browser or on Unix-like systems commonly returns abbreviations, + /// such as "CET" or "CEST". On Windows returns the full name, for example + /// "Pacific Standard Time". + String get timeZoneName => value.timeZoneName; + + /// The time zone offset, which + /// is the difference between local time and UTC. + /// + /// The offset is positive for time zones east of UTC. + /// + /// Note, that JavaScript, Python and C return the difference between UTC and + /// local time. Java, C# and Ruby return the difference between local time and + /// UTC. + /// + /// For example, using local time in San Francisco, United States: + /// ```dart + /// final dateUS = DateTime.parse('2021-11-01 20:18:04Z').toLocal(); + /// print(dateUS); // 2021-11-01 13:18:04.000 + /// print(dateUS.timeZoneName); // PDT ( Pacific Daylight Time ) + /// print(dateUS.timeZoneOffset.inHours); // -7 + /// print(dateUS.timeZoneOffset.inMinutes); // -420 + /// ``` + /// + /// For example, using local time in Canberra, Australia: + /// ```dart + /// final dateAus = DateTime.parse('2021-11-01 20:18:04Z').toLocal(); + /// print(dateAus); // 2021-11-02 07:18:04.000 + /// print(dateAus.timeZoneName); // AEDT ( Australian Eastern Daylight Time ) + /// print(dateAus.timeZoneOffset.inHours); // 11 + /// print(dateAus.timeZoneOffset.inMinutes); // 660 + /// ``` + Duration get timeZoneOffset => value.timeZoneOffset; + + /// The year. + /// + /// ```dart + /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); + /// print(moonLanding.year); // 1969 + /// ``` + int get year => value.year; + + /// The month `[1..12]`. + /// + /// ```dart + /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); + /// print(moonLanding.month); // 7 + /// assert(moonLanding.month == DateTime.july); + /// ``` + int get month => value.month; + + /// The day of the month `[1..31]`. + /// + /// ```dart + /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); + /// print(moonLanding.day); // 20 + /// ``` + int get day => value.day; + + /// The hour of the day, expressed as in a 24-hour clock `[0..23]`. + /// + /// ```dart + /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); + /// print(moonLanding.hour); // 20 + /// ``` + int get hour => value.hour; + + /// The minute `[0...59]`. + /// + /// ```dart + /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); + /// print(moonLanding.minute); // 18 + /// ``` + int get minute => value.minute; + + /// The second `[0...59]`. + /// + /// ```dart + /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); + /// print(moonLanding.second); // 4 + /// ``` + int get second => value.second; + + /// The millisecond `[0...999]`. + /// + /// ```dart + /// final date = DateTime.parse('1970-01-01 05:01:01.234567Z'); + /// print(date.millisecond); // 234 + /// ``` + int get millisecond => value.microsecond; + + /// The microsecond `[0...999]`. + /// + /// ```dart + /// final date = DateTime.parse('1970-01-01 05:01:01.234567Z'); + /// print(date.microsecond); // 567 + /// ``` + int get microsecond => value.microsecond; + + /// The day of the week [monday]..[sunday]. + /// + /// In accordance with ISO 8601 + /// a week starts with Monday, which has the value 1. + /// + /// ```dart + /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); + /// print(moonLanding.weekday); // 7 + /// assert(moonLanding.weekday == DateTime.sunday); + /// ``` + int get weekday => value.weekday; +} + +extension SignalDateTimeNullExt on Signal { + /// Returns true if [this] occurs before [other]. + /// + /// The comparison is independent + /// of whether the time is in UTC or in the local time zone. + /// + /// ```dart + /// final now = DateTime.now(); + /// final earlier = now.subtract(const Duration(seconds: 5)); + /// print(earlier.isBefore(now)); // true + /// print(!now.isBefore(now)); // true + /// + /// // This relation stays the same, even when changing timezones. + /// print(earlier.isBefore(now.toUtc())); // true + /// print(earlier.toUtc().isBefore(now)); // true + /// + /// print(!now.toUtc().isBefore(now)); // true + /// print(!now.isBefore(now.toUtc())); // true + /// ``` + bool? isBefore(DateTime other) => value?.isBefore(other); + + /// Returns true if [this] occurs after [other]. + /// + /// The comparison is independent + /// of whether the time is in UTC or in the local time zone. + /// + /// ```dart + /// final now = DateTime.now(); + /// final later = now.add(const Duration(seconds: 5)); + /// print(later.isAfter(now)); // true + /// print(!now.isBefore(now)); // true + /// + /// // This relation stays the same, even when changing timezones. + /// print(later.isAfter(now.toUtc())); // true + /// print(later.toUtc().isAfter(now)); // true + /// + /// print(!now.toUtc().isAfter(now)); // true + /// print(!now.isAfter(now.toUtc())); // true + /// ``` + bool? isAfter(DateTime other) => value?.isAfter(other); + + /// Returns true if [this] occurs at the same moment as [other]. + /// + /// The comparison is independent of whether the time is in UTC or in the local + /// time zone. + /// + /// ```dart + /// final now = DateTime.now(); + /// final later = now.add(const Duration(seconds: 5)); + /// print(!later.isAtSameMomentAs(now)); // true + /// print(now.isAtSameMomentAs(now)); // true + /// + /// // This relation stays the same, even when changing timezones. + /// print(!later.isAtSameMomentAs(now.toUtc())); // true + /// print(!later.toUtc().isAtSameMomentAs(now)); // true + /// + /// print(now.toUtc().isAtSameMomentAs(now)); // true + /// print(now.isAtSameMomentAs(now.toUtc())); // true + /// ``` + bool? isAtSameMomentAs(DateTime other) => value?.isAtSameMomentAs(other); + + /// Compares this DateTime Object to [other], + /// returning zero if the values are equal. + /// + /// A [compareTo] function returns: + /// * a negative value if this DateTime [isBefore] [other]. + /// * `0` if this DateTime [isAtSameMomentAs] [other], and + /// * a positive value otherwise (when this DateTime [isAfter] [other]). + /// + /// ```dart + /// final now = DateTime.now(); + /// final future = now.add(const Duration(days: 2)); + /// final past = now.subtract(const Duration(days: 2)); + /// final newDate = now.toUtc(); + /// + /// print(now.compareTo(future)); // -1 + /// print(now.compareTo(past)); // 1 + /// print(now.compareTo(newDate)); // 0 + /// ``` + int? compareTo(DateTime other) => value?.compareTo(other); + + /// Returns this DateTime value in the local time zone. + /// + /// Returns [this] if it is already in the local time zone. + /// Otherwise this method is equivalent to: + /// + /// ```dart template:expression + /// DateTime.fromMicrosecondsSinceEpoch(microsecondsSinceEpoch, + /// isUtc: false) + /// ``` + DateTime? toLocal() => value?.toLocal(); + + /// Returns this DateTime value in the UTC time zone. + /// + /// Returns [this] if it is already in UTC. + /// Otherwise this method is equivalent to: + /// + /// ```dart template:expression + /// DateTime.fromMicrosecondsSinceEpoch(microsecondsSinceEpoch, + /// isUtc: true) + /// ``` + DateTime? toUtc() => value?.toUtc(); + + /// Returns an ISO-8601 full-precision extended format representation. + /// + /// The format is `yyyy-MM-ddTHH:mm:ss.mmmuuuZ` for UTC time, and + /// `yyyy-MM-ddTHH:mm:ss.mmmuuu` (no trailing "Z") for local/non-UTC time, + /// where: + /// + /// * `yyyy` is a, possibly negative, four digit representation of the year, + /// if the year is in the range -9999 to 9999, + /// otherwise it is a signed six digit representation of the year. + /// * `MM` is the month in the range 01 to 12, + /// * `dd` is the day of the month in the range 01 to 31, + /// * `HH` are hours in the range 00 to 23, + /// * `mm` are minutes in the range 00 to 59, + /// * `ss` are seconds in the range 00 to 59 (no leap seconds), + /// * `mmm` are milliseconds in the range 000 to 999, and + /// * `uuu` are microseconds in the range 001 to 999. If [microsecond] equals + /// 0, then this part is omitted. + /// + /// The resulting string can be parsed back using [parse]. + /// ```dart + /// final moonLanding = DateTime.utc(1969, 7, 20, 20, 18, 04); + /// final isoDate = moonLanding.toIso8601String(); + /// print(isoDate); // 1969-07-20T20:18:04.000Z + /// ``` + String? toIso8601String() => value?.toIso8601String(); + + /// Returns a new [DateTime] instance with [duration] added to [this]. + /// + /// ```dart + /// final today = DateTime.now(); + /// final fiftyDaysFromNow = today.add(const Duration(days: 50)); + /// ``` + /// + /// Notice that the duration being added is actually 50 * 24 * 60 * 60 + /// seconds. If the resulting `DateTime` has a different daylight saving offset + /// than `this`, then the result won't have the same time-of-day as `this`, and + /// may not even hit the calendar date 50 days later. + /// + /// Be careful when working with dates in local time. + DateTime? add(Duration duration) => value?.add(duration); + + /// Returns a new [DateTime] instance with [duration] subtracted from [this]. + /// + /// ```dart + /// final today = DateTime.now(); + /// final fiftyDaysAgo = today.subtract(const Duration(days: 50)); + /// ``` + /// + /// Notice that the duration being subtracted is actually 50 * 24 * 60 * 60 + /// seconds. If the resulting `DateTime` has a different daylight saving offset + /// than `this`, then the result won't have the same time-of-day as `this`, and + /// may not even hit the calendar date 50 days earlier. + /// + /// Be careful when working with dates in local time. + DateTime? subtract(Duration duration) => value?.subtract(duration); + + /// Returns a [Duration] with the difference when subtracting [other] from + /// [this]. + /// + /// The returned [Duration] will be negative if [other] occurs after [this]. + /// + /// ```dart + /// final berlinWallFell = DateTime.utc(1989, DateTime.november, 9); + /// final dDay = DateTime.utc(1944, DateTime.june, 6); + /// + /// final difference = berlinWallFell.difference(dDay); + /// print(difference.inDays); // 16592 + /// ``` + /// + /// The difference is measured in seconds and fractions of seconds. + /// The difference above counts the number of fractional seconds between + /// midnight at the beginning of those dates. + /// If the dates above had been in local time, not UTC, then the difference + /// between two midnights may not be a multiple of 24 hours due to daylight + /// saving differences. + /// + /// For example, in Australia, similar code using local time instead of UTC: + /// + /// ```dart + /// final berlinWallFell = DateTime(1989, DateTime.november, 9); + /// final dDay = DateTime(1944, DateTime.june, 6); + /// final difference = berlinWallFell.difference(dDay); + /// print(difference.inDays); // 16591 + /// assert(difference.inDays == 16592); + /// ``` + /// will fail because the difference is actually 16591 days and 23 hours, and + /// [Duration.inDays] only returns the number of whole days. + Duration? difference(DateTime other) => value?.difference(other); + + /// The number of milliseconds since + /// the "Unix epoch" 1970-01-01T00:00:00Z (UTC). + /// + /// This value is independent of the time zone. + /// + /// This value is at most + /// 8,640,000,000,000,000ms (100,000,000 days) from the Unix epoch. + /// In other words: `millisecondsSinceEpoch.abs() <= 8640000000000000`. + int? get millisecondsSinceEpoch => value?.millisecondsSinceEpoch; + + /// The number of microseconds since + /// the "Unix epoch" 1970-01-01T00:00:00Z (UTC). + /// + /// This value is independent of the time zone. + /// + /// This value is at most + /// 8,640,000,000,000,000,000us (100,000,000 days) from the Unix epoch. + /// In other words: `microsecondsSinceEpoch.abs() <= 8640000000000000000`. + /// + /// Note that this value does not fit into 53 bits (the size of a IEEE double). + /// A JavaScript number is not able to hold this value. + int? get microsecondsSinceEpoch => value?.microsecondsSinceEpoch; + + /// The time zone name. + /// + /// This value is provided by the operating system and may be an + /// abbreviation or a full name. + /// + /// In the browser or on Unix-like systems commonly returns abbreviations, + /// such as "CET" or "CEST". On Windows returns the full name, for example + /// "Pacific Standard Time". + String? get timeZoneName => value?.timeZoneName; + + /// The time zone offset, which + /// is the difference between local time and UTC. + /// + /// The offset is positive for time zones east of UTC. + /// + /// Note, that JavaScript, Python and C return the difference between UTC and + /// local time. Java, C# and Ruby return the difference between local time and + /// UTC. + /// + /// For example, using local time in San Francisco, United States: + /// ```dart + /// final dateUS = DateTime.parse('2021-11-01 20:18:04Z').toLocal(); + /// print(dateUS); // 2021-11-01 13:18:04.000 + /// print(dateUS.timeZoneName); // PDT ( Pacific Daylight Time ) + /// print(dateUS.timeZoneOffset.inHours); // -7 + /// print(dateUS.timeZoneOffset.inMinutes); // -420 + /// ``` + /// + /// For example, using local time in Canberra, Australia: + /// ```dart + /// final dateAus = DateTime.parse('2021-11-01 20:18:04Z').toLocal(); + /// print(dateAus); // 2021-11-02 07:18:04.000 + /// print(dateAus.timeZoneName); // AEDT ( Australian Eastern Daylight Time ) + /// print(dateAus.timeZoneOffset.inHours); // 11 + /// print(dateAus.timeZoneOffset.inMinutes); // 660 + /// ``` + Duration? get timeZoneOffset => value?.timeZoneOffset; + + /// The year. + /// + /// ```dart + /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); + /// print(moonLanding.year); // 1969 + /// ``` + int? get year => value?.year; + + /// The month `[1..12]`. + /// + /// ```dart + /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); + /// print(moonLanding.month); // 7 + /// assert(moonLanding.month == DateTime.july); + /// ``` + int? get month => value?.month; + + /// The day of the month `[1..31]`. + /// + /// ```dart + /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); + /// print(moonLanding.day); // 20 + /// ``` + int? get day => value?.day; + + /// The hour of the day, expressed as in a 24-hour clock `[0..23]`. + /// + /// ```dart + /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); + /// print(moonLanding.hour); // 20 + /// ``` + int? get hour => value?.hour; + + /// The minute `[0...59]`. + /// + /// ```dart + /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); + /// print(moonLanding.minute); // 18 + /// ``` + int? get minute => value?.minute; + + /// The second `[0...59]`. + /// + /// ```dart + /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); + /// print(moonLanding.second); // 4 + /// ``` + int? get second => value?.second; + + /// The millisecond `[0...999]`. + /// + /// ```dart + /// final date = DateTime.parse('1970-01-01 05:01:01.234567Z'); + /// print(date.millisecond); // 234 + /// ``` + int? get millisecond => value?.microsecond; + + /// The microsecond `[0...999]`. + /// + /// ```dart + /// final date = DateTime.parse('1970-01-01 05:01:01.234567Z'); + /// print(date.microsecond); // 567 + /// ``` + int? get microsecond => value?.microsecond; + + /// The day of the week [monday]..[sunday]. + /// + /// In accordance with ISO 8601 + /// a week starts with Monday, which has the value 1. + /// + /// ```dart + /// final moonLanding = DateTime.parse('1969-07-20 20:18:04Z'); + /// print(moonLanding.weekday); // 7 + /// assert(moonLanding.weekday == DateTime.sunday); + /// ``` + int? get weekday => value?.weekday; +} diff --git a/packages/reactter/lib/src/signal/extensions/signal_double.dart b/packages/reactter/lib/src/signal/extensions/signal_double.dart new file mode 100644 index 00000000..087ad353 --- /dev/null +++ b/packages/reactter/lib/src/signal/extensions/signal_double.dart @@ -0,0 +1,334 @@ +// coverage:ignore-file +part of '../signal.dart'; + +extension SignalDoubleExt on Signal { + double operator +(num other) => value + other; + + double operator -(num other) => value - other; + + double operator *(num other) => value * other; + + double operator %(num other) => value % other; + + double operator /(num other) => value / other; + + int operator ~/(num other) => value ~/ other; + + double operator -() => -value; + + double remainder(num other) => value.remainder(other); + + /// Returns the absolute value of this integer. + /// + /// For any integer `value`, + /// the result is the same as `value < 0 ? -value : value`. + /// + /// Integer overflow may cause the result of `-value` to stay negative. + double abs() => value.abs(); + + /// The sign of the double's numerical value. + /// + /// Returns -1.0 if the value is less than zero, + /// +1.0 if the value is greater than zero, + /// and the value itself if it is -0.0, 0.0 or NaN. + double get sign => value.sign; + + /// Returns the integer closest to this number. + /// + /// Rounds away from zero when there is no closest integer: + /// `Signal(3.5).round() == 4` and `Signal(-3.5).round() == -4`. + /// + /// Throws an [UnsupportedError] if this number is not finite + /// (NaN or an infinity). + /// ```dart + /// print(Signal(3.0).round()); // 3 + /// print(Signal(3.25).round()); // 3 + /// print(Signal(3.5).round()); // 4 + /// print(Signal(3.75).round()); // 4 + /// print(Signal(-3.5).round()); // -4 + /// ``` + int round() => value.round(); + + /// Returns the greatest integer no greater than this number. + /// + /// Rounds the number towards negative infinity. + /// + /// Throws an [UnsupportedError] if this number is not finite + /// (NaN or infinity). + /// ```dart + /// print(Signal(1.99999).floor()); // 1 + /// print(Signal(2.0).floor()); // 2 + /// print(Signal(2.99999).floor()); // 2 + /// print(Signal(-1.99999).floor()); // -2 + /// print(Signal(-2.0).floor()); // -2 + /// print(Signal(-2.00001).floor()); // -3 + /// ``` + int floor() => value.floor(); + + /// Returns the least integer that is not smaller than this number. + /// + /// Rounds the number towards infinity. + /// + /// Throws an [UnsupportedError] if this number is not finite + /// (NaN or an infinity). + /// ```dart + /// print(Signal(1.99999).ceil()); // 2 + /// print(Signal(2.0).ceil()); // 2 + /// print(Signal(2.00001).ceil()); // 3 + /// print(Signal(-1.99999).ceil()); // -1 + /// print(Signal(-2.0).ceil()); // -2 + /// print(Signal(-2.00001).ceil()); // -2 + /// ``` + int ceil() => value.ceil(); + + /// Returns the integer obtained by discarding any fractional + /// part of this number. + /// + /// Rounds the number towards zero. + /// + /// Throws an [UnsupportedError] if this number is not finite + /// (NaN or an infinity). + /// ```dart + /// print(Signal(2.00001).truncate()); // 2 + /// print(Signal(1.99999).truncate()); // 1 + /// print(Signal(0.5).truncate()); // 0 + /// print(Signal(-0.5).truncate()); // 0 + /// print(Signal(-1.5).truncate()); // -1 + /// print(Signal(-2.5).truncate()); // -2 + /// ``` + int truncate() => value.truncate(); + + /// Returns the integer double value closest to `this`. + /// + /// Rounds away from zero when there is no closest integer: + /// `Signal(3.5).roundToDouble() == 4` and `Signal(-3.5).roundToDouble() == -4`. + /// + /// If this is already an integer valued double, including `-0.0`, or it is not + /// a finite value, the value is returned unmodified. + /// + /// For the purpose of rounding, `-0.0` is considered to be below `0.0`, + /// and `-0.0` is therefore considered closer to negative numbers than `0.0`. + /// This means that for a value `d` in the range `-0.5 < d < 0.0`, + /// the result is `-0.0`. + /// ```dart + /// print(Signal(3.0).roundToDouble()); // 3.0 + /// print(Signal(3.25).roundToDouble()); // 3.0 + /// print(Signal(3.5).roundToDouble()); // 4.0 + /// print(Signal(3.75).roundToDouble()); // 4.0 + /// print(Signal(-3.5).roundToDouble()); // -4.0 + /// ``` + double roundToDouble() => value.roundToDouble(); + + /// Returns the greatest integer double value no greater than `this`. + /// + /// If this is already an integer valued double, including `-0.0`, or it is not + /// a finite value, the value is returned unmodified. + /// + /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. + /// A number `d` in the range `0.0 < d < 1.0` will return `0.0`. + /// ```dart + /// print(Signal(1.99999).floorToDouble()); // 1.0 + /// print(Signal(2.0).floorToDouble()); // 2.0 + /// print(Signal(2.99999).floorToDouble()); // 2.0 + /// print(Signal(-1.99999).floorToDouble()); // -2.0 + /// print(Signal(-2.0).floorToDouble()); // -2.0 + /// print(Signal(-2.00001).floorToDouble()); // -3.0 + /// ``` + double floorToDouble() => value.floorToDouble(); + + /// Returns the least integer double value no smaller than `this`. + /// + /// If this is already an integer valued double, including `-0.0`, or it is not + /// a finite value, the value is returned unmodified. + /// + /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. + /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`. + /// ```dart + /// print(Signal(1.99999).ceilToDouble()); // 2.0 + /// print(Signal(2.0).ceilToDouble()); // 2.0 + /// print(Signal(2.00001).ceilToDouble()); // 3.0 + /// print(Signal(-1.99999).ceilToDouble()); // -1.0 + /// print(Signal(-2.0).ceilToDouble()); // -2.0 + /// print(Signal(-2.00001).ceilToDouble()); // -2.0 + /// ``` + double ceilToDouble() => value.ceilToDouble(); + + /// Returns the integer double value obtained by discarding any fractional + /// digits from `this`. + /// + /// If this is already an integer valued double, including `-0.0`, or it is not + /// a finite value, the value is returned unmodified. + /// + /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. + /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`, and + /// in the range `0.0 < d < 1.0` it will return 0.0. + /// ```dart + /// print(Signal(2.5).truncateToDouble()); // 2.0 + /// print(Signal(2.00001).truncateToDouble()); // 2.0 + /// print(Signal(1.99999).truncateToDouble()); // 1.0 + /// print(Signal(0.5).truncateToDouble()); // 0.0 + /// print(Signal(-0.5).truncateToDouble()); // -0.0 + /// print(Signal(-1.5).truncateToDouble()); // -1.0 + /// print(Signal(-2.5).truncateToDouble()); // -2.0 + /// ``` + double truncateToDouble() => value.truncateToDouble(); +} + +extension SignalDoubleNullExt on Signal { + double? remainder(num other) => value?.remainder(other); + + /// Returns the absolute value of this integer. + /// + /// For any integer `value`, + /// the result is the same as `value < 0 ? -value : value`. + /// + /// Integer overflow may cause the result of `-value` to stay negative. + double? abs() => value?.abs(); + + /// The sign of the double's numerical value. + /// + /// Returns -1.0 if the value is less than zero, + /// +1.0 if the value is greater than zero, + /// and the value itself if it is -0.0, 0.0 or NaN. + double? get sign => value?.sign; + + /// Returns the integer closest to this number. + /// + /// Rounds away from zero when there is no closest integer: + /// `Signal(3.5).round() == 4` and `Signal(-3.5).round() == -4`. + /// + /// Throws an [UnsupportedError] if this number is not finite + /// (NaN or an infinity). + /// ```dart + /// print(Signal(3.0).round()); // 3 + /// print(Signal(3.25).round()); // 3 + /// print(Signal(3.5).round()); // 4 + /// print(Signal(3.75).round()); // 4 + /// print(Signal(-3.5).round()); // -4 + /// ``` + int? round() => value?.round(); + + /// Returns the greatest integer no greater than this number. + /// + /// Rounds the number towards negative infinity. + /// + /// Throws an [UnsupportedError] if this number is not finite + /// (NaN or infinity). + /// ```dart + /// print(Signal(1.99999).floor()); // 1 + /// print(Signal(2.0).floor()); // 2 + /// print(Signal(2.99999).floor()); // 2 + /// print(Signal(-1.99999).floor()); // -2 + /// print(Signal(-2.0).floor()); // -2 + /// print(Signal(-2.00001).floor()); // -3 + /// ``` + int? floor() => value?.floor(); + + /// Returns the least integer that is not smaller than this number. + /// + /// Rounds the number towards infinity. + /// + /// Throws an [UnsupportedError] if this number is not finite + /// (NaN or an infinity). + /// ```dart + /// print(Signal(1.99999).ceil()); // 2 + /// print(Signal(2.0).ceil()); // 2 + /// print(Signal(2.00001).ceil()); // 3 + /// print(Signal(-1.99999).ceil()); // -1 + /// print(Signal(-2.0).ceil()); // -2 + /// print(Signal(-2.00001).ceil()); // -2 + /// ``` + int? ceil() => value?.ceil(); + + /// Returns the integer obtained by discarding any fractional + /// part of this number. + /// + /// Rounds the number towards zero. + /// + /// Throws an [UnsupportedError] if this number is not finite + /// (NaN or an infinity). + /// ```dart + /// print(Signal(2.00001).truncate()); // 2 + /// print(Signal(1.99999).truncate()); // 1 + /// print(Signal(0.5).truncate()); // 0 + /// print(Signal(-0.5).truncate()); // 0 + /// print(Signal(-1.5).truncate()); // -1 + /// print(Signal(-2.5).truncate()); // -2 + /// ``` + int? truncate() => value?.truncate(); + + /// Returns the integer double value closest to `this`. + /// + /// Rounds away from zero when there is no closest integer: + /// `Signal(3.5).roundToDouble() == 4` and `Signal(-3.5).roundToDouble() == -4`. + /// + /// If this is already an integer valued double, including `-0.0`, or it is not + /// a finite value, the value is returned unmodified. + /// + /// For the purpose of rounding, `-0.0` is considered to be below `0.0`, + /// and `-0.0` is therefore considered closer to negative numbers than `0.0`. + /// This means that for a value `d` in the range `-0.5 < d < 0.0`, + /// the result is `-0.0`. + /// ```dart + /// print(Signal(3.0).roundToDouble()); // 3.0 + /// print(Signal(3.25).roundToDouble()); // 3.0 + /// print(Signal(3.5).roundToDouble()); // 4.0 + /// print(Signal(3.75).roundToDouble()); // 4.0 + /// print(Signal(-3.5).roundToDouble()); // -4.0 + /// ``` + double? roundToDouble() => value?.roundToDouble(); + + /// Returns the greatest integer double value no greater than `this`. + /// + /// If this is already an integer valued double, including `-0.0`, or it is not + /// a finite value, the value is returned unmodified. + /// + /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. + /// A number `d` in the range `0.0 < d < 1.0` will return `0.0`. + /// ```dart + /// print(Signal(1.99999).floorToDouble()); // 1.0 + /// print(Signal(2.0).floorToDouble()); // 2.0 + /// print(Signal(2.99999).floorToDouble()); // 2.0 + /// print(Signal(-1.99999).floorToDouble()); // -2.0 + /// print(Signal(-2.0).floorToDouble()); // -2.0 + /// print(Signal(-2.00001).floorToDouble()); // -3.0 + /// ``` + double? floorToDouble() => value?.floorToDouble(); + + /// Returns the least integer double value no smaller than `this`. + /// + /// If this is already an integer valued double, including `-0.0`, or it is not + /// a finite value, the value is returned unmodified. + /// + /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. + /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`. + /// ```dart + /// print(Signal(1.99999).ceilToDouble()); // 2.0 + /// print(Signal(2.0).ceilToDouble()); // 2.0 + /// print(Signal(2.00001).ceilToDouble()); // 3.0 + /// print(Signal(-1.99999).ceilToDouble()); // -1.0 + /// print(Signal(-2.0).ceilToDouble()); // -2.0 + /// print(Signal(-2.00001).ceilToDouble()); // -2.0 + /// ``` + double? ceilToDouble() => value?.ceilToDouble(); + + /// Returns the integer double value obtained by discarding any fractional + /// digits from `this`. + /// + /// If this is already an integer valued double, including `-0.0`, or it is not + /// a finite value, the value is returned unmodified. + /// + /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. + /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`, and + /// in the range `0.0 < d < 1.0` it will return 0.0. + /// ```dart + /// print(Signal(2.5).truncateToDouble()); // 2.0 + /// print(Signal(2.00001).truncateToDouble()); // 2.0 + /// print(Signal(1.99999).truncateToDouble()); // 1.0 + /// print(Signal(0.5).truncateToDouble()); // 0.0 + /// print(Signal(-0.5).truncateToDouble()); // -0.0 + /// print(Signal(-1.5).truncateToDouble()); // -1.0 + /// print(Signal(-2.5).truncateToDouble()); // -2.0 + /// ``` + double? truncateToDouble() => value?.truncateToDouble(); +} diff --git a/packages/reactter/lib/src/signal/extensions/signal_int.dart b/packages/reactter/lib/src/signal/extensions/signal_int.dart new file mode 100644 index 00000000..b573575a --- /dev/null +++ b/packages/reactter/lib/src/signal/extensions/signal_int.dart @@ -0,0 +1,476 @@ +// coverage:ignore-file +part of '../signal.dart'; + +extension SignalIntExt on Signal { + /// Bit-wise and operator. + /// + /// Treating both `this` and [other] as sufficiently large two's component + /// integers, the result is a number with only the bits set that are set in + /// both `this` and [other] + /// + /// If both operands are negative, the result is negative, otherwise + /// the result is non-negative. + /// ```dart + /// print((Signal(2) & 1).toRadixString(2)); // 0010 & 0001 -> 0000 + /// print((Signal(3) & 1).toRadixString(2)); // 0011 & 0001 -> 0001 + /// print((Signal(10) & 2).toRadixString(2)); // 1010 & 0010 -> 0010 + /// ``` + int operator &(int other) => value & other; + + /// Bit-wise or operator. + /// + /// Treating both `this` and [other] as sufficiently large two's component + /// integers, the result is a number with the bits set that are set in either + /// of `this` and [other] + /// + /// If both operands are non-negative, the result is non-negative, + /// otherwise the result is negative. + /// + /// Example: + /// ```dart + /// print((Signal(2) | 1).toRadixString(2)); // 0010 | 0001 -> 0011 + /// print((Signal(3) | 1).toRadixString(2)); // 0011 | 0001 -> 0011 + /// print((Signal(10) | 2).toRadixString(2)); // 1010 | 0010 -> 1010 + /// ``` + int operator |(int other) => value | other; + + /// Bit-wise exclusive-or operator. + /// + /// Treating both `this` and [other] as sufficiently large two's component + /// integers, the result is a number with the bits set that are set in one, + /// but not both, of `this` and [other] + /// + /// If the operands have the same sign, the result is non-negative, + /// otherwise the result is negative. + /// + /// Example: + /// ```dart + /// print((Signal(2) ^ 1).toRadixString(2)); // 0010 ^ 0001 -> 0011 + /// print((Signal(3) ^ 1).toRadixString(2)); // 0011 ^ 0001 -> 0010 + /// print((Signal(10) ^ 2).toRadixString(2)); // 1010 ^ 0010 -> 1000 + /// ``` + int operator ^(int other) => value ^ other; + + /// The bit-wise negate operator. + /// + /// Treating `this` as a sufficiently large two's component integer, + /// the result is a number with the opposite bits set. + /// + /// This maps any integer `x` to `-x - 1`. + int operator ~() => ~value; + + /// Shift the bits of this integer to the left by [shiftAmount]. + /// + /// Shifting to the left makes the number larger, effectively multiplying + /// the number by `pow(2, shiftIndex)`. + /// + /// There is no limit on the size of the result. It may be relevant to + /// limit intermediate values by using the "and" operator with a suitable + /// mask. + /// + /// It is an error if [shiftAmount] is negative. + /// + /// Example: + /// ```dart + /// print((Signal(3) << 1).toRadixString(2)); // 0011 -> 0110 + /// print((Signal(9) << 2).toRadixString(2)); // 1001 -> 100100 + /// print((Signal(10) << 3).toRadixString(2)); // 1010 -> 1010000 + /// ``` + int operator <<(int shiftAmount) => value << shiftAmount; + + /// Shift the bits of this integer to the right by [shiftAmount]. + /// + /// Shifting to the right makes the number smaller and drops the least + /// significant bits, effectively doing an integer division by + /// `pow(2, shiftIndex)`. + /// + /// It is an error if [shiftAmount] is negative. + /// + /// Example: + /// ```dart + /// print((Signal(3) >> 1).toRadixString(2)); // 0011 -> 0001 + /// print((Signal(9) >> 2).toRadixString(2)); // 1001 -> 0010 + /// print((Signal(10) >> 3).toRadixString(2)); // 1010 -> 0001 + /// print((Signal(-6) >> 2).toRadixString); // 111...1010 -> 111...1110 == -2 + /// print((Signal(-85) >> 3).toRadixString); // 111...10101011 -> 111...11110101 == -11 + /// ``` + int operator >>(int shiftAmount) => value >> shiftAmount; + + /// Bitwise unsigned right shift by [shiftAmount] bits. + /// + /// The least significant [shiftAmount] bits are dropped, + /// the remaining bits (if any) are shifted down, + /// and zero-bits are shifted in as the new most significant bits. + /// + /// The [shiftAmount] must be non-negative. + /// + /// Example: + /// ```dart + /// print((Signal(3) >>> 1).toRadixString(2)); // 0011 -> 0001 + /// print((Signal(9) >>> 2).toRadixString(2)); // 1001 -> 0010 + /// print((Signal(-9) >>> 2).toRadixString(2)); // 111...1011 -> 001...1110 (> 0) + /// ``` + int operator >>>(int shiftAmount) => value >>> shiftAmount; + + /// Return the negative value of this integer. + /// + /// The result of negating an integer always has the opposite sign, except + /// for zero, which is its own negation. + int operator -() => -value; + + /// Returns this integer to the power of [exponent] modulo [modulus]. + /// + /// The [exponent] must be non-negative and [modulus] must be + /// positive. + int modPow(int exponent, int modulus) => value.modPow(exponent, modulus); + + /// Returns the modular multiplicative inverse of this integer + /// modulo [modulus]. + /// + /// The [modulus] must be positive. + /// + /// It is an error if no modular inverse exists. + int modInverse(int modulus) => value.modInverse(modulus); + + /// Returns the greatest common divisor of this integer and [other]. + /// + /// If either number is non-zero, the result is the numerically greatest + /// integer dividing both `this` and `other`. + /// + /// The greatest common divisor is independent of the order, + /// so `x.gcd(y)` is always the same as `y.gcd(x)`. + /// + /// For any integer `x`, `x.gcd(x)` is `x.abs()`. + /// + /// If both `this` and `other` is zero, the result is also zero. + /// + /// Example: + /// ```dart + /// print(Signal(4).gcd(2)); // 2 + /// print(Signal(8).gcd(4)); // 4 + /// print(Signal(10).gcd(12)); // 2 + /// print(Signal(10).gcd(0)); // 10 + /// print(Signal(-2).gcd(-3)); // 1 + /// ``` + int gcd(int other) => value.gcd(other); + + /// Returns true if and only if this integer is even. + bool get isEven => value.isEven; + + /// Returns true if and only if this integer is odd. + bool get isOdd => value.isOdd; + + /// Returns the minimum number of bits required to store this integer. + /// + /// The number of bits excludes the sign bit, which gives the natural length + /// for non-negative (unsigned) values. Negative values are complemented to + /// return the bit position of the first bit that differs from the sign bit. + /// + /// To find the number of bits needed to store the value as a signed value, + /// add one, i.e. use `x.bitLength + 1`. + /// ```dart + /// x.bitLength == (-x-1).bitLength; + /// + /// Signal(3).bitLength == 2; // 00000011 + /// Signal(2).bitLength == 2; // 00000010 + /// Signal(1).bitLength == 1; // 00000001 + /// Signal(0).bitLength == 0; // 00000000 + /// Signal(-1).bitLength == 0; // 11111111 + /// Signal(-2).bitLength == 1; // 11111110 + /// Signal(-3).bitLength == 2; // 11111101 + /// Signal(-4).bitLength == 2; // 11111100 + /// ``` + int get bitLength => value.bitLength; + + /// Returns the least significant [width] bits of this integer as a + /// non-negative number (i.e. unsigned representation). The returned value has + /// zeros in all bit positions higher than [width]. + /// ```dart + /// Signal(-1).toUnsigned(5) == 31 // 11111111 -> 00011111 + /// ``` + /// This operation can be used to simulate arithmetic from low level languages. + /// For example, to increment an 8 bit quantity: + /// ```dart + /// q = (q + 1).toUnsigned(8); + /// ``` + /// `q` will count from `0` up to `255` and then wrap around to `0`. + /// + /// If the input fits in [width] bits without truncation, the result is the + /// same as the input. The minimum width needed to avoid truncation of `x` is + /// given by `x.bitLength`, i.e. + /// ```dart + /// x == x.toUnsigned(x.bitLength); + /// ``` + int toUnsigned(int width) => value.toUnsigned(width); + + /// Returns the least significant [width] bits of this integer, extending the + /// highest retained bit to the sign. This is the same as truncating the value + /// to fit in [width] bits using an signed 2-s complement representation. The + /// returned value has the same bit value in all positions higher than [width]. + /// + /// ```dart + /// // V--sign bit-V + /// Signal(16).toSigned(5) == -16; // 00010000 -> 11110000 + /// Signal(239).toSigned(5) == 15; // 11101111 -> 00001111 + /// // ^ ^ + /// ``` + /// This operation can be used to simulate arithmetic from low level languages. + /// For example, to increment an 8 bit signed quantity: + /// ```dart + /// q = (q + 1).toSigned(8); + /// ``` + /// `q` will count from `0` up to `127`, wrap to `-128` and count back up to + /// `127`. + /// + /// If the input value fits in [width] bits without truncation, the result is + /// the same as the input. The minimum width needed to avoid truncation of `x` + /// is `x.bitLength + 1`, i.e. + /// ```dart + /// x == x.toSigned(x.bitLength + 1); + /// ``` + int toSigned(int width) => value.toSigned(width); + + /// Returns the absolute value of this integer. + /// + /// For any integer `value`, + /// the result is the same as `value < 0 ? -value : value`. + /// + /// Integer overflow may cause the result of `-value` to stay negative. + int abs() => value.abs(); + + /// Returns the sign of this integer. + /// + /// Returns 0 for zero, -1 for values less than zero and + /// +1 for values greater than zero. + int get sign => value.sign; + + /// Returns `this`. + int round() => value.round(); + + /// Returns `this`. + int floor() => value.floor(); + + /// Returns `this`. + int ceil() => value.ceil(); + + /// Returns `this`. + int truncate() => value.truncate(); + + /// Returns `this.toDouble()`. + double roundToDouble() => value.roundToDouble(); + + /// Returns `this.toDouble()`. + double floorToDouble() => value.floorToDouble(); + + /// Returns `this.toDouble()`. + double ceilToDouble() => value.ceilToDouble(); + + /// Returns `this.toDouble()`. + double truncateToDouble() => value.truncateToDouble(); + + /// Converts [this] to a string representation in the given [radix]. + /// + /// In the string representation, lower-case letters are used for digits above + /// '9', with 'a' being 10 and 'z' being 35. + /// + /// The [radix] argument must be an integer in the range 2 to 36. + /// + /// Example: + /// ```dart + /// // Binary (base 2). + /// print(Signal(12).toRadixString(2)); // 1100 + /// print(Signal(31).toRadixString(2)); // 11111 + /// print(Signal(2021).toRadixString(2)); // 11111100101 + /// print(Signal(-12).toRadixString(2)); // -1100 + /// // Octal (base 8). + /// print(Signal(12).toRadixString(8)); // 14 + /// print(Signal(31).toRadixString(8)); // 37 + /// print(Signal(2021).toRadixString(8)); // 3745 + /// // Hexadecimal (base 16). + /// print(Signal(12).toRadixString(16)); // c + /// print(Signal(31).toRadixString(16)); // 1f + /// print(Signal(2021).toRadixString(16)); // 7e5 + /// // Base 36. + /// print(Signal(35 * 36 + 1).toRadixString(36)); // z1 + /// ``` + String toRadixString(int radix) => value.toRadixString(radix); +} + +extension SignalIntNullExt on Signal { + /// Returns this integer to the power of [exponent] modulo [modulus]. + /// + /// The [exponent] must be non-negative and [modulus] must be + /// positive. + int? modPow(int exponent, int modulus) => value?.modPow(exponent, modulus); + + /// Returns the modular multiplicative inverse of this integer + /// modulo [modulus]. + /// + /// The [modulus] must be positive. + /// + /// It is an error if no modular inverse exists. + int? modInverse(int modulus) => value?.modInverse(modulus); + + /// Returns the greatest common divisor of this integer and [other]. + /// + /// If either number is non-zero, the result is the numerically greatest + /// integer dividing both `this` and `other`. + /// + /// The greatest common divisor is independent of the order, + /// so `x.gcd(y)` is always the same as `y.gcd(x)`. + /// + /// For any integer `x`, `x.gcd(x)` is `x.abs()`. + /// + /// If both `this` and `other` is zero, the result is also zero. + /// + /// Example: + /// ```dart + /// print(Signal(4).gcd(2)); // 2 + /// print(Signal(8).gcd(4)); // 4 + /// print(Signal(10).gcd(12)); // 2 + /// print(Signal(10).gcd(0)); // 10 + /// print(Signal(-2).gcd(-3)); // 1 + /// ``` + int? gcd(int other) => value?.gcd(other); + + /// Returns true if and only if this integer is even. + bool? get isEven => value?.isEven; + + /// Returns true if and only if this integer is odd. + bool? get isOdd => value?.isOdd; + + /// Returns the minimum number of bits required to store this integer. + /// + /// The number of bits excludes the sign bit, which gives the natural length + /// for non-negative (unsigned) values. Negative values are complemented to + /// return the bit position of the first bit that differs from the sign bit. + /// + /// To find the number of bits needed to store the value as a signed value, + /// add one, i.e. use `x.bitLength + 1`. + /// ```dart + /// x.bitLength == (-x-1).bitLength; + /// + /// Signal(3).bitLength == 2; // 00000011 + /// Signal(2).bitLength == 2; // 00000010 + /// Signal(1).bitLength == 1; // 00000001 + /// Signal(0).bitLength == 0; // 00000000 + /// Signal(-1).bitLength == 0; // 11111111 + /// Signal(-2).bitLength == 1; // 11111110 + /// Signal(-3).bitLength == 2; // 11111101 + /// Signal(-4).bitLength == 2; // 11111100 + /// ``` + int? get bitLength => value?.bitLength; + + /// Returns the least significant [width] bits of this integer as a + /// non-negative number (i.e. unsigned representation). The returned value has + /// zeros in all bit positions higher than [width]. + /// ```dart + /// Signal(-1).toUnsigned(5) == 31 // 11111111 -> 00011111 + /// ``` + /// This operation can be used to simulate arithmetic from low level languages. + /// For example, to increment an 8 bit quantity: + /// ```dart + /// q = (q + 1).toUnsigned(8); + /// ``` + /// `q` will count from `0` up to `255` and then wrap around to `0`. + /// + /// If the input fits in [width] bits without truncation, the result is the + /// same as the input. The minimum width needed to avoid truncation of `x` is + /// given by `x.bitLength`, i.e. + /// ```dart + /// x == x.toUnsigned(x.bitLength); + /// ``` + int? toUnsigned(int width) => value?.toUnsigned(width); + + /// Returns the least significant [width] bits of this integer, extending the + /// highest retained bit to the sign. This is the same as truncating the value + /// to fit in [width] bits using an signed 2-s complement representation. The + /// returned value has the same bit value in all positions higher than [width]. + /// + /// ```dart + /// // V--sign bit-V + /// Signal(16).toSigned(5) == -16; // 00010000 -> 11110000 + /// Signal(239).toSigned(5) == 15; // 11101111 -> 00001111 + /// // ^ ^ + /// ``` + /// This operation can be used to simulate arithmetic from low level languages. + /// For example, to increment an 8 bit signed quantity: + /// ```dart + /// q = (q + 1).toSigned(8); + /// ``` + /// `q` will count from `0` up to `127`, wrap to `-128` and count back up to + /// `127`. + /// + /// If the input value fits in [width] bits without truncation, the result is + /// the same as the input. The minimum width needed to avoid truncation of `x` + /// is `x.bitLength + 1`, i.e. + /// ```dart + /// x == x.toSigned(x.bitLength + 1); + /// ``` + int? toSigned(int width) => value?.toSigned(width); + + /// Returns the absolute value of this integer. + /// + /// For any integer `value`, + /// the result is the same as `value < 0 ? -value : value`. + /// + /// Integer overflow may cause the result of `-value` to stay negative. + int? abs() => value?.abs(); + + /// Returns the sign of this integer. + /// + /// Returns 0 for zero, -1 for values less than zero and + /// +1 for values greater than zero. + int? get sign => value?.sign; + + /// Returns `this`. + int? round() => value?.round(); + + /// Returns `this`. + int? floor() => value?.floor(); + + /// Returns `this`. + int? ceil() => value?.ceil(); + + /// Returns `this`. + int? truncate() => value?.truncate(); + + /// Returns `this.toDouble()`. + double? roundToDouble() => value?.roundToDouble(); + + /// Returns `this.toDouble()`. + double? floorToDouble() => value?.floorToDouble(); + + /// Returns `this.toDouble()`. + double? ceilToDouble() => value?.ceilToDouble(); + + /// Returns `this.toDouble()`. + double? truncateToDouble() => value?.truncateToDouble(); + + /// Converts [this] to a string representation in the given [radix]. + /// + /// In the string representation, lower-case letters are used for digits above + /// '9', with 'a' being 10 and 'z' being 35. + /// + /// The [radix] argument must be an integer in the range 2 to 36. + /// + /// Example: + /// ```dart + /// // Binary (base 2). + /// print(Signal(12).toRadixString(2)); // 1100 + /// print(Signal(31).toRadixString(2)); // 11111 + /// print(Signal(2021).toRadixString(2)); // 11111100101 + /// print(Signal(-12).toRadixString(2)); // -1100 + /// // Octal (base 8). + /// print(Signal(12).toRadixString(8)); // 14 + /// print(Signal(31).toRadixString(8)); // 37 + /// print(Signal(2021).toRadixString(8)); // 3745 + /// // Hexadecimal (base 16). + /// print(Signal(12).toRadixString(16)); // c + /// print(Signal(31).toRadixString(16)); // 1f + /// print(Signal(2021).toRadixString(16)); // 7e5 + /// // Base 36. + /// print(Signal(35 * 36 + 1).toRadixString(36)); // z1 + /// ``` + String? toRadixString(int radix) => value?.toRadixString(radix); +} diff --git a/packages/reactter/lib/src/signal/extensions/signal_iterable.dart b/packages/reactter/lib/src/signal/extensions/signal_iterable.dart new file mode 100644 index 00000000..75bcd116 --- /dev/null +++ b/packages/reactter/lib/src/signal/extensions/signal_iterable.dart @@ -0,0 +1,1070 @@ +// coverage:ignore-file +part of '../signal.dart'; + +extension SignalIterableExt on Signal> { + /// Returns a new `Iterator` that allows iterating the elements of this + /// `Iterable`. + /// + /// Iterable classes may specify the iteration order of their elements + /// (for example [List] always iterate in index order), + /// or they may leave it unspecified (for example a hash-based [Set] + /// may iterate in any order). + /// + /// Each time `iterator` is read, it returns a new iterator, + /// which can be used to iterate through all the elements again. + /// The iterators of the same iterable can be stepped through independently, + /// but should return the same elements in the same order, + /// as long as the underlying collection isn't changed. + /// + /// Modifying the collection may cause new iterators to produce + /// different elements, and may change the order of existing elements. + /// A [List] specifies its iteration order precisely, + /// so modifying the list changes the iteration order predictably. + /// A hash-based [Set] may change its iteration order completely + /// when adding a new element to the set. + /// + /// Modifying the underlying collection after creating the new iterator + /// may cause an error the next time [Iterator.moveNext] is called + /// on that iterator. + /// Any *modifiable* iterable class should specify which operations will + /// break iteration. + Iterator get iterator => value.iterator; + + /// Provides a view of this iterable as an iterable of [R] instances. + /// + /// If this iterable only contains instances of [R], all operations + /// will work correctly. If any operation tries to access an element + /// that is not an instance of [R], the access will throw instead. + /// + /// When the returned iterable creates a new object that depends on + /// the type [R], e.g., from [toList], it will have exactly the type [R]. + Iterable cast() => value.cast(); + + /// Returns the lazy concatenation of this iterable and [other]. + /// + /// The returned iterable will provide the same elements as this iterable, + /// and, after that, the elements of [other], in the same order as in the + /// original iterables. + /// + /// Example: + /// ```dart + /// var planets = ['Earth', 'Jupiter']; + /// var updated = planets.followedBy(['Mars', 'Venus']); + /// print(updated); // (Earth, Jupiter, Mars, Venus) + /// ``` + Iterable followedBy(Iterable other) => value.followedBy(other); + + /// The current elements of this iterable modified by [toElement]. + /// + /// Returns a new lazy [Iterable] with elements that are created by + /// calling `toElement` on each element of this `Iterable` in + /// iteration order. + /// + /// The returned iterable is lazy, so it won't iterate the elements of + /// this iterable until it is itself iterated, and then it will apply + /// [toElement] to create one element at a time. + /// The converted elements are not cached. + /// Iterating multiple times over the returned [Iterable] + /// will invoke the supplied [toElement] function once per element + /// for on each iteration. + /// + /// Methods on the returned iterable are allowed to omit calling `toElement` + /// on any element where the result isn't needed. + /// For example, [elementAt] may call `toElement` only once. + /// + /// Equivalent to: + /// ``` + /// Iterable map(T toElement(E e)) sync* { + /// for (var value in this) { + /// yield toElement(value); + /// } + /// } + /// ``` + /// Example: + /// ```dart import:convert + /// var products = jsonDecode(''' + /// [ + /// {"name": "Screwdriver", "price": 42.00}, + /// {"name": "Wingnut", "price": 0.50} + /// ] + /// '''); + /// var values = products.map((product) => product['price'] as double); + /// var totalPrice = values.fold(0.0, (a, b) => a + b); // 42.5. + /// ``` + Iterable map(T Function(E e) toElement) => value.map(toElement); + + /// Returns a new lazy [Iterable] with all elements that satisfy the + /// predicate [test]. + /// + /// The matching elements have the same order in the returned iterable + /// as they have in [iterator]. + /// + /// This method returns a view of the mapped elements. + /// As long as the returned [Iterable] is not iterated over, + /// the supplied function [test] will not be invoked. + /// Iterating will not cache results, and thus iterating multiple times over + /// the returned [Iterable] may invoke the supplied + /// function [test] multiple times on the same element. + /// + /// Example: + /// ```dart + /// final numbers = [1, 2, 3, 5, 6, 7]; + /// var result = numbers.where((x) => x < 5); // (1, 2, 3) + /// result = numbers.where((x) => x > 5); // (6, 7) + /// result = numbers.where((x) => x.isEven); // (2, 6) + /// ``` + Iterable where(bool Function(E element) test) => value.where(test); + + /// Returns a new lazy [Iterable] with all elements that have type [T]. + /// + /// The matching elements have the same order in the returned iterable + /// as they have in [iterator]. + /// + /// This method returns a view of the mapped elements. + /// Iterating will not cache results, and thus iterating multiple times over + /// the returned [Iterable] may yield different results, + /// if the underlying elements change between iterations. + Iterable whereType() => value.whereType(); + + /// Expands each element of this [Iterable] into zero or more elements. + /// + /// The resulting Iterable runs through the elements returned + /// by [toElements] for each element of this, in iteration order. + /// + /// The returned [Iterable] is lazy, and calls [toElements] for each element + /// of this iterable every time the returned iterable is iterated. + /// + /// Example: + /// ```dart + /// Iterable count(int n) sync* { + /// for (var i = 1; i <= n; i++) { + /// yield i; + /// } + /// } + /// + /// var numbers = [1, 3, 0, 2]; + /// print(numbers.expand(count)); // (1, 1, 2, 3, 1, 2) + /// ``` + /// + /// Equivalent to: + /// ``` + /// Iterable expand(Iterable toElements(E e)) sync* { + /// for (var value in this) { + /// yield* toElements(value); + /// } + /// } + /// ``` + Iterable expand(Iterable Function(E element) toElements) => + value.expand(toElements); + + /// Whether the collection contains an element equal to [element]. + /// + /// This operation will check each element in order for being equal to + /// [element], unless it has a more efficient way to find an element + /// equal to [element]. + /// + /// The equality used to determine whether [element] is equal to an element of + /// the iterable defaults to the [Object.==] of the element. + /// + /// Some types of iterable may have a different equality used for its elements. + /// For example, a [Set] may have a custom equality + /// (see [Set.identity]) that its `contains` uses. + /// Likewise the `Iterable` returned by a [Map.keys] call + /// should use the same equality that the `Map` uses for keys. + /// + /// Example: + /// ```dart + /// final gasPlanets = {1: 'Jupiter', 2: 'Saturn'}; + /// final containsOne = gasPlanets.keys.contains(1); // true + /// final containsFive = gasPlanets.keys.contains(5); // false + /// final containsJupiter = gasPlanets.values.contains('Jupiter'); // true + /// final containsMercury = gasPlanets.values.contains('Mercury'); // false + /// ``` + bool contains(Object? element) => value.contains(element); + + /// Invokes [action] on each element of this iterable in iteration order. + /// + /// Example: + /// ```dart + /// final numbers = [1, 2, 6, 7]; + /// numbers.forEach(print); + /// // 1 + /// // 2 + /// // 6 + /// // 7 + /// ``` + void forEach(void Function(E element) action) => value.forEach(action); + + /// Reduces a collection to a single value by iteratively combining elements + /// of the collection using the provided function. + /// + /// The iterable must have at least one element. + /// If it has only one element, that element is returned. + /// + /// Otherwise this method starts with the first element from the iterator, + /// and then combines it with the remaining elements in iteration order, + /// as if by: + /// ``` + /// E value = iterable.first; + /// iterable.skip(1).forEach((element) { + /// value = combine(value, element); + /// }); + /// return value; + /// ``` + /// Example of calculating the sum of an iterable: + /// ```dart + /// final numbers = [10, 2, 5, 0.5]; + /// final result = numbers.reduce((value, element) => value + element); + /// print(result); // 17.5 + /// ``` + E reduce(E Function(E value, E element) combine) => value.reduce(combine); + + /// Reduces a collection to a single value by iteratively combining each + /// element of the collection with an existing value + /// + /// Uses [initialValue] as the initial value, + /// then iterates through the elements and updates the value with + /// each element using the [combine] function, as if by: + /// ``` + /// var value = initialValue; + /// for (E element in this) { + /// value = combine(value, element); + /// } + /// return value; + /// ``` + /// Example of calculating the sum of an iterable: + /// ```dart + /// final numbers = [10, 2, 5, 0.5]; + /// const initialValue = 100.0; + /// final result = numbers.fold( + /// initialValue, (previousValue, element) => previousValue + element); + /// print(result); // 117.5 + /// ``` + T fold(T initialValue, T Function(T previousValue, E element) combine) => + value.fold(initialValue, combine); + + /// Checks whether every element of this iterable satisfies [test]. + /// + /// Checks every element in iteration order, and returns `false` if + /// any of them make [test] return `false`, otherwise returns `true`. + /// + /// Example: + /// ```dart + /// final planetsByMass = {0.06: 'Mercury', 0.81: 'Venus', + /// 0.11: 'Mars'}; + /// // Checks whether all keys are smaller than 1. + /// final every = planetsByMass.keys.every((key) => key < 1.0); // true + /// ``` + bool every(bool Function(E element) test) => value.every(test); + + /// Converts each element to a [String] and concatenates the strings. + /// + /// Iterates through elements of this iterable, + /// converts each one to a [String] by calling [Object.toString], + /// and then concatenates the strings, with the + /// [separator] string interleaved between the elements. + /// + /// Example: + /// ```dart + /// final planetsByMass = {0.06: 'Mercury', 0.81: 'Venus', + /// 0.11: 'Mars'}; + /// final joinedNames = planetsByMass.values.join('-'); // Mercury-Venus-Mars + /// ``` + String join([String separator = ""]) => value.join(separator); + + /// Checks whether any element of this iterable satisfies [test]. + /// + /// Checks every element in iteration order, and returns `true` if + /// any of them make [test] return `true`, otherwise returns false. + /// + /// Example: + /// ```dart + /// final numbers = [1, 2, 3, 5, 6, 7]; + /// var result = numbers.any((element) => element >= 5); // true; + /// result = numbers.any((element) => element >= 10); // false; + /// ``` + bool any(bool Function(E element) test) => value.any(test); + + /// Creates a [List] containing the elements of this [Iterable]. + /// + /// The elements are in iteration order. + /// The list is fixed-length if [growable] is false. + /// + /// Example: + /// ```dart + /// final planets = {1: 'Mercury', 2: 'Venus', 3: 'Mars'}; + /// final keysList = planets.keys.toList(growable: false); // [1, 2, 3] + /// final valuesList = + /// planets.values.toList(growable: false); // [Mercury, Venus, Mars] + /// ``` + List toList({bool growable = true}) => value.toList(growable: growable); + + /// Creates a [Set] containing the same elements as this iterable. + /// + /// The set may contain fewer elements than the iterable, + /// if the iterable contains an element more than once, + /// or it contains one or more elements that are equal. + /// The order of the elements in the set is not guaranteed to be the same + /// as for the iterable. + /// + /// Example: + /// ```dart + /// final planets = {1: 'Mercury', 2: 'Venus', 3: 'Mars'}; + /// final valueSet = planets.values.toSet(); // {Mercury, Venus, Mars} + /// ``` + Set toSet() => value.toSet(); + + /// Returns the number of elements in [this]. + /// + /// Counting all elements may involve iterating through all elements and can + /// therefore be slow. + /// Some iterables have a more efficient way to find the number of elements. + int get length => value.length; + + /// Whether this collection has no elements. + /// + /// May be computed by checking if `iterator.moveNext()` returns `false`. + /// + /// Example: + /// ```dart + /// final emptyList = []; + /// print(emptyList.isEmpty); // true; + /// print(emptyList.iterator.moveNext()); // false + /// ``` + bool get isEmpty => value.isEmpty; + + /// Whether this collection has at least one element. + /// + /// May be computed by checking if `iterator.moveNext()` returns `true`. + /// + /// Example: + /// ```dart + /// final numbers = {1, 2, 3}; + /// print(numbers.isNotEmpty); // true; + /// print(numbers.iterator.moveNext()); // true + /// ``` + bool get isNotEmpty => value.isNotEmpty; + + /// Returns a lazy iterable of the [count] first elements of this iterable. + /// + /// The returned `Iterable` may contain fewer than `count` elements, if `this` + /// contains fewer than `count` elements. + /// + /// The elements can be computed by stepping through [iterator] until [count] + /// elements have been seen. + /// + /// The `count` must not be negative. + /// + /// Example: + /// ```dart + /// final numbers = [1, 2, 3, 5, 6, 7]; + /// final result = numbers.take(4); // (1, 2, 3, 5) + /// final takeAll = numbers.take(100); // (1, 2, 3, 5, 6, 7) + /// ``` + Iterable take(int count) => value.take(count); + + /// Returns a lazy iterable of the leading elements satisfying [test]. + /// + /// The filtering happens lazily. Every new iterator of the returned + /// iterable starts iterating over the elements of `this`. + /// + /// The elements can be computed by stepping through [iterator] until an + /// element is found where `test(element)` is false. At that point, + /// the returned iterable stops (its `moveNext()` returns false). + /// + /// Example: + /// ```dart + /// final numbers = [1, 2, 3, 5, 6, 7]; + /// var result = numbers.takeWhile((x) => x < 5); // (1, 2, 3) + /// result = numbers.takeWhile((x) => x != 3); // (1, 2) + /// result = numbers.takeWhile((x) => x != 4); // (1, 2, 3, 5, 6, 7) + /// result = numbers.takeWhile((x) => x.isOdd); // (1) + /// ``` + Iterable takeWhile(bool Function(E value) test) => value.takeWhile(test); + + /// Returns an [Iterable] that provides all but the first [count] elements. + /// + /// When the returned iterable is iterated, it starts iterating over `this`, + /// first skipping past the initial [count] elements. + /// If `this` has fewer than `count` elements, then the resulting Iterable is + /// empty. + /// After that, the remaining elements are iterated in the same order as + /// in this iterable. + /// + /// Some iterables may be able to find later elements without first iterating + /// through earlier elements, for example when iterating a [List]. + /// Such iterables are allowed to ignore the initial skipped elements. + /// + /// Example: + /// ```dart + /// final numbers = [1, 2, 3, 5, 6, 7]; + /// final result = numbers.skip(4); // (6, 7) + /// final skipAll = numbers.skip(100); // () - no elements. + /// ``` + /// + /// The [count] must not be negative. + Iterable skip(int count) => value.skip(count); + + /// Returns an `Iterable` that skips leading elements while [test] is satisfied. + /// + /// The filtering happens lazily. Every new [Iterator] of the returned + /// iterable iterates over all elements of `this`. + /// + /// The returned iterable provides elements by iterating this iterable, + /// but skipping over all initial elements where `test(element)` returns + /// true. If all elements satisfy `test` the resulting iterable is empty, + /// otherwise it iterates the remaining elements in their original order, + /// starting with the first element for which `test(element)` returns `false`. + /// + /// Example: + /// ```dart + /// final numbers = [1, 2, 3, 5, 6, 7]; + /// var result = numbers.skipWhile((x) => x < 5); // (5, 6, 7) + /// result = numbers.skipWhile((x) => x != 3); // (3, 5, 6, 7) + /// result = numbers.skipWhile((x) => x != 4); // () + /// result = numbers.skipWhile((x) => x.isOdd); // (2, 3, 5, 6, 7) + /// ``` + Iterable skipWhile(bool Function(E value) test) => value.skipWhile(test); + + /// Returns the first element. + /// + /// Throws a [StateError] if `this` is empty. + /// Otherwise returns the first element in the iteration order, + /// equivalent to `this.elementAt(0)`. + E get first => value.first; + + /// Returns the last element. + /// + /// Throws a [StateError] if `this` is empty. + /// Otherwise may iterate through the elements and returns the last one + /// seen. + /// Some iterables may have more efficient ways to find the last element + /// (for example a list can directly access the last element, + /// without iterating through the previous ones). + E get last => value.last; + + /// Checks that this iterable has only one element, and returns that element. + /// + /// Throws a [StateError] if `this` is empty or has more than one element. + E get single => value.single; + + /// Returns the first element that satisfies the given predicate [test]. + /// + /// Iterates through elements and returns the first to satisfy [test]. + /// + /// Example: + /// ```dart + /// final numbers = [1, 2, 3, 5, 6, 7]; + /// var result = numbers.firstWhere((element) => element < 5); // 1 + /// result = numbers.firstWhere((element) => element > 5); // 6 + /// result = + /// numbers.firstWhere((element) => element > 10, orElse: () => -1); // -1 + /// ``` + /// + /// If no element satisfies [test], the result of invoking the [orElse] + /// function is returned. + /// If [orElse] is omitted, it defaults to throwing a [StateError]. + E firstWhere(bool Function(E element) test, {E Function()? orElse}) => + value.firstWhere(test, orElse: orElse); + + /// Returns the last element that satisfies the given predicate [test]. + /// + /// An iterable that can access its elements directly may check its + /// elements in any order (for example a list starts by checking the + /// last element and then moves towards the start of the list). + /// The default implementation iterates elements in iteration order, + /// checks `test(element)` for each, + /// and finally returns that last one that matched. + /// + /// Example: + /// ```dart + /// final numbers = [1, 2, 3, 5, 6, 7]; + /// var result = numbers.lastWhere((element) => element < 5); // 3 + /// result = numbers.lastWhere((element) => element > 5); // 7 + /// result = numbers.lastWhere((element) => element > 10, + /// orElse: () => -1); // -1 + /// ``` + /// + /// If no element satisfies [test], the result of invoking the [orElse] + /// function is returned. + /// If [orElse] is omitted, it defaults to throwing a [StateError]. + E lastWhere(bool Function(E element) test, {E Function()? orElse}) => + value.lastWhere(test, orElse: orElse); + + /// Returns the single element that satisfies [test]. + /// + /// Checks elements to see if `test(element)` returns true. + /// If exactly one element satisfies [test], that element is returned. + /// If more than one matching element is found, throws [StateError]. + /// If no matching element is found, returns the result of [orElse]. + /// If [orElse] is omitted, it defaults to throwing a [StateError]. + /// + /// Example: + /// ```dart + /// final numbers = [2, 2, 10]; + /// var result = numbers.singleWhere((element) => element > 5); // 10 + /// ``` + /// When no matching element is found, the result of calling [orElse] is + /// returned instead. + /// ```dart continued + /// result = numbers.singleWhere((element) => element == 1, + /// orElse: () => -1); // -1 + /// ``` + /// There must not be more than one matching element. + /// ```dart continued + /// result = numbers.singleWhere((element) => element == 2); // Throws Error. + /// ``` + E singleWhere(bool Function(E element) test, {E Function()? orElse}) => + value.singleWhere(test, orElse: orElse); + + /// Returns the [index]th element. + /// + /// The [index] must be non-negative and less than [length]. + /// Index zero represents the first element (so `iterable.elementAt(0)` is + /// equivalent to `iterable.first`). + /// + /// May iterate through the elements in iteration order, ignoring the + /// first [index] elements and then returning the next. + /// Some iterables may have a more efficient way to find the element. + /// + /// Example: + /// ```dart + /// final numbers = [1, 2, 3, 5, 6, 7]; + /// final elementAt = numbers.elementAt(4); // 6 + /// ``` + E elementAt(int index) => value.elementAt(index); +} + +extension SignalIterableNullExt on Signal?> { + /// Returns a new `Iterator` that allows iterating the elements of this + /// `Iterable`. + /// + /// Iterable classes may specify the iteration order of their elements + /// (for example [List] always iterate in index order), + /// or they may leave it unspecified (for example a hash-based [Set] + /// may iterate in any order). + /// + /// Each time `iterator` is read, it returns a new iterator, + /// which can be used to iterate through all the elements again. + /// The iterators of the same iterable can be stepped through independently, + /// but should return the same elements in the same order, + /// as long as the underlying collection isn't changed. + /// + /// Modifying the collection may cause new iterators to produce + /// different elements, and may change the order of existing elements. + /// A [List] specifies its iteration order precisely, + /// so modifying the list changes the iteration order predictably. + /// A hash-based [Set] may change its iteration order completely + /// when adding a new element to the set. + /// + /// Modifying the underlying collection after creating the new iterator + /// may cause an error the next time [Iterator.moveNext] is called + /// on that iterator. + /// Any *modifiable* iterable class should specify which operations will + /// break iteration. + Iterator? get iterator => value?.iterator; + + /// Provides a view of this iterable as an iterable of [R] instances. + /// + /// If this iterable only contains instances of [R], all operations + /// will work correctly. If any operation tries to access an element + /// that is not an instance of [R], the access will throw instead. + /// + /// When the returned iterable creates a new object that depends on + /// the type [R], e.g., from [toList], it will have exactly the type [R]. + Iterable? cast() => value?.cast(); + + /// Returns the lazy concatenation of this iterable and [other]. + /// + /// The returned iterable will provide the same elements as this iterable, + /// and, after that, the elements of [other], in the same order as in the + /// original iterables. + /// + /// Example: + /// ```dart + /// var planets = ['Earth', 'Jupiter']; + /// var updated = planets.followedBy(['Mars', 'Venus']); + /// print(updated); // (Earth, Jupiter, Mars, Venus) + /// ``` + Iterable? followedBy(Iterable other) => value?.followedBy(other); + + /// The current elements of this iterable modified by [toElement]. + /// + /// Returns a new lazy [Iterable] with elements that are created by + /// calling `toElement` on each element of this `Iterable` in + /// iteration order. + /// + /// The returned iterable is lazy, so it won't iterate the elements of + /// this iterable until it is itself iterated, and then it will apply + /// [toElement] to create one element at a time. + /// The converted elements are not cached. + /// Iterating multiple times over the returned [Iterable] + /// will invoke the supplied [toElement] function once per element + /// for on each iteration. + /// + /// Methods on the returned iterable are allowed to omit calling `toElement` + /// on any element where the result isn't needed. + /// For example, [elementAt] may call `toElement` only once. + /// + /// Equivalent to: + /// ``` + /// Iterable map(T toElement(E e)) sync* { + /// for (var value in this) { + /// yield toElement(value); + /// } + /// } + /// ``` + /// Example: + /// ```dart import:convert + /// var products = jsonDecode(''' + /// [ + /// {"name": "Screwdriver", "price": 42.00}, + /// {"name": "Wingnut", "price": 0.50} + /// ] + /// '''); + /// var values = products.map((product) => product['price'] as double); + /// var totalPrice = values.fold(0.0, (a, b) => a + b); // 42.5. + /// ``` + Iterable? map(T Function(E e) toElement) => value?.map(toElement); + + /// Returns a new lazy [Iterable] with all elements that satisfy the + /// predicate [test]. + /// + /// The matching elements have the same order in the returned iterable + /// as they have in [iterator]. + /// + /// This method returns a view of the mapped elements. + /// As long as the returned [Iterable] is not iterated over, + /// the supplied function [test] will not be invoked. + /// Iterating will not cache results, and thus iterating multiple times over + /// the returned [Iterable] may invoke the supplied + /// function [test] multiple times on the same element. + /// + /// Example: + /// ```dart + /// final numbers = [1, 2, 3, 5, 6, 7]; + /// var result = numbers.where((x) => x < 5); // (1, 2, 3) + /// result = numbers.where((x) => x > 5); // (6, 7) + /// result = numbers.where((x) => x.isEven); // (2, 6) + /// ``` + Iterable? where(bool Function(E element) test) => value?.where(test); + + /// Returns a new lazy [Iterable] with all elements that have type [T]. + /// + /// The matching elements have the same order in the returned iterable + /// as they have in [iterator]. + /// + /// This method returns a view of the mapped elements. + /// Iterating will not cache results, and thus iterating multiple times over + /// the returned [Iterable] may yield different results, + /// if the underlying elements change between iterations. + Iterable? whereType() => value?.whereType(); + + /// Expands each element of this [Iterable] into zero or more elements. + /// + /// The resulting Iterable runs through the elements returned + /// by [toElements] for each element of this, in iteration order. + /// + /// The returned [Iterable] is lazy, and calls [toElements] for each element + /// of this iterable every time the returned iterable is iterated. + /// + /// Example: + /// ```dart + /// Iterable count(int n) sync* { + /// for (var i = 1; i <= n; i++) { + /// yield i; + /// } + /// } + /// + /// var numbers = [1, 3, 0, 2]; + /// print(numbers.expand(count)); // (1, 1, 2, 3, 1, 2) + /// ``` + /// + /// Equivalent to: + /// ``` + /// Iterable expand(Iterable toElements(E e)) sync* { + /// for (var value in this) { + /// yield* toElements(value); + /// } + /// } + /// ``` + Iterable? expand(Iterable Function(E element) toElements) => + value?.expand(toElements); + + /// Whether the collection contains an element equal to [element]. + /// + /// This operation will check each element in order for being equal to + /// [element], unless it has a more efficient way to find an element + /// equal to [element]. + /// + /// The equality used to determine whether [element] is equal to an element of + /// the iterable defaults to the [Object.==] of the element. + /// + /// Some types of iterable may have a different equality used for its elements. + /// For example, a [Set] may have a custom equality + /// (see [Set.identity]) that its `contains` uses. + /// Likewise the `Iterable` returned by a [Map.keys] call + /// should use the same equality that the `Map` uses for keys. + /// + /// Example: + /// ```dart + /// final gasPlanets = {1: 'Jupiter', 2: 'Saturn'}; + /// final containsOne = gasPlanets.keys.contains(1); // true + /// final containsFive = gasPlanets.keys.contains(5); // false + /// final containsJupiter = gasPlanets.values.contains('Jupiter'); // true + /// final containsMercury = gasPlanets.values.contains('Mercury'); // false + /// ``` + bool? contains(Object? element) => value?.contains(element); + + /// Invokes [action] on each element of this iterable in iteration order. + /// + /// Example: + /// ```dart + /// final numbers = [1, 2, 6, 7]; + /// numbers.forEach(print); + /// // 1 + /// // 2 + /// // 6 + /// // 7 + /// ``` + void forEach(void Function(E element) action) => value?.forEach(action); + + /// Reduces a collection to a single value by iteratively combining elements + /// of the collection using the provided function. + /// + /// The iterable must have at least one element. + /// If it has only one element, that element is returned. + /// + /// Otherwise this method starts with the first element from the iterator, + /// and then combines it with the remaining elements in iteration order, + /// as if by: + /// ``` + /// E value = iterable.first; + /// iterable.skip(1).forEach((element) { + /// value = combine(value, element); + /// }); + /// return value; + /// ``` + /// Example of calculating the sum of an iterable: + /// ```dart + /// final numbers = [10, 2, 5, 0.5]; + /// final result = numbers.reduce((value, element) => value + element); + /// print(result); // 17.5 + /// ``` + E? reduce(E Function(E value, E element) combine) => value?.reduce(combine); + + /// Reduces a collection to a single value by iteratively combining each + /// element of the collection with an existing value + /// + /// Uses [initialValue] as the initial value, + /// then iterates through the elements and updates the value with + /// each element using the [combine] function, as if by: + /// ``` + /// var value = initialValue; + /// for (E element in this) { + /// value = combine(value, element); + /// } + /// return value; + /// ``` + /// Example of calculating the sum of an iterable: + /// ```dart + /// final numbers = [10, 2, 5, 0.5]; + /// const initialValue = 100.0; + /// final result = numbers.fold( + /// initialValue, (previousValue, element) => previousValue + element); + /// print(result); // 117.5 + /// ``` + T? fold(T initialValue, T Function(T previousValue, E element) combine) => + value?.fold(initialValue, combine); + + /// Checks whether every element of this iterable satisfies [test]. + /// + /// Checks every element in iteration order, and returns `false` if + /// any of them make [test] return `false`, otherwise returns `true`. + /// + /// Example: + /// ```dart + /// final planetsByMass = {0.06: 'Mercury', 0.81: 'Venus', + /// 0.11: 'Mars'}; + /// // Checks whether all keys are smaller than 1. + /// final every = planetsByMass.keys.every((key) => key < 1.0); // true + /// ``` + bool? every(bool Function(E element) test) => value?.every(test); + + /// Converts each element to a [String] and concatenates the strings. + /// + /// Iterates through elements of this iterable, + /// converts each one to a [String] by calling [Object.toString], + /// and then concatenates the strings, with the + /// [separator] string interleaved between the elements. + /// + /// Example: + /// ```dart + /// final planetsByMass = {0.06: 'Mercury', 0.81: 'Venus', + /// 0.11: 'Mars'}; + /// final joinedNames = planetsByMass.values.join('-'); // Mercury-Venus-Mars + /// ``` + String? join([String separator = ""]) => value?.join(separator); + + /// Checks whether any element of this iterable satisfies [test]. + /// + /// Checks every element in iteration order, and returns `true` if + /// any of them make [test] return `true`, otherwise returns false. + /// + /// Example: + /// ```dart + /// final numbers = [1, 2, 3, 5, 6, 7]; + /// var result = numbers.any((element) => element >= 5); // true; + /// result = numbers.any((element) => element >= 10); // false; + /// ``` + bool? any(bool Function(E element) test) => value?.any(test); + + /// Creates a [List] containing the elements of this [Iterable]. + /// + /// The elements are in iteration order. + /// The list is fixed-length if [growable] is false. + /// + /// Example: + /// ```dart + /// final planets = {1: 'Mercury', 2: 'Venus', 3: 'Mars'}; + /// final keysList = planets.keys.toList(growable: false); // [1, 2, 3] + /// final valuesList = + /// planets.values.toList(growable: false); // [Mercury, Venus, Mars] + /// ``` + List? toList({bool growable = true}) => value?.toList(growable: growable); + + /// Creates a [Set] containing the same elements as this iterable. + /// + /// The set may contain fewer elements than the iterable, + /// if the iterable contains an element more than once, + /// or it contains one or more elements that are equal. + /// The order of the elements in the set is not guaranteed to be the same + /// as for the iterable. + /// + /// Example: + /// ```dart + /// final planets = {1: 'Mercury', 2: 'Venus', 3: 'Mars'}; + /// final valueSet = planets.values.toSet(); // {Mercury, Venus, Mars} + /// ``` + Set? toSet() => value?.toSet(); + + /// Returns the number of elements in [this]. + /// + /// Counting all elements may involve iterating through all elements and can + /// therefore be slow. + /// Some iterables have a more efficient way to find the number of elements. + int? get length => value?.length; + + /// Whether this collection has no elements. + /// + /// May be computed by checking if `iterator.moveNext()` returns `false`. + /// + /// Example: + /// ```dart + /// final emptyList = []; + /// print(emptyList.isEmpty); // true; + /// print(emptyList.iterator.moveNext()); // false + /// ``` + bool? get isEmpty => value?.isEmpty; + + /// Whether this collection has at least one element. + /// + /// May be computed by checking if `iterator.moveNext()` returns `true`. + /// + /// Example: + /// ```dart + /// final numbers = {1, 2, 3}; + /// print(numbers.isNotEmpty); // true; + /// print(numbers.iterator.moveNext()); // true + /// ``` + bool? get isNotEmpty => value?.isNotEmpty; + + /// Returns a lazy iterable of the [count] first elements of this iterable. + /// + /// The returned `Iterable` may contain fewer than `count` elements, if `this` + /// contains fewer than `count` elements. + /// + /// The elements can be computed by stepping through [iterator] until [count] + /// elements have been seen. + /// + /// The `count` must not be negative. + /// + /// Example: + /// ```dart + /// final numbers = [1, 2, 3, 5, 6, 7]; + /// final result = numbers.take(4); // (1, 2, 3, 5) + /// final takeAll = numbers.take(100); // (1, 2, 3, 5, 6, 7) + /// ``` + Iterable? take(int count) => value?.take(count); + + /// Returns a lazy iterable of the leading elements satisfying [test]. + /// + /// The filtering happens lazily. Every new iterator of the returned + /// iterable starts iterating over the elements of `this`. + /// + /// The elements can be computed by stepping through [iterator] until an + /// element is found where `test(element)` is false. At that point, + /// the returned iterable stops (its `moveNext()` returns false). + /// + /// Example: + /// ```dart + /// final numbers = [1, 2, 3, 5, 6, 7]; + /// var result = numbers.takeWhile((x) => x < 5); // (1, 2, 3) + /// result = numbers.takeWhile((x) => x != 3); // (1, 2) + /// result = numbers.takeWhile((x) => x != 4); // (1, 2, 3, 5, 6, 7) + /// result = numbers.takeWhile((x) => x.isOdd); // (1) + /// ``` + Iterable? takeWhile(bool Function(E value) test) => value?.takeWhile(test); + + /// Returns an [Iterable] that provides all but the first [count] elements. + /// + /// When the returned iterable is iterated, it starts iterating over `this`, + /// first skipping past the initial [count] elements. + /// If `this` has fewer than `count` elements, then the resulting Iterable is + /// empty. + /// After that, the remaining elements are iterated in the same order as + /// in this iterable. + /// + /// Some iterables may be able to find later elements without first iterating + /// through earlier elements, for example when iterating a [List]. + /// Such iterables are allowed to ignore the initial skipped elements. + /// + /// Example: + /// ```dart + /// final numbers = [1, 2, 3, 5, 6, 7]; + /// final result = numbers.skip(4); // (6, 7) + /// final skipAll = numbers.skip(100); // () - no elements. + /// ``` + /// + /// The [count] must not be negative. + Iterable? skip(int count) => value?.skip(count); + + /// Returns an `Iterable` that skips leading elements while [test] is satisfied. + /// + /// The filtering happens lazily. Every new [Iterator] of the returned + /// iterable iterates over all elements of `this`. + /// + /// The returned iterable provides elements by iterating this iterable, + /// but skipping over all initial elements where `test(element)` returns + /// true. If all elements satisfy `test` the resulting iterable is empty, + /// otherwise it iterates the remaining elements in their original order, + /// starting with the first element for which `test(element)` returns `false`. + /// + /// Example: + /// ```dart + /// final numbers = [1, 2, 3, 5, 6, 7]; + /// var result = numbers.skipWhile((x) => x < 5); // (5, 6, 7) + /// result = numbers.skipWhile((x) => x != 3); // (3, 5, 6, 7) + /// result = numbers.skipWhile((x) => x != 4); // () + /// result = numbers.skipWhile((x) => x.isOdd); // (2, 3, 5, 6, 7) + /// ``` + Iterable? skipWhile(bool Function(E value) test) => value?.skipWhile(test); + + /// Returns the first element. + /// + /// Throws a [StateError] if `this` is empty. + /// Otherwise returns the first element in the iteration order, + /// equivalent to `this.elementAt(0)`. + E? get first => value?.first; + + /// Returns the last element. + /// + /// Throws a [StateError] if `this` is empty. + /// Otherwise may iterate through the elements and returns the last one + /// seen. + /// Some iterables may have more efficient ways to find the last element + /// (for example a list can directly access the last element, + /// without iterating through the previous ones). + E? get last => value?.last; + + /// Checks that this iterable has only one element, and returns that element. + /// + /// Throws a [StateError] if `this` is empty or has more than one element. + E? get single => value?.single; + + /// Returns the first element that satisfies the given predicate [test]. + /// + /// Iterates through elements and returns the first to satisfy [test]. + /// + /// Example: + /// ```dart + /// final numbers = [1, 2, 3, 5, 6, 7]; + /// var result = numbers.firstWhere((element) => element < 5); // 1 + /// result = numbers.firstWhere((element) => element > 5); // 6 + /// result = + /// numbers.firstWhere((element) => element > 10, orElse: () => -1); // -1 + /// ``` + /// + /// If no element satisfies [test], the result of invoking the [orElse] + /// function is returned. + /// If [orElse] is omitted, it defaults to throwing a [StateError]. + E? firstWhere(bool Function(E element) test, {E Function()? orElse}) => + value?.firstWhere(test, orElse: orElse); + + /// Returns the last element that satisfies the given predicate [test]. + /// + /// An iterable that can access its elements directly may check its + /// elements in any order (for example a list starts by checking the + /// last element and then moves towards the start of the list). + /// The default implementation iterates elements in iteration order, + /// checks `test(element)` for each, + /// and finally returns that last one that matched. + /// + /// Example: + /// ```dart + /// final numbers = [1, 2, 3, 5, 6, 7]; + /// var result = numbers.lastWhere((element) => element < 5); // 3 + /// result = numbers.lastWhere((element) => element > 5); // 7 + /// result = numbers.lastWhere((element) => element > 10, + /// orElse: () => -1); // -1 + /// ``` + /// + /// If no element satisfies [test], the result of invoking the [orElse] + /// function is returned. + /// If [orElse] is omitted, it defaults to throwing a [StateError]. + E? lastWhere(bool Function(E element) test, {E Function()? orElse}) => + value?.lastWhere(test, orElse: orElse); + + /// Returns the single element that satisfies [test]. + /// + /// Checks elements to see if `test(element)` returns true. + /// If exactly one element satisfies [test], that element is returned. + /// If more than one matching element is found, throws [StateError]. + /// If no matching element is found, returns the result of [orElse]. + /// If [orElse] is omitted, it defaults to throwing a [StateError]. + /// + /// Example: + /// ```dart + /// final numbers = [2, 2, 10]; + /// var result = numbers.singleWhere((element) => element > 5); // 10 + /// ``` + /// When no matching element is found, the result of calling [orElse] is + /// returned instead. + /// ```dart continued + /// result = numbers.singleWhere((element) => element == 1, + /// orElse: () => -1); // -1 + /// ``` + /// There must not be more than one matching element. + /// ```dart continued + /// result = numbers.singleWhere((element) => element == 2); // Throws Error. + /// ``` + E? singleWhere(bool Function(E element) test, {E Function()? orElse}) => + value?.singleWhere(test, orElse: orElse); + + /// Returns the [index]th element. + /// + /// The [index] must be non-negative and less than [length]. + /// Index zero represents the first element (so `iterable.elementAt(0)` is + /// equivalent to `iterable.first`). + /// + /// May iterate through the elements in iteration order, ignoring the + /// first [index] elements and then returning the next. + /// Some iterables may have a more efficient way to find the element. + /// + /// Example: + /// ```dart + /// final numbers = [1, 2, 3, 5, 6, 7]; + /// final elementAt = numbers.elementAt(4); // 6 + /// ``` + E? elementAt(int index) => value?.elementAt(index); +} diff --git a/packages/reactter/lib/src/signal/extensions/signal_list.dart b/packages/reactter/lib/src/signal/extensions/signal_list.dart new file mode 100644 index 00000000..97d7bd81 --- /dev/null +++ b/packages/reactter/lib/src/signal/extensions/signal_list.dart @@ -0,0 +1,1210 @@ +// coverage:ignore-file +part of '../signal.dart'; + +extension SignalListExt on Signal> { + /// The object at the given [index] in the list. + /// + /// The [index] must be a valid index of this list, + /// which means that `index` must be non-negative and + /// less than [length]. + E operator [](int index) => value[index]; + + /// Sets the value at the given [index] in the list to [value]. + /// + /// The [index] must be a valid index of this list, + /// which means that `index` must be non-negative and + /// less than [length]. + void operator []=(int index, E valueToSet) => + update((_) => value[index] = valueToSet); + + /// Returns the first element. + /// + /// Throws a [StateError] if `this` is empty. + /// Otherwise returns the first element in the iteration order, + /// equivalent to `this.elementAt(0)`. + E get first => value.first; + + /// The first element of the list. + /// + /// The list must be non-empty when accessing its first element. + /// + /// The first element of a list can be modified, unlike an [Iterable]. + /// A `list.first` is equivalent to `list[0]`, + /// both for getting and setting the value. + /// + /// ```dart + /// final numbers = [1, 2, 3]; + /// print(numbers.first); // 1 + /// numbers.first = 10; + /// print(numbers.first); // 10 + /// numbers.clear(); + /// numbers.first; // Throws. + /// ``` + set first(E valueToSet) => update((_) => value.first = valueToSet); + + /// Returns the last element. + /// + /// Throws a [StateError] if `this` is empty. + /// Otherwise may iterate through the elements and returns the last one + /// seen. + /// Some iterables may have more efficient ways to find the last element + /// (for example a list can directly access the last element, + /// without iterating through the previous ones). + E get last => value.last; + + /// The last element of the list. + /// + /// The list must be non-empty when accessing its last element. + /// + /// The last element of a list can be modified, unlike an [Iterable]. + /// A `list.last` is equivalent to `theList[theList.length - 1]`, + /// both for getting and setting the value. + /// + /// ```dart + /// final numbers = [1, 2, 3]; + /// print(numbers.last); // 3 + /// numbers.last = 10; + /// print(numbers.last); // 10 + /// numbers.clear(); + /// numbers.last; // Throws. + /// ``` + set last(E valueToSet) => update((_) => value.last = valueToSet); + + /// The number of objects in this list. + /// + /// The valid indices for a list are `0` through `length - 1`. + /// ```dart + /// final numbers = [1, 2, 3]; + /// print(numbers.length); // 3 + /// ``` + int get length => value.length; + + /// Setting the `length` changes the number of elements in the list. + /// + /// The list must be growable. + /// If [newLength] is greater than current length, + /// new entries are initialized to `null`, + /// so [newLength] must not be greater than the current length + /// if the element type [E] is non-nullable. + /// + /// ```dart + /// final maybeNumbers = [1, null, 3]; + /// maybeNumbers.length = 5; + /// print(maybeNumbers); // [1, null, 3, null, null] + /// maybeNumbers.length = 2; + /// print(maybeNumbers); // [1, null] + /// + /// final numbers = [1, 2, 3]; + /// numbers.length = 1; + /// print(numbers); // [1] + /// numbers.length = 5; // Throws, cannot add `null`s. + /// ``` + set length(int newLength) => update((_) => value.length = newLength); + + /// Adds [value] to the end of this list, + /// extending the length by one. + /// + /// The list must be growable. + /// + /// ```dart + /// final numbers = [1, 2, 3]; + /// numbers.add(4); + /// print(numbers); // [1, 2, 3, 4] + /// ``` + void add(E valueToAdd) => update((_) => value.add(valueToAdd)); + + /// Appends all objects of [iterable] to the end of this list. + /// + /// Extends the length of the list by the number of objects in [iterable]. + /// The list must be growable. + /// + /// ```dart + /// final numbers = [1, 2, 3]; + /// numbers.addAll([4, 5, 6]); + /// print(numbers); // [1, 2, 3, 4, 5, 6] + /// ``` + void addAll(Iterable iterable) => update((_) => value.addAll(iterable)); + + /// Sorts this list according to the order specified by the [compare] function. + /// + /// The [compare] function must act as a [Comparator]. + /// ```dart + /// final numbers = ['two', 'three', 'four']; + /// // Sort from shortest to longest. + /// numbers.sort((a, b) => a.length.compareTo(b.length)); + /// print(numbers); // [two, four, three] + /// ``` + /// The default [List] implementations use [Comparable.compare] if + /// [compare] is omitted. + /// ```dart + /// final numbers = [13, 2, -11, 0]; + /// numbers.sort(); + /// print(numbers); // [-11, 0, 2, 13] + /// ``` + /// In that case, the elements of the list must be [Comparable] to + /// each other. + /// + /// A [Comparator] may compare objects as equal (return zero), even if they + /// are distinct objects. + /// The sort function is not guaranteed to be stable, so distinct objects + /// that compare as equal may occur in any order in the result: + /// ```dart + /// final numbers = ['one', 'two', 'three', 'four']; + /// numbers.sort((a, b) => a.length.compareTo(b.length)); + /// print(numbers); // [one, two, four, three] OR [two, one, four, three] + /// ``` + void sort([int Function(E a, E b)? compare]) => + update((_) => value.sort(compare)); + + /// Shuffles the elements of this list randomly. + /// ```dart + /// final numbers = [1, 2, 3, 4, 5]; + /// numbers.shuffle(); + /// print(numbers); // [1, 3, 4, 5, 2] OR some other random result. + /// ``` + void shuffle([Random? random]) => update((_) => value.shuffle(random)); + + /// Removes all objects from this list; the length of the list becomes zero. + /// + /// The list must be growable. + /// + /// ```dart + /// final numbers = [1, 2, 3]; + /// numbers.clear(); + /// print(numbers.length); // 0 + /// print(numbers); // [] + /// ``` + void clear() => update((_) => value.clear()); + + /// Inserts [element] at position [index] in this list. + /// + /// This increases the length of the list by one and shifts all objects + /// at or after the index towards the end of the list. + /// + /// The list must be growable. + /// The [index] value must be non-negative and no greater than [length]. + /// + /// ```dart + /// final numbers = [1, 2, 3, 4]; + /// const index = 2; + /// numbers.insert(index, 10); + /// print(numbers); // [1, 2, 10, 3, 4] + /// ``` + void insert(int index, E element) => + update((_) => value.insert(index, element)); + + /// Inserts all objects of [iterable] at position [index] in this list. + /// + /// This increases the length of the list by the length of [iterable] and + /// shifts all later objects towards the end of the list. + /// + /// The list must be growable. + /// The [index] value must be non-negative and no greater than [length]. + /// ```dart + /// final numbers = [1, 2, 3, 4]; + /// final insertItems = [10, 11]; + /// numbers.insertAll(2, insertItems); + /// print(numbers); // [1, 2, 10, 11, 3, 4] + /// ``` + void insertAll(int index, Iterable iterable) => + update((_) => value.insertAll(index, iterable)); + + /// Overwrites elements with the objects of [iterable]. + /// + /// The elements of [iterable] are written into this list, + /// starting at position [index]. + /// This operation does not increase the length of the list. + /// + /// The [index] must be non-negative and no greater than [length]. + /// + /// The [iterable] must not have more elements than what can fit from [index] + /// to [length]. + /// + /// If `iterable` is based on this list, its values may change _during_ the + /// `setAll` operation. + /// ```dart + /// final list = ['a', 'b', 'c', 'd']; + /// list.setAll(1, ['bee', 'sea']); + /// print(list); // [a, bee, sea, d] + /// ``` + void setAll(int index, Iterable iterable) => + update((_) => value.setAll(index, iterable)); + + /// Removes the first occurrence of [value] from this list. + /// + /// Returns true if [value] was in the list, false otherwise. + /// The list must be growable. + /// + /// ```dart + /// final parts = ['head', 'shoulders', 'knees', 'toes']; + /// final retVal = parts.remove('head'); // true + /// print(parts); // [shoulders, knees, toes] + /// ``` + /// The method has no effect if [value] was not in the list. + /// ```dart + /// final parts = ['shoulders', 'knees', 'toes']; + /// // Note: 'head' has already been removed. + /// final retVal = parts.remove('head'); // false + /// print(parts); // [shoulders, knees, toes] + /// ``` + bool remove(Object? valueToRemove) { + late bool result; + update((_) => result = value.remove(valueToRemove)); + return result; + } + + /// Removes the object at position [index] from this list. + /// + /// This method reduces the length of `this` by one and moves all later objects + /// down by one position. + /// + /// Returns the removed value. + /// + /// The [index] must be in the range `0 ≤ index < length`. + /// The list must be growable. + /// ```dart + /// final parts = ['head', 'shoulder', 'knees', 'toes']; + /// final retVal = parts.removeAt(2); // knees + /// print(parts); // [head, shoulder, toes] + /// ``` + E removeAt(int index) { + late E result; + update((_) => result = value.removeAt(index)); + return result; + } + + /// Removes and returns the last object in this list. + /// + /// The list must be growable and non-empty. + /// ```dart + /// final parts = ['head', 'shoulder', 'knees', 'toes']; + /// final retVal = parts.removeLast(); // toes + /// print(parts); // [head, shoulder, knees] + /// ``` + E removeLast() { + late E result; + update((_) => result = value.removeLast()); + return result; + } + + /// Removes all objects from this list that satisfy [test]. + /// + /// An object `o` satisfies [test] if `test(o)` is true. + /// ```dart + /// final numbers = ['one', 'two', 'three', 'four']; + /// numbers.removeWhere((item) => item.length == 3); + /// print(numbers); // [three, four] + /// ``` + /// The list must be growable. + void removeWhere(bool Function(E element) test) => + update((_) => value.removeWhere(test)); + + /// Removes all objects from this list that fail to satisfy [test]. + /// + /// An object `o` satisfies [test] if `test(o)` is true. + /// ```dart + /// final numbers = ['one', 'two', 'three', 'four']; + /// numbers.retainWhere((item) => item.length == 3); + /// print(numbers); // [one, two] + /// ``` + /// The list must be growable. + void retainWhere(bool Function(E element) test) => + update((_) => value.retainWhere(test)); + + /// Writes some elements of [iterable] into a range of this list. + /// + /// Copies the objects of [iterable], skipping [skipCount] objects first, + /// into the range from [start], inclusive, to [end], exclusive, of this list. + /// ```dart + /// final list1 = [1, 2, 3, 4]; + /// final list2 = [5, 6, 7, 8, 9]; + /// // Copies the 4th and 5th items in list2 as the 2nd and 3rd items + /// // of list1. + /// const skipCount = 3; + /// list1.setRange(1, 3, list2, skipCount); + /// print(list1); // [1, 8, 9, 4] + /// ``` + /// The provided range, given by [start] and [end], must be valid. + /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. + /// An empty range (with `end == start`) is valid. + /// + /// The [iterable] must have enough objects to fill the range from `start` + /// to `end` after skipping [skipCount] objects. + /// + /// If `iterable` is this list, the operation correctly copies the elements + /// originally in the range from `skipCount` to `skipCount + (end - start)` to + /// the range `start` to `end`, even if the two ranges overlap. + /// + /// If `iterable` depends on this list in some other way, no guarantees are + /// made. + void setRange(int start, int end, Iterable iterable, + [int skipCount = 0]) => + update((_) => value.setRange(start, end, iterable, skipCount)); + + /// Removes a range of elements from the list. + /// + /// Removes the elements with positions greater than or equal to [start] + /// and less than [end], from the list. + /// This reduces the list's length by `end - start`. + /// + /// The provided range, given by [start] and [end], must be valid. + /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. + /// An empty range (with `end == start`) is valid. + /// + /// The list must be growable. + /// ```dart + /// final numbers = [1, 2, 3, 4, 5]; + /// numbers.removeRange(1, 4); + /// print(numbers); // [1, 5] + /// ``` + void removeRange(int start, int end) => + update((_) => value.removeRange(start, end)); + + /// Overwrites a range of elements with [fillValue]. + /// + /// Sets the positions greater than or equal to [start] and less than [end], + /// to [fillValue]. + /// + /// The provided range, given by [start] and [end], must be valid. + /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. + /// An empty range (with `end == start`) is valid. + /// + /// If the element type is not nullable, the [fillValue] must be provided and + /// must be non-`null`. + /// + /// Example: + /// ```dart + /// final words = List.filled(5, 'old'); + /// print(words); // [old, old, old, old, old] + /// words.fillRange(1, 3, 'new'); + /// print(words); // [old, new, new, old, old] + /// ``` + void fillRange(int start, int end, [E? fillValue]) => + update((_) => value.fillRange(start, end, fillValue)); + + /// Replaces a range of elements with the elements of [replacements]. + /// + /// Removes the objects in the range from [start] to [end], + /// then inserts the elements of [replacements] at [start]. + /// ```dart + /// final numbers = [1, 2, 3, 4, 5]; + /// final replacements = [6, 7]; + /// numbers.replaceRange(1, 4, replacements); + /// print(numbers); // [1, 6, 7, 5] + /// ``` + /// The provided range, given by [start] and [end], must be valid. + /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. + /// An empty range (with `end == start`) is valid. + /// + /// The operation `list.replaceRange(start, end, replacements)` + /// is roughly equivalent to: + /// ```dart + /// final numbers = [1, 2, 3, 4, 5]; + /// numbers.removeRange(1, 4); + /// final replacements = [6, 7]; + /// numbers.insertAll(1, replacements); + /// print(numbers); // [1, 6, 7, 5] + /// ``` + /// but may be more efficient. + /// + /// The list must be growable. + /// This method does not work on fixed-length lists, even when [replacements] + /// has the same number of elements as the replaced range. In that case use + /// [setRange] instead. + void replaceRange(int start, int end, Iterable replacements) => + update((_) => value.replaceRange(start, end, replacements)); + + /// Returns the concatenation of this list and [other]. + /// + /// Returns a new list containing the elements of this list followed by + /// the elements of [other]. + /// + /// The default behavior is to return a normal growable list. + /// Some list types may choose to return a list of the same type as themselves + /// (see [Uint8List.+]); + List operator +(List other) => value + other; + + /// Returns a view of this list as a list of [R] instances. + /// + /// If this list contains only instances of [R], all read operations + /// will work correctly. If any operation tries to read an element + /// that is not an instance of [R], the access will throw instead. + /// + /// Elements added to the list (e.g., by using [add] or [addAll]) + /// must be instances of [R] to be valid arguments to the adding function, + /// and they must also be instances of [E] to be accepted by + /// this list as well. + /// + /// Methods which accept `Object?` as argument, like [contains] and [remove], + /// will pass the argument directly to the this list's method + /// without any checks. + /// That means that you can do `listOfStrings.cast().remove("a")` + /// successfully, even if it looks like it shouldn't have any effect. + /// + /// Typically implemented as `List.castFrom(this)`. + List cast() => value.cast(); + + /// An [Iterable] of the objects in this list in reverse order. + /// ```dart + /// final numbers = ['two', 'three', 'four']; + /// final reverseOrder = numbers.reversed; + /// print(reverseOrder.toList()); // [four, three, two] + /// ``` + Iterable get reversed => value.reversed; + + /// The first index of [element] in this list. + /// + /// Searches the list from index [start] to the end of the list. + /// The first time an object `o` is encountered so that `o == element`, + /// the index of `o` is returned. + /// ```dart + /// final notes = ['do', 're', 'mi', 're']; + /// print(notes.indexOf('re')); // 1 + /// + /// final indexWithStart = notes.indexOf('re', 2); // 3 + /// ``` + /// Returns -1 if [element] is not found. + /// ```dart + /// final notes = ['do', 're', 'mi', 're']; + /// final index = notes.indexOf('fa'); // -1 + /// ``` + int indexOf(E element, [int start = 0]) => value.indexOf(element, start); + + /// The first index in the list that satisfies the provided [test]. + /// + /// Searches the list from index [start] to the end of the list. + /// The first time an object `o` is encountered so that `test(o)` is true, + /// the index of `o` is returned. + /// + /// ```dart + /// final notes = ['do', 're', 'mi', 're']; + /// final first = notes.indexWhere((note) => note.startsWith('r')); // 1 + /// final second = notes.indexWhere((note) => note.startsWith('r'), 2); // 3 + /// ``` + /// + /// Returns -1 if [element] is not found. + /// ```dart + /// final notes = ['do', 're', 'mi', 're']; + /// final index = notes.indexWhere((note) => note.startsWith('k')); // -1 + /// ``` + int indexWhere(bool Function(E element) test, [int start = 0]) => + value.indexWhere(test); + + /// The last index in the list that satisfies the provided [test]. + /// + /// Searches the list from index [start] to 0. + /// The first time an object `o` is encountered so that `test(o)` is true, + /// the index of `o` is returned. + /// If [start] is omitted, it defaults to the [length] of the list. + /// + /// ```dart + /// final notes = ['do', 're', 'mi', 're']; + /// final first = notes.lastIndexWhere((note) => note.startsWith('r')); // 3 + /// final second = notes.lastIndexWhere((note) => note.startsWith('r'), + /// 2); // 1 + /// ``` + /// + /// Returns -1 if [element] is not found. + /// ```dart + /// final notes = ['do', 're', 'mi', 're']; + /// final index = notes.lastIndexWhere((note) => note.startsWith('k')); + /// print(index); // -1 + /// ``` + int lastIndexWhere(bool Function(E element) test, [int? start]) => + value.lastIndexWhere(test, start); + + /// The last index of [element] in this list. + /// + /// Searches the list backwards from index [start] to 0. + /// + /// The first time an object `o` is encountered so that `o == element`, + /// the index of `o` is returned. + /// ```dart + /// final notes = ['do', 're', 'mi', 're']; + /// const startIndex = 2; + /// final index = notes.lastIndexOf('re', startIndex); // 1 + /// ``` + /// If [start] is not provided, this method searches from the end of the + /// list. + /// ```dart + /// final notes = ['do', 're', 'mi', 're']; + /// final index = notes.lastIndexOf('re'); // 3 + /// ``` + /// Returns -1 if [element] is not found. + /// ```dart + /// final notes = ['do', 're', 'mi', 're']; + /// final index = notes.lastIndexOf('fa'); // -1 + /// ``` + int lastIndexOf(E element, [int? start]) => value.lastIndexOf(element, start); + + /// Returns a new list containing the elements between [start] and [end]. + /// + /// The new list is a `List` containing the elements of this list at + /// positions greater than or equal to [start] and less than [end] in the same + /// order as they occur in this list. + /// + /// ```dart + /// final colors = ['red', 'green', 'blue', 'orange', 'pink']; + /// print(colors.sublist(1, 3)); // [green, blue] + /// ``` + /// + /// If [end] is omitted, it defaults to the [length] of this list. + /// + /// ```dart + /// final colors = ['red', 'green', 'blue', 'orange', 'pink']; + /// print(colors.sublist(3)); // [orange, pink] + /// ``` + /// + /// The `start` and `end` positions must satisfy the relations + /// 0 ≤ `start` ≤ `end` ≤ [length]. + /// If `end` is equal to `start`, then the returned list is empty. + List sublist(int start, [int? end]) => value.sublist(start, end); + + /// Creates an [Iterable] that iterates over a range of elements. + /// + /// The returned iterable iterates over the elements of this list + /// with positions greater than or equal to [start] and less than [end]. + /// + /// The provided range, [start] and [end], must be valid at the time + /// of the call. + /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. + /// An empty range (with `end == start`) is valid. + /// + /// The returned [Iterable] behaves like `skip(start).take(end - start)`. + /// That is, it does *not* break if this list changes size, it just + /// ends early if it reaches the end of the list early + /// (if `end`, or even `start`, becomes greater than [length]). + /// ```dart + /// final colors = ['red', 'green', 'blue', 'orange', 'pink']; + /// final firstRange = colors.getRange(0, 3); + /// print(firstRange.join(', ')); // red, green, blue + /// + /// final secondRange = colors.getRange(2, 5); + /// print(secondRange.join(', ')); // blue, orange, pink + /// ``` + Iterable getRange(int start, int end) => value.getRange(start, end); + + /// An unmodifiable [Map] view of this list. + /// + /// The map uses the indices of this list as keys and the corresponding objects + /// as values. The `Map.keys` [Iterable] iterates the indices of this list + /// in numerical order. + /// ```dart + /// var words = ['fee', 'fi', 'fo', 'fum']; + /// var map = words.asMap(); // {0: fee, 1: fi, 2: fo, 3: fum} + /// map.keys.toList(); // [0, 1, 2, 3] + /// ``` + Map asMap() => value.asMap(); +} + +extension SignalListNullExt on Signal?> { + /// Sets the value at the given [index] in the list to [value]. + /// + /// The [index] must be a valid index of this list, + /// which means that `index` must be non-negative and + /// less than [length]. + void operator []=(int index, E valueToSet) { + if (value == null) return; + update((_) => value?[index] = valueToSet); + } + + /// The last element of the list. + /// + /// The list must be non-empty when accessing its last element. + /// + /// The last element of a list can be modified, unlike an [Iterable]. + /// A `list.last` is equivalent to `theList[theList.length - 1]`, + /// both for getting and setting the value. + /// + /// ```dart + /// final numbers = [1, 2, 3]; + /// print(numbers.last); // 3 + /// numbers.last = 10; + /// print(numbers.last); // 10 + /// numbers.clear(); + /// numbers.last; // Throws. + /// ``` + set last(E valueToSet) { + if (value == null) return; + update((_) => value?.last = valueToSet); + } + + /// Setting the `length` changes the number of elements in the list. + /// + /// The list must be growable. + /// If [newLength] is greater than current length, + /// new entries are initialized to `null`, + /// so [newLength] must not be greater than the current length + /// if the element type [E] is non-nullable. + /// + /// ```dart + /// final maybeNumbers = [1, null, 3]; + /// maybeNumbers.length = 5; + /// print(maybeNumbers); // [1, null, 3, null, null] + /// maybeNumbers.length = 2; + /// print(maybeNumbers); // [1, null] + /// + /// final numbers = [1, 2, 3]; + /// numbers.length = 1; + /// print(numbers); // [1] + /// numbers.length = 5; // Throws, cannot add `null`s. + /// ``` + set length(int newLength) { + if (value == null) return; + update((_) => value?.length = newLength); + } + + /// Adds [value] to the end of this list, + /// extending the length by one. + /// + /// The list must be growable. + /// + /// ```dart + /// final numbers = [1, 2, 3]; + /// numbers.add(4); + /// print(numbers); // [1, 2, 3, 4] + /// ``` + void add(E valueToAdd) { + if (value == null) return; + update((_) => value?.add(valueToAdd)); + } + + /// Appends all objects of [iterable] to the end of this list. + /// + /// Extends the length of the list by the number of objects in [iterable]. + /// The list must be growable. + /// + /// ```dart + /// final numbers = [1, 2, 3]; + /// numbers.addAll([4, 5, 6]); + /// print(numbers); // [1, 2, 3, 4, 5, 6] + /// ``` + void addAll(Iterable iterable) { + if (value == null) return; + update((_) => value?.addAll(iterable)); + } + + /// Sorts this list according to the order specified by the [compare] function. + /// + /// The [compare] function must act as a [Comparator]. + /// ```dart + /// final numbers = ['two', 'three', 'four']; + /// // Sort from shortest to longest. + /// numbers.sort((a, b) => a.length.compareTo(b.length)); + /// print(numbers); // [two, four, three] + /// ``` + /// The default [List] implementations use [Comparable.compare] if + /// [compare] is omitted. + /// ```dart + /// final numbers = [13, 2, -11, 0]; + /// numbers.sort(); + /// print(numbers); // [-11, 0, 2, 13] + /// ``` + /// In that case, the elements of the list must be [Comparable] to + /// each other. + /// + /// A [Comparator] may compare objects as equal (return zero), even if they + /// are distinct objects. + /// The sort function is not guaranteed to be stable, so distinct objects + /// that compare as equal may occur in any order in the result: + /// ```dart + /// final numbers = ['one', 'two', 'three', 'four']; + /// numbers.sort((a, b) => a.length.compareTo(b.length)); + /// print(numbers); // [one, two, four, three] OR [two, one, four, three] + /// ``` + void sort([int Function(E a, E b)? compare]) { + if (value == null) return; + update((_) => value?.sort(compare)); + } + + /// Shuffles the elements of this list randomly. + /// ```dart + /// final numbers = [1, 2, 3, 4, 5]; + /// numbers.shuffle(); + /// print(numbers); // [1, 3, 4, 5, 2] OR some other random result. + /// ``` + void shuffle([Random? random]) { + if (value == null) return; + update((_) => value?.shuffle(random)); + } + + /// Removes all objects from this list; the length of the list becomes zero. + /// + /// The list must be growable. + /// + /// ```dart + /// final numbers = [1, 2, 3]; + /// numbers.clear(); + /// print(numbers.length); // 0 + /// print(numbers); // [] + /// ``` + void clear() { + if (value == null) return; + update((_) => value?.clear()); + } + + /// Inserts [element] at position [index] in this list. + /// + /// This increases the length of the list by one and shifts all objects + /// at or after the index towards the end of the list. + /// + /// The list must be growable. + /// The [index] value must be non-negative and no greater than [length]. + /// + /// ```dart + /// final numbers = [1, 2, 3, 4]; + /// const index = 2; + /// numbers.insert(index, 10); + /// print(numbers); // [1, 2, 10, 3, 4] + /// ``` + void insert(int index, E element) { + if (value == null) return; + update((_) => value?.insert(index, element)); + } + + /// Inserts all objects of [iterable] at position [index] in this list. + /// + /// This increases the length of the list by the length of [iterable] and + /// shifts all later objects towards the end of the list. + /// + /// The list must be growable. + /// The [index] value must be non-negative and no greater than [length]. + /// ```dart + /// final numbers = [1, 2, 3, 4]; + /// final insertItems = [10, 11]; + /// numbers.insertAll(2, insertItems); + /// print(numbers); // [1, 2, 10, 11, 3, 4] + /// ``` + void insertAll(int index, Iterable iterable) { + if (value == null) return; + update((_) => value?.insertAll(index, iterable)); + } + + /// Overwrites elements with the objects of [iterable]. + /// + /// The elements of [iterable] are written into this list, + /// starting at position [index]. + /// This operation does not increase the length of the list. + /// + /// The [index] must be non-negative and no greater than [length]. + /// + /// The [iterable] must not have more elements than what can fit from [index] + /// to [length]. + /// + /// If `iterable` is based on this list, its values may change _during_ the + /// `setAll` operation. + /// ```dart + /// final list = ['a', 'b', 'c', 'd']; + /// list.setAll(1, ['bee', 'sea']); + /// print(list); // [a, bee, sea, d] + /// ``` + void setAll(int index, Iterable iterable) { + if (value == null) return; + update((_) => value?.setAll(index, iterable)); + } + + /// Removes the first occurrence of [value] from this list. + /// + /// Returns true if [value] was in the list, false otherwise. + /// The list must be growable. + /// + /// ```dart + /// final parts = ['head', 'shoulders', 'knees', 'toes']; + /// final retVal = parts.remove('head'); // true + /// print(parts); // [shoulders, knees, toes] + /// ``` + /// The method has no effect if [value] was not in the list. + /// ```dart + /// final parts = ['shoulders', 'knees', 'toes']; + /// // Note: 'head' has already been removed. + /// final retVal = parts.remove('head'); // false + /// print(parts); // [shoulders, knees, toes] + /// ``` + bool? remove(Object? valueToRemove) { + if (value == null) return null; + late bool result; + update((_) => result = value!.remove(valueToRemove)); + return result; + } + + /// Removes the object at position [index] from this list. + /// + /// This method reduces the length of `this` by one and moves all later objects + /// down by one position. + /// + /// Returns the removed value. + /// + /// The [index] must be in the range `0 ≤ index < length`. + /// The list must be growable. + /// ```dart + /// final parts = ['head', 'shoulder', 'knees', 'toes']; + /// final retVal = parts.removeAt(2); // knees + /// print(parts); // [head, shoulder, toes] + /// ``` + E? removeAt(int index) { + if (value == null) return null; + late E result; + update((_) => result = value!.removeAt(index)); + return result; + } + + /// Removes and returns the last object in this list. + /// + /// The list must be growable and non-empty. + /// ```dart + /// final parts = ['head', 'shoulder', 'knees', 'toes']; + /// final retVal = parts.removeLast(); // toes + /// print(parts); // [head, shoulder, knees] + /// ``` + E? removeLast() { + if (value == null) return null; + late E? result; + update((_) => result = value!.removeLast()); + return result; + } + + /// Removes all objects from this list that satisfy [test]. + /// + /// An object `o` satisfies [test] if `test(o)` is true. + /// ```dart + /// final numbers = ['one', 'two', 'three', 'four']; + /// numbers.removeWhere((item) => item.length == 3); + /// print(numbers); // [three, four] + /// ``` + /// The list must be growable. + void removeWhere(bool Function(E element) test) { + if (value == null) return; + update((_) => value?.removeWhere(test)); + } + + /// Removes all objects from this list that fail to satisfy [test]. + /// + /// An object `o` satisfies [test] if `test(o)` is true. + /// ```dart + /// final numbers = ['one', 'two', 'three', 'four']; + /// numbers.retainWhere((item) => item.length == 3); + /// print(numbers); // [one, two] + /// ``` + /// The list must be growable. + void retainWhere(bool Function(E element) test) { + if (value == null) return; + update((_) => value?.retainWhere(test)); + } + + /// Writes some elements of [iterable] into a range of this list. + /// + /// Copies the objects of [iterable], skipping [skipCount] objects first, + /// into the range from [start], inclusive, to [end], exclusive, of this list. + /// ```dart + /// final list1 = [1, 2, 3, 4]; + /// final list2 = [5, 6, 7, 8, 9]; + /// // Copies the 4th and 5th items in list2 as the 2nd and 3rd items + /// // of list1. + /// const skipCount = 3; + /// list1.setRange(1, 3, list2, skipCount); + /// print(list1); // [1, 8, 9, 4] + /// ``` + /// The provided range, given by [start] and [end], must be valid. + /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. + /// An empty range (with `end == start`) is valid. + /// + /// The [iterable] must have enough objects to fill the range from `start` + /// to `end` after skipping [skipCount] objects. + /// + /// If `iterable` is this list, the operation correctly copies the elements + /// originally in the range from `skipCount` to `skipCount + (end - start)` to + /// the range `start` to `end`, even if the two ranges overlap. + /// + /// If `iterable` depends on this list in some other way, no guarantees are + /// made. + void setRange(int start, int end, Iterable iterable, [int skipCount = 0]) { + if (value == null) return; + update((_) => value?.setRange(start, end, iterable, skipCount)); + } + + /// Removes a range of elements from the list. + /// + /// Removes the elements with positions greater than or equal to [start] + /// and less than [end], from the list. + /// This reduces the list's length by `end - start`. + /// + /// The provided range, given by [start] and [end], must be valid. + /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. + /// An empty range (with `end == start`) is valid. + /// + /// The list must be growable. + /// ```dart + /// final numbers = [1, 2, 3, 4, 5]; + /// numbers.removeRange(1, 4); + /// print(numbers); // [1, 5] + /// ``` + void removeRange(int start, int end) { + if (value == null) return; + update((_) => value?.removeRange(start, end)); + } + + /// Overwrites a range of elements with [fillValue]. + /// + /// Sets the positions greater than or equal to [start] and less than [end], + /// to [fillValue]. + /// + /// The provided range, given by [start] and [end], must be valid. + /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. + /// An empty range (with `end == start`) is valid. + /// + /// If the element type is not nullable, the [fillValue] must be provided and + /// must be non-`null`. + /// + /// Example: + /// ```dart + /// final words = List.filled(5, 'old'); + /// print(words); // [old, old, old, old, old] + /// words.fillRange(1, 3, 'new'); + /// print(words); // [old, new, new, old, old] + /// ``` + void fillRange(int start, int end, [E? fillValue]) { + if (value == null) return; + update((_) => value?.fillRange(start, end, fillValue)); + } + + /// Replaces a range of elements with the elements of [replacements]. + /// + /// Removes the objects in the range from [start] to [end], + /// then inserts the elements of [replacements] at [start]. + /// ```dart + /// final numbers = [1, 2, 3, 4, 5]; + /// final replacements = [6, 7]; + /// numbers.replaceRange(1, 4, replacements); + /// print(numbers); // [1, 6, 7, 5] + /// ``` + /// The provided range, given by [start] and [end], must be valid. + /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. + /// An empty range (with `end == start`) is valid. + /// + /// The operation `list.replaceRange(start, end, replacements)` + /// is roughly equivalent to: + /// ```dart + /// final numbers = [1, 2, 3, 4, 5]; + /// numbers.removeRange(1, 4); + /// final replacements = [6, 7]; + /// numbers.insertAll(1, replacements); + /// print(numbers); // [1, 6, 7, 5] + /// ``` + /// but may be more efficient. + /// + /// The list must be growable. + /// This method does not work on fixed-length lists, even when [replacements] + /// has the same number of elements as the replaced range. In that case use + /// [setRange] instead. + void replaceRange(int start, int end, Iterable replacements) { + if (value == null) return; + update((_) => value?.replaceRange(start, end, replacements)); + } + + /// The object at the given [index] in the list. + /// + /// The [index] must be a valid index of this list, + /// which means that `index` must be non-negative and + /// less than [length]. + E? operator [](int index) => value?[index]; + + /// Returns a view of this list as a list of [R] instances. + /// + /// If this list contains only instances of [R], all read operations + /// will work correctly. If any operation tries to read an element + /// that is not an instance of [R], the access will throw instead. + /// + /// Elements added to the list (e.g., by using [add] or [addAll]) + /// must be instances of [R] to be valid arguments to the adding function, + /// and they must also be instances of [E] to be accepted by + /// this list as well. + /// + /// Methods which accept `Object?` as argument, like [contains] and [remove], + /// will pass the argument directly to the this list's method + /// without any checks. + /// That means that you can do `listOfStrings.cast().remove("a")` + /// successfully, even if it looks like it shouldn't have any effect. + /// + /// Typically implemented as `List.castFrom(this)`. + List? cast() => value?.cast(); + + /// The first element of the list. + /// + /// The list must be non-empty when accessing its first element. + /// + /// The first element of a list can be modified, unlike an [Iterable]. + /// A `list.first` is equivalent to `list[0]`, + /// both for getting and setting the value. + /// + /// ```dart + /// final numbers = [1, 2, 3]; + /// print(numbers.first); // 1 + /// numbers.first = 10; + /// print(numbers.first); // 10 + /// numbers.clear(); + /// numbers.first; // Throws. + /// ``` + set first(E valueToSet) => value?.first = valueToSet; + + /// The number of objects in this list. + /// + /// The valid indices for a list are `0` through `length - 1`. + /// ```dart + /// final numbers = [1, 2, 3]; + /// print(numbers.length); // 3 + /// ``` + int get length => value?.length ?? 0; + + /// An [Iterable] of the objects in this list in reverse order. + /// ```dart + /// final numbers = ['two', 'three', 'four']; + /// final reverseOrder = numbers.reversed; + /// print(reverseOrder.toList()); // [four, three, two] + /// ``` + Iterable? get reversed => value?.reversed; + + /// The first index of [element] in this list. + /// + /// Searches the list from index [start] to the end of the list. + /// The first time an object `o` is encountered so that `o == element`, + /// the index of `o` is returned. + /// ```dart + /// final notes = ['do', 're', 'mi', 're']; + /// print(notes.indexOf('re')); // 1 + /// + /// final indexWithStart = notes.indexOf('re', 2); // 3 + /// ``` + /// Returns -1 if [element] is not found. + /// ```dart + /// final notes = ['do', 're', 'mi', 're']; + /// final index = notes.indexOf('fa'); // -1 + /// ``` + int? indexOf(E element, [int start = 0]) => value?.indexOf(element, start); + + /// The first index in the list that satisfies the provided [test]. + /// + /// Searches the list from index [start] to the end of the list. + /// The first time an object `o` is encountered so that `test(o)` is true, + /// the index of `o` is returned. + /// + /// ```dart + /// final notes = ['do', 're', 'mi', 're']; + /// final first = notes.indexWhere((note) => note.startsWith('r')); // 1 + /// final second = notes.indexWhere((note) => note.startsWith('r'), 2); // 3 + /// ``` + /// + /// Returns -1 if [element] is not found. + /// ```dart + /// final notes = ['do', 're', 'mi', 're']; + /// final index = notes.indexWhere((note) => note.startsWith('k')); // -1 + /// ``` + int? indexWhere(bool Function(E element) test, [int start = 0]) => + value?.indexWhere(test); + + /// The last index in the list that satisfies the provided [test]. + /// + /// Searches the list from index [start] to 0. + /// The first time an object `o` is encountered so that `test(o)` is true, + /// the index of `o` is returned. + /// If [start] is omitted, it defaults to the [length] of the list. + /// + /// ```dart + /// final notes = ['do', 're', 'mi', 're']; + /// final first = notes.lastIndexWhere((note) => note.startsWith('r')); // 3 + /// final second = notes.lastIndexWhere((note) => note.startsWith('r'), + /// 2); // 1 + /// ``` + /// + /// Returns -1 if [element] is not found. + /// ```dart + /// final notes = ['do', 're', 'mi', 're']; + /// final index = notes.lastIndexWhere((note) => note.startsWith('k')); + /// print(index); // -1 + /// ``` + int? lastIndexWhere(bool Function(E element) test, [int? start]) => + value?.lastIndexWhere(test, start); + + /// The last index of [element] in this list. + /// + /// Searches the list backwards from index [start] to 0. + /// + /// The first time an object `o` is encountered so that `o == element`, + /// the index of `o` is returned. + /// ```dart + /// final notes = ['do', 're', 'mi', 're']; + /// const startIndex = 2; + /// final index = notes.lastIndexOf('re', startIndex); // 1 + /// ``` + /// If [start] is not provided, this method searches from the end of the + /// list. + /// ```dart + /// final notes = ['do', 're', 'mi', 're']; + /// final index = notes.lastIndexOf('re'); // 3 + /// ``` + /// Returns -1 if [element] is not found. + /// ```dart + /// final notes = ['do', 're', 'mi', 're']; + /// final index = notes.lastIndexOf('fa'); // -1 + /// ``` + int? lastIndexOf(E element, [int? start]) => + value?.lastIndexOf(element, start); + + /// Returns a new list containing the elements between [start] and [end]. + /// + /// The new list is a `List` containing the elements of this list at + /// positions greater than or equal to [start] and less than [end] in the same + /// order as they occur in this list. + /// + /// ```dart + /// final colors = ['red', 'green', 'blue', 'orange', 'pink']; + /// print(colors.sublist(1, 3)); // [green, blue] + /// ``` + /// + /// If [end] is omitted, it defaults to the [length] of this list. + /// + /// ```dart + /// final colors = ['red', 'green', 'blue', 'orange', 'pink']; + /// print(colors.sublist(3)); // [orange, pink] + /// ``` + /// + /// The `start` and `end` positions must satisfy the relations + /// 0 ≤ `start` ≤ `end` ≤ [length]. + /// If `end` is equal to `start`, then the returned list is empty. + List? sublist(int start, [int? end]) => value?.sublist(start, end); + + /// Creates an [Iterable] that iterates over a range of elements. + /// + /// The returned iterable iterates over the elements of this list + /// with positions greater than or equal to [start] and less than [end]. + /// + /// The provided range, [start] and [end], must be valid at the time + /// of the call. + /// A range from [start] to [end] is valid if 0 ≤ `start` ≤ `end` ≤ [length]. + /// An empty range (with `end == start`) is valid. + /// + /// The returned [Iterable] behaves like `skip(start).take(end - start)`. + /// That is, it does *not* break if this list changes size, it just + /// ends early if it reaches the end of the list early + /// (if `end`, or even `start`, becomes greater than [length]). + /// ```dart + /// final colors = ['red', 'green', 'blue', 'orange', 'pink']; + /// final firstRange = colors.getRange(0, 3); + /// print(firstRange.join(', ')); // red, green, blue + /// + /// final secondRange = colors.getRange(2, 5); + /// print(secondRange.join(', ')); // blue, orange, pink + /// ``` + Iterable? getRange(int start, int end) => value?.getRange(start, end); + + /// An unmodifiable [Map] view of this list. + /// + /// The map uses the indices of this list as keys and the corresponding objects + /// as values. The `Map.keys` [Iterable] iterates the indices of this list + /// in numerical order. + /// ```dart + /// var words = ['fee', 'fi', 'fo', 'fum']; + /// var map = words.asMap(); // {0: fee, 1: fi, 2: fo, 3: fum} + /// map.keys.toList(); // [0, 1, 2, 3] + /// ``` + Map? asMap() => value?.asMap(); +} diff --git a/packages/reactter/lib/src/signal/extensions/signal_map.dart b/packages/reactter/lib/src/signal/extensions/signal_map.dart new file mode 100644 index 00000000..21fab21c --- /dev/null +++ b/packages/reactter/lib/src/signal/extensions/signal_map.dart @@ -0,0 +1,553 @@ +// coverage:ignore-file +part of '../signal.dart'; + +extension SignalMapExt on Signal> { + /// Associates the [key] with the given [value]. + /// + /// If the key was already in the map, its associated value is changed. + /// Otherwise the key/value pair is added to the map. + void operator []=(K key, V valueToSet) => + update((_) => value[key] = valueToSet); + + /// Adds all key/value pairs of [newEntries] to this map. + /// + /// If a key of [newEntries] is already in this map, + /// the corresponding value is overwritten. + /// + /// The operation is equivalent to doing `this[entry.key] = entry.value` + /// for each [MapEntry] of the iterable. + /// ```dart + /// final planets = {1: 'Mercury', 2: 'Venus', + /// 3: 'Earth', 4: 'Mars'}; + /// final gasGiants = {5: 'Jupiter', 6: 'Saturn'}; + /// final iceGiants = {7: 'Uranus', 8: 'Neptune'}; + /// planets.addEntries(gasGiants.entries); + /// planets.addEntries(iceGiants.entries); + /// print(planets); + /// // {1: Mercury, 2: Venus, 3: Earth, 4: Mars, 5: Jupiter, 6: Saturn, + /// // 7: Uranus, 8: Neptune} + /// ``` + void addEntries(Iterable> newEntries) => + update((_) => value.addEntries(newEntries)); + + /// Updates the value for the provided [key]. + /// + /// Returns the new value associated with the key. + /// + /// If the key is present, invokes [update] with the current value and stores + /// the new value in the map. + /// + /// If the key is not present and [ifAbsent] is provided, calls [ifAbsent] + /// and adds the key with the returned value to the map. + /// + /// If the key is not present, [ifAbsent] must be provided. + /// ```dart + /// final planetsFromSun = {1: 'Mercury', 2: 'unknown', + /// 3: 'Earth'}; + /// // Update value for known key value 2. + /// planetsFromSun.update(2, (value) => 'Venus'); + /// print(planetsFromSun); // {1: Mercury, 2: Venus, 3: Earth} + /// + /// final largestPlanets = {1: 'Jupiter', 2: 'Saturn', + /// 3: 'Neptune'}; + /// // Key value 8 is missing from list, add it using [ifAbsent]. + /// largestPlanets.update(8, (value) => 'New', ifAbsent: () => 'Mercury'); + /// print(largestPlanets); // {1: Jupiter, 2: Saturn, 3: Neptune, 8: Mercury} + /// ``` + V updateMap(K key, V Function(V value) fnUpdate, {V Function()? ifAbsent}) { + late V result; + update( + (_) => result = value.update( + key, + fnUpdate, + ifAbsent: ifAbsent, + ), + ); + return result; + } + + /// Updates all values. + /// + /// Iterates over all entries in the map and updates them with the result + /// of invoking [update]. + /// ```dart + /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; + /// terrestrial.updateAll((key, value) => value.toUpperCase()); + /// print(terrestrial); // {1: MERCURY, 2: VENUS, 3: EARTH} + /// ``` + void updateAll(V Function(K key, V value) fnUpdate) => + update((_) => value.updateAll(fnUpdate)); + + /// Removes all entries of this map that satisfy the given [test]. + /// ```dart + /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; + /// terrestrial.removeWhere((key, value) => value.startsWith('E')); + /// print(terrestrial); // {1: Mercury, 2: Venus} + /// ``` + void removeWhere(bool Function(K key, V value) test) => + update((_) => value.removeWhere(test)); + + /// Look up the value of [key], or add a new entry if it isn't there. + /// + /// Returns the value associated to [key], if there is one. + /// Otherwise calls [ifAbsent] to get a new value, associates [key] to + /// that value, and then returns the new value. + /// ```dart + /// final diameters = {1.0: 'Earth'}; + /// final otherDiameters = {0.383: 'Mercury', 0.949: 'Venus'}; + /// + /// for (final item in otherDiameters.entries) { + /// diameters.putIfAbsent(item.key, () => item.value); + /// } + /// print(diameters); // {1.0: Earth, 0.383: Mercury, 0.949: Venus} + /// + /// // If the key already exists, the current value is returned. + /// final result = diameters.putIfAbsent(0.383, () => 'Random'); + /// print(result); // Mercury + /// print(diameters); // {1.0: Earth, 0.383: Mercury, 0.949: Venus} + /// ``` + /// Calling [ifAbsent] must not add or remove keys from the map. + V putIfAbsent(K key, V Function() ifAbsent) { + late V result; + update((_) => result = value.putIfAbsent(key, ifAbsent)); + return result; + } + + /// Adds all key/value pairs of [other] to this map. + /// + /// If a key of [other] is already in this map, its value is overwritten. + /// + /// The operation is equivalent to doing `this[key] = value` for each key + /// and associated value in other. It iterates over [other], which must + /// therefore not change during the iteration. + /// ```dart + /// final planets = {1: 'Mercury', 2: 'Earth'}; + /// planets.addAll({5: 'Jupiter', 6: 'Saturn'}); + /// print(planets); // {1: Mercury, 2: Earth, 5: Jupiter, 6: Saturn} + /// ``` + void addAll(Map other) => update((_) => value.addAll(other)); + + /// Removes [key] and its associated value, if present, from the map. + /// + /// Returns the value associated with `key` before it was removed. + /// Returns `null` if `key` was not in the map. + /// + /// Note that some maps allow `null` as a value, + /// so a returned `null` value doesn't always mean that the key was absent. + /// ```dart + /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; + /// final removedValue = terrestrial.remove(2); // Venus + /// print(terrestrial); // {1: Mercury, 3: Earth} + /// ``` + V? remove(Object? key) { + late V? result; + update((_) => result = value.remove(key)); + return result; + } + + /// Removes all entries from the map. + /// + /// After this, the map is empty. + /// ```dart + /// final planets = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; + /// planets.clear(); // {} + /// ``` + void clear() => update((_) => value.clear()); + + /// Provides a view of this map as having [RK] keys and [RV] instances, + /// if necessary. + /// + /// If this map is already a `Map`, it is returned unchanged. + /// + /// If this set contains only keys of type [RK] and values of type [RV], + /// all read operations will work correctly. + /// If any operation exposes a non-[RK] key or non-[RV] value, + /// the operation will throw instead. + /// + /// Entries added to the map must be valid for both a `Map` and a + /// `Map`. + /// + /// Methods which accept `Object?` as argument, + /// like [containsKey], [remove] and [operator []], + /// will pass the argument directly to the this map's method + /// without any checks. + /// That means that you can do `mapWithStringKeys.cast().remove("a")` + /// successfully, even if it looks like it shouldn't have any effect. + Map cast() => value.cast(); + + /// Whether this map contains the given [value]. + /// + /// Returns true if any of the values in the map are equal to `value` + /// according to the `==` operator. + /// ```dart + /// final moonCount = {'Mercury': 0, 'Venus': 0, 'Earth': 1, + /// 'Mars': 2, 'Jupiter': 79, 'Saturn': 82, 'Uranus': 27, 'Neptune': 14 }; + /// final moons3 = moonCount.containsValue(3); // false + /// final moons82 = moonCount.containsValue(82); // true + /// ``` + bool containsValue(Object? valueToEvaluate) => + value.containsValue(valueToEvaluate); + + /// Whether this map contains the given [key]. + /// + /// Returns true if any of the keys in the map are equal to `key` + /// according to the equality used by the map. + /// ```dart + /// final moonCount = {'Mercury': 0, 'Venus': 0, 'Earth': 1, + /// 'Mars': 2, 'Jupiter': 79, 'Saturn': 82, 'Uranus': 27, 'Neptune': 14 }; + /// final containsUranus = moonCount.containsKey('Uranus'); // true + /// final containsPluto = moonCount.containsKey('Pluto'); // false + /// ``` + bool containsKey(Object? key) => value.containsKey(key); + + /// The value for the given [key], or `null` if [key] is not in the map. + /// + /// Some maps allow `null` as a value. + /// For those maps, a lookup using this operator cannot distinguish between a + /// key not being in the map, and the key being there with a `null` value. + /// Methods like [containsKey] or [putIfAbsent] can be used if the distinction + /// is important. + V? operator [](Object? key) => value[key]; + + /// The map entries of [this]. + Iterable> get entries => value.entries; + + /// Returns a new map where all entries of this map are transformed by + /// the given [convert] function. + Map map(MapEntry Function(K key, V value) convert) => + value.map(convert); + + /// Applies [action] to each key/value pair of the map. + /// + /// Calling `action` must not add or remove keys from the map. + /// ```dart + /// final planetsByMass = {0.81: 'Venus', 1: 'Earth', + /// 0.11: 'Mars', 17.15: 'Neptune'}; + /// + /// planetsByMass.forEach((key, value) { + /// print('$key: $value'); + /// // 0.81: Venus + /// // 1: Earth + /// // 0.11: Mars + /// // 17.15: Neptune + /// }); + /// ``` + void forEach(void Function(K key, V value) action) => value.forEach(action); + + /// The keys of [this]. + /// + /// The returned iterable has efficient `length` and `contains` operations, + /// based on [length] and [containsKey] of the map. + /// + /// The order of iteration is defined by the individual `Map` implementation, + /// but must be consistent between changes to the map. + /// + /// Modifying the map while iterating the keys may break the iteration. + Iterable get keys => value.keys; + + /// The values of [this]. + /// + /// The values are iterated in the order of their corresponding keys. + /// This means that iterating [keys] and [values] in parallel will + /// provide matching pairs of keys and values. + /// + /// The returned iterable has an efficient `length` method based on the + /// [length] of the map. Its [Iterable.contains] method is based on + /// `==` comparison. + /// + /// Modifying the map while iterating the values may break the iteration. + Iterable get values => value.values; + + /// The number of key/value pairs in the map. + int get length => value.length; + + /// Whether there is no key/value pair in the map. + bool get isEmpty => value.isEmpty; + + /// Whether there is at least one key/value pair in the map. + bool get isNotEmpty => value.isNotEmpty; +} + +extension SignalMapNullExt on Signal?> { + /// Associates the [key] with the given [value]. + /// + /// If the key was already in the map, its associated value is changed. + /// Otherwise the key/value pair is added to the map. + void operator []=(K key, V valueToSet) { + if (value == null) return; + update((_) => value?[key] = valueToSet); + } + + /// Adds all key/value pairs of [newEntries] to this map. + /// + /// If a key of [newEntries] is already in this map, + /// the corresponding value is overwritten. + /// + /// The operation is equivalent to doing `this[entry.key] = entry.value` + /// for each [MapEntry] of the iterable. + /// ```dart + /// final planets = {1: 'Mercury', 2: 'Venus', + /// 3: 'Earth', 4: 'Mars'}; + /// final gasGiants = {5: 'Jupiter', 6: 'Saturn'}; + /// final iceGiants = {7: 'Uranus', 8: 'Neptune'}; + /// planets.addEntries(gasGiants.entries); + /// planets.addEntries(iceGiants.entries); + /// print(planets); + /// // {1: Mercury, 2: Venus, 3: Earth, 4: Mars, 5: Jupiter, 6: Saturn, + /// // 7: Uranus, 8: Neptune} + /// ``` + void addEntries(Iterable> newEntries) { + if (value == null) return; + update((_) => value!.addEntries(newEntries)); + } + + /// Updates the value for the provided [key]. + /// + /// Returns the new value associated with the key. + /// + /// If the key is present, invokes [update] with the current value and stores + /// the new value in the map. + /// + /// If the key is not present and [ifAbsent] is provided, calls [ifAbsent] + /// and adds the key with the returned value to the map. + /// + /// If the key is not present, [ifAbsent] must be provided. + /// ```dart + /// final planetsFromSun = {1: 'Mercury', 2: 'unknown', + /// 3: 'Earth'}; + /// // Update value for known key value 2. + /// planetsFromSun.update(2, (value) => 'Venus'); + /// print(planetsFromSun); // {1: Mercury, 2: Venus, 3: Earth} + /// + /// final largestPlanets = {1: 'Jupiter', 2: 'Saturn', + /// 3: 'Neptune'}; + /// // Key value 8 is missing from list, add it using [ifAbsent]. + /// largestPlanets.update(8, (value) => 'New', ifAbsent: () => 'Mercury'); + /// print(largestPlanets); // {1: Jupiter, 2: Saturn, 3: Neptune, 8: Mercury} + /// ``` + V? updateMap(K key, V Function(V value) fnUpdate, {V Function()? ifAbsent}) { + if (value == null) return null; + late V result; + update( + (_) => result = value!.update( + key, + fnUpdate, + ifAbsent: ifAbsent, + ), + ); + return result; + } + + /// Updates all values. + /// + /// Iterates over all entries in the map and updates them with the result + /// of invoking [update]. + /// ```dart + /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; + /// terrestrial.updateAll((key, value) => value.toUpperCase()); + /// print(terrestrial); // {1: MERCURY, 2: VENUS, 3: EARTH} + /// ``` + void updateAll(V Function(K key, V value) fnUpdate) { + if (value == null) return; + update((_) => value!.updateAll(fnUpdate)); + } + + /// Removes all entries of this map that satisfy the given [test]. + /// ```dart + /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; + /// terrestrial.removeWhere((key, value) => value.startsWith('E')); + /// print(terrestrial); // {1: Mercury, 2: Venus} + /// ``` + void removeWhere(bool Function(K key, V value) test) { + if (value == null) return; + update((_) => value!.removeWhere(test)); + } + + /// Look up the value of [key], or add a new entry if it isn't there. + /// + /// Returns the value associated to [key], if there is one. + /// Otherwise calls [ifAbsent] to get a new value, associates [key] to + /// that value, and then returns the new value. + /// ```dart + /// final diameters = {1.0: 'Earth'}; + /// final otherDiameters = {0.383: 'Mercury', 0.949: 'Venus'}; + /// + /// for (final item in otherDiameters.entries) { + /// diameters.putIfAbsent(item.key, () => item.value); + /// } + /// print(diameters); // {1.0: Earth, 0.383: Mercury, 0.949: Venus} + /// + /// // If the key already exists, the current value is returned. + /// final result = diameters.putIfAbsent(0.383, () => 'Random'); + /// print(result); // Mercury + /// print(diameters); // {1.0: Earth, 0.383: Mercury, 0.949: Venus} + /// ``` + /// Calling [ifAbsent] must not add or remove keys from the map. + V? putIfAbsent(K key, V Function() ifAbsent) { + if (value == null) return null; + late V result; + update((_) => result = value!.putIfAbsent(key, ifAbsent)); + return result; + } + + /// Adds all key/value pairs of [other] to this map. + /// + /// If a key of [other] is already in this map, its value is overwritten. + /// + /// The operation is equivalent to doing `this[key] = value` for each key + /// and associated value in other. It iterates over [other], which must + /// therefore not change during the iteration. + /// ```dart + /// final planets = {1: 'Mercury', 2: 'Earth'}; + /// planets.addAll({5: 'Jupiter', 6: 'Saturn'}); + /// print(planets); // {1: Mercury, 2: Earth, 5: Jupiter, 6: Saturn} + /// ``` + void addAll(Map other) { + if (value == null) return; + update((_) => value!.addAll(other)); + } + + /// Removes [key] and its associated value, if present, from the map. + /// + /// Returns the value associated with `key` before it was removed. + /// Returns `null` if `key` was not in the map. + /// + /// Note that some maps allow `null` as a value, + /// so a returned `null` value doesn't always mean that the key was absent. + /// ```dart + /// final terrestrial = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; + /// final removedValue = terrestrial.remove(2); // Venus + /// print(terrestrial); // {1: Mercury, 3: Earth} + /// ``` + V? remove(Object? key) { + if (value == null) return null; + late V? result; + update((_) => result = value!.remove(key)); + return result; + } + + /// Removes all entries from the map. + /// + /// After this, the map is empty. + /// ```dart + /// final planets = {1: 'Mercury', 2: 'Venus', 3: 'Earth'}; + /// planets.clear(); // {} + /// ``` + void clear() { + if (value == null) return; + update((_) => value!.clear()); + } + + /// Provides a view of this map as having [RK] keys and [RV] instances, + /// if necessary. + /// + /// If this map is already a `Map`, it is returned unchanged. + /// + /// If this set contains only keys of type [RK] and values of type [RV], + /// all read operations will work correctly. + /// If any operation exposes a non-[RK] key or non-[RV] value, + /// the operation will throw instead. + /// + /// Entries added to the map must be valid for both a `Map` and a + /// `Map`. + /// + /// Methods which accept `Object?` as argument, + /// like [containsKey], [remove] and [operator []], + /// will pass the argument directly to the this map's method + /// without any checks. + /// That means that you can do `mapWithStringKeys.cast().remove("a")` + /// successfully, even if it looks like it shouldn't have any effect. + Map? cast() => value?.cast(); + + /// Whether this map contains the given [value]. + /// + /// Returns true if any of the values in the map are equal to `value` + /// according to the `==` operator. + /// ```dart + /// final moonCount = {'Mercury': 0, 'Venus': 0, 'Earth': 1, + /// 'Mars': 2, 'Jupiter': 79, 'Saturn': 82, 'Uranus': 27, 'Neptune': 14 }; + /// final moons3 = moonCount.containsValue(3); // false + /// final moons82 = moonCount.containsValue(82); // true + /// ``` + bool? containsValue(Object? valueToEvaluate) => + value?.containsValue(valueToEvaluate); + + /// Whether this map contains the given [key]. + /// + /// Returns true if any of the keys in the map are equal to `key` + /// according to the equality used by the map. + /// ```dart + /// final moonCount = {'Mercury': 0, 'Venus': 0, 'Earth': 1, + /// 'Mars': 2, 'Jupiter': 79, 'Saturn': 82, 'Uranus': 27, 'Neptune': 14 }; + /// final containsUranus = moonCount.containsKey('Uranus'); // true + /// final containsPluto = moonCount.containsKey('Pluto'); // false + /// ``` + bool? containsKey(Object? key) => value?.containsKey(key); + + /// The value for the given [key], or `null` if [key] is not in the map. + /// + /// Some maps allow `null` as a value. + /// For those maps, a lookup using this operator cannot distinguish between a + /// key not being in the map, and the key being there with a `null` value. + /// Methods like [containsKey] or [putIfAbsent] can be used if the distinction + /// is important. + V? operator [](Object? key) => value?[key]; + + /// The map entries of [this]. + Iterable>? get entries => value?.entries; + + /// Returns a new map where all entries of this map are transformed by + /// the given [convert] function. + Map? map(MapEntry Function(K key, V value) convert) => + value?.map(convert); + + /// Applies [action] to each key/value pair of the map. + /// + /// Calling `action` must not add or remove keys from the map. + /// ```dart + /// final planetsByMass = {0.81: 'Venus', 1: 'Earth', + /// 0.11: 'Mars', 17.15: 'Neptune'}; + /// + /// planetsByMass.forEach((key, value) { + /// print('$key: $value'); + /// // 0.81: Venus + /// // 1: Earth + /// // 0.11: Mars + /// // 17.15: Neptune + /// }); + /// ``` + void forEach(void Function(K key, V value) action) => value?.forEach(action); + + /// The keys of [this]. + /// + /// The returned iterable has efficient `length` and `contains` operations, + /// based on [length] and [containsKey] of the map. + /// + /// The order of iteration is defined by the individual `Map` implementation, + /// but must be consistent between changes to the map. + /// + /// Modifying the map while iterating the keys may break the iteration. + Iterable? get keys => value?.keys; + + /// The values of [this]. + /// + /// The values are iterated in the order of their corresponding keys. + /// This means that iterating [keys] and [values] in parallel will + /// provide matching pairs of keys and values. + /// + /// The returned iterable has an efficient `length` method based on the + /// [length] of the map. Its [Iterable.contains] method is based on + /// `==` comparison. + /// + /// Modifying the map while iterating the values may break the iteration. + Iterable? get values => value?.values; + + /// The number of key/value pairs in the map. + int? get length => value?.length; + + /// Whether there is no key/value pair in the map. + bool? get isEmpty => value?.isEmpty; + + /// Whether there is at least one key/value pair in the map. + bool? get isNotEmpty => value?.isNotEmpty; +} diff --git a/packages/reactter/lib/src/signal/extensions/signal_num.dart b/packages/reactter/lib/src/signal/extensions/signal_num.dart new file mode 100644 index 00000000..cb144c0a --- /dev/null +++ b/packages/reactter/lib/src/signal/extensions/signal_num.dart @@ -0,0 +1,783 @@ +// coverage:ignore-file +part of '../signal.dart'; + +extension SignalNumExt on Signal { + /// Adds [other] to this number. + /// + /// The result is an [Signal] of [double], as described by [double.+], + /// if both `this` and [other] is an [Signal] of [double], + /// otherwise the result is a [Signal] of [int]. + Signal operator +(Signal other) => Signal(value + other.value); + + /// Subtracts [other] from this number. + /// + /// The result is an [Signal] of [double], as described by [double.-], + /// if both `this` and [other] is an [Signal] of [double], + /// otherwise the result is a [Signal] of [int]. + Signal operator -(Signal other) => Signal(value - other.value); + + /// Multiplies this number by [other]. + /// + /// The result is an [Signal] of [double], as described by [double.*], + /// if both `this` and [other] is an [Signal] of [double], + /// otherwise the result is a [Signal] of [int]. + Signal operator *(Signal other) => Signal(value * other.value); + + /// Euclidean modulo of this number by [other]. + /// + /// Returns the remainder of the Euclidean division. + /// The Euclidean division of two integers `a` and `b` + /// yields two integers `q` and `r` such that + /// `a == b * q + r` and `0 <= r < b.abs()`. + /// + /// The Euclidean division is only defined for integers, but can be easily + /// extended to work with doubles. In that case, `q` is still an integer, + /// but `r` may have a non-integer value that still satisfies `0 <= r < |b|`. + /// + /// The sign of the returned value `r` is always positive. + /// + /// See [remainder] for the remainder of the truncating division. + /// + /// The result is an [Signal] of [double], as described by [double.%], + /// if both `this` and [other] are [Signal] of [double], + /// otherwise the result is a [Signal] of [int]. + /// + /// Example: + /// ```dart + /// print(Signal(5) % Signal(3)); // Signal(2) + /// print(Signal(-5) % Signal(3)); // Signal(1) + /// print(Signal(5) % Signal(-3)); // Signal(2) + /// print(Signal(-5) % Signal(-3)); // Signal(1) + /// ``` + Signal operator %(Signal other) => Signal(value % other.value); + + /// Divides this number by [other]. + Signal operator /(Signal other) => Signal(value / other.value); + + /// Truncating division operator. + /// + /// Performs truncating division of this number by [other]. + /// Truncating division is division where a fractional result + /// is converted to an integer by rounding towards zero. + /// + /// If both operands are [int]s, then [other] must not be zero. + /// Then `a ~/ b` corresponds to `a.remainder(b)` + /// such that `a == (a ~/ b) * b + a.remainder(b)`. + /// + /// If either operand is a [double], then the other operand is converted + /// to a double before performing the division and truncation of the result. + /// Then `a ~/ b` is equivalent to `(a / b).truncate()`. + /// This means that the intermediate result of the double division + /// must be a finite integer (not an infinity or [double.nan]). + Signal operator ~/(Signal other) => Signal(value ~/ other.value); + + /// The negation of this value. + /// + /// The negation of a number is a number of the same kind + /// (`int` or `double`) representing the negation of the + /// numbers numerical value (the result of subtracting the + /// number from zero), if that value *exists*. + /// + /// Negating a double gives a number with the same magnitude + /// as the original value (`number.abs() == (-number).abs()`), + /// and the opposite sign (`-(number.sign) == (-number).sign`). + /// + /// Negating an integer, `-number`, is equivalent to subtracting + /// it from zero, `0 - number`. + /// + /// (Both properties generally also hold for the other type, + /// but with a few edge case exceptions). + Signal operator -() => Signal(-value); + + /// Whether this number is numerically smaller than [other]. + /// + /// Returns `true` if this number is smaller than [other]. + /// Returns `false` if this number is greater than or equal to [other] + /// or if either value is a NaN value like [double.nan]. + bool operator <(Signal other) => value < other.value; + + /// Whether this number is numerically smaller than or equal to [other]. + /// + /// Returns `true` if this number is smaller than or equal to [other]. + /// Returns `false` if this number is greater than [other] + /// or if either value is a NaN value like [double.nan]. + bool operator <=(Signal other) => value <= other.value; + + /// Whether this number is numerically greater than [other]. + /// + /// Returns `true` if this number is greater than [other]. + /// Returns `false` if this number is smaller than or equal to [other] + /// or if either value is a NaN value like [double.nan]. + bool operator >(Signal other) => value > other.value; + + /// Whether this number is numerically greater than or equal to [other]. + /// + /// Returns `true` if this number is greater than or equal to [other]. + /// Returns `false` if this number is smaller than [other] + /// or if either value is a NaN value like [double.nan]. + bool operator >=(Signal other) => value >= other.value; + + /// Compares this to `other`. + /// + /// Returns a negative number if `this` is less than `other`, zero if they are + /// equal, and a positive number if `this` is greater than `other`. + /// + /// The ordering represented by this method is a total ordering of [num] + /// values. All distinct doubles are non-equal, as are all distinct integers, + /// but integers are equal to doubles if they have the same numerical + /// value. + /// + /// For doubles, the `compareTo` operation is different from the partial + /// ordering given by [operator==], [operator<] and [operator>]. For example, + /// IEEE doubles impose that `0.0 == -0.0` and all comparison operations on + /// NaN return false. + /// + /// This function imposes a complete ordering for doubles. When using + /// `compareTo`, the following properties hold: + /// + /// - All NaN values are considered equal, and greater than any numeric value. + /// - -0.0 is less than 0.0 (and the integer 0), but greater than any non-zero + /// negative value. + /// - Negative infinity is less than all other values and positive infinity is + /// greater than all non-NaN values. + /// - All other values are compared using their numeric value. + /// + /// Examples: + /// ```dart + /// print(Signal(1).compareTo(2)); // => -1 + /// print(Signal(2).compareTo(1)); // => 1 + /// print(Signal(1).compareTo(1)); // => 0 + /// + /// // The following comparisons yield different results than the + /// // corresponding comparison operators. + /// print(Signal(-0.0).compareTo(0.0)); // => -1 + /// print(double.nan.compareTo(double.nan)); // => 0 + /// print(double.infinity.compareTo(double.nan)); // => -1 + /// + /// // -0.0, and NaN comparison operators have rules imposed by the IEEE + /// // standard. + /// print(-0.0 == 0.0); // => true + /// print(double.nan == double.nan); // => false + /// print(double.infinity < double.nan); // => false + /// print(double.nan < double.infinity); // => false + /// print(double.nan == double.infinity); // => false + /// ``` + int compareTo(num other) => value.compareTo(other); + + /// The remainder of the truncating division of `this` by [other]. + /// + /// The result `r` of this operation satisfies: + /// `this == (this ~/ other) * other + r`. + /// As a consequence, the remainder `r` has the same sign as the divider + /// `this`. + /// + /// The result is an [int], as described by [int.remainder], + /// if both `this` and [other] are integers, + /// otherwise the result is a [double]. + /// + /// Example: + /// ```dart + /// print(Signal(5).remainder(3)); // 2 + /// print(Signal(-5).remainder(3)); // -2 + /// print(Signal(5).remainder(-3)); // 2 + /// print(Signal(-5).remainder(-3)); // -2 + /// ``` + num remainder(num other) => value.remainder(other); + + /// Whether this number is a Not-a-Number value. + /// + /// Is `true` if this number is the [double.nan] value + /// or any other of the possible [double] NaN values. + /// Is `false` if this number is an integer, + /// a finite double or an infinite double ([double.infinity] + /// or [double.negativeInfinity]). + /// + /// All numbers satisfy exactly one of [isInfinite], [isFinite] + /// and `isNaN`. + bool get isNaN => value.isNaN; + + /// Whether this number is negative. + /// + /// A number is negative if it's smaller than zero, + /// or if it is the double `-0.0`. + /// This precludes a NaN value like [double.nan] from being negative. + bool get isNegative => value.isNegative; + + /// Whether this number is positive infinity or negative infinity. + /// + /// Only satisfied by [double.infinity] and [double.negativeInfinity]. + /// + /// All numbers satisfy exactly one of `isInfinite`, [isFinite] + /// and [isNaN]. + bool get isInfinite => value.isInfinite; + + /// Whether this number is finite. + /// + /// The only non-finite numbers are NaN values, positive infinity, and + /// negative infinity. All integers are finite. + /// + /// All numbers satisfy exactly one of [isInfinite], `isFinite` + /// and [isNaN]. + bool get isFinite => value.isFinite; + + /// The absolute value of this number. + /// + /// The absolute value is the value itself, if the value is non-negative, + /// and `-value` if the value is negative. + /// + /// Integer overflow may cause the result of `-value` to stay negative. + /// + /// ```dart + /// print(Signal(2).abs()); // 2 + /// print(Signal(-2.5).abs()); // 2.5 + /// ``` + num abs() => value.abs(); + + /// Negative one, zero or positive one depending on the sign and + /// numerical value of this number. + /// + /// The value minus one if this number is less than zero, + /// plus one if this number is greater than zero, + /// and zero if this number is equal to zero. + /// + /// Returns NaN if this number is a [double] NaN value. + /// + /// Returns a number of the same type as this number. + /// For doubles, `Signal(-0.0).sign` is `-0.0`. + /// + /// The result satisfies: + /// ```dart + /// n == n.sign * n.abs() + /// ``` + /// for all numbers `n` (except NaN, because NaN isn't `==` to itself). + num get sign => value.sign; + + /// The integer closest to this number. + /// + /// Rounds away from zero when there is no closest integer: + /// `Signal(3.5).round() == 4` and `Signal(-3.5).round() == -4`. + /// + /// The number must be finite (see [isFinite]). + /// + /// If the value is greater than the highest representable positive integer, + /// the result is that highest positive integer. + /// If the value is smaller than the highest representable negative integer, + /// the result is that highest negative integer. + int round() => value.round(); + + /// The greatest integer no greater than this number. + /// + /// Rounds fractional values towards negative infinity. + /// + /// The number must be finite (see [isFinite]). + /// + /// If the value is greater than the highest representable positive integer, + /// the result is that highest positive integer. + /// If the value is smaller than the highest representable negative integer, + /// the result is that highest negative integer. + int floor() => value.floor(); + + /// The least integer no smaller than `this`. + /// + /// Rounds fractional values towards positive infinity. + /// + /// The number must be finite (see [isFinite]). + /// + /// If the value is greater than the highest representable positive integer, + /// the result is that highest positive integer. + /// If the value is smaller than the highest representable negative integer, + /// the result is that highest negative integer. + int ceil() => value.ceil(); + + /// The integer obtained by discarding any fractional digits from `this`. + /// + /// Rounds fractional values towards zero. + /// + /// The number must be finite (see [isFinite]). + /// + /// If the value is greater than the highest representable positive integer, + /// the result is that highest positive integer. + /// If the value is smaller than the highest representable negative integer, + /// the result is that highest negative integer. + int truncate() => value.truncate(); + + /// The double integer value closest to this value. + /// + /// Rounds away from zero when there is no closest integer: + /// `Signal(3.5).roundToDouble() == 4` and `Signal(-3.5).roundToDouble() == -4`. + /// + /// If this is already an integer valued double, including `-0.0`, or it is a + /// non-finite double value, the value is returned unmodified. + /// + /// For the purpose of rounding, `-0.0` is considered to be below `0.0`, + /// and `-0.0` is therefore considered closer to negative numbers than `0.0`. + /// This means that for a value `d` in the range `-0.5 < d < 0.0`, + /// the result is `-0.0`. + double roundToDouble() => value.roundToDouble(); + + /// Returns the greatest double integer value no greater than `this`. + /// + /// If this is already an integer valued double, including `-0.0`, or it is a + /// non-finite double value, the value is returned unmodified. + /// + /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. + /// A number `d` in the range `0.0 < d < 1.0` will return `0.0`. + double floorToDouble() => value.floorToDouble(); + + /// Returns the least double integer value no smaller than `this`. + /// + /// If this is already an integer valued double, including `-0.0`, or it is a + /// non-finite double value, the value is returned unmodified. + /// + /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. + /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`. + double ceilToDouble() => value.ceilToDouble(); + + /// Returns the double integer value obtained by discarding any fractional + /// digits from the double value of `this`. + /// + /// If this is already an integer valued double, including `-0.0`, or it is a + /// non-finite double value, the value is returned unmodified. + /// + /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. + /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`, and + /// in the range `0.0 < d < 1.0` it will return 0.0. + double truncateToDouble() => value.truncateToDouble(); + + /// Returns this [num] clamped to be in the range [lowerLimit]-[upperLimit]. + /// + /// The comparison is done using [compareTo] and therefore takes `-0.0` into + /// account. This also implies that [double.nan] is treated as the maximal + /// double value. + /// + /// The arguments [lowerLimit] and [upperLimit] must form a valid range where + /// `lowerLimit.compareTo(upperLimit) <= 0`. + /// + /// Example: + /// ```dart + /// var result = Signal(10.5).clamp(5, 10.0); // 10.0 + /// result = Signal(0.75).clamp(5, 10.0); // 5 + /// result = Signal(-10).clamp(-5, 5.0); // -5 + /// result = Signal(-0.0).clamp(-5, 5.0); // -0.0 + /// ``` + num clamp(num lowerLimit, num upperLimit) => + value.clamp(lowerLimit, upperLimit); + + /// Truncates this [num] to an integer and returns the result as an [int]. + /// + /// Equivalent to [truncate]. + int toInt() => value.toInt(); + + /// This number as a [double]. + /// + /// If an integer number is not precisely representable as a [double], + /// an approximation is returned. + double toDouble() => value.toDouble(); + + /// A decimal-point string-representation of this number. + /// + /// Converts this number to a [double] + /// before computing the string representation, + /// as by [toDouble]. + /// + /// If the absolute value of `this` is greater than or equal to `10^21`, then + /// this methods returns an exponential representation computed by + /// `this.toStringAsExponential()`. Otherwise the result + /// is the closest string representation with exactly [fractionDigits] digits + /// after the decimal point. If [fractionDigits] equals 0, then the decimal + /// point is omitted. + /// + /// The parameter [fractionDigits] must be an integer satisfying: + /// `0 <= fractionDigits <= 20`. + /// + /// Examples: + /// ```dart + /// Signal(1).toStringAsFixed(3); // 1.000 + /// Signal(4321.12345678).toStringAsFixed(3); // 4321.123 + /// Signal(4321.12345678).toStringAsFixed(5); // 4321.12346 + /// Signal(123456789012345).toStringAsFixed(3); // 123456789012345.000 + /// Signal(10000000000000000).toStringAsFixed(4); // 10000000000000000.0000 + /// Signal(5.25).toStringAsFixed(0); // 5 + /// ``` + String toStringAsFixed(int fractionDigits) => + value.toStringAsFixed(fractionDigits); + + /// An exponential string-representation of this number. + /// + /// Converts this number to a [double] + /// before computing the string representation. + /// + /// If [fractionDigits] is given, then it must be an integer satisfying: + /// `0 <= fractionDigits <= 20`. In this case the string contains exactly + /// [fractionDigits] after the decimal point. Otherwise, without the parameter, + /// the returned string uses the shortest number of digits that accurately + /// represent this number. + /// + /// If [fractionDigits] equals 0, then the decimal point is omitted. + /// Examples: + /// ```dart + /// Signal(1).toStringAsExponential(); // 1e+0 + /// Signa(1).toStringAsExponential(3); // 1.000e+0 + /// Signal(123456).toStringAsExponential(); // 1.23456e+5 + /// Signal(123456).toStringAsExponential(3); // 1.235e+5 + /// Signal(123).toStringAsExponential(0); // 1e+2 + /// ``` + String toStringAsExponential([int? fractionDigits]) => + value.toStringAsExponential(fractionDigits); + + /// A string representation with [precision] significant digits. + /// + /// Converts this number to a [double] + /// and returns a string representation of that value + /// with exactly [precision] significant digits. + /// + /// The parameter [precision] must be an integer satisfying: + /// `1 <= precision <= 21`. + /// + /// Examples: + /// ```dart + /// Signal(1).toStringAsPrecision(2); // 1.0 + /// Signal(1e15).toStringAsPrecision(3); // 1.00e+15 + /// Signal(1234567).toStringAsPrecision(3); // 1.23e+6 + /// Signal(1234567).toStringAsPrecision(9); // 1234567.00 + /// Signal(12345678901234567890).toStringAsPrecision(20); // 12345678901234567168 + /// Signal(12345678901234567890).toStringAsPrecision(14); // 1.2345678901235e+19 + /// Signal(0.00000012345).toStringAsPrecision(15); // 1.23450000000000e-7 + /// Signal(0.0000012345).toStringAsPrecision(15); // 0.00000123450000000000 + /// ``` + String toStringAsPrecision(int precision) => + value.toStringAsPrecision(precision); +} + +extension SignalNumNullExt on Signal { + /// Compares this to `other`. + /// + /// Returns a negative number if `this` is less than `other`, zero if they are + /// equal, and a positive number if `this` is greater than `other`. + /// + /// The ordering represented by this method is a total ordering of [num] + /// values. All distinct doubles are non-equal, as are all distinct integers, + /// but integers are equal to doubles if they have the same numerical + /// value. + /// + /// For doubles, the `compareTo` operation is different from the partial + /// ordering given by [operator==], [operator<] and [operator>]. For example, + /// IEEE doubles impose that `0.0 == -0.0` and all comparison operations on + /// NaN return false. + /// + /// This function imposes a complete ordering for doubles. When using + /// `compareTo`, the following properties hold: + /// + /// - All NaN values are considered equal, and greater than any numeric value. + /// - -0.0 is less than 0.0 (and the integer 0), but greater than any non-zero + /// negative value. + /// - Negative infinity is less than all other values and positive infinity is + /// greater than all non-NaN values. + /// - All other values are compared using their numeric value. + /// + /// Examples: + /// ```dart + /// print(Signal(1).compareTo(2)); // => -1 + /// print(Signal(2).compareTo(1)); // => 1 + /// print(Signal(1).compareTo(1)); // => 0 + /// + /// // The following comparisons yield different results than the + /// // corresponding comparison operators. + /// print(Signal(-0.0).compareTo(0.0)); // => -1 + /// print(double.nan.compareTo(double.nan)); // => 0 + /// print(double.infinity.compareTo(double.nan)); // => -1 + /// + /// // -0.0, and NaN comparison operators have rules imposed by the IEEE + /// // standard. + /// print(-0.0 == 0.0); // => true + /// print(double.nan == double.nan); // => false + /// print(double.infinity < double.nan); // => false + /// print(double.nan < double.infinity); // => false + /// print(double.nan == double.infinity); // => false + /// ``` + int? compareTo(num other) => value?.compareTo(other); + + /// The remainder of the truncating division of `this` by [other]. + /// + /// The result `r` of this operation satisfies: + /// `this == (this ~/ other) * other + r`. + /// As a consequence, the remainder `r` has the same sign as the divider + /// `this`. + /// + /// The result is an [int], as described by [int.remainder], + /// if both `this` and [other] are integers, + /// otherwise the result is a [double]. + /// + /// Example: + /// ```dart + /// print(Signal(5).remainder(3)); // 2 + /// print(Signal(-5).remainder(3)); // -2 + /// print(Signal(5).remainder(-3)); // 2 + /// print(Signal(-5).remainder(-3)); // -2 + /// ``` + num? remainder(num other) => value?.remainder(other); + + /// Whether this number is a Not-a-Number value. + /// + /// Is `true` if this number is the [double.nan] value + /// or any other of the possible [double] NaN values. + /// Is `false` if this number is an integer, + /// a finite double or an infinite double ([double.infinity] + /// or [double.negativeInfinity]). + /// + /// All numbers satisfy exactly one of [isInfinite], [isFinite] + /// and `isNaN`. + bool? get isNaN => value?.isNaN; + + /// Whether this number is negative. + /// + /// A number is negative if it's smaller than zero, + /// or if it is the double `-0.0`. + /// This precludes a NaN value like [double.nan] from being negative. + bool? get isNegative => value?.isNegative; + + /// Whether this number is positive infinity or negative infinity. + /// + /// Only satisfied by [double.infinity] and [double.negativeInfinity]. + /// + /// All numbers satisfy exactly one of `isInfinite`, [isFinite] + /// and [isNaN]. + bool? get isInfinite => value?.isInfinite; + + /// Whether this number is finite. + /// + /// The only non-finite numbers are NaN values, positive infinity, and + /// negative infinity. All integers are finite. + /// + /// All numbers satisfy exactly one of [isInfinite], `isFinite` + /// and [isNaN]. + bool? get isFinite => value?.isFinite; + + /// The absolute value of this number. + /// + /// The absolute value is the value itself, if the value is non-negative, + /// and `-value` if the value is negative. + /// + /// Integer overflow may cause the result of `-value` to stay negative. + /// + /// ```dart + /// print(Signal(2).abs()); // 2 + /// print(Signal(-2.5).abs()); // 2.5 + /// ``` + num? abs() => value?.abs(); + + /// Negative one, zero or positive one depending on the sign and + /// numerical value of this number. + /// + /// The value minus one if this number is less than zero, + /// plus one if this number is greater than zero, + /// and zero if this number is equal to zero. + /// + /// Returns NaN if this number is a [double] NaN value. + /// + /// Returns a number of the same type as this number. + /// For doubles, `Signal(-0.0).sign` is `-0.0`. + /// + /// The result satisfies: + /// ```dart + /// n == n.sign * n.abs() + /// ``` + /// for all numbers `n` (except NaN, because NaN isn't `==` to itself). + num? get sign => value?.sign; + + /// The integer closest to this number. + /// + /// Rounds away from zero when there is no closest integer: + /// `Signal(3.5).round() == 4` and `Signal(-3.5).round() == -4`. + /// + /// The number must be finite (see [isFinite]). + /// + /// If the value is greater than the highest representable positive integer, + /// the result is that highest positive integer. + /// If the value is smaller than the highest representable negative integer, + /// the result is that highest negative integer. + int? round() => value?.round(); + + /// The greatest integer no greater than this number. + /// + /// Rounds fractional values towards negative infinity. + /// + /// The number must be finite (see [isFinite]). + /// + /// If the value is greater than the highest representable positive integer, + /// the result is that highest positive integer. + /// If the value is smaller than the highest representable negative integer, + /// the result is that highest negative integer. + int? floor() => value?.floor(); + + /// The least integer no smaller than `this`. + /// + /// Rounds fractional values towards positive infinity. + /// + /// The number must be finite (see [isFinite]). + /// + /// If the value is greater than the highest representable positive integer, + /// the result is that highest positive integer. + /// If the value is smaller than the highest representable negative integer, + /// the result is that highest negative integer. + int? ceil() => value?.ceil(); + + /// The integer obtained by discarding any fractional digits from `this`. + /// + /// Rounds fractional values towards zero. + /// + /// The number must be finite (see [isFinite]). + /// + /// If the value is greater than the highest representable positive integer, + /// the result is that highest positive integer. + /// If the value is smaller than the highest representable negative integer, + /// the result is that highest negative integer. + int? truncate() => value?.truncate(); + + /// The double integer value closest to this value. + /// + /// Rounds away from zero when there is no closest integer: + /// `Signal(3.5).roundToDouble() == 4` and `Signal(-3.5).roundToDouble() == -4`. + /// + /// If this is already an integer valued double, including `-0.0`, or it is a + /// non-finite double value, the value is returned unmodified. + /// + /// For the purpose of rounding, `-0.0` is considered to be below `0.0`, + /// and `-0.0` is therefore considered closer to negative numbers than `0.0`. + /// This means that for a value `d` in the range `-0.5 < d < 0.0`, + /// the result is `-0.0`. + double? roundToDouble() => value?.roundToDouble(); + + /// Returns the greatest double integer value no greater than `this`. + /// + /// If this is already an integer valued double, including `-0.0`, or it is a + /// non-finite double value, the value is returned unmodified. + /// + /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. + /// A number `d` in the range `0.0 < d < 1.0` will return `0.0`. + double? floorToDouble() => value?.floorToDouble(); + + /// Returns the least double integer value no smaller than `this`. + /// + /// If this is already an integer valued double, including `-0.0`, or it is a + /// non-finite double value, the value is returned unmodified. + /// + /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. + /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`. + double? ceilToDouble() => value?.ceilToDouble(); + + /// Returns the double integer value obtained by discarding any fractional + /// digits from the double value of `this`. + /// + /// If this is already an integer valued double, including `-0.0`, or it is a + /// non-finite double value, the value is returned unmodified. + /// + /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. + /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`, and + /// in the range `0.0 < d < 1.0` it will return 0.0. + double? truncateToDouble() => value?.truncateToDouble(); + + /// Returns this [num] clamped to be in the range [lowerLimit]-[upperLimit]. + /// + /// The comparison is done using [compareTo] and therefore takes `-0.0` into + /// account. This also implies that [double.nan] is treated as the maximal + /// double value. + /// + /// The arguments [lowerLimit] and [upperLimit] must form a valid range where + /// `lowerLimit.compareTo(upperLimit) <= 0`. + /// + /// Example: + /// ```dart + /// var result = Signal(10.5).clamp(5, 10.0); // 10.0 + /// result = Signal(0.75).clamp(5, 10.0); // 5 + /// result = Signal(-10).clamp(-5, 5.0); // -5 + /// result = Signal(-0.0).clamp(-5, 5.0); // -0.0 + /// ``` + num? clamp(num lowerLimit, num upperLimit) => + value?.clamp(lowerLimit, upperLimit); + + /// Truncates this [num] to an integer and returns the result as an [int]. + /// + /// Equivalent to [truncate]. + int? toInt() => value?.toInt(); + + /// This number as a [double]. + /// + /// If an integer number is not precisely representable as a [double], + /// an approximation is returned. + double? toDouble() => value?.toDouble(); + + /// A decimal-point string-representation of this number. + /// + /// Converts this number to a [double] + /// before computing the string representation, + /// as by [toDouble]. + /// + /// If the absolute value of `this` is greater than or equal to `10^21`, then + /// this methods returns an exponential representation computed by + /// `this.toStringAsExponential()`. Otherwise the result + /// is the closest string representation with exactly [fractionDigits] digits + /// after the decimal point. If [fractionDigits] equals 0, then the decimal + /// point is omitted. + /// + /// The parameter [fractionDigits] must be an integer satisfying: + /// `0 <= fractionDigits <= 20`. + /// + /// Examples: + /// ```dart + /// Signal(1).toStringAsFixed(3); // 1.000 + /// Signal(4321.12345678).toStringAsFixed(3); // 4321.123 + /// (4321.12345678).toStringAsFixed(5); // 4321.12346 + /// Signal(123456789012345).toStringAsFixed(3); // 123456789012345.000 + /// Signal(10000000000000000).toStringAsFixed(4); // 10000000000000000.0000 + /// Signal(5.25).toStringAsFixed(0); // 5 + /// ``` + String? toStringAsFixed(int fractionDigits) => + value?.toStringAsFixed(fractionDigits); + + /// An exponential string-representation of this number. + /// + /// Converts this number to a [double] + /// before computing the string representation. + /// + /// If [fractionDigits] is given, then it must be an integer satisfying: + /// `0 <= fractionDigits <= 20`. In this case the string contains exactly + /// [fractionDigits] after the decimal point. Otherwise, without the parameter, + /// the returned string uses the shortest number of digits that accurately + /// represent this number. + /// + /// If [fractionDigits] equals 0, then the decimal point is omitted. + /// Examples: + /// ```dart + /// Signal(1).toStringAsExponential(); // 1e+0 + /// Signal(1).toStringAsExponential(3); // 1.000e+0 + /// Signal(123456).toStringAsExponential(); // 1.23456e+5 + /// Signal(123456).toStringAsExponential(3); // 1.235e+5 + /// Signal(123).toStringAsExponential(0); // 1e+2 + /// ``` + String? toStringAsExponential([int? fractionDigits]) => + value?.toStringAsExponential(fractionDigits); + + /// A string representation with [precision] significant digits. + /// + /// Converts this number to a [double] + /// and returns a string representation of that value + /// with exactly [precision] significant digits. + /// + /// The parameter [precision] must be an integer satisfying: + /// `1 <= precision <= 21`. + /// + /// Examples: + /// ```dart + /// Signal(1).toStringAsPrecision(2); // 1.0 + /// Signal(1e15).toStringAsPrecision(3); // 1.00e+15 + /// Signal(1234567).toStringAsPrecision(3); // 1.23e+6 + /// Signal(1234567).toStringAsPrecision(9); // 1234567.00 + /// Signal(12345678901234567890).toStringAsPrecision(20); // 12345678901234567168 + /// Signal(12345678901234567890).toStringAsPrecision(14); // 1.2345678901235e+19 + /// Signal(0.00000012345).toStringAsPrecision(15); // 1.23450000000000e-7 + /// Signal(0.0000012345).toStringAsPrecision(15); // 0.00000123450000000000 + /// ``` + String? toStringAsPrecision(int precision) => + value?.toStringAsPrecision(precision); +} diff --git a/packages/reactter/lib/src/signal/extensions/signal_set.dart b/packages/reactter/lib/src/signal/extensions/signal_set.dart new file mode 100644 index 00000000..94bb2f1d --- /dev/null +++ b/packages/reactter/lib/src/signal/extensions/signal_set.dart @@ -0,0 +1,462 @@ +// coverage:ignore-file +part of '../signal.dart'; + +extension SignalSetExt on Signal> { + /// Adds [value] to the set. + /// + /// Returns `true` if [value] (or an equal value) was not yet in the set. + /// Otherwise returns `false` and the set is not changed. + /// + /// Example: + /// ```dart + /// final dateTimes = {}; + /// final time1 = DateTime.fromMillisecondsSinceEpoch(0); + /// final time2 = DateTime.fromMillisecondsSinceEpoch(0); + /// // time1 and time2 are equal, but not identical. + /// assert(time1 == time2); + /// assert(!identical(time1, time2)); + /// final time1Added = dateTimes.add(time1); + /// print(time1Added); // true + /// // A value equal to time2 exists already in the set, and the call to + /// // add doesn't change the set. + /// final time2Added = dateTimes.add(time2); + /// print(time2Added); // false + /// + /// print(dateTimes); // {1970-01-01 02:00:00.000} + /// assert(dateTimes.length == 1); + /// assert(identical(time1, dateTimes.first)); + /// print(dateTimes.length); + /// ``` + bool add(E valueToAdd) { + late bool result; + update((_) => result = value.add(valueToAdd)); + return result; + } + + /// Adds all [elements] to this set. + /// + /// Equivalent to adding each element in [elements] using [add], + /// but some collections may be able to optimize it. + /// ```dart + /// final characters = {'A', 'B'}; + /// characters.addAll({'A', 'B', 'C'}); + /// print(characters); // {A, B, C} + /// ``` + void addAll(Iterable elements) => update((_) => value.addAll(elements)); + + /// Removes [value] from the set. + /// + /// Returns `true` if [value] was in the set, and `false` if not. + /// The method has no effect if [value] was not in the set. + /// ```dart + /// final characters = {'A', 'B', 'C'}; + /// final didRemoveB = characters.remove('B'); // true + /// final didRemoveD = characters.remove('D'); // false + /// print(characters); // {A, C} + /// ``` + bool remove(Object? valueToRemove) { + late bool result; + update((_) => result = value.remove(valueToRemove)); + return result; + } + + /// Removes each element of [elements] from this set. + /// ```dart + /// final characters = {'A', 'B', 'C'}; + /// characters.removeAll({'A', 'B', 'X'}); + /// print(characters); // {C} + /// ``` + void removeAll(Iterable elements) => + update((_) => value.removeAll(elements)); + + /// Removes all elements of this set that are not elements in [elements]. + /// + /// Checks for each element of [elements] whether there is an element in this + /// set that is equal to it (according to `this.contains`), and if so, the + /// equal element in this set is retained, and elements that are not equal + /// to any element in [elements] are removed. + /// ```dart + /// final characters = {'A', 'B', 'C'}; + /// characters.retainAll({'A', 'B', 'X'}); + /// print(characters); // {A, B} + /// ``` + void retainAll(Iterable elements) => + update((_) => value.retainAll(elements)); + + /// Removes all elements of this set that satisfy [test]. + /// ```dart + /// final characters = {'A', 'B', 'C'}; + /// characters.removeWhere((element) => element.startsWith('B')); + /// print(characters); // {A, C} + /// ``` + void removeWhere(bool Function(E element) test) => + update((_) => value.removeWhere(test)); + + /// Removes all elements of this set that fail to satisfy [test]. + /// ```dart + /// final characters = {'A', 'B', 'C'}; + /// characters.retainWhere( + /// (element) => element.startsWith('B') || element.startsWith('C')); + /// print(characters); // {B, C} + /// ``` + void retainWhere(bool Function(E element) test) => + update((_) => value.removeWhere(test)); + + /// Removes all elements from the set. + /// ```dart + /// final characters = {'A', 'B', 'C'}; + /// characters.clear(); // {} + /// ``` + void clear() => update((_) => value.clear()); + + /// Provides a view of this set as a set of [R] instances. + /// + /// If this set contains only instances of [R], all read operations + /// will work correctly. If any operation tries to access an element + /// that is not an instance of [R], the access will throw instead. + /// + /// Elements added to the set (e.g., by using [add] or [addAll]) + /// must be instances of [R] to be valid arguments to the adding function, + /// and they must be instances of [E] as well to be accepted by + /// this set as well. + /// + /// Methods which accept one or more `Object?` as argument, + /// like [contains], [remove] and [removeAll], + /// will pass the argument directly to the this set's method + /// without any checks. + /// That means that you can do `setOfStrings.cast().remove("a")` + /// successfully, even if it looks like it shouldn't have any effect. + Set cast() => value.cast(); + + /// An iterator that iterates over the elements of this set. + /// + /// The order of iteration is defined by the individual `Set` implementation, + /// but must be consistent between changes to the set. + Iterator get iterator => value.iterator; + + /// Whether [value] is in the set. + /// ```dart + /// final characters = {'A', 'B', 'C'}; + /// final containsB = characters.contains('B'); // true + /// final containsD = characters.contains('D'); // false + /// ``` + bool contains(Object? valueToEvaluate) => value.contains(valueToEvaluate); + + /// If an object equal to [object] is in the set, return it. + /// + /// Checks whether [object] is in the set, like [contains], and if so, + /// returns the object in the set, otherwise returns `null`. + /// + /// If the equality relation used by the set is not identity, + /// then the returned object may not be *identical* to [object]. + /// Some set implementations may not be able to implement this method. + /// If the [contains] method is computed, + /// rather than being based on an actual object instance, + /// then there may not be a specific object instance representing the + /// set element. + /// ```dart + /// final characters = {'A', 'B', 'C'}; + /// final containsB = characters.lookup('B'); + /// print(containsB); // B + /// final containsD = characters.lookup('D'); + /// print(containsD); // null + /// ``` + E? lookup(Object? object) => value.lookup(object); + + /// Whether this set contains all the elements of [other]. + /// ```dart + /// final characters = {'A', 'B', 'C'}; + /// final containsAB = characters.containsAll({'A', 'B'}); + /// print(containsAB); // true + /// final containsAD = characters.containsAll({'A', 'D'}); + /// print(containsAD); // false + /// ``` + bool containsAll(Iterable other) => value.containsAll(other); + + /// Creates a new set which is the intersection between this set and [other]. + /// + /// That is, the returned set contains all the elements of this [Set] that + /// are also elements of [other] according to `other.contains`. + /// ```dart + /// final characters1 = {'A', 'B', 'C'}; + /// final characters2 = {'A', 'E', 'F'}; + /// final unionSet = characters1.intersection(characters2); + /// print(unionSet); // {A} + /// ``` + Set intersection(Set other) => value.intersection(other); + + /// Creates a new set which contains all the elements of this set and [other]. + /// + /// That is, the returned set contains all the elements of this [Set] and + /// all the elements of [other]. + /// ```dart + /// final characters1 = {'A', 'B', 'C'}; + /// final characters2 = {'A', 'E', 'F'}; + /// final unionSet1 = characters1.union(characters2); + /// print(unionSet1); // {A, B, C, E, F} + /// final unionSet2 = characters2.union(characters1); + /// print(unionSet2); // {A, E, F, B, C} + /// ``` + Set union(Set other) => value.union(other); + + /// Creates a new set with the elements of this that are not in [other]. + /// + /// That is, the returned set contains all the elements of this [Set] that + /// are not elements of [other] according to `other.contains`. + /// ```dart + /// final characters1 = {'A', 'B', 'C'}; + /// final characters2 = {'A', 'E', 'F'}; + /// final differenceSet1 = characters1.difference(characters2); + /// print(differenceSet1); // {B, C} + /// final differenceSet2 = characters2.difference(characters1); + /// print(differenceSet2); // {E, F} + /// ``` + Set difference(Set other) => value.difference(other); + + /// Creates a [Set] with the same elements and behavior as this `Set`. + /// + /// The returned set behaves the same as this set + /// with regard to adding and removing elements. + /// It initially contains the same elements. + /// If this set specifies an ordering of the elements, + /// the returned set will have the same order. + Set toSet() => value.toSet(); +} + +extension SignalSetNullExt on Signal?> { + /// Adds [value] to the set. + /// + /// Returns `true` if [value] (or an equal value) was not yet in the set. + /// Otherwise returns `false` and the set is not changed. + /// + /// Example: + /// ```dart + /// final dateTimes = {}; + /// final time1 = DateTime.fromMillisecondsSinceEpoch(0); + /// final time2 = DateTime.fromMillisecondsSinceEpoch(0); + /// // time1 and time2 are equal, but not identical. + /// assert(time1 == time2); + /// assert(!identical(time1, time2)); + /// final time1Added = dateTimes.add(time1); + /// print(time1Added); // true + /// // A value equal to time2 exists already in the set, and the call to + /// // add doesn't change the set. + /// final time2Added = dateTimes.add(time2); + /// print(time2Added); // false + /// + /// print(dateTimes); // {1970-01-01 02:00:00.000} + /// assert(dateTimes.length == 1); + /// assert(identical(time1, dateTimes.first)); + /// print(dateTimes.length); + /// ``` + bool? add(E valueToAdd) { + if (value == null) return null; + late bool result; + update((_) => result = value!.add(valueToAdd)); + return result; + } + + /// Adds all [elements] to this set. + /// + /// Equivalent to adding each element in [elements] using [add], + /// but some collections may be able to optimize it. + /// ```dart + /// final characters = {'A', 'B'}; + /// characters.addAll({'A', 'B', 'C'}); + /// print(characters); // {A, B, C} + /// ``` + void addAll(Iterable elements) { + if (value == null) return; + update((_) => value!.addAll(elements)); + } + + /// Removes [value] from the set. + /// + /// Returns `true` if [value] was in the set, and `false` if not. + /// The method has no effect if [value] was not in the set. + /// ```dart + /// final characters = {'A', 'B', 'C'}; + /// final didRemoveB = characters.remove('B'); // true + /// final didRemoveD = characters.remove('D'); // false + /// print(characters); // {A, C} + /// ``` + bool? remove(Object? valueToRemove) { + if (value == null) return null; + late bool result; + update((_) => result = value!.remove(valueToRemove)); + return result; + } + + /// Removes each element of [elements] from this set. + /// ```dart + /// final characters = {'A', 'B', 'C'}; + /// characters.removeAll({'A', 'B', 'X'}); + /// print(characters); // {C} + /// ``` + void removeAll(Iterable elements) { + if (value == null) return; + update((_) => value!.removeAll(elements)); + } + + /// Removes all elements of this set that are not elements in [elements]. + /// + /// Checks for each element of [elements] whether there is an element in this + /// set that is equal to it (according to `this.contains`), and if so, the + /// equal element in this set is retained, and elements that are not equal + /// to any element in [elements] are removed. + /// ```dart + /// final characters = {'A', 'B', 'C'}; + /// characters.retainAll({'A', 'B', 'X'}); + /// print(characters); // {A, B} + /// ``` + void retainAll(Iterable elements) { + if (value == null) return; + update((_) => value!.retainAll(elements)); + } + + /// Removes all elements of this set that satisfy [test]. + /// ```dart + /// final characters = {'A', 'B', 'C'}; + /// characters.removeWhere((element) => element.startsWith('B')); + /// print(characters); // {A, C} + /// ``` + void removeWhere(bool Function(E element) test) { + if (value == null) return; + update((_) => value!.removeWhere(test)); + } + + /// Removes all elements of this set that fail to satisfy [test]. + /// ```dart + /// final characters = {'A', 'B', 'C'}; + /// characters.retainWhere( + /// (element) => element.startsWith('B') || element.startsWith('C')); + /// print(characters); // {B, C} + /// ``` + void retainWhere(bool Function(E element) test) { + if (value == null) return; + update((_) => value!.removeWhere(test)); + } + + /// Removes all elements from the set. + /// ```dart + /// final characters = {'A', 'B', 'C'}; + /// characters.clear(); // {} + /// ``` + void clear() { + if (value == null) return; + update((_) => value!.clear()); + } + + /// Provides a view of this set as a set of [R] instances. + /// + /// If this set contains only instances of [R], all read operations + /// will work correctly. If any operation tries to access an element + /// that is not an instance of [R], the access will throw instead. + /// + /// Elements added to the set (e.g., by using [add] or [addAll]) + /// must be instances of [R] to be valid arguments to the adding function, + /// and they must be instances of [E] as well to be accepted by + /// this set as well. + /// + /// Methods which accept one or more `Object?` as argument, + /// like [contains], [remove] and [removeAll], + /// will pass the argument directly to the this set's method + /// without any checks. + /// That means that you can do `setOfStrings.cast().remove("a")` + /// successfully, even if it looks like it shouldn't have any effect. + Set? cast() => value?.cast(); + + /// An iterator that iterates over the elements of this set. + /// + /// The order of iteration is defined by the individual `Set` implementation, + /// but must be consistent between changes to the set. + Iterator? get iterator => value?.iterator; + + /// Whether [value] is in the set. + /// ```dart + /// final characters = {'A', 'B', 'C'}; + /// final containsB = characters.contains('B'); // true + /// final containsD = characters.contains('D'); // false + /// ``` + bool? contains(Object? valueToEvaluate) => value?.contains(valueToEvaluate); + + /// If an object equal to [object] is in the set, return it. + /// + /// Checks whether [object] is in the set, like [contains], and if so, + /// returns the object in the set, otherwise returns `null`. + /// + /// If the equality relation used by the set is not identity, + /// then the returned object may not be *identical* to [object]. + /// Some set implementations may not be able to implement this method. + /// If the [contains] method is computed, + /// rather than being based on an actual object instance, + /// then there may not be a specific object instance representing the + /// set element. + /// ```dart + /// final characters = {'A', 'B', 'C'}; + /// final containsB = characters.lookup('B'); + /// print(containsB); // B + /// final containsD = characters.lookup('D'); + /// print(containsD); // null + /// ``` + E? lookup(Object? object) => value?.lookup(object); + + /// Whether this set contains all the elements of [other]. + /// ```dart + /// final characters = {'A', 'B', 'C'}; + /// final containsAB = characters.containsAll({'A', 'B'}); + /// print(containsAB); // true + /// final containsAD = characters.containsAll({'A', 'D'}); + /// print(containsAD); // false + /// ``` + bool? containsAll(Iterable other) => value?.containsAll(other); + + /// Creates a new set which is the intersection between this set and [other]. + /// + /// That is, the returned set contains all the elements of this [Set] that + /// are also elements of [other] according to `other.contains`. + /// ```dart + /// final characters1 = {'A', 'B', 'C'}; + /// final characters2 = {'A', 'E', 'F'}; + /// final unionSet = characters1.intersection(characters2); + /// print(unionSet); // {A} + /// ``` + Set? intersection(Set other) => value?.intersection(other); + + /// Creates a new set which contains all the elements of this set and [other]. + /// + /// That is, the returned set contains all the elements of this [Set] and + /// all the elements of [other]. + /// ```dart + /// final characters1 = {'A', 'B', 'C'}; + /// final characters2 = {'A', 'E', 'F'}; + /// final unionSet1 = characters1.union(characters2); + /// print(unionSet1); // {A, B, C, E, F} + /// final unionSet2 = characters2.union(characters1); + /// print(unionSet2); // {A, E, F, B, C} + /// ``` + Set? union(Set other) => value?.union(other); + + /// Creates a new set with the elements of this that are not in [other]. + /// + /// That is, the returned set contains all the elements of this [Set] that + /// are not elements of [other] according to `other.contains`. + /// ```dart + /// final characters1 = {'A', 'B', 'C'}; + /// final characters2 = {'A', 'E', 'F'}; + /// final differenceSet1 = characters1.difference(characters2); + /// print(differenceSet1); // {B, C} + /// final differenceSet2 = characters2.difference(characters1); + /// print(differenceSet2); // {E, F} + /// ``` + Set? difference(Set other) => value?.difference(other); + + /// Creates a [Set] with the same elements and behavior as this `Set`. + /// + /// The returned set behaves the same as this set + /// with regard to adding and removing elements. + /// It initially contains the same elements. + /// If this set specifies an ordering of the elements, + /// the returned set will have the same order. + Set? toSet() => value?.toSet(); +} diff --git a/packages/reactter/lib/src/signal/extensions/signal_string.dart b/packages/reactter/lib/src/signal/extensions/signal_string.dart new file mode 100644 index 00000000..074ea378 --- /dev/null +++ b/packages/reactter/lib/src/signal/extensions/signal_string.dart @@ -0,0 +1,1057 @@ +// coverage:ignore-file +part of '../signal.dart'; + +extension SignalStringExt on Signal { + /// The character (as a single-code-unit [String]) at the given [index]. + /// + /// The returned string represents exactly one UTF-16 code unit, which may be + /// half of a surrogate pair. A single member of a surrogate pair is an + /// invalid UTF-16 string: + /// ```dart + /// var clef = '\u{1D11E}'; + /// // These represent invalid UTF-16 strings. + /// clef[0].codeUnits; // [0xD834] + /// clef[1].codeUnits; // [0xDD1E] + /// ``` + /// This method is equivalent to + /// `String.fromCharCode(this.codeUnitAt(index))`. + String operator [](int index) => value[index]; + + /// Creates a new string by concatenating this string with [other]. + /// + /// Example: + /// ```dart + /// const string = 'dart' + 'lang'; // 'dartlang' + /// ``` + String operator +(Signal other) => value + other.value; + + /// Creates a new string by concatenating this string with itself a number + /// of times. + /// + /// The result of `str * n` is equivalent to + /// `str + str + ...`(n times)`... + str`. + /// + /// ```dart + /// const string = 'Dart'; + /// final multiplied = string * 3; + /// print(multiplied); // 'DartDartDart' + /// ``` + /// Returns an empty string if [times] is zero or negative. + String operator *(int times) => value * times; + + /// Returns the 16-bit UTF-16 code unit at the given [index]. + int codeUnitAt(int index) => value.codeUnitAt(index); + + /// The length of the string. + /// + /// Returns the number of UTF-16 code units in this string. The number + /// of [runes] might be fewer if the string contains characters outside + /// the Basic Multilingual Plane (plane 0): + /// ```dart + /// 'Dart'.length; // 4 + /// 'Dart'.runes.length; // 4 + /// + /// var clef = '\u{1D11E}'; + /// clef.length; // 2 + /// clef.runes.length; // 1 + /// ``` + int get length => value.length; + + /// Compares this string to [other]. + /// + /// Returns a negative value if `this` is ordered before `other`, + /// a positive value if `this` is ordered after `other`, + /// or zero if `this` and `other` are equivalent. + /// + /// The ordering is the same as the ordering of the code units at the first + /// position where the two strings differ. + /// If one string is a prefix of the other, + /// then the shorter string is ordered before the longer string. + /// If the strings have exactly the same content, they are equivalent with + /// regard to the ordering. + /// Ordering does not check for Unicode equivalence. + /// The comparison is case sensitive. + /// ```dart + /// var relation = 'Dart'.compareTo('Go'); + /// print(relation); // < 0 + /// relation = 'Go'.compareTo('Forward'); + /// print(relation); // > 0 + /// relation = 'Forward'.compareTo('Forward'); + /// print(relation); // 0 + /// ``` + int compareTo(String other) => value.compareTo(other); + + /// Whether this string ends with [other]. + /// + /// For example: + /// ```dart + /// const string = 'Dart is open source'; + /// print(string.endsWith('urce')); // true + /// ``` + bool endsWith(String other) => value.endsWith(other); + + /// Whether this string starts with a match of [pattern]. + /// + /// ```dart + /// const string = 'Dart is open source'; + /// print(string.startsWith('Dar')); // true + /// print(string.startsWith(RegExp(r'[A-Z][a-z]'))); // true + /// ``` + /// If [index] is provided, this method checks if the substring starting + /// at that index starts with a match of [pattern]: + /// ```dart + /// const string = 'Dart'; + /// print(string.startsWith('art', 0)); // false + /// print(string.startsWith('art', 1)); // true + /// print(string.startsWith(RegExp(r'\w{3}'), 2)); // false + /// ``` + /// [index] must not be negative or greater than [length]. + /// + /// A [RegExp] containing '^' does not match if the [index] is greater than + /// zero and the regexp is not multi-line. + /// The pattern works on the string as a whole, and does not extract + /// a substring starting at [index] first: + /// ```dart + /// const string = 'Dart'; + /// print(string.startsWith(RegExp(r'^art'), 1)); // false + /// print(string.startsWith(RegExp(r'art'), 1)); // true + /// ``` + bool startsWith(Pattern pattern, [int index = 0]) => + value.startsWith(pattern, index); + + /// Returns the position of the first match of [pattern] in this string, + /// starting at [start], inclusive: + /// ```dart + /// const string = 'Dartisans'; + /// print(string.indexOf('art')); // 1 + /// print(string.indexOf(RegExp(r'[A-Z][a-z]'))); // 0 + /// ``` + /// Returns -1 if no match is found: + /// ```dart + /// const string = 'Dartisans'; + /// string.indexOf(RegExp(r'dart')); // -1 + /// ``` + /// The [start] must be non-negative and not greater than [length]. + int indexOf(Pattern pattern, [int start = 0]) => + value.indexOf(pattern, start); + + /// The starting position of the last match [pattern] in this string. + /// + /// Finds a match of pattern by searching backward starting at [start]: + /// ```dart + /// const string = 'Dartisans'; + /// print(string.lastIndexOf('a')); // 6 + /// print(string.lastIndexOf(RegExp(r'a(r|n)'))); // 6 + /// ``` + /// Returns -1 if [pattern] could not be found in this string. + /// ```dart + /// const string = 'Dartisans'; + /// print(string.lastIndexOf(RegExp(r'DART'))); // -1 + /// ``` + /// If [start] is omitted, search starts from the end of the string. + /// If supplied, [start] must be non-negative and not greater than [length]. + int lastIndexOf(Pattern pattern, [int? start]) => + value.lastIndexOf(pattern, start); + + /// Whether this string is empty. + bool get isEmpty => value.isEmpty; + + /// Whether this string is not empty. + bool get isNotEmpty => value.isNotEmpty; + + /// The substring of this string from [start], inclusive, to [end], exclusive. + /// + /// Example: + /// ```dart + /// const string = 'dartlang'; + /// var result = string.substring(1); // 'artlang' + /// result = string.substring(1, 4); // 'art' + /// ``` + /// + /// Both [start] and [end] must be non-negative and no greater than [length]; + /// [end], if provided, must be greater than or equal to [start]. + String substring(int start, [int? end]) => value.substring(start, end); + + /// The string without any leading and trailing whitespace. + /// + /// If the string contains leading or trailing whitespace, a new string with no + /// leading and no trailing whitespace is returned: + /// ```dart + /// final trimmed = '\tDart is fun\n'.trim(); + /// print(trimmed); // 'Dart is fun' + /// ``` + /// Otherwise, the original string itself is returned: + /// ```dart + /// const string1 = 'Dart'; + /// final string2 = string1.trim(); // 'Dart' + /// print(identical(string1, string2)); // true + /// ``` + /// Whitespace is defined by the Unicode White_Space property (as defined in + /// version 6.2 or later) and the BOM character, 0xFEFF. + /// + /// Here is the list of trimmed characters according to Unicode version 6.3: + /// ```plaintext + /// 0009..000D ; White_Space # Cc .. + /// 0020 ; White_Space # Zs SPACE + /// 0085 ; White_Space # Cc + /// 00A0 ; White_Space # Zs NO-BREAK SPACE + /// 1680 ; White_Space # Zs OGHAM SPACE MARK + /// 2000..200A ; White_Space # Zs EN QUAD..HAIR SPACE + /// 2028 ; White_Space # Zl LINE SEPARATOR + /// 2029 ; White_Space # Zp PARAGRAPH SEPARATOR + /// 202F ; White_Space # Zs NARROW NO-BREAK SPACE + /// 205F ; White_Space # Zs MEDIUM MATHEMATICAL SPACE + /// 3000 ; White_Space # Zs IDEOGRAPHIC SPACE + /// + /// FEFF ; BOM ZERO WIDTH NO_BREAK SPACE + /// ``` + /// Some later versions of Unicode do not include U+0085 as a whitespace + /// character. Whether it is trimmed depends on the Unicode version + /// used by the system. + String trim() => value.trim(); + + /// The string without any leading whitespace. + /// + /// As [trim], but only removes leading whitespace. + /// ```dart + /// final string = ' Dart '.trimLeft(); + /// print(string); // 'Dart ' + /// ``` + String trimLeft() => value.trimLeft(); + + /// The string without any trailing whitespace. + /// + /// As [trim], but only removes trailing whitespace. + /// ```dart + /// final string = ' Dart '.trimRight(); + /// print(string); // ' Dart' + /// ``` + String trimRight() => value.trimRight(); + + /// Pads this string on the left if it is shorter than [width]. + /// + /// Returns a new string that prepends [padding] onto this string + /// one time for each position the length is less than [width]. + /// + /// ```dart + /// const string = 'D'; + /// print(string.padLeft(4)); // ' D' + /// print(string.padLeft(2, 'x')); // 'xD' + /// print(string.padLeft(4, 'y')); // 'yyyD' + /// print(string.padLeft(4, '>>')); // '>>>>>>D' + /// ``` + /// + /// If [width] is already smaller than or equal to `this.length`, + /// no padding is added. A negative `width` is treated as zero. + /// + /// If [padding] has length different from 1, the result will not + /// have length `width`. This may be useful for cases where the + /// padding is a longer string representing a single character, like + /// `" "` or `"\u{10002}`". + /// In that case, the user should make sure that `this.length` is + /// the correct measure of the string's length. + String padLeft(int width, [String padding = ' ']) => + value.padLeft(width, padding); + + /// Pads this string on the right if it is shorter than [width]. + /// + /// Returns a new string that appends [padding] after this string + /// one time for each position the length is less than [width]. + /// + /// ```dart + /// const string = 'D'; + /// print(string.padRight(4)); // 'D ' + /// print(string.padRight(2, 'x')); // 'Dx' + /// print(string.padRight(4, 'y')); // 'Dyyy' + /// print(string.padRight(4, '>>')); // 'D>>>>>>' + /// ``` + /// + /// If [width] is already smaller than or equal to `this.length`, + /// no padding is added. A negative `width` is treated as zero. + /// + /// If [padding] has length different from 1, the result will not + /// have length `width`. This may be useful for cases where the + /// padding is a longer string representing a single character, like + /// `" "` or `"\u{10002}`". + /// In that case, the user should make sure that `this.length` is + /// the correct measure of the string's length. + String padRight(int width, [String padding = ' ']) => + value.padRight(width, padding); + + /// Whether this string contains a match of [other]. + /// + /// Example: + /// ```dart + /// const string = 'Dart strings'; + /// final containsD = string.contains('D'); // true + /// final containsUpperCase = string.contains(RegExp(r'[A-Z]')); // true + /// ``` + /// If [startIndex] is provided, this method matches only at or after that + /// index: + /// ```dart + /// const string = 'Dart strings'; + /// final containsD = string.contains(RegExp('D'), 0); // true + /// final caseSensitive = string.contains(RegExp(r'[A-Z]'), 1); // false + /// ``` + /// The [startIndex] must not be negative or greater than [length]. + bool contains(Pattern other, [int startIndex = 0]) => + value.contains(other, startIndex); + + /// Creates a new string with the first occurrence of [from] replaced by [to]. + /// + /// Finds the first match of [from] in this string, starting from [startIndex], + /// and creates a new string where that match is replaced with the [to] string. + /// + /// Example: + /// ```dart + /// '0.0001'.replaceFirst(RegExp(r'0'), ''); // '.0001' + /// '0.0001'.replaceFirst(RegExp(r'0'), '7', 1); // '0.7001' + /// ``` + String replaceFirst(Pattern from, String to, [int startIndex = 0]) => + value.replaceFirst(from, to, startIndex); + + /// Replace the first occurrence of [from] in this string. + /// + /// ```dart + /// const string = 'Dart is fun'; + /// print(string.replaceFirstMapped( + /// 'fun', (m) => 'open source')); // Dart is open source + /// + /// print(string.replaceFirstMapped( + /// RegExp(r'\w(\w*)'), (m) => '<${m[0]}-${m[1]}>')); // is fun + /// ``` + /// + /// Returns a new string, which is this string + /// except that the first match of [from], starting from [startIndex], + /// is replaced by the result of calling [replace] with the match Object. + /// + /// The [startIndex] must be non-negative and no greater than [length]. + String replaceFirstMapped(Pattern from, String Function(Match match) replace, + [int startIndex = 0]) => + value.replaceFirstMapped(from, replace, startIndex); + + /// Replaces all substrings that match [from] with [replace]. + /// + /// Creates a new string in which the non-overlapping substrings matching + /// [from] (the ones iterated by `from.allMatches(thisString)`) are replaced + /// by the literal string [replace]. + /// ```dart + /// 'resume'.replaceAll(RegExp(r'e'), 'é'); // 'résumé' + /// ``` + /// Notice that the [replace] string is not interpreted. If the replacement + /// depends on the match (for example, on a [RegExp]'s capture groups), use + /// the [replaceAllMapped] method instead. + String replaceAll(Pattern from, String replace) => + value.replaceAll(from, replace); + + /// Replace all substrings that match [from] by a computed string. + /// + /// Creates a new string in which the non-overlapping substrings that match + /// [from] (the ones iterated by `from.allMatches(thisString)`) are replaced + /// by the result of calling [replace] on the corresponding [Match] Object. + /// + /// This can be used to replace matches with new content that depends on the + /// match, unlike [replaceAll] where the replacement string is always the same. + /// + /// The [replace] function is called with the [Match] generated + /// by the pattern, and its result is used as replacement. + /// + /// The function defined below converts each word in a string to simplified + /// 'pig latin' using [replaceAllMapped]: + /// ```dart + /// String pigLatin(String words) => words.replaceAllMapped( + /// RegExp(r'\b(\w*?)([aeiou]\w*)', caseSensitive: false), + /// (Match m) => "${m[2]}${m[1]}${m[1]!.isEmpty ? 'way' : 'ay'}"); + /// + /// final result = pigLatin('I have a secret now!'); + /// print(result); // 'Iway avehay away ecretsay ownay!' + /// ``` + String replaceAllMapped(Pattern from, String Function(Match match) replace) => + value.replaceAllMapped(from, replace); + + /// Replaces the substring from [start] to [end] with [replacement]. + /// + /// Creates a new string equivalent to: + /// ```dart + /// this.substring(0, start) + replacement + this.substring(end) + /// ``` + /// Example: + /// ```dart + /// const string = 'Dart is fun'; + /// final result = string.replaceRange(8, null, 'open source'); + /// print(result); // Dart is open source + /// ``` + /// The [start] and [end] indices must specify a valid range of this string. + /// That is `0 <= start <= end <= this.length`. + /// If [end] is `null`, it defaults to [length]. + String replaceRange(int start, int? end, String replacement) => + value.replaceRange(start, end, replacement); + + /// Splits the string at matches of [pattern] and returns a list of substrings. + /// + /// Finds all the matches of `pattern` in this string, + /// as by using [Pattern.allMatches], + /// and returns the list of the substrings between the matches, + /// before the first match, and after the last match. + /// ```dart + /// const string = 'Hello world!'; + /// final splitted = string.split(' '); + /// print(splitted); // [Hello, world!]; + /// ``` + /// If the pattern doesn't match this string at all, + /// the result is always a list containing only the original string. + /// + /// If the [pattern] is a [String], then it's always the case that: + /// ```dart + /// string.split(pattern).join(pattern) == string + /// ``` + /// + /// If the first match is an empty match at the start of the string, + /// the empty substring before it is not included in the result. + /// If the last match is an empty match at the end of the string, + /// the empty substring after it is not included in the result. + /// If a match is empty, and it immediately follows a previous + /// match (it starts at the position where the previous match ended), + /// then the empty substring between the two matches is not + /// included in the result. + /// ```dart + /// const string = 'abba'; + /// final re = RegExp(r'b*'); + /// // re.allMatches(string) will find four matches: + /// // * empty match before first "a". + /// // * match of "bb" + /// // * empty match after "bb", before second "a" + /// // * empty match after second "a". + /// print(string.split(re)); // [a, a] + /// ``` + /// + /// A non-empty match at the start or end of the string, or after another + /// match, is not treated specially, and will introduce empty substrings + /// in the result: + /// ```dart + /// const string = 'abbaa'; + /// final splitted = string.split('a'); // ['', 'bb', '', ''] + /// ``` + /// + /// If this string is the empty string, the result is an empty list + /// if `pattern` matches the empty string, since the empty string + /// before and after the first-and-last empty match are not included. + /// (It is still a list containing the original empty string `[""]` + /// if the pattern doesn't match). + /// ```dart + /// const string = ''; + /// print(string.split('')); // [] + /// print(string.split('a')); // [] + /// ``` + /// + /// Splitting with an empty pattern splits the string into single-code unit + /// strings. + /// ```dart + /// const string = 'Pub'; + /// print(string.split('')); // [P, u, b] + /// + /// // Same as: + /// var codeUnitStrings = [ + /// for (final unit in string.codeUnits) String.fromCharCode(unit) + /// ]; + /// print(codeUnitStrings); // [P, u, b] + /// ``` + /// + /// Splitting happens at UTF-16 code unit boundaries, + /// and not at rune (Unicode code point) boundaries: + /// ```dart + /// // String made up of two code units, but one rune. + /// const string = '\u{1D11E}'; + /// final splitted = string.split(''); + /// print(splitted); // ['\ud834', '\udd1e'] - 2 unpaired surrogate values + /// ``` + /// To get a list of strings containing the individual runes of a string, + /// you should not use split. + /// You can instead get a string for each rune as follows: + /// ```dart + /// const string = '\u{1F642}'; + /// for (final rune in string.runes) { + /// print(String.fromCharCode(rune)); + /// } + /// ``` + List split(Pattern pattern) => value.split(pattern); + + /// Splits the string, converts its parts, and combines them into a new + /// string. + /// + /// The [pattern] is used to split the string + /// into parts and separating matches. + /// Each match of [Pattern.allMatches] of [pattern] on this string is + /// used as a match, and the substrings between the end of one match + /// (or the start of the string) and the start of the next match (or the + /// end of the string) is treated as a non-matched part. + /// (There is no omission of leading or trailing empty matchs, like + /// in [split], all matches and parts between the are included.) + /// + /// Each match is converted to a string by calling [onMatch]. If [onMatch] + /// is omitted, the matched substring is used. + /// + /// Each non-matched part is converted to a string by a call to [onNonMatch]. + /// If [onNonMatch] is omitted, the non-matching substring itself is used. + /// + /// Then all the converted parts are concatenated into the resulting string. + /// ```dart + /// final result = 'Eats shoots leaves'.splitMapJoin(RegExp(r'shoots'), + /// onMatch: (m) => '${m[0]}', // (or no onMatch at all) + /// onNonMatch: (n) => '*'); + /// print(result); // *shoots* + /// ``` + String splitMapJoin(Pattern pattern, + {String Function(Match)? onMatch, + String Function(String)? onNonMatch}) => + value.splitMapJoin(pattern); + + /// An unmodifiable list of the UTF-16 code units of this string. + List get codeUnits => value.codeUnits; + + /// An [Iterable] of Unicode code-points of this string. + /// + /// If the string contains surrogate pairs, they are combined and returned + /// as one integer by this iterator. Unmatched surrogate halves are treated + /// like valid 16-bit code-units. + Runes get runes => value.runes; + + /// Converts all characters in this string to lower case. + /// + /// If the string is already in all lower case, this method returns `this`. + /// ```dart + /// 'ALPHABET'.toLowerCase(); // 'alphabet' + /// 'abc'.toLowerCase(); // 'abc' + /// ``` + /// This function uses the language independent Unicode mapping and thus only + /// works in some languages. + String toLowerCase() => value.toLowerCase(); + + /// Converts all characters in this string to upper case. + /// + /// If the string is already in all upper case, this method returns `this`. + /// ```dart + /// 'alphabet'.toUpperCase(); // 'ALPHABET' + /// 'ABC'.toUpperCase(); // 'ABC' + /// ``` + /// This function uses the language independent Unicode mapping and thus only + /// works in some languages. + String toUpperCase() => value.toUpperCase(); +} + +extension SignalStringNullExt on Signal { + /// The character (as a single-code-unit [String]) at the given [index]. + /// + /// The returned string represents exactly one UTF-16 code unit, which may be + /// half of a surrogate pair. A single member of a surrogate pair is an + /// invalid UTF-16 string: + /// ```dart + /// var clef = '\u{1D11E}'; + /// // These represent invalid UTF-16 strings. + /// clef[0].codeUnits; // [0xD834] + /// clef[1].codeUnits; // [0xDD1E] + /// ``` + /// This method is equivalent to + /// `String.fromCharCode(this.codeUnitAt(index))`. + String? operator [](int index) => value?[index]; + + /// Returns the 16-bit UTF-16 code unit at the given [index]. + int? codeUnitAt(int index) => value?.codeUnitAt(index); + + /// The length of the string. + /// + /// Returns the number of UTF-16 code units in this string. The number + /// of [runes] might be fewer if the string contains characters outside + /// the Basic Multilingual Plane (plane 0): + /// ```dart + /// 'Dart'.length; // 4 + /// 'Dart'.runes.length; // 4 + /// + /// var clef = '\u{1D11E}'; + /// clef.length; // 2 + /// clef.runes.length; // 1 + /// ``` + int? get length => value?.length; + + /// Compares this string to [other]. + /// + /// Returns a negative value if `this` is ordered before `other`, + /// a positive value if `this` is ordered after `other`, + /// or zero if `this` and `other` are equivalent. + /// + /// The ordering is the same as the ordering of the code units at the first + /// position where the two strings differ. + /// If one string is a prefix of the other, + /// then the shorter string is ordered before the longer string. + /// If the strings have exactly the same content, they are equivalent with + /// regard to the ordering. + /// Ordering does not check for Unicode equivalence. + /// The comparison is case sensitive. + /// ```dart + /// var relation = 'Dart'.compareTo('Go'); + /// print(relation); // < 0 + /// relation = 'Go'.compareTo('Forward'); + /// print(relation); // > 0 + /// relation = 'Forward'.compareTo('Forward'); + /// print(relation); // 0 + /// ``` + int? compareTo(String other) => value?.compareTo(other); + + /// Whether this string ends with [other]. + /// + /// For example: + /// ```dart + /// const string = 'Dart is open source'; + /// print(string.endsWith('urce')); // true + /// ``` + bool? endsWith(String other) => value?.endsWith(other); + + /// Whether this string starts with a match of [pattern]. + /// + /// ```dart + /// const string = 'Dart is open source'; + /// print(string.startsWith('Dar')); // true + /// print(string.startsWith(RegExp(r'[A-Z][a-z]'))); // true + /// ``` + /// If [index] is provided, this method checks if the substring starting + /// at that index starts with a match of [pattern]: + /// ```dart + /// const string = 'Dart'; + /// print(string.startsWith('art', 0)); // false + /// print(string.startsWith('art', 1)); // true + /// print(string.startsWith(RegExp(r'\w{3}'), 2)); // false + /// ``` + /// [index] must not be negative or greater than [length]. + /// + /// A [RegExp] containing '^' does not match if the [index] is greater than + /// zero and the regexp is not multi-line. + /// The pattern works on the string as a whole, and does not extract + /// a substring starting at [index] first: + /// ```dart + /// const string = 'Dart'; + /// print(string.startsWith(RegExp(r'^art'), 1)); // false + /// print(string.startsWith(RegExp(r'art'), 1)); // true + /// ``` + bool? startsWith(Pattern pattern, [int index = 0]) => + value?.startsWith(pattern, index); + + /// Returns the position of the first match of [pattern] in this string, + /// starting at [start], inclusive: + /// ```dart + /// const string = 'Dartisans'; + /// print(string.indexOf('art')); // 1 + /// print(string.indexOf(RegExp(r'[A-Z][a-z]'))); // 0 + /// ``` + /// Returns -1 if no match is found: + /// ```dart + /// const string = 'Dartisans'; + /// string.indexOf(RegExp(r'dart')); // -1 + /// ``` + /// The [start] must be non-negative and not greater than [length]. + int? indexOf(Pattern pattern, [int start = 0]) => + value?.indexOf(pattern, start); + + /// The starting position of the last match [pattern] in this string. + /// + /// Finds a match of pattern by searching backward starting at [start]: + /// ```dart + /// const string = 'Dartisans'; + /// print(string.lastIndexOf('a')); // 6 + /// print(string.lastIndexOf(RegExp(r'a(r|n)'))); // 6 + /// ``` + /// Returns -1 if [pattern] could not be found in this string. + /// ```dart + /// const string = 'Dartisans'; + /// print(string.lastIndexOf(RegExp(r'DART'))); // -1 + /// ``` + /// If [start] is omitted, search starts from the end of the string. + /// If supplied, [start] must be non-negative and not greater than [length]. + int? lastIndexOf(Pattern pattern, [int? start]) => + value?.lastIndexOf(pattern, start); + + /// Whether this string is empty. + bool? get isEmpty => value?.isEmpty; + + /// Whether this string is not empty. + bool? get isNotEmpty => value?.isNotEmpty; + + /// The substring of this string from [start], inclusive, to [end], exclusive. + /// + /// Example: + /// ```dart + /// const string = 'dartlang'; + /// var result = string.substring(1); // 'artlang' + /// result = string.substring(1, 4); // 'art' + /// ``` + /// + /// Both [start] and [end] must be non-negative and no greater than [length]; + /// [end], if provided, must be greater than or equal to [start]. + String? substring(int start, [int? end]) => value?.substring(start, end); + + /// The string without any leading and trailing whitespace. + /// + /// If the string contains leading or trailing whitespace, a new string with no + /// leading and no trailing whitespace is returned: + /// ```dart + /// final trimmed = '\tDart is fun\n'.trim(); + /// print(trimmed); // 'Dart is fun' + /// ``` + /// Otherwise, the original string itself is returned: + /// ```dart + /// const string1 = 'Dart'; + /// final string2 = string1.trim(); // 'Dart' + /// print(identical(string1, string2)); // true + /// ``` + /// Whitespace is defined by the Unicode White_Space property (as defined in + /// version 6.2 or later) and the BOM character, 0xFEFF. + /// + /// Here is the list of trimmed characters according to Unicode version 6.3: + /// ```plaintext + /// 0009..000D ; White_Space # Cc .. + /// 0020 ; White_Space # Zs SPACE + /// 0085 ; White_Space # Cc + /// 00A0 ; White_Space # Zs NO-BREAK SPACE + /// 1680 ; White_Space # Zs OGHAM SPACE MARK + /// 2000..200A ; White_Space # Zs EN QUAD..HAIR SPACE + /// 2028 ; White_Space # Zl LINE SEPARATOR + /// 2029 ; White_Space # Zp PARAGRAPH SEPARATOR + /// 202F ; White_Space # Zs NARROW NO-BREAK SPACE + /// 205F ; White_Space # Zs MEDIUM MATHEMATICAL SPACE + /// 3000 ; White_Space # Zs IDEOGRAPHIC SPACE + /// + /// FEFF ; BOM ZERO WIDTH NO_BREAK SPACE + /// ``` + /// Some later versions of Unicode do not include U+0085 as a whitespace + /// character. Whether it is trimmed depends on the Unicode version + /// used by the system. + String? trim() => value?.trim(); + + /// The string without any leading whitespace. + /// + /// As [trim], but only removes leading whitespace. + /// ```dart + /// final string = ' Dart '.trimLeft(); + /// print(string); // 'Dart ' + /// ``` + String? trimLeft() => value?.trimLeft(); + + /// The string without any trailing whitespace. + /// + /// As [trim], but only removes trailing whitespace. + /// ```dart + /// final string = ' Dart '.trimRight(); + /// print(string); // ' Dart' + /// ``` + String? trimRight() => value?.trimRight(); + + /// Pads this string on the left if it is shorter than [width]. + /// + /// Returns a new string that prepends [padding] onto this string + /// one time for each position the length is less than [width]. + /// + /// ```dart + /// const string = 'D'; + /// print(string.padLeft(4)); // ' D' + /// print(string.padLeft(2, 'x')); // 'xD' + /// print(string.padLeft(4, 'y')); // 'yyyD' + /// print(string.padLeft(4, '>>')); // '>>>>>>D' + /// ``` + /// + /// If [width] is already smaller than or equal to `this.length`, + /// no padding is added. A negative `width` is treated as zero. + /// + /// If [padding] has length different from 1, the result will not + /// have length `width`. This may be useful for cases where the + /// padding is a longer string representing a single character, like + /// `" "` or `"\u{10002}`". + /// In that case, the user should make sure that `this.length` is + /// the correct measure of the string's length. + String? padLeft(int width, [String padding = ' ']) => + value?.padLeft(width, padding); + + /// Pads this string on the right if it is shorter than [width]. + /// + /// Returns a new string that appends [padding] after this string + /// one time for each position the length is less than [width]. + /// + /// ```dart + /// const string = 'D'; + /// print(string.padRight(4)); // 'D ' + /// print(string.padRight(2, 'x')); // 'Dx' + /// print(string.padRight(4, 'y')); // 'Dyyy' + /// print(string.padRight(4, '>>')); // 'D>>>>>>' + /// ``` + /// + /// If [width] is already smaller than or equal to `this.length`, + /// no padding is added. A negative `width` is treated as zero. + /// + /// If [padding] has length different from 1, the result will not + /// have length `width`. This may be useful for cases where the + /// padding is a longer string representing a single character, like + /// `" "` or `"\u{10002}`". + /// In that case, the user should make sure that `this.length` is + /// the correct measure of the string's length. + String? padRight(int width, [String padding = ' ']) => + value?.padRight(width, padding); + + /// Whether this string contains a match of [other]. + /// + /// Example: + /// ```dart + /// const string = 'Dart strings'; + /// final containsD = string.contains('D'); // true + /// final containsUpperCase = string.contains(RegExp(r'[A-Z]')); // true + /// ``` + /// If [startIndex] is provided, this method matches only at or after that + /// index: + /// ```dart + /// const string = 'Dart strings'; + /// final containsD = string.contains(RegExp('D'), 0); // true + /// final caseSensitive = string.contains(RegExp(r'[A-Z]'), 1); // false + /// ``` + /// The [startIndex] must not be negative or greater than [length]. + bool? contains(Pattern other, [int startIndex = 0]) => + value?.contains(other, startIndex); + + /// Creates a new string with the first occurrence of [from] replaced by [to]. + /// + /// Finds the first match of [from] in this string, starting from [startIndex], + /// and creates a new string where that match is replaced with the [to] string. + /// + /// Example: + /// ```dart + /// '0.0001'.replaceFirst(RegExp(r'0'), ''); // '.0001' + /// '0.0001'.replaceFirst(RegExp(r'0'), '7', 1); // '0.7001' + /// ``` + String? replaceFirst(Pattern from, String to, [int startIndex = 0]) => + value?.replaceFirst(from, to, startIndex); + + /// Replace the first occurrence of [from] in this string. + /// + /// ```dart + /// const string = 'Dart is fun'; + /// print(string.replaceFirstMapped( + /// 'fun', (m) => 'open source')); // Dart is open source + /// + /// print(string.replaceFirstMapped( + /// RegExp(r'\w(\w*)'), (m) => '<${m[0]}-${m[1]}>')); // is fun + /// ``` + /// + /// Returns a new string, which is this string + /// except that the first match of [from], starting from [startIndex], + /// is replaced by the result of calling [replace] with the match Object. + /// + /// The [startIndex] must be non-negative and no greater than [length]. + String? replaceFirstMapped(Pattern from, String Function(Match match) replace, + [int startIndex = 0]) => + value?.replaceFirstMapped(from, replace, startIndex); + + /// Replaces all substrings that match [from] with [replace]. + /// + /// Creates a new string in which the non-overlapping substrings matching + /// [from] (the ones iterated by `from.allMatches(thisString)`) are replaced + /// by the literal string [replace]. + /// ```dart + /// 'resume'.replaceAll(RegExp(r'e'), 'é'); // 'résumé' + /// ``` + /// Notice that the [replace] string is not interpreted. If the replacement + /// depends on the match (for example, on a [RegExp]'s capture groups), use + /// the [replaceAllMapped] method instead. + String? replaceAll(Pattern from, String replace) => + value?.replaceAll(from, replace); + + /// Replace all substrings that match [from] by a computed string. + /// + /// Creates a new string in which the non-overlapping substrings that match + /// [from] (the ones iterated by `from.allMatches(thisString)`) are replaced + /// by the result of calling [replace] on the corresponding [Match] Object. + /// + /// This can be used to replace matches with new content that depends on the + /// match, unlike [replaceAll] where the replacement string is always the same. + /// + /// The [replace] function is called with the [Match] generated + /// by the pattern, and its result is used as replacement. + /// + /// The function defined below converts each word in a string to simplified + /// 'pig latin' using [replaceAllMapped]: + /// ```dart + /// String pigLatin(String words) => words.replaceAllMapped( + /// RegExp(r'\b(\w*?)([aeiou]\w*)', caseSensitive: false), + /// (Match m) => "${m[2]}${m[1]}${m[1]!.isEmpty ? 'way' : 'ay'}"); + /// + /// final result = pigLatin('I have a secret now!'); + /// print(result); // 'Iway avehay away ecretsay ownay!' + /// ``` + String? replaceAllMapped( + Pattern from, String Function(Match match) replace) => + value?.replaceAllMapped(from, replace); + + /// Replaces the substring from [start] to [end] with [replacement]. + /// + /// Creates a new string equivalent to: + /// ```dart + /// this.substring(0, start) + replacement + this.substring(end) + /// ``` + /// Example: + /// ```dart + /// const string = 'Dart is fun'; + /// final result = string.replaceRange(8, null, 'open source'); + /// print(result); // Dart is open source + /// ``` + /// The [start] and [end] indices must specify a valid range of this string. + /// That is `0 <= start <= end <= this.length`. + /// If [end] is `null`, it defaults to [length]. + String? replaceRange(int start, int? end, String replacement) => + value?.replaceRange(start, end, replacement); + + /// Splits the string at matches of [pattern] and returns a list of substrings. + /// + /// Finds all the matches of `pattern` in this string, + /// as by using [Pattern.allMatches], + /// and returns the list of the substrings between the matches, + /// before the first match, and after the last match. + /// ```dart + /// const string = 'Hello world!'; + /// final splitted = string.split(' '); + /// print(splitted); // [Hello, world!]; + /// ``` + /// If the pattern doesn't match this string at all, + /// the result is always a list containing only the original string. + /// + /// If the [pattern] is a [String], then it's always the case that: + /// ```dart + /// string.split(pattern).join(pattern) == string + /// ``` + /// + /// If the first match is an empty match at the start of the string, + /// the empty substring before it is not included in the result. + /// If the last match is an empty match at the end of the string, + /// the empty substring after it is not included in the result. + /// If a match is empty, and it immediately follows a previous + /// match (it starts at the position where the previous match ended), + /// then the empty substring between the two matches is not + /// included in the result. + /// ```dart + /// const string = 'abba'; + /// final re = RegExp(r'b*'); + /// // re.allMatches(string) will find four matches: + /// // * empty match before first "a". + /// // * match of "bb" + /// // * empty match after "bb", before second "a" + /// // * empty match after second "a". + /// print(string.split(re)); // [a, a] + /// ``` + /// + /// A non-empty match at the start or end of the string, or after another + /// match, is not treated specially, and will introduce empty substrings + /// in the result: + /// ```dart + /// const string = 'abbaa'; + /// final splitted = string.split('a'); // ['', 'bb', '', ''] + /// ``` + /// + /// If this string is the empty string, the result is an empty list + /// if `pattern` matches the empty string, since the empty string + /// before and after the first-and-last empty match are not included. + /// (It is still a list containing the original empty string `[""]` + /// if the pattern doesn't match). + /// ```dart + /// const string = ''; + /// print(string.split('')); // [] + /// print(string.split('a')); // [] + /// ``` + /// + /// Splitting with an empty pattern splits the string into single-code unit + /// strings. + /// ```dart + /// const string = 'Pub'; + /// print(string.split('')); // [P, u, b] + /// + /// // Same as: + /// var codeUnitStrings = [ + /// for (final unit in string.codeUnits) String.fromCharCode(unit) + /// ]; + /// print(codeUnitStrings); // [P, u, b] + /// ``` + /// + /// Splitting happens at UTF-16 code unit boundaries, + /// and not at rune (Unicode code point) boundaries: + /// ```dart + /// // String made up of two code units, but one rune. + /// const string = '\u{1D11E}'; + /// final splitted = string.split(''); + /// print(splitted); // ['\ud834', '\udd1e'] - 2 unpaired surrogate values + /// ``` + /// To get a list of strings containing the individual runes of a string, + /// you should not use split. + /// You can instead get a string for each rune as follows: + /// ```dart + /// const string = '\u{1F642}'; + /// for (final rune in string.runes) { + /// print(String.fromCharCode(rune)); + /// } + /// ``` + List? split(Pattern pattern) => value?.split(pattern); + + /// Splits the string, converts its parts, and combines them into a new + /// string. + /// + /// The [pattern] is used to split the string + /// into parts and separating matches. + /// Each match of [Pattern.allMatches] of [pattern] on this string is + /// used as a match, and the substrings between the end of one match + /// (or the start of the string) and the start of the next match (or the + /// end of the string) is treated as a non-matched part. + /// (There is no omission of leading or trailing empty matchs, like + /// in [split], all matches and parts between the are included.) + /// + /// Each match is converted to a string by calling [onMatch]. If [onMatch] + /// is omitted, the matched substring is used. + /// + /// Each non-matched part is converted to a string by a call to [onNonMatch]. + /// If [onNonMatch] is omitted, the non-matching substring itself is used. + /// + /// Then all the converted parts are concatenated into the resulting string. + /// ```dart + /// final result = 'Eats shoots leaves'.splitMapJoin(RegExp(r'shoots'), + /// onMatch: (m) => '${m[0]}', // (or no onMatch at all) + /// onNonMatch: (n) => '*'); + /// print(result); // *shoots* + /// ``` + String? splitMapJoin(Pattern pattern, + {String Function(Match)? onMatch, + String Function(String)? onNonMatch}) => + value?.splitMapJoin(pattern); + + /// An unmodifiable list of the UTF-16 code units of this string. + List? get codeUnits => value?.codeUnits; + + /// An [Iterable] of Unicode code-points of this string. + /// + /// If the string contains surrogate pairs, they are combined and returned + /// as one integer by this iterator. Unmatched surrogate halves are treated + /// like valid 16-bit code-units. + Runes? get runes => value?.runes; + + /// Converts all characters in this string to lower case. + /// + /// If the string is already in all lower case, this method returns `this`. + /// ```dart + /// 'ALPHABET'.toLowerCase(); // 'alphabet' + /// 'abc'.toLowerCase(); // 'abc' + /// ``` + /// This function uses the language independent Unicode mapping and thus only + /// works in some languages. + String? toLowerCase() => value?.toLowerCase(); + + /// Converts all characters in this string to upper case. + /// + /// If the string is already in all upper case, this method returns `this`. + /// ```dart + /// 'alphabet'.toUpperCase(); // 'ALPHABET' + /// 'ABC'.toUpperCase(); // 'ABC' + /// ``` + /// This function uses the language independent Unicode mapping and thus only + /// works in some languages. + String? toUpperCase() => value?.toUpperCase(); +} diff --git a/packages/reactter/lib/src/signal.dart b/packages/reactter/lib/src/signal/signal.dart similarity index 91% rename from packages/reactter/lib/src/signal.dart rename to packages/reactter/lib/src/signal/signal.dart index 12ea3145..f67f466b 100644 --- a/packages/reactter/lib/src/signal.dart +++ b/packages/reactter/lib/src/signal/signal.dart @@ -1,5 +1,18 @@ +import 'dart:math'; import 'package:reactter/src/framework.dart'; +part 'extensions/signal_bigint.dart'; +part 'extensions/signal_bool.dart'; +part 'extensions/signal_date_time.dart'; +part 'extensions/signal_double.dart'; +part 'extensions/signal_int.dart'; +part 'extensions/signal_iterable.dart'; +part 'extensions/signal_list.dart'; +part 'extensions/signal_map.dart'; +part 'extensions/signal_num.dart'; +part 'extensions/signal_set.dart'; +part 'extensions/signal_string.dart'; + /// This enumeration is used to represent different events that can occur when /// getting or setting the value of a `Signal` object. enum SignalEvent { onGetValue, onSetValue } From 2431a5b4bab60471bed2f1cc1486cb9c57d27365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Tue, 31 Dec 2024 02:49:03 -0600 Subject: [PATCH 080/141] build: Bump version to 8.0.0-dev.5 and update dependencies in pubspec.yaml. --- CHANGELOG.md | 96 +++++++++++++------------- packages/flutter_reactter/pubspec.lock | 58 ++++++++-------- packages/flutter_reactter/pubspec.yaml | 11 ++- packages/reactter/pubspec.yaml | 2 +- 4 files changed, 87 insertions(+), 80 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0a0d316..bcefe72f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,68 +1,68 @@ # Reactter -## 8.0.0-dev.4 +## 8.0.0-dev.5 ### Enhancements - Add Reactter devtools. -- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtInterface/removeObserver.html) methods to manage the observers. -- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). -- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). -- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. -- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. -- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtStateBase/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtStateBase/debugInfo.html) to states and hooks to get info for debugging. -- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. -- Add [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtStateBase-class.html) abstract class to implement the base logic of the state. -- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtInterface/createState.html) method to create a new state. -- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. -- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. -- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. +- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtInterface/removeObserver.html) methods to manage the observers. +- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). +- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). +- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. +- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. +- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtStateBase/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtStateBase/debugInfo.html) to states and hooks to get info for debugging. +- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. +- Add [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtStateBase-class.html) abstract class to implement the base logic of the state. +- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtInterface/createState.html) method to create a new state. +- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. +- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. +- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. - Enhance event handling and notifier logic for improved stability and performance. - Enhance state management and error handling. -- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.4/flutter_reactter/RtComponent-class.html). +- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.5/flutter_reactter/RtComponent-class.html). - Enhance dependency management to ensure to re-build when is detected changes while building. ### Breakings -- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/Rt.html) instead. -- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtStateBase-class.html) instead. +- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/Rt.html) instead. +- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtStateBase-class.html) instead. - Replace [`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html) to `UseAsyncStateStatus.idle`. -- Move `asyncFunction` to the first argument of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/UseAsyncState/withArg.html). -- Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. -- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtInterface/isActive.html) instead. -- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtInterface/getDependencyMode.html) instead. -- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/DependencyMode.html) instead. -- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/Lifecycle.html) instead. -- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/Lifecycle.html) instead. -- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`LifecycleObserver.onCreated`](https://pub.dev/documentation/reactter/8.0.0-dev.4//reactter/LifecycleObserver/onCreated.html) instead. -- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.4//reactter/RtDependency-class.html) instead. -- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0-dev.4//reactter/RtHook-class.html) instead. -- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0-dev.4//reactter/RtInterface-class.html) instead. -- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtState/notify.html) instead. -- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/UseDependency-class.html) instead. -- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtAction-class.html) instead. -- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtActionCallable-class.html) instead. -- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MemoMultiInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/MemoMultiInterceptor-class.html) instead. -- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/MemoSafeAsyncInterceptor-class.html) instead. -- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/MemoTemporaryCacheInterceptor-class.html) instead. -- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/MemoWrapperInterceptor-class.html) instead. +- Move `asyncFunction` to the first argument of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/UseAsyncState/withArg.html). +- Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. +- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtInterface/isActive.html) instead. +- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtInterface/getDependencyMode.html) instead. +- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/DependencyMode.html) instead. +- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/Lifecycle.html) instead. +- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/Lifecycle.html) instead. +- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`LifecycleObserver.onCreated`](https://pub.dev/documentation/reactter/8.0.0-dev.5//reactter/LifecycleObserver/onCreated.html) instead. +- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.5//reactter/RtDependency-class.html) instead. +- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0-dev.5//reactter/RtHook-class.html) instead. +- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0-dev.5//reactter/RtInterface-class.html) instead. +- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtState/notify.html) instead. +- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/UseDependency-class.html) instead. +- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtAction-class.html) instead. +- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtActionCallable-class.html) instead. +- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MemoMultiInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/MemoMultiInterceptor-class.html) instead. +- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/MemoSafeAsyncInterceptor-class.html) instead. +- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/MemoTemporaryCacheInterceptor-class.html) instead. +- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/MemoWrapperInterceptor-class.html) instead. - Remove [`Obj`](https://pub.dev/documentation/reactter/7.3.0/reactter/Obj-class.html). -- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.4/flutter_reactter/RtProvider/RtProvider.init.html) instead. -- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.4/flutter_reactter/RtComponent-class.html) instead. -- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.4/flutter_reactter/RtConsumer-class.html) instead. -- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.4/flutter_reactter/RtProvider-class.html) instead. -- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.4/flutter_reactter/RtMultiProvider-class.html) instead. -- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.4/flutter_reactter/RtScope-class.html) instead. -- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.4/flutter_reactter/RtSignalWatcher-class.html) instead. -- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.4/flutter_reactter/RtDependencyNotFoundException-class.html) instead. -- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.4/flutter_reactter/RtScopeNotFoundException-class.html) instead. +- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.5/flutter_reactter/RtProvider/RtProvider.init.html) instead. +- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.5/flutter_reactter/RtComponent-class.html) instead. +- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.5/flutter_reactter/RtConsumer-class.html) instead. +- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.5/flutter_reactter/RtProvider-class.html) instead. +- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.5/flutter_reactter/RtMultiProvider-class.html) instead. +- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.5/flutter_reactter/RtScope-class.html) instead. +- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.5/flutter_reactter/RtSignalWatcher-class.html) instead. +- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.5/flutter_reactter/RtDependencyNotFoundException-class.html) instead. +- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.5/flutter_reactter/RtScopeNotFoundException-class.html) instead. ## Fixes -- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/UseEffect-class.html) causing dependencies to not be unwatched. -- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtInterface/register.html) method. -- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/RtInterface/batch.html) method to work correctly. -- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0-dev.4/reactter/Notifier-class.html) class. +- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/UseEffect-class.html) causing dependencies to not be unwatched. +- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtInterface/register.html) method. +- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtInterface/batch.html) method to work correctly. +- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/Notifier-class.html) class. - Fix to propagate state param to `boundInstance`. - Remove listeners correctly from single-use listeners and handle null cases. diff --git a/packages/flutter_reactter/pubspec.lock b/packages/flutter_reactter/pubspec.lock index e924cd73..1ec6bca9 100644 --- a/packages/flutter_reactter/pubspec.lock +++ b/packages/flutter_reactter/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" fake_async: dependency: transitive description: @@ -89,26 +89,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.7" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.8" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lints: dependency: transitive description: @@ -129,18 +129,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: "direct main" description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.15.0" path: dependency: transitive description: @@ -153,10 +153,10 @@ packages: dependency: transitive description: name: platform - sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "3.1.5" process: dependency: transitive description: @@ -169,15 +169,15 @@ packages: dependency: "direct main" description: name: reactter - sha256: ad773608411600531cbc3513ae05a4ce91610ccb93701f24e79d26999b663f04 + sha256: "40cf4326b22af64563f2ac96039a9c7b4c8a577ce185e68d86c6d609a61c133d" url: "https://pub.dev" source: hosted - version: "7.3.0" + version: "8.0.0-dev.5" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: @@ -190,10 +190,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.0" stream_channel: dependency: transitive description: @@ -206,10 +206,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" sync_http: dependency: transitive description: @@ -230,10 +230,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.3" vector_math: dependency: transitive description: @@ -246,18 +246,18 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.3.0" webdriver: dependency: transitive description: name: webdriver - sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" + sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.4" sdks: - dart: ">=3.2.0-0 <4.0.0" - flutter: ">=1.17.0" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/packages/flutter_reactter/pubspec.yaml b/packages/flutter_reactter/pubspec.yaml index 4a5ab150..6de9b7a3 100644 --- a/packages/flutter_reactter/pubspec.yaml +++ b/packages/flutter_reactter/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://2devs-team.github.io/reactter/ documentation: https://2devs-team.github.io/reactter/ repository: https://github.com/2devs-team/reactter/tree/master/packages/flutter_reactter license: MIT License -version: 7.3.1 +version: 8.0.0-dev.5 environment: sdk: ">=2.14.0 <4.0.0" @@ -14,7 +14,8 @@ dependencies: flutter: sdk: flutter meta: ^1.7.0 - reactter: ^7.3.0 + reactter: ^8.0.0-dev.5 + # path: ../reactter dev_dependencies: flutter_driver: @@ -22,3 +23,9 @@ dev_dependencies: flutter_lints: ^2.0.1 flutter_test: sdk: flutter + +scripts: + test: "flutter test --coverage" + analyze: "dart analyze ." + public-dry-run: "dart pub publish --dry-run" + public: "dart pub publish" \ No newline at end of file diff --git a/packages/reactter/pubspec.yaml b/packages/reactter/pubspec.yaml index e3422532..9483e778 100644 --- a/packages/reactter/pubspec.yaml +++ b/packages/reactter/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://2devs-team.github.io/reactter/ documentation: https://2devs-team.github.io/reactter/ repository: https://github.com/2devs-team/reactter/tree/master/packages/reactter license: MIT License -version: 8.0.0-dev.4 +version: 8.0.0-dev.5 environment: sdk: ">=2.14.0 <4.0.0" From abc84a1fb1c469b84be9606ab3410f0f9d787070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Thu, 2 Jan 2025 01:41:02 -0600 Subject: [PATCH 081/141] refactor(example): Update flutter_reactter dependency version. --- .../lib/examples/3_shopping_cart/data/data_source.dart | 1 - packages/flutter_reactter/example/pubspec.yaml | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/data/data_source.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/data/data_source.dart index 96c5d111..220b748e 100644 --- a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/data/data_source.dart +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/data/data_source.dart @@ -4,7 +4,6 @@ import 'package:examples/examples/3_shopping_cart/models/cart.dart'; import 'package:examples/examples/3_shopping_cart/models/cart_item.dart'; import 'package:examples/examples/3_shopping_cart/models/product.dart'; import 'package:examples/examples/3_shopping_cart/utils/lorem_gen.dart'; -import 'package:flutter/material.dart'; const delay = Duration(milliseconds: 500); diff --git a/packages/flutter_reactter/example/pubspec.yaml b/packages/flutter_reactter/example/pubspec.yaml index 7256ae2b..cbcd3395 100644 --- a/packages/flutter_reactter/example/pubspec.yaml +++ b/packages/flutter_reactter/example/pubspec.yaml @@ -12,8 +12,8 @@ dependencies: http: ^0.13.6 url_launcher: ^6.1.2 intl: ^0.17.0 - flutter_reactter: - path: ../ + flutter_reactter: ^8.0.0-dev.5 + # path: ../ dev_dependencies: custom_lint: ^0.5.11 From 6662825f19c2c0d509845a8893984b858e7bb7d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Thu, 2 Jan 2025 17:11:38 -0600 Subject: [PATCH 082/141] refactor(example): Simplify condition checks and improve error handling in various components --- .../controllers/calculator_controller.dart | 9 +++++---- .../examples/3_shopping_cart/views/cart_view.dart | 13 ++++++++----- .../lib/examples/6_tree/states/tree_list.dart | 2 +- .../lib/examples/6_tree/states/tree_node.dart | 2 +- packages/flutter_reactter/example/lib/main.dart | 4 +--- .../lib/src/framework/dependency.dart | 1 - .../lib/src/widgets/tile_builder.dart | 1 - 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/flutter_reactter/example/lib/examples/2_calculator/controllers/calculator_controller.dart b/packages/flutter_reactter/example/lib/examples/2_calculator/controllers/calculator_controller.dart index 51ea6c61..c8a8f54d 100644 --- a/packages/flutter_reactter/example/lib/examples/2_calculator/controllers/calculator_controller.dart +++ b/packages/flutter_reactter/example/lib/examples/2_calculator/controllers/calculator_controller.dart @@ -62,10 +62,11 @@ class CalculatorController { } void _insertNumber(int value) { - if (_lastAction - case != ActionCalculator.number && - != ActionCalculator.point && - != ActionCalculator.sign) { + if ([ + ActionCalculator.number, + ActionCalculator.point, + ActionCalculator.sign, + ].contains(_lastAction)) { _resetResult(); } diff --git a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/views/cart_view.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/views/cart_view.dart index 1f104b0e..991a7867 100644 --- a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/views/cart_view.dart +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/views/cart_view.dart @@ -43,11 +43,14 @@ class CartView extends RtComponent { itemBuilder: (context, index) { return RtSelector( selector: (inst, watch) { - return watch(inst.uCartItems) - .value - .elementAtOrNull(index) - ?.quantity ?? - 0; + try { + return watch(inst.uCartItems) + .value + .elementAt(index) + .quantity; + } catch (e) { + return 0; + } }, builder: (_, cartController, __, ___) { final item = cartController.uCartItems.value[index]; diff --git a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart index fdef7708..384cc733 100644 --- a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart +++ b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart @@ -3,7 +3,7 @@ import 'dart:collection'; import 'package:examples/examples/6_tree/states/tree_node.dart'; import 'package:flutter_reactter/reactter.dart'; -base class TreeList extends LinkedList +class TreeList extends LinkedList with RtContextMixin, RtStateBase { TreeList._(); diff --git a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart index 1fb68c95..c426efff 100644 --- a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart +++ b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart @@ -4,7 +4,7 @@ import 'dart:collection'; import 'package:flutter_reactter/flutter_reactter.dart'; -base class TreeNode extends LinkedListEntry +class TreeNode extends LinkedListEntry with RtStateBase, RtContextMixin { /// A unique identifier for each instance of [TreeNode]. static int _lastId = 0; diff --git a/packages/flutter_reactter/example/lib/main.dart b/packages/flutter_reactter/example/lib/main.dart index 406369bd..58368523 100644 --- a/packages/flutter_reactter/example/lib/main.dart +++ b/packages/flutter_reactter/example/lib/main.dart @@ -1,8 +1,6 @@ import 'package:examples/app.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_reactter/flutter_reactter.dart'; -Future main() async { - Rt.initializeDevTools(); +void main() { runApp(const MyApp()); } diff --git a/packages/flutter_reactter/lib/src/framework/dependency.dart b/packages/flutter_reactter/lib/src/framework/dependency.dart index e678a853..24fd0a76 100644 --- a/packages/flutter_reactter/lib/src/framework/dependency.dart +++ b/packages/flutter_reactter/lib/src/framework/dependency.dart @@ -23,7 +23,6 @@ abstract class Dependency { void listen(covariant void Function() callback); - @mustBeOverridden @mustCallSuper void unlisten() { _listener = null; diff --git a/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart b/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart index 1c07e62d..a775cc06 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart @@ -26,7 +26,6 @@ class TreeNodeTileBuilder extends StatelessWidget { horizontalTitleGap: 0, minVerticalPadding: 0, contentPadding: EdgeInsets.zero, - minTileHeight: 24, selected: isSelected, selectedTileColor: ColorPalette.of(context).selected, onTap: onTap, From 25db4f28b0593f248bba2e8bfa70f4331b84f11c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 3 Jan 2025 23:49:25 -0600 Subject: [PATCH 083/141] refactor(core): Update `dependency_injection` to improve type safety. --- .../reactter/lib/src/core/dependency_injection.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/reactter/lib/src/core/dependency_injection.dart b/packages/reactter/lib/src/core/dependency_injection.dart index baf43f2d..cdc3b7d8 100644 --- a/packages/reactter/lib/src/core/dependency_injection.dart +++ b/packages/reactter/lib/src/core/dependency_injection.dart @@ -520,7 +520,7 @@ abstract class DependencyInjection implements IContext { eventHandler.emit(instance, Lifecycle.deleted, instance); eventHandler.emit(dependencyRegister, Lifecycle.deleted, instance); - if (instance is IState && !(instance as IState).isDisposed) { + if (instance is IState && !instance.isDisposed) { instance.dispose(); } } @@ -540,11 +540,13 @@ abstract class DependencyInjection implements IContext { } /// Returns the [DependencyRegister] of [T] type with a given [dependencyRef]. - DependencyRegister? getDependencyRegisterByRef( + DependencyRegister? getDependencyRegisterByRef( DependencyRef? dependencyRef, ) { - return _dependencyRegisters.lookup(dependencyRef as Object) - as DependencyRegister?; + if (dependencyRef == null) return null; + + return _dependencyRegisters.lookup(dependencyRef as dynamic) + as DependencyRegister?; } void _notifyDependencyFailed( From fa9fe9e491d6708fe09bc12d232d68d341e09e1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Sat, 4 Jan 2025 00:15:36 -0600 Subject: [PATCH 084/141] refactor(example): Improve UI elements and restructure example pages for better readability. --- .../lib/{examples_page.dart => examples.dart} | 18 -- .../controllers/calculator_controller.dart | 2 +- .../widgets/calculator_result.dart | 3 +- .../3_shopping_cart/views/cart_view.dart | 5 +- .../3_shopping_cart/widgets/quantity.dart | 15 +- .../4_github_search/widgets/search_bar.dart | 11 +- .../lib/examples/5_todo/todo_page.dart | 27 ++- .../examples/5_todo/widgets/input_bar.dart | 1 + .../examples/5_todo/widgets/todo_filter.dart | 155 +++++++++--------- .../examples/5_todo/widgets/todo_item.dart | 3 +- .../examples/6_tree/widgets/tree_item.dart | 90 +++++----- .../flutter_reactter/example/lib/main.dart | 93 ++++++++++- .../{app.dart => title_oute_observer.dart} | 31 +--- 13 files changed, 258 insertions(+), 196 deletions(-) rename packages/flutter_reactter/example/lib/{examples_page.dart => examples.dart} (90%) rename packages/flutter_reactter/example/lib/{app.dart => title_oute_observer.dart} (59%) diff --git a/packages/flutter_reactter/example/lib/examples_page.dart b/packages/flutter_reactter/example/lib/examples.dart similarity index 90% rename from packages/flutter_reactter/example/lib/examples_page.dart rename to packages/flutter_reactter/example/lib/examples.dart index 48134d05..9ff31a88 100644 --- a/packages/flutter_reactter/example/lib/examples_page.dart +++ b/packages/flutter_reactter/example/lib/examples.dart @@ -116,24 +116,6 @@ final examples = [ ), ]; -class ExamplesPage extends StatelessWidget { - const ExamplesPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Title( - title: 'Reactter | Examples', - color: Theme.of(context).primaryColor, - child: Scaffold( - appBar: AppBar( - title: const Text("Examples"), - ), - body: CustomList(items: examples), - ), - ); - } -} - /// A ListItem that contains data to display a message. class ExampleItem implements ListItem { final String routeName; diff --git a/packages/flutter_reactter/example/lib/examples/2_calculator/controllers/calculator_controller.dart b/packages/flutter_reactter/example/lib/examples/2_calculator/controllers/calculator_controller.dart index c8a8f54d..abf7d1a3 100644 --- a/packages/flutter_reactter/example/lib/examples/2_calculator/controllers/calculator_controller.dart +++ b/packages/flutter_reactter/example/lib/examples/2_calculator/controllers/calculator_controller.dart @@ -62,7 +62,7 @@ class CalculatorController { } void _insertNumber(int value) { - if ([ + if (![ ActionCalculator.number, ActionCalculator.point, ActionCalculator.sign, diff --git a/packages/flutter_reactter/example/lib/examples/2_calculator/widgets/calculator_result.dart b/packages/flutter_reactter/example/lib/examples/2_calculator/widgets/calculator_result.dart index 2e708cab..a4726e54 100644 --- a/packages/flutter_reactter/example/lib/examples/2_calculator/widgets/calculator_result.dart +++ b/packages/flutter_reactter/example/lib/examples/2_calculator/widgets/calculator_result.dart @@ -21,7 +21,8 @@ class CalculatorResult extends StatelessWidget { return Text( result, - style: const TextStyle( + style: TextStyle( + color: Colors.grey.shade100, fontSize: 48, fontWeight: FontWeight.w200, ), diff --git a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/views/cart_view.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/views/cart_view.dart index 991a7867..958ac995 100644 --- a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/views/cart_view.dart +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/views/cart_view.dart @@ -107,7 +107,10 @@ class CartView extends RtComponent { else Text( formatCurrency(total), - style: Theme.of(context).textTheme.titleLarge, + style: Theme.of(context) + .textTheme + .titleLarge! + .copyWith(color: Colors.grey.shade200), ), ], ); diff --git a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/quantity.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/quantity.dart index 762dd10e..1155c7af 100644 --- a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/quantity.dart +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/widgets/quantity.dart @@ -18,9 +18,9 @@ class Quantity extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - decoration: const ShapeDecoration( - color: Colors.black38, - shape: StadiumBorder(), + decoration: ShapeDecoration( + color: Theme.of(context).highlightColor, + shape: const StadiumBorder(), ), child: Row( mainAxisSize: MainAxisSize.min, @@ -34,13 +34,8 @@ class Quantity extends StatelessWidget { ), Padding( padding: const EdgeInsets.symmetric(horizontal: 8), - child: Text( - "$quantity", - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith(color: Colors.white), - ), + child: Text("$quantity", + style: Theme.of(context).textTheme.titleMedium), ), CustomIconButton( icon: Icons.add, diff --git a/packages/flutter_reactter/example/lib/examples/4_github_search/widgets/search_bar.dart b/packages/flutter_reactter/example/lib/examples/4_github_search/widgets/search_bar.dart index 174a5fde..4909f00c 100644 --- a/packages/flutter_reactter/example/lib/examples/4_github_search/widgets/search_bar.dart +++ b/packages/flutter_reactter/example/lib/examples/4_github_search/widgets/search_bar.dart @@ -28,13 +28,9 @@ class SearchBar extends StatelessWidget { decoration: InputDecoration( isDense: true, filled: true, + fillColor: Theme.of(context).colorScheme.surface, hintText: 'Type a username or repository (like "flutter/flutter")', - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(30), - borderSide: const BorderSide( - strokeAlign: BorderSide.strokeAlignOutside, - ), - ), + border: const OutlineInputBorder(), counter: const SizedBox(), prefixIcon: const Icon(Icons.search), suffixIcon: Padding( @@ -48,9 +44,6 @@ class SearchBar extends StatelessWidget { width: 0.5, color: Theme.of(context).colorScheme.primary, ), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30.0), - ), ), onPressed: searchInputController.onButtonPressed, child: const Text('Search'), diff --git a/packages/flutter_reactter/example/lib/examples/5_todo/todo_page.dart b/packages/flutter_reactter/example/lib/examples/5_todo/todo_page.dart index 75d874b3..eff1526f 100644 --- a/packages/flutter_reactter/example/lib/examples/5_todo/todo_page.dart +++ b/packages/flutter_reactter/example/lib/examples/5_todo/todo_page.dart @@ -14,13 +14,11 @@ class TodoPage extends StatelessWidget { return RtProvider( TodoController.new, builder: (context, todoController, _) { - final size = MediaQuery.of(context).size; - return Scaffold( appBar: AppBar( title: const Text("To-Do List"), bottom: PreferredSize( - preferredSize: Size.fromHeight(size.width >= 480 ? 82 : 110), + preferredSize: const Size.fromHeight(40), child: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -30,8 +28,10 @@ class TodoPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ InputBar(onAdd: todoController.addTodo), - const SizedBox(height: 4), - const TodoFilter(), + // const Padding( + // padding: EdgeInsets.symmetric(vertical: 8.0), + // child: TodoFilter(), + // ), ], ), ), @@ -39,7 +39,22 @@ class TodoPage extends StatelessWidget { ), ), ), - body: const TodoList(), + body: const Padding( + padding: EdgeInsets.all(8), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TodoFilter(), + SizedBox(height: 4), + Expanded( + child: Card( + child: TodoList(), + ), + ), + ], + ), + ), ); }, ); diff --git a/packages/flutter_reactter/example/lib/examples/5_todo/widgets/input_bar.dart b/packages/flutter_reactter/example/lib/examples/5_todo/widgets/input_bar.dart index 2f968d49..a624eb3b 100644 --- a/packages/flutter_reactter/example/lib/examples/5_todo/widgets/input_bar.dart +++ b/packages/flutter_reactter/example/lib/examples/5_todo/widgets/input_bar.dart @@ -51,6 +51,7 @@ class InputBar extends StatelessWidget { decoration: InputDecoration( isDense: true, filled: true, + fillColor: Theme.of(context).colorScheme.surface, hintText: 'What needs to be done?', border: const OutlineInputBorder( borderSide: BorderSide( diff --git a/packages/flutter_reactter/example/lib/examples/5_todo/widgets/todo_filter.dart b/packages/flutter_reactter/example/lib/examples/5_todo/widgets/todo_filter.dart index 3ed56ad9..1af399cd 100644 --- a/packages/flutter_reactter/example/lib/examples/5_todo/widgets/todo_filter.dart +++ b/packages/flutter_reactter/example/lib/examples/5_todo/widgets/todo_filter.dart @@ -10,85 +10,86 @@ class TodoFilter extends StatelessWidget { @override Widget build(BuildContext context) { - return Wrap( - alignment: WrapAlignment.end, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - FittedBox( - child: SizedBox( - height: 30, - child: RtSelector( - selector: (inst, watch) => watch(inst.uReduce).value.filteredBy, - builder: (_, __, filteredBy, ___) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Text('Filter by:'), - RtSelector( - selector: (inst, watch) { - return watch(inst.uReduce).value.todoList.length; - }, - builder: (_, todoController, allCount, __) { - return RadioWithLabel( - label: 'All($allCount)', - value: TodoListType.all, - groupValue: filteredBy, - onChanged: todoController.filterBy, - ); - }, - ), - RtSelector( - selector: (inst, watch) { - final uReduce = watch(inst.uReduce).value; - final allCount = uReduce.todoList.length; - final doneCount = uReduce.doneCount; + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + RtSelector( + selector: (inst, watch) => watch(inst.uReduce).value.filteredBy, + builder: (_, __, filteredBy, ___) { + return Row( + mainAxisSize: MainAxisSize.max, + children: [ + const Text('Filter by:'), + const SizedBox(width: 4), + RtSelector( + selector: (inst, watch) { + return watch(inst.uReduce).value.todoList.length; + }, + builder: (_, todoController, allCount, __) { + return RadioWithLabel( + label: 'All($allCount)', + value: TodoListType.all, + groupValue: filteredBy, + onChanged: todoController.filterBy, + ); + }, + ), + RtSelector( + selector: (inst, watch) { + final uReduce = watch(inst.uReduce).value; + final allCount = uReduce.todoList.length; + final doneCount = uReduce.doneCount; - return allCount - doneCount; - }, - builder: (_, todoController, activeCount, __) { - return RadioWithLabel( - label: 'Active($activeCount)', - value: TodoListType.todo, - groupValue: filteredBy, - onChanged: todoController.filterBy, - ); - }, - ), - RtSelector( - selector: (inst, watch) { - return watch(inst.uReduce).value.doneCount; - }, - builder: (_, todoController, doneCount, __) { - return RadioWithLabel( - label: 'Completed($doneCount)', - value: TodoListType.done, - groupValue: filteredBy, - onChanged: todoController.filterBy, - ); - }, - ), - ], - ); - }, - ), + return allCount - doneCount; + }, + builder: (_, todoController, activeCount, __) { + return RadioWithLabel( + label: 'Active($activeCount)', + value: TodoListType.todo, + groupValue: filteredBy, + onChanged: todoController.filterBy, + ); + }, + ), + RtSelector( + selector: (inst, watch) { + return watch(inst.uReduce).value.doneCount; + }, + builder: (_, todoController, doneCount, __) { + return Row( + children: [ + RadioWithLabel( + label: 'Completed($doneCount)', + value: TodoListType.done, + groupValue: filteredBy, + onChanged: todoController.filterBy, + ), + const SizedBox(width: 4), + IconButton( + tooltip: 'Clear completed', + color: Colors.red.shade400, + padding: EdgeInsets.zero, + constraints: + const BoxConstraints.tightFor(width: 24), + splashRadius: 18, + iconSize: 24, + icon: const Icon(Icons.delete), + onPressed: doneCount > 0 + ? todoController.clearCompleted + : null, + ), + ], + ); + }, + ), + ], + ); + }, ), - ), - const SizedBox(width: 8), - RtSelector( - selector: (inst, watch) { - return watch(inst.uReduce).value.doneCount > 0; - }, - builder: (_, todoController, hasCount, __) { - return OutlinedButton( - style: OutlinedButton.styleFrom( - foregroundColor: Colors.red, - ), - onPressed: hasCount ? todoController.clearCompleted : null, - child: const Text('Clear completed'), - ); - }, - ), - ], + ], + ), ); } } diff --git a/packages/flutter_reactter/example/lib/examples/5_todo/widgets/todo_item.dart b/packages/flutter_reactter/example/lib/examples/5_todo/widgets/todo_item.dart index 4a111802..7640464d 100644 --- a/packages/flutter_reactter/example/lib/examples/5_todo/widgets/todo_item.dart +++ b/packages/flutter_reactter/example/lib/examples/5_todo/widgets/todo_item.dart @@ -20,6 +20,7 @@ class TodoItem extends StatelessWidget { value: todo.isDone, onChanged: (_) => onTap?.call(), dense: true, + visualDensity: VisualDensity.compact, controlAffinity: ListTileControlAffinity.leading, title: Text( todo.title, @@ -33,7 +34,7 @@ class TodoItem extends StatelessWidget { onPressed: onRemove, color: Colors.red.shade400, padding: EdgeInsets.zero, - constraints: const BoxConstraints.tightFor(width: 42), + constraints: const BoxConstraints.tightFor(width: 24), splashRadius: 18, iconSize: 24, icon: const Icon(Icons.close_outlined), diff --git a/packages/flutter_reactter/example/lib/examples/6_tree/widgets/tree_item.dart b/packages/flutter_reactter/example/lib/examples/6_tree/widgets/tree_item.dart index 177aff9f..e77d7fad 100644 --- a/packages/flutter_reactter/example/lib/examples/6_tree/widgets/tree_item.dart +++ b/packages/flutter_reactter/example/lib/examples/6_tree/widgets/tree_item.dart @@ -27,35 +27,38 @@ class TreeItem extends RtComponent { @override Widget render(context, treeNode) { - return InkWell( - onTap: () => _openDialog(context), - child: Stack( - clipBehavior: Clip.none, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 2), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox(width: 22.0 * treeNode.depth), - _buildNode(), - const Expanded( - child: Divider( - height: 2, - thickness: 2, + return SizedBox( + height: 36, + child: InkWell( + onTap: () => _openDialog(context), + child: Stack( + clipBehavior: Clip.none, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 2), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(width: 22.0 * treeNode.depth), + _buildNode(), + const Expanded( + child: Divider( + height: 2, + thickness: 2, + ), ), - ), - _buildCount(), - ], - ), - ], + _buildCount(), + ], + ), + ], + ), ), - ), - _buildTreeLine(), - ], + _buildTreeLine(), + ], + ), ), ); } @@ -142,7 +145,7 @@ class TreeItem extends RtComponent { decoration: ShapeDecoration( shape: RoundedRectangleBorder( side: BorderSide( - color: Theme.of(context).colorScheme.outlineVariant, + color: Theme.of(context).dividerColor, width: 2, ), borderRadius: const BorderRadius.all(Radius.circular(20)), @@ -189,7 +192,7 @@ class TreeItem extends RtComponent { decoration: ShapeDecoration( shape: RoundedRectangleBorder( side: BorderSide( - color: Theme.of(context).colorScheme.outlineVariant, + color: Theme.of(context).dividerColor, width: 2, ), ), @@ -224,8 +227,8 @@ class TreeItem extends RtComponent { return Positioned( top: -2, - bottom: -4, left: -6, + bottom: 2, child: Align( alignment: Alignment.topLeft, child: Row( @@ -237,7 +240,7 @@ class TreeItem extends RtComponent { height: treeNode.parent != null && watch(treeNode.parent!.uChildren).value.first == treeNode - ? 22 + ? 21 : null, child: const VerticalDivider( width: 2, @@ -245,16 +248,21 @@ class TreeItem extends RtComponent { ), ); }), - RtWatcher((context, watch) { - return SizedBox( - width: watch(treeNode.uChildren).value.isEmpty ? 32 : 4, - height: 46, - child: const Divider( - height: 2, - thickness: 2, - ), - ); - }), + Column( + mainAxisSize: MainAxisSize.max, + children: [ + const SizedBox(height: 19), + RtWatcher((context, watch) { + return SizedBox( + width: watch(treeNode.uChildren).value.isEmpty ? 32 : 4, + child: const Divider( + height: 2, + thickness: 2, + ), + ); + }), + ], + ), ], ), ), diff --git a/packages/flutter_reactter/example/lib/main.dart b/packages/flutter_reactter/example/lib/main.dart index 58368523..4d3e1638 100644 --- a/packages/flutter_reactter/example/lib/main.dart +++ b/packages/flutter_reactter/example/lib/main.dart @@ -1,5 +1,96 @@ -import 'package:examples/app.dart'; +/// This file contains the main entry point of the example app. +/// It also contains a small example using a global state for the theme mode. +/// The main app widget contains a list of examples that can be navigated to. +/// Each example has its own page that demonstrates the use of the Reactter package. +/// For viewing the examples online, you can visit https://zapp.run/pub/flutter_reactter + +import 'package:examples/custom_list.dart'; +import 'package:examples/title_oute_observer.dart'; +import 'package:examples/examples.dart'; +import 'package:examples/page_404.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_reactter/flutter_reactter.dart'; + +/// Global state for theme mode +final uThemeMode = UseState(ThemeMode.dark); + +class IndexPage extends StatelessWidget { + const IndexPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Title( + title: 'Reactter | Examples', + color: Theme.of(context).primaryColor, + child: Scaffold( + appBar: AppBar( + title: const Text("Examples"), + actions: [ + IconButton( + tooltip: "Change theme mode", + icon: RtWatcher((context, watch) { + /// Get and watch the global state + /// This will rebuild the icon button when the global state changes + /// and update the icon to reflect the current theme mode + final themeMode = watch(uThemeMode).value; + + return Icon( + // Use the value of the global state to determine the icon + themeMode == ThemeMode.dark + ? Icons.light_mode + : Icons.dark_mode, + ); + }), + onPressed: () { + // Change the value of the global state to toggle the theme mode + uThemeMode.value = uThemeMode.value == ThemeMode.dark + ? ThemeMode.light + : ThemeMode.dark; + }, + ), + ], + ), + body: CustomList(items: examples), + ), + ); + } +} + +class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return RtWatcher((context, watch) { + /// Get and watch the global state + /// This will rebuild the app when the global state changes + final themeMode = watch(uThemeMode).value; + + return MaterialApp( + debugShowCheckedModeBanner: false, + // Use the value of the global state + themeMode: themeMode, + theme: ThemeData.light().copyWith( + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + darkTheme: ThemeData.dark().copyWith( + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + initialRoute: '/', + routes: { + '/': (context) => const IndexPage(), + for (final e in examples) e.routeName: (context) => e.renderPage(), + }, + navigatorObservers: [TitleRouteObserver()], + onUnknownRoute: (RouteSettings settings) { + return MaterialPageRoute( + builder: (_) => const Page404(), + ); + }, + ); + }); + } +} void main() { runApp(const MyApp()); diff --git a/packages/flutter_reactter/example/lib/app.dart b/packages/flutter_reactter/example/lib/title_oute_observer.dart similarity index 59% rename from packages/flutter_reactter/example/lib/app.dart rename to packages/flutter_reactter/example/lib/title_oute_observer.dart index 317bca00..dfefcf6c 100644 --- a/packages/flutter_reactter/example/lib/app.dart +++ b/packages/flutter_reactter/example/lib/title_oute_observer.dart @@ -1,36 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:examples/examples_page.dart'; -import 'package:examples/page_404.dart'; - -class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return MaterialApp( - debugShowCheckedModeBanner: false, - theme: ThemeData.light().copyWith( - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - darkTheme: ThemeData.dark().copyWith( - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - initialRoute: '/', - routes: { - '/': (context) => const ExamplesPage(), - for (final e in examples) e.routeName: (context) => e.renderPage(), - }, - onUnknownRoute: (RouteSettings settings) { - return MaterialPageRoute( - builder: (_) => const Page404(), - ); - }, - navigatorObservers: [TitleRouteObserver()], - ); - } -} +import 'package:examples/examples.dart'; class TitleRouteObserver extends RouteObserver { static Map titleRoute = { From 6ffa221f21f964c7b4a2b8987fcca7079cb20702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Sat, 4 Jan 2025 00:19:28 -0600 Subject: [PATCH 085/141] build: Update dependencies and SDK constraints across multiple packages. --- CHANGELOG.md | 96 ++++++------- .../flutter_reactter/example/pubspec.lock | 59 ++++---- .../flutter_reactter/example/pubspec.yaml | 10 +- packages/flutter_reactter/pubspec.lock | 132 ++++-------------- packages/flutter_reactter/pubspec.yaml | 17 ++- packages/reactter/pubspec.lock | 109 ++++++--------- packages/reactter/pubspec.yaml | 14 +- .../reactter_devtools_extension/pubspec.lock | 50 +++---- .../reactter_devtools_extension/pubspec.yaml | 3 +- 9 files changed, 205 insertions(+), 285 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bcefe72f..da2a11c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,68 +1,68 @@ # Reactter -## 8.0.0-dev.5 +## 8.0.0-dev.14 ### Enhancements - Add Reactter devtools. -- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtInterface/removeObserver.html) methods to manage the observers. -- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). -- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). -- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. -- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. -- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtStateBase/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtStateBase/debugInfo.html) to states and hooks to get info for debugging. -- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. -- Add [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtStateBase-class.html) abstract class to implement the base logic of the state. -- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtInterface/createState.html) method to create a new state. -- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. -- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. -- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. +- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtInterface/removeObserver.html) methods to manage the observers. +- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). +- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). +- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. +- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. +- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtStateBase/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtStateBase/debugInfo.html) to states and hooks to get info for debugging. +- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. +- Add [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtStateBase-class.html) abstract class to implement the base logic of the state. +- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtInterface/createState.html) method to create a new state. +- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. +- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. +- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. - Enhance event handling and notifier logic for improved stability and performance. - Enhance state management and error handling. -- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.5/flutter_reactter/RtComponent-class.html). +- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.14/flutter_reactter/RtComponent-class.html). - Enhance dependency management to ensure to re-build when is detected changes while building. ### Breakings -- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/Rt.html) instead. -- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtStateBase-class.html) instead. +- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/Rt.html) instead. +- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtStateBase-class.html) instead. - Replace [`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html) to `UseAsyncStateStatus.idle`. -- Move `asyncFunction` to the first argument of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/UseAsyncState/withArg.html). -- Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. -- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtInterface/isActive.html) instead. -- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtInterface/getDependencyMode.html) instead. -- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/DependencyMode.html) instead. -- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/Lifecycle.html) instead. -- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/Lifecycle.html) instead. -- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`LifecycleObserver.onCreated`](https://pub.dev/documentation/reactter/8.0.0-dev.5//reactter/LifecycleObserver/onCreated.html) instead. -- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.5//reactter/RtDependency-class.html) instead. -- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0-dev.5//reactter/RtHook-class.html) instead. -- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0-dev.5//reactter/RtInterface-class.html) instead. -- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtState/notify.html) instead. -- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/UseDependency-class.html) instead. -- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtAction-class.html) instead. -- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtActionCallable-class.html) instead. -- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MemoMultiInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/MemoMultiInterceptor-class.html) instead. -- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/MemoSafeAsyncInterceptor-class.html) instead. -- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/MemoTemporaryCacheInterceptor-class.html) instead. -- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/MemoWrapperInterceptor-class.html) instead. +- Move `asyncFunction` to the first argument of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/UseAsyncState/withArg.html). +- Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. +- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtInterface/isActive.html) instead. +- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtInterface/getDependencyMode.html) instead. +- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/DependencyMode.html) instead. +- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/Lifecycle.html) instead. +- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/Lifecycle.html) instead. +- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`LifecycleObserver.onCreated`](https://pub.dev/documentation/reactter/8.0.0-dev.14//reactter/LifecycleObserver/onCreated.html) instead. +- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.14//reactter/RtDependency-class.html) instead. +- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0-dev.14//reactter/RtHook-class.html) instead. +- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0-dev.14//reactter/RtInterface-class.html) instead. +- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtState/notify.html) instead. +- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/UseDependency-class.html) instead. +- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtAction-class.html) instead. +- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtActionCallable-class.html) instead. +- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MemoMultiInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/MemoMultiInterceptor-class.html) instead. +- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/MemoSafeAsyncInterceptor-class.html) instead. +- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/MemoTemporaryCacheInterceptor-class.html) instead. +- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/MemoWrapperInterceptor-class.html) instead. - Remove [`Obj`](https://pub.dev/documentation/reactter/7.3.0/reactter/Obj-class.html). -- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.5/flutter_reactter/RtProvider/RtProvider.init.html) instead. -- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.5/flutter_reactter/RtComponent-class.html) instead. -- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.5/flutter_reactter/RtConsumer-class.html) instead. -- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.5/flutter_reactter/RtProvider-class.html) instead. -- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.5/flutter_reactter/RtMultiProvider-class.html) instead. -- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.5/flutter_reactter/RtScope-class.html) instead. -- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.5/flutter_reactter/RtSignalWatcher-class.html) instead. -- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.5/flutter_reactter/RtDependencyNotFoundException-class.html) instead. -- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.5/flutter_reactter/RtScopeNotFoundException-class.html) instead. +- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.14/flutter_reactter/RtProvider/RtProvider.init.html) instead. +- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.14/flutter_reactter/RtComponent-class.html) instead. +- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.14/flutter_reactter/RtConsumer-class.html) instead. +- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.14/flutter_reactter/RtProvider-class.html) instead. +- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.14/flutter_reactter/RtMultiProvider-class.html) instead. +- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.14/flutter_reactter/RtScope-class.html) instead. +- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.14/flutter_reactter/RtSignalWatcher-class.html) instead. +- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.14/flutter_reactter/RtDependencyNotFoundException-class.html) instead. +- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.14/flutter_reactter/RtScopeNotFoundException-class.html) instead. ## Fixes -- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/UseEffect-class.html) causing dependencies to not be unwatched. -- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtInterface/register.html) method. -- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/RtInterface/batch.html) method to work correctly. -- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0-dev.5/reactter/Notifier-class.html) class. +- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/UseEffect-class.html) causing dependencies to not be unwatched. +- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtInterface/register.html) method. +- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtInterface/batch.html) method to work correctly. +- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/Notifier-class.html) class. - Fix to propagate state param to `boundInstance`. - Remove listeners correctly from single-use listeners and handle null cases. diff --git a/packages/flutter_reactter/example/pubspec.lock b/packages/flutter_reactter/example/pubspec.lock index 793582e5..6305f768 100644 --- a/packages/flutter_reactter/example/pubspec.lock +++ b/packages/flutter_reactter/example/pubspec.lock @@ -93,10 +93,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.1" convert: dependency: transitive description: @@ -117,26 +117,26 @@ packages: dependency: "direct dev" description: name: custom_lint - sha256: "22bd87a362f433ba6aae127a7bac2838645270737f3721b180916d7c5946cb5d" + sha256: b09a939c3bb062fdf072aa4c8eb5447231338854c4fefb23e4c9f7cef41cb3ff url: "https://pub.dev" source: hosted - version: "0.5.11" + version: "0.5.0" custom_lint_builder: dependency: transitive description: name: custom_lint_builder - sha256: "0d48e002438950f9582e574ef806b2bea5719d8d14c0f9f754fbad729bcf3b19" + sha256: f1430048ccddcd82cbf7722fce7701530fd535b82e91f45b0c81ed07814143bf url: "https://pub.dev" source: hosted - version: "0.5.14" + version: "0.5.0" custom_lint_core: dependency: transitive description: name: custom_lint_core - sha256: "2952837953022de610dacb464f045594854ced6506ac7f76af28d4a6490e189b" + sha256: a39e98cba1d96076e2e5c96aad582a776909c78b4d7480d0efdcc3791b225c1b url: "https://pub.dev" source: hosted - version: "0.5.14" + version: "0.5.0" dart_style: dependency: transitive description: @@ -169,10 +169,11 @@ packages: flutter_reactter: dependency: "direct main" description: - path: ".." - relative: true - source: path - version: "7.3.1" + name: flutter_reactter + sha256: "349458f417688f8fd22ec8b7ec3f39389eb9992aff9e9fb781151985c060ccac" + url: "https://pub.dev" + source: hosted + version: "8.0.0-dev.12" flutter_web_plugins: dependency: transitive description: flutter @@ -226,6 +227,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.17.0" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" json_annotation: dependency: transitive description: @@ -262,18 +271,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.2.0" meta: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.9.1" package_config: dependency: transitive description: @@ -317,16 +326,18 @@ packages: reactter: dependency: transitive description: - path: "../../reactter" - relative: true - source: path - version: "7.3.0" + name: reactter + sha256: b7aea3400aa8dfd282c98e159ccb9bfd2ae321010e75210f02f40616801876d6 + url: "https://pub.dev" + source: hosted + version: "8.0.0-dev.11" reactter_lint: dependency: "direct dev" description: - path: "../../reactter_lint" - relative: true - source: path + name: reactter_lint + sha256: e04dd220eeca470f94a74360344d2c9358c16f87adcccb407f6f19952b819d20 + url: "https://pub.dev" + source: hosted version: "1.0.0" rxdart: dependency: transitive @@ -510,5 +521,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.3.0-0 <4.0.0" + dart: ">=3.0.0-0 <4.0.0" flutter: ">=3.7.0" diff --git a/packages/flutter_reactter/example/pubspec.yaml b/packages/flutter_reactter/example/pubspec.yaml index cbcd3395..926fbf4e 100644 --- a/packages/flutter_reactter/example/pubspec.yaml +++ b/packages/flutter_reactter/example/pubspec.yaml @@ -4,7 +4,7 @@ version: 3.0.0+1 publish_to: "none" environment: - sdk: ">3.2.0 <4.0.0" + sdk: ">=2.19.2 <4.0.0" dependencies: flutter: @@ -12,14 +12,12 @@ dependencies: http: ^0.13.6 url_launcher: ^6.1.2 intl: ^0.17.0 - flutter_reactter: ^8.0.0-dev.5 - # path: ../ + flutter_reactter: ^8.0.0-dev.14 dev_dependencies: - custom_lint: ^0.5.11 + custom_lint: ^0.5.0 flutter_lints: ^2.0.2 - reactter_lint: - path: ../../reactter_lint + reactter_lint: ^1.0.0 flutter: uses-material-design: true diff --git a/packages/flutter_reactter/pubspec.lock b/packages/flutter_reactter/pubspec.lock index 1ec6bca9..f491e900 100644 --- a/packages/flutter_reactter/pubspec.lock +++ b/packages/flutter_reactter/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.19.0" + version: "1.17.1" fake_async: dependency: transitive description: @@ -49,24 +49,11 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" - file: - dependency: transitive - description: - name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" - url: "https://pub.dev" - source: hosted - version: "7.0.0" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" - flutter_driver: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" flutter_lints: dependency: "direct dev" description: @@ -80,35 +67,14 @@ packages: description: flutter source: sdk version: "0.0.0" - fuchsia_remote_debug_protocol: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" - url: "https://pub.dev" - source: hosted - version: "10.0.7" - leak_tracker_flutter_testing: + js: dependency: transitive description: - name: leak_tracker_flutter_testing - sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "3.0.8" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" + version: "0.6.7" lints: dependency: transitive description: @@ -121,103 +87,79 @@ packages: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.15" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.2.0" meta: dependency: "direct main" description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.9.1" path: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.9.0" - platform: - dependency: transitive - description: - name: platform - sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" - url: "https://pub.dev" - source: hosted - version: "3.1.5" - process: - dependency: transitive - description: - name: process - sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" - url: "https://pub.dev" - source: hosted - version: "5.0.2" + version: "1.8.3" reactter: dependency: "direct main" description: name: reactter - sha256: "40cf4326b22af64563f2ac96039a9c7b4c8a577ce185e68d86c6d609a61c133d" + sha256: c2a9f823b1570f42842aed54f4765dc86b294ce7ea8e6004c3772de1354feca2 url: "https://pub.dev" source: hosted - version: "8.0.0-dev.5" + version: "8.0.0-dev.13" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" source_span: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.9.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.3.0" - sync_http: - dependency: transitive - description: - name: sync_http - sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" - url: "https://pub.dev" - source: hosted - version: "0.3.1" + version: "1.2.0" term_glyph: dependency: transitive description: @@ -230,10 +172,10 @@ packages: dependency: transitive description: name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "0.5.1" vector_math: dependency: transitive description: @@ -242,22 +184,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b - url: "https://pub.dev" - source: hosted - version: "14.3.0" - webdriver: - dependency: transitive - description: - name: webdriver - sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8" - url: "https://pub.dev" - source: hosted - version: "3.0.4" sdks: - dart: ">=3.4.0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + dart: ">=3.0.0-0 <4.0.0" + flutter: ">=1.17.0" diff --git a/packages/flutter_reactter/pubspec.yaml b/packages/flutter_reactter/pubspec.yaml index 6de9b7a3..79654e74 100644 --- a/packages/flutter_reactter/pubspec.yaml +++ b/packages/flutter_reactter/pubspec.yaml @@ -4,25 +4,28 @@ homepage: https://2devs-team.github.io/reactter/ documentation: https://2devs-team.github.io/reactter/ repository: https://github.com/2devs-team/reactter/tree/master/packages/flutter_reactter license: MIT License -version: 8.0.0-dev.5 +version: 8.0.0-dev.14 + +topics: + - reactive + - state + - dependency + - event environment: - sdk: ">=2.14.0 <4.0.0" + sdk: ">=2.19.2 <4.0.0" flutter: ">=1.17.0" dependencies: flutter: sdk: flutter meta: ^1.7.0 - reactter: ^8.0.0-dev.5 - # path: ../reactter + reactter: ^8.0.0-dev.13 dev_dependencies: - flutter_driver: - sdk: flutter - flutter_lints: ^2.0.1 flutter_test: sdk: flutter + flutter_lints: ^2.0.1 scripts: test: "flutter test --coverage" diff --git a/packages/reactter/pubspec.lock b/packages/reactter/pubspec.lock index 95d1d890..eab3a9df 100644 --- a/packages/reactter/pubspec.lock +++ b/packages/reactter/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a url: "https://pub.dev" source: hosted - version: "67.0.0" + version: "61.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 url: "https://pub.dev" source: hosted - version: "6.4.1" + version: "5.13.0" args: dependency: transitive description: @@ -61,10 +61,10 @@ packages: dependency: transitive description: name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.19.0" + version: "1.17.1" convert: dependency: transitive description: @@ -77,10 +77,10 @@ packages: dependency: transitive description: name: coverage - sha256: "3945034e86ea203af7a056d98e98e42a5518fff200d6e8e6647e1886b07e936e" + sha256: "595a29b55ce82d53398e1bcc2cba525d7bd7c59faeb2d2540e9d42c390cfeeeb" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.6.4" crypto: dependency: transitive description: @@ -101,10 +101,10 @@ packages: dependency: transitive description: name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 url: "https://pub.dev" source: hosted - version: "7.0.0" + version: "7.0.1" flutter: dependency: transitive description: flutter @@ -119,10 +119,10 @@ packages: dependency: transitive description: name: frontend_server_client - sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "3.2.0" glob: dependency: transitive description: @@ -163,38 +163,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" - url: "https://pub.dev" - source: hosted - version: "10.0.7" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" - url: "https://pub.dev" - source: hosted - version: "3.0.8" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" lints: dependency: "direct dev" description: name: lints - sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "2.1.1" logging: dependency: transitive description: @@ -207,26 +183,26 @@ packages: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.15" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.2.0" meta: dependency: "direct main" description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.9.1" mime: dependency: transitive description: @@ -255,10 +231,10 @@ packages: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.8.3" pool: dependency: transitive description: @@ -303,15 +279,15 @@ packages: dependency: transitive description: name: shelf_web_socket - sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "1.0.4" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" source_map_stack_trace: dependency: transitive description: @@ -332,34 +308,34 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.9.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.2.0" term_glyph: dependency: transitive description: @@ -372,26 +348,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "713a8789d62f3233c46b4a90b174737b2c04cb6ae4500f2aa8b1be8f03f5e67f" + sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4" url: "https://pub.dev" source: hosted - version: "1.25.8" + version: "1.24.1" test_api: dependency: transitive description: name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "0.5.1" test_core: dependency: transitive description: name: test_core - sha256: "12391302411737c176b0b5d6491f466b0dd56d4763e347b6714efbaa74d7953d" + sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93" url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.5.1" typed_data: dependency: transitive description: @@ -412,10 +388,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b + sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 url: "https://pub.dev" source: hosted - version: "14.3.0" + version: "11.10.0" watcher: dependency: transitive description: @@ -449,5 +425,4 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.6.0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + dart: ">=3.0.0 <4.0.0" diff --git a/packages/reactter/pubspec.yaml b/packages/reactter/pubspec.yaml index 9483e778..eb151851 100644 --- a/packages/reactter/pubspec.yaml +++ b/packages/reactter/pubspec.yaml @@ -4,18 +4,24 @@ homepage: https://2devs-team.github.io/reactter/ documentation: https://2devs-team.github.io/reactter/ repository: https://github.com/2devs-team/reactter/tree/master/packages/reactter license: MIT License -version: 8.0.0-dev.5 +version: 8.0.0-dev.13 + +topics: + - reactive + - state + - dependency + - event environment: - sdk: ">=2.14.0 <4.0.0" + sdk: ">=2.19.2 <4.0.0" dependencies: meta: ^1.7.0 dev_dependencies: fake_async: ^1.3.1 - lints: ^5.1.1 - test: ^1.25.2 + lints: ^2.0.1 + test: ^1.24.0 flutter_test: sdk: flutter diff --git a/packages/reactter_devtools_extension/pubspec.lock b/packages/reactter_devtools_extension/pubspec.lock index 01cc07bd..ff734f3c 100644 --- a/packages/reactter_devtools_extension/pubspec.lock +++ b/packages/reactter_devtools_extension/pubspec.lock @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" crypto: dependency: transitive description: @@ -129,10 +129,11 @@ packages: flutter_reactter: dependency: "direct main" description: - path: "../flutter_reactter" - relative: true - source: path - version: "7.3.1" + name: flutter_reactter + sha256: "9a6c6a67bb39245b2ef183685e859c8d3664b6e474d315bb36d1e8c6b37bc966" + url: "https://pub.dev" + source: hosted + version: "8.0.0-dev.8" flutter_test: dependency: "direct dev" description: flutter @@ -190,18 +191,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" url: "https://pub.dev" source: hosted - version: "10.0.5" + version: "10.0.7" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.8" leak_tracker_testing: dependency: transitive description: @@ -285,10 +286,11 @@ packages: reactter: dependency: transitive description: - path: "../reactter" - relative: true - source: path - version: "7.3.0" + name: reactter + sha256: "2d0df33feb1d79b077e3a664ad22d87a94a20c33415bc67e63b9f61d2e20a39d" + url: "https://pub.dev" + source: hosted + version: "8.0.0-dev.8" shelf: dependency: transitive description: @@ -301,7 +303,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: @@ -322,10 +324,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.0" stream_channel: dependency: transitive description: @@ -338,10 +340,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" term_glyph: dependency: transitive description: @@ -354,10 +356,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.7.3" typed_data: dependency: transitive description: @@ -394,10 +396,10 @@ packages: dependency: "direct main" description: name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "14.3.0" web: dependency: transitive description: @@ -439,5 +441,5 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=3.4.0-282.1.beta <4.0.0" + dart: ">=3.4.0 <4.0.0" flutter: ">=3.22.0-0.1.pre" diff --git a/packages/reactter_devtools_extension/pubspec.yaml b/packages/reactter_devtools_extension/pubspec.yaml index ea7ae01d..ef025406 100644 --- a/packages/reactter_devtools_extension/pubspec.yaml +++ b/packages/reactter_devtools_extension/pubspec.yaml @@ -13,8 +13,7 @@ dependencies: devtools_app_shared: ^0.1.1 vm_service: ^14.2.5 queue: ^3.1.0+2 - flutter_reactter: - path: ../flutter_reactter + flutter_reactter: ^8.0.0-dev.14 dev_dependencies: flutter_test: From 111275d1ddd501f9d3fdcade4afe3d40949c9374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Sat, 4 Jan 2025 00:57:17 -0600 Subject: [PATCH 086/141] refactor(signal): Simplify arithmetic operators to accept `num` directly instead of `Signal`. --- .../lib/src/signal/extensions/signal_num.dart | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/packages/reactter/lib/src/signal/extensions/signal_num.dart b/packages/reactter/lib/src/signal/extensions/signal_num.dart index cb144c0a..082b66a6 100644 --- a/packages/reactter/lib/src/signal/extensions/signal_num.dart +++ b/packages/reactter/lib/src/signal/extensions/signal_num.dart @@ -4,24 +4,24 @@ part of '../signal.dart'; extension SignalNumExt on Signal { /// Adds [other] to this number. /// - /// The result is an [Signal] of [double], as described by [double.+], - /// if both `this` and [other] is an [Signal] of [double], - /// otherwise the result is a [Signal] of [int]. - Signal operator +(Signal other) => Signal(value + other.value); + /// The result is a [double], as described by [double.+], + /// if both `this` and [other] is a [double], + /// otherwise the result is an [int]. + num operator +(num other) => value + other; /// Subtracts [other] from this number. /// - /// The result is an [Signal] of [double], as described by [double.-], - /// if both `this` and [other] is an [Signal] of [double], - /// otherwise the result is a [Signal] of [int]. - Signal operator -(Signal other) => Signal(value - other.value); + /// The result is a [double], as described by [double.-], + /// if both `this` and [other] is a [double], + /// otherwise the result is a [int]. + num operator -(num other) => value - other; /// Multiplies this number by [other]. /// - /// The result is an [Signal] of [double], as described by [double.*], - /// if both `this` and [other] is an [Signal] of [double], - /// otherwise the result is a [Signal] of [int]. - Signal operator *(Signal other) => Signal(value * other.value); + /// The result is a [double], as described by [double.*], + /// if both `this` and [other] is a [double], + /// otherwise the result is an [int]. + num operator *(num other) => value * other; /// Euclidean modulo of this number by [other]. /// @@ -38,21 +38,21 @@ extension SignalNumExt on Signal { /// /// See [remainder] for the remainder of the truncating division. /// - /// The result is an [Signal] of [double], as described by [double.%], - /// if both `this` and [other] are [Signal] of [double], - /// otherwise the result is a [Signal] of [int]. + /// The result is a [double], as described by [double.%], + /// if both `this` and [other] are [double], + /// otherwise the result is an [int]. /// /// Example: /// ```dart - /// print(Signal(5) % Signal(3)); // Signal(2) - /// print(Signal(-5) % Signal(3)); // Signal(1) - /// print(Signal(5) % Signal(-3)); // Signal(2) - /// print(Signal(-5) % Signal(-3)); // Signal(1) + /// print(Signal(5) % 3); // 2 + /// print(Signal(-5) % 3); // 1 + /// print(Signal(5) % -3); // 2 + /// print(Signal(-5) % -3); // 1 /// ``` - Signal operator %(Signal other) => Signal(value % other.value); + num operator %(num other) => value % other; /// Divides this number by [other]. - Signal operator /(Signal other) => Signal(value / other.value); + double operator /(num other) => value / other; /// Truncating division operator. /// @@ -69,7 +69,7 @@ extension SignalNumExt on Signal { /// Then `a ~/ b` is equivalent to `(a / b).truncate()`. /// This means that the intermediate result of the double division /// must be a finite integer (not an infinity or [double.nan]). - Signal operator ~/(Signal other) => Signal(value ~/ other.value); + int operator ~/(num other) => value ~/ other; /// The negation of this value. /// @@ -87,35 +87,35 @@ extension SignalNumExt on Signal { /// /// (Both properties generally also hold for the other type, /// but with a few edge case exceptions). - Signal operator -() => Signal(-value); + num operator -() => -value; /// Whether this number is numerically smaller than [other]. /// /// Returns `true` if this number is smaller than [other]. /// Returns `false` if this number is greater than or equal to [other] /// or if either value is a NaN value like [double.nan]. - bool operator <(Signal other) => value < other.value; + bool operator <(num other) => value < other; /// Whether this number is numerically smaller than or equal to [other]. /// /// Returns `true` if this number is smaller than or equal to [other]. /// Returns `false` if this number is greater than [other] /// or if either value is a NaN value like [double.nan]. - bool operator <=(Signal other) => value <= other.value; + bool operator <=(num other) => value <= other; /// Whether this number is numerically greater than [other]. /// /// Returns `true` if this number is greater than [other]. /// Returns `false` if this number is smaller than or equal to [other] /// or if either value is a NaN value like [double.nan]. - bool operator >(Signal other) => value > other.value; + bool operator >(num other) => value > other; /// Whether this number is numerically greater than or equal to [other]. /// /// Returns `true` if this number is greater than or equal to [other]. /// Returns `false` if this number is smaller than [other] /// or if either value is a NaN value like [double.nan]. - bool operator >=(Signal other) => value >= other.value; + bool operator >=(num other) => value >= other; /// Compares this to `other`. /// From 458d537dd6619235f4faa47b6c8b90de940a62ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Sat, 4 Jan 2025 02:11:15 -0600 Subject: [PATCH 087/141] build: Bump version to 8.0.0-dev.15. --- CHANGELOG.md | 96 +++++++++--------- .../doc/screenshots/devtools.png | 1 + .../doc/screenshots/example_app.png | 1 + .../flutter_reactter/example/pubspec.lock | 22 ++-- .../flutter_reactter/example/pubspec.yaml | 6 +- packages/flutter_reactter/pubspec.lock | 4 +- packages/flutter_reactter/pubspec.yaml | 4 +- .../reactter/doc/screenshots/devtools.png | 1 + .../reactter/doc/screenshots/example_app.png | 1 + packages/reactter/pubspec.yaml | 9 +- screenshots/devtools.png | Bin 0 -> 413167 bytes screenshots/example_app.png | Bin 0 -> 259584 bytes 12 files changed, 78 insertions(+), 67 deletions(-) create mode 120000 packages/flutter_reactter/doc/screenshots/devtools.png create mode 120000 packages/flutter_reactter/doc/screenshots/example_app.png create mode 120000 packages/reactter/doc/screenshots/devtools.png create mode 120000 packages/reactter/doc/screenshots/example_app.png create mode 100644 screenshots/devtools.png create mode 100644 screenshots/example_app.png diff --git a/CHANGELOG.md b/CHANGELOG.md index da2a11c6..12bee5bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,68 +1,68 @@ # Reactter -## 8.0.0-dev.14 +## 8.0.0-dev.15 ### Enhancements - Add Reactter devtools. -- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtInterface/removeObserver.html) methods to manage the observers. -- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). -- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). -- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. -- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. -- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtStateBase/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtStateBase/debugInfo.html) to states and hooks to get info for debugging. -- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. -- Add [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtStateBase-class.html) abstract class to implement the base logic of the state. -- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtInterface/createState.html) method to create a new state. -- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. -- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. -- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. +- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtInterface/removeObserver.html) methods to manage the observers. +- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). +- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). +- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. +- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. +- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtStateBase/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtStateBase/debugInfo.html) to states and hooks to get info for debugging. +- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. +- Add [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtStateBase-class.html) abstract class to implement the base logic of the state. +- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtInterface/createState.html) method to create a new state. +- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. +- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. +- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. - Enhance event handling and notifier logic for improved stability and performance. - Enhance state management and error handling. -- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.14/flutter_reactter/RtComponent-class.html). +- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.15/flutter_reactter/RtComponent-class.html). - Enhance dependency management to ensure to re-build when is detected changes while building. ### Breakings -- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/Rt.html) instead. -- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtStateBase-class.html) instead. +- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/Rt.html) instead. +- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtStateBase-class.html) instead. - Replace [`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html) to `UseAsyncStateStatus.idle`. -- Move `asyncFunction` to the first argument of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/UseAsyncState/withArg.html). -- Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. -- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtInterface/isActive.html) instead. -- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtInterface/getDependencyMode.html) instead. -- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/DependencyMode.html) instead. -- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/Lifecycle.html) instead. -- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/Lifecycle.html) instead. -- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`LifecycleObserver.onCreated`](https://pub.dev/documentation/reactter/8.0.0-dev.14//reactter/LifecycleObserver/onCreated.html) instead. -- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.14//reactter/RtDependency-class.html) instead. -- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0-dev.14//reactter/RtHook-class.html) instead. -- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0-dev.14//reactter/RtInterface-class.html) instead. -- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtState/notify.html) instead. -- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/UseDependency-class.html) instead. -- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtAction-class.html) instead. -- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtActionCallable-class.html) instead. -- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MemoMultiInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/MemoMultiInterceptor-class.html) instead. -- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/MemoSafeAsyncInterceptor-class.html) instead. -- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/MemoTemporaryCacheInterceptor-class.html) instead. -- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/MemoWrapperInterceptor-class.html) instead. +- Move `asyncFunction` to the first argument of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/UseAsyncState/withArg.html). +- Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. +- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtInterface/isActive.html) instead. +- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtInterface/getDependencyMode.html) instead. +- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/DependencyMode.html) instead. +- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/Lifecycle.html) instead. +- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/Lifecycle.html) instead. +- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`LifecycleObserver.onCreated`](https://pub.dev/documentation/reactter/8.0.0-dev.15//reactter/LifecycleObserver/onCreated.html) instead. +- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.15//reactter/RtDependency-class.html) instead. +- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0-dev.15//reactter/RtHook-class.html) instead. +- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0-dev.15//reactter/RtInterface-class.html) instead. +- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtState/notify.html) instead. +- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/UseDependency-class.html) instead. +- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtAction-class.html) instead. +- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtActionCallable-class.html) instead. +- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MemoMultiInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/MemoMultiInterceptor-class.html) instead. +- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/MemoSafeAsyncInterceptor-class.html) instead. +- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/MemoTemporaryCacheInterceptor-class.html) instead. +- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/MemoWrapperInterceptor-class.html) instead. - Remove [`Obj`](https://pub.dev/documentation/reactter/7.3.0/reactter/Obj-class.html). -- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.14/flutter_reactter/RtProvider/RtProvider.init.html) instead. -- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.14/flutter_reactter/RtComponent-class.html) instead. -- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.14/flutter_reactter/RtConsumer-class.html) instead. -- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.14/flutter_reactter/RtProvider-class.html) instead. -- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.14/flutter_reactter/RtMultiProvider-class.html) instead. -- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.14/flutter_reactter/RtScope-class.html) instead. -- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.14/flutter_reactter/RtSignalWatcher-class.html) instead. -- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.14/flutter_reactter/RtDependencyNotFoundException-class.html) instead. -- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.14/flutter_reactter/RtScopeNotFoundException-class.html) instead. +- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.15/flutter_reactter/RtProvider/RtProvider.init.html) instead. +- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.15/flutter_reactter/RtComponent-class.html) instead. +- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.15/flutter_reactter/RtConsumer-class.html) instead. +- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.15/flutter_reactter/RtProvider-class.html) instead. +- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.15/flutter_reactter/RtMultiProvider-class.html) instead. +- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.15/flutter_reactter/RtScope-class.html) instead. +- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.15/flutter_reactter/RtSignalWatcher-class.html) instead. +- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.15/flutter_reactter/RtDependencyNotFoundException-class.html) instead. +- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.15/flutter_reactter/RtScopeNotFoundException-class.html) instead. ## Fixes -- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/UseEffect-class.html) causing dependencies to not be unwatched. -- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtInterface/register.html) method. -- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/RtInterface/batch.html) method to work correctly. -- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0-dev.14/reactter/Notifier-class.html) class. +- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/UseEffect-class.html) causing dependencies to not be unwatched. +- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtInterface/register.html) method. +- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtInterface/batch.html) method to work correctly. +- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/Notifier-class.html) class. - Fix to propagate state param to `boundInstance`. - Remove listeners correctly from single-use listeners and handle null cases. diff --git a/packages/flutter_reactter/doc/screenshots/devtools.png b/packages/flutter_reactter/doc/screenshots/devtools.png new file mode 120000 index 00000000..9907c222 --- /dev/null +++ b/packages/flutter_reactter/doc/screenshots/devtools.png @@ -0,0 +1 @@ +/Users/leon/projects/reactter/screenshots/devtools.png \ No newline at end of file diff --git a/packages/flutter_reactter/doc/screenshots/example_app.png b/packages/flutter_reactter/doc/screenshots/example_app.png new file mode 120000 index 00000000..3a018387 --- /dev/null +++ b/packages/flutter_reactter/doc/screenshots/example_app.png @@ -0,0 +1 @@ +/Users/leon/projects/reactter/screenshots/example_app.png \ No newline at end of file diff --git a/packages/flutter_reactter/example/pubspec.lock b/packages/flutter_reactter/example/pubspec.lock index 6305f768..cbcef412 100644 --- a/packages/flutter_reactter/example/pubspec.lock +++ b/packages/flutter_reactter/example/pubspec.lock @@ -117,26 +117,26 @@ packages: dependency: "direct dev" description: name: custom_lint - sha256: b09a939c3bb062fdf072aa4c8eb5447231338854c4fefb23e4c9f7cef41cb3ff + sha256: "22bd87a362f433ba6aae127a7bac2838645270737f3721b180916d7c5946cb5d" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.5.11" custom_lint_builder: dependency: transitive description: name: custom_lint_builder - sha256: f1430048ccddcd82cbf7722fce7701530fd535b82e91f45b0c81ed07814143bf + sha256: "0d48e002438950f9582e574ef806b2bea5719d8d14c0f9f754fbad729bcf3b19" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.5.14" custom_lint_core: dependency: transitive description: name: custom_lint_core - sha256: a39e98cba1d96076e2e5c96aad582a776909c78b4d7480d0efdcc3791b225c1b + sha256: "2952837953022de610dacb464f045594854ced6506ac7f76af28d4a6490e189b" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.5.14" dart_style: dependency: transitive description: @@ -170,10 +170,10 @@ packages: dependency: "direct main" description: name: flutter_reactter - sha256: "349458f417688f8fd22ec8b7ec3f39389eb9992aff9e9fb781151985c060ccac" + sha256: "24f56d3e520d978e20fb39e20fcca9365a4eb16eaa0eaedf6827855f996c2814" url: "https://pub.dev" source: hosted - version: "8.0.0-dev.12" + version: "8.0.0-dev.14" flutter_web_plugins: dependency: transitive description: flutter @@ -327,10 +327,10 @@ packages: dependency: transitive description: name: reactter - sha256: b7aea3400aa8dfd282c98e159ccb9bfd2ae321010e75210f02f40616801876d6 + sha256: c2a9f823b1570f42842aed54f4765dc86b294ce7ea8e6004c3772de1354feca2 url: "https://pub.dev" source: hosted - version: "8.0.0-dev.11" + version: "8.0.0-dev.13" reactter_lint: dependency: "direct dev" description: @@ -521,5 +521,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.0.0-0 <4.0.0" + dart: ">=3.0.0 <4.0.0" flutter: ">=3.7.0" diff --git a/packages/flutter_reactter/example/pubspec.yaml b/packages/flutter_reactter/example/pubspec.yaml index 926fbf4e..1d218903 100644 --- a/packages/flutter_reactter/example/pubspec.yaml +++ b/packages/flutter_reactter/example/pubspec.yaml @@ -12,11 +12,11 @@ dependencies: http: ^0.13.6 url_launcher: ^6.1.2 intl: ^0.17.0 - flutter_reactter: ^8.0.0-dev.14 + flutter_reactter: ^8.0.0-dev.15 dev_dependencies: - custom_lint: ^0.5.0 - flutter_lints: ^2.0.2 + custom_lint: ^0.5.11 + flutter_lints: ^2.0.3 reactter_lint: ^1.0.0 flutter: diff --git a/packages/flutter_reactter/pubspec.lock b/packages/flutter_reactter/pubspec.lock index f491e900..251f9db1 100644 --- a/packages/flutter_reactter/pubspec.lock +++ b/packages/flutter_reactter/pubspec.lock @@ -119,10 +119,10 @@ packages: dependency: "direct main" description: name: reactter - sha256: c2a9f823b1570f42842aed54f4765dc86b294ce7ea8e6004c3772de1354feca2 + sha256: "69fc36ffc793fcb17bbea5fc498f7de72210694fbafea011ff3aca2158b1792e" url: "https://pub.dev" source: hosted - version: "8.0.0-dev.13" + version: "8.0.0-dev.15" sky_engine: dependency: transitive description: flutter diff --git a/packages/flutter_reactter/pubspec.yaml b/packages/flutter_reactter/pubspec.yaml index 79654e74..1c152595 100644 --- a/packages/flutter_reactter/pubspec.yaml +++ b/packages/flutter_reactter/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://2devs-team.github.io/reactter/ documentation: https://2devs-team.github.io/reactter/ repository: https://github.com/2devs-team/reactter/tree/master/packages/flutter_reactter license: MIT License -version: 8.0.0-dev.14 +version: 8.0.0-dev.15 topics: - reactive @@ -20,7 +20,7 @@ dependencies: flutter: sdk: flutter meta: ^1.7.0 - reactter: ^8.0.0-dev.13 + reactter: ^8.0.0-dev.15 dev_dependencies: flutter_test: diff --git a/packages/reactter/doc/screenshots/devtools.png b/packages/reactter/doc/screenshots/devtools.png new file mode 120000 index 00000000..9907c222 --- /dev/null +++ b/packages/reactter/doc/screenshots/devtools.png @@ -0,0 +1 @@ +/Users/leon/projects/reactter/screenshots/devtools.png \ No newline at end of file diff --git a/packages/reactter/doc/screenshots/example_app.png b/packages/reactter/doc/screenshots/example_app.png new file mode 120000 index 00000000..3a018387 --- /dev/null +++ b/packages/reactter/doc/screenshots/example_app.png @@ -0,0 +1 @@ +/Users/leon/projects/reactter/screenshots/example_app.png \ No newline at end of file diff --git a/packages/reactter/pubspec.yaml b/packages/reactter/pubspec.yaml index eb151851..9caeebe5 100644 --- a/packages/reactter/pubspec.yaml +++ b/packages/reactter/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://2devs-team.github.io/reactter/ documentation: https://2devs-team.github.io/reactter/ repository: https://github.com/2devs-team/reactter/tree/master/packages/reactter license: MIT License -version: 8.0.0-dev.13 +version: 8.0.0-dev.15 topics: - reactive @@ -12,6 +12,13 @@ topics: - dependency - event +screenshots: + - description: "Reactter DevTools Extension" + path: doc/screenshots/devtools.png + - description: "Reactter Example App" + path: doc/screenshots/example_app.png + + environment: sdk: ">=2.19.2 <4.0.0" diff --git a/screenshots/devtools.png b/screenshots/devtools.png new file mode 100644 index 0000000000000000000000000000000000000000..2130726f6b8bf6ebc37b443d1c12cad7181a041b GIT binary patch literal 413167 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 z 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

38A^>wCf`=$hgIBH`2 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

_`PcMv=4*XU7hn5 zR}(Kivxz1lwfRax7P_gABeHk$&I@qlqXf_4Y7^zwVt>P<{-;egF~(2r$K|LP>QQgo zG1+T?mJvaPdx|@Uwh~)quLN#sW)G(l&1uBMTA-PH@wWLg*g_ ztEyB3DHI&cP;i`EKbc#1y3f~|+@4Y36tH7~f}`995hz4?XRhiIZoJg&c!t#X_G9uK zTt7Z9oA{B;ycZ!LkcY~l+*m6em&ophm1Mc8p*d!Iuw4EXXd(c(Ms#MQpaq+RvIm<` z01+SIOQK4TxAEYxM>12Qm{t~%5Z=jtw!{lSKoLO0Sr5FU3StQ>aK{l2Z%BiQNWa~4 zcUo1!-~MEPy632kutekBdWLiUD<6AgJE}a1)<9S2PE@4%tLm~8Wy+NUh8}0bQsy@C(SZBQ}d8JV2D+^Q+X}D-}J#2S}QToenbyHr~9w)%x~4@_Vc28}{q!JVva>PaZ7xU{lWdS*sGJ zaxJSDR+(>;lh4b|vtjYv)a+TDewaSeDU_gcz30BW(2vj94%N21*&)q4v0_B|aELf) zg-9Y(*rdwhCJFlW^AoNfH=?Dhs9*SGd;r{%7Dq4j($EL0_rr@gKRb3ZNh7pJ^24X90jH9?zE!$ot_ z(9m=(h`#S~dI8Cf@-qA~%7+-=#yNQ{j>D~;ZU`Q)C(iHzg~D@FE{uVtoCW{s) zW9b(d=?H)C`U^)qv6G2j{=_DY>4Y(YuJHUmUqc{}j*^y+fTfbw*%Ge9z1p3FF`0MJ}1-+o79wPhRhyZO}Iz!W<#){mcCyZB?Ocm>mJrKzi{ z{82moRd$WpZbWA3IWq759td&JuHmYQLjF-PDMj#X=I4zG1Fo}0hBhrg8A+7DT%>&}awDM}@*tJ(8_2iXiwB4r8$e^yyux5I zy&2tP1>kLu$2!<3TDKLOEVd75SBLkMkMH!~{I&3d`OA6WszkP#q0rp%UhkQMsL|ck zt0zNX_>PR#Qg=k5ohJ*qExL77YrM&zg7(4NNsPpAu;52Idc!rkzyAqDFwP;-zS;4r zw?h!q+Wu_m&O*9JhG@e2Lp`E=y>$tI+TCN+_k~SX>4tgC$HwQwxW4$rVV%*}NT)MF z)8z2T!z3Ul2iM@(P(fn?p!SvTVR@4shkp)tS8D8Q?CkCaaZoBtuli+5VcS#7`%#}- z?9Aks8>?`vjArO4&7pzo^YnoZVv85LIo#I9nk${DxM=l?b4&fQgPKlu+PQ%ylR~|U zePiBWf94V`Bkp!?1v0JSl6AQ+DK@VRUwVWVr$mXf42Rnl8aCnvt2GMc$_FDOc4Os6 zG~&=foR0~sv>1fnV>&lmM>P9AKMe?Xw522ds9vZYjX0m>Q;OR&%8a>Yf8%Y`KT}uc zg@BI4-0K_k`h*LG+$a4bcRdPaS4if5Uel?BxRU+8-N{3ian35EZMA{H2J))bF>Z zGg5+(p<;|ZJvMr?b$}G z%7Qv2LMtbb;F?$*v&)xJ7)dMZfe|1H>jc9QzqxmTH{mE92nQX}&@$?PmU~F{fCFG- z;1I9F$wEVaFbA2%mf=(*28cS-6)zn%#l){Q@P<&iK`yg&VVGD~j(c6WMQWAu7a-8E zQW91am#G0{FyG9m8K+rj2_`d$^> zgK4uVFqOtjR8~)>5~ZTGWP(Ewh__LOrwIm~2|=b{riU0nGwRhe@_gPK>qZt$s6ud# z4CH-q8EVeU$jlsTRJzNsH*X*78F}qwYpS^trl}46lRbWU;FZbXR#$hf23iF^X6ke4 zECU6s{c{HoABHZx{8fCsY~e*9bH`a^=YqLDyFpi z)$JLoXGtg(;u~veZ%kK-C2-Fb>CyHhk@GTrkOqhb5D&!0ddRsg*yQ?f>T~^^ttDP% z{7d0tn|Y1b%gd*%V|#O%MKJ6@0)o4Vn_D3Gl<)R~OgynL2bb%GPI}c^Tp6g1!{|a` zu7o&lk5<{%tJ!?~CmNR_-Ovz){9K8?^N9;}$GgS(`5;=m=la*ByXzl=D^r0N{AN|` z*OK?mMf7H^!_xyTVmgm}3l1?$nKFG-Xb{N&0Jx;Lzafw=kqkoRe~ybo1Ns4)vq)jo zYON5|DOsMP0zOHs=QDV6rg}g^^_hYJX|cAreR>cQ)b1cp-(sgM9TW^L=-$eWm|*=@ zuI1?d+7z-}n1Uf52Pll3p^^-Fw;2E=cu48@4_vP5$K)<^bQ*y;9C8wp;^eB-7J)$_ z{GSJZSwAii0_FHX=5|80*LC8j9_i_{_D&Fke%jjF6eIX}9r7apjBfjw#!y~-9H<-S zg=+zM3v!`dz;!E|gb?l5pPu#OatGFxo|VyDp7Q?Hxj32X8d+XoHSfkW@kV}fKbEbH z-VkK&M7@yweE|qQw_ctlYfQ(-7Y|X;gLyr2J74#%$mU?WDn}I={`$2X-be(h*j`@V ze`1`!%mMz5B%);3XGy0`hY2YrCfcvqv0xO? z@+7LQ&M7JB^EK3PkJFjgUyw-Tk?ttEzId9nAaNI4%v!tXP1ld^r?6c_%34PH! zU5F~f!2#ebPU;nFcD)yD659qQ5{6Hsn^7OlpAIahG*mHVW0=SQL+N~*_C4CD!!rOw zE#Ch8=$8^EM;UmvA+xs>m0pb54j7Y}e0yUcekKnroXJeIgUI~c9B#s9xC*z27S&b^hapYXIz{+Gxpl9t0bC+dq-3Z!ZJW!(B-?rm4ZSDA$p4{ck>c zppL_xvfRO@bQ5&Zi&?y4{DW}){P!Yhp8=R`=1yDy7$j@FyCCnsrl?%?fh*|SI{`s6 z%@{*MB~ZAFW)kMg(}8uNbfG1?6K3dW@%~J+4EOaEXHq}l@09k{p9lpnJPPQ@3k8g^ z{^t|GuwNnET)&vM#tg7F8c96<-*@u+E8wUnjr{YwJj}#^_|~lDZPlxPuIKSg^HSw(y8Zw9vsSr*ES%@bVrAnag$Iwozn%nedwnx6f!WNWU=Z;?cdT#g&65?6nC*33|K6qE`87Rd z>i^#F%Y1+s$``^&zJKlL=a84920yRLcF-`Lu<$VVk}V(S-%si@*fSPj0uOdq2_=6V z)9-Cqy~lrLIzq^F}hGf8NcPnip+*-&(5w-A4AHhaUp`Q zd4D|gx|vWiApG;vfO(C-+*9Tui3;35zxZPN636-4k^i|T1w*Pr2a>BQ|K6ww!mYr4 zxwg?BM)hK?RNVX57C7dFYnU&w;+Q=;-#;g?zPx!d0@l1OJ!Da#{^$L+UGu%XM8^WM zP?QQLYMm?$h9k0z2-PA9oeHh)0tYRDu7UI*zg5&{Rs5?yh=2Z9U=JjKQq_6p5rqos zKfm~5`+NMvj`YtSpwss@n z*McpyzhBAM3Y1ma)h;mWeO2|A@1`Ysi}-U6v%1@M$GqyEZk-hyBys+Cy%zwrn_=Uw z=D*jY|82aye~dSR)au3QhVYk)%zryxb!o>qq1bYD^pp5sLJ8g%pXRr$+Q9P93wazT zy+D?h(~3|OD;8ROEwty7RAF5%sxZN=OwOSf`}*3#Z{Urg&jf~McY>jFY5)BKulf-H zcL`TtZ2fm3;%{F$@BMq#y#KDMj1?(Mq9@JRxlT`i;<&+|1iO;@36UWNLf_lHzMfSe z6wO04kr^mPqoSAJs?kl9s3^_NFKEu6AwOpi<`@zF?Qp^gfv490u?Waa;9m^)kJHQw z04`|I5p^Z@d!rw#)C^`WtNw2D5R)ucLYzGq03AN{-wLR42@3T zI>$@&t)b+5eC3ZS;C~yVhnc{OTlP&MBKoI){0#O|_c8PQ&t~)~D>hr8j}-Xhg`D}b zF0Vj#m2!#~CYSz)Iq1)Lzf_4xq1;Gh^4spRJN(quO(z|wfK90&OWd$z+!<`gFxEx9 zj~GwcKSP;l(i0&KqsrZzqp_{4tKmOkg~j43G$-$PSvBG`Bu| zXz^i#gM*7AQviDpht(59sl;$*3II9;217G{6gaEIDb4e!jY7QOJ)fTlsgf2#por}Z zwNsA!{rwu*fuT&F$M>+B`!mJystbJ)ya?#%zCZ&QLJ8<9d>u}h2CY}V!o^KDJlrpH z^UnSr%`;!21CMk8d4H9Ca4OeeEhJr{EcDv)ePa-bhj>Kwlc&NH(CT$oivLQKqJrP{Y6g zl9H0LMlRj{;f7Yr0)2e^IeDe{1Bvu!AZ+k^aRG46%>d`pKaaVUmm{8`>Fa-7KzMyR z(`0&T=jZ8iAo*BcKIvTM%7@$Ygo-Skqnn51KSNsw3K$R&L7m&9 zr!@5A9`zBSdF&2{i0?+AqErf-r{$8=WFg8e)P5B0(<>iEmr0p*3Y2rjOrUpGaKuwM z+Pj2~;wuPYYAP)Fo@vP=2D9VluE*fHyDBh-iMC{EXjYHIsO$Q=l1vI2=~EewTTj`n zx)tOVSv7hS`dREW8;!pMbn-X$_wGTYT0NnNYraLiY+`j^=rlUe0BWC4ZVwj9!3Xn| ziSRI6Bgv(jq;{sn(s@6yf$5>Pa1|1z8I$CwB z09}y;k@h2j_eT>VqI0e1C$BnJP6u2;MKwox^G1Mm+an?ZS4j|!TZYy3{?HvhCF>o4 zh`2ajd7qP$;~yOCnVO0-G$i7~a=tl;iraMRBE@yXcnRRu9L^gcEQVKBa86H8%W7-2 z?$e+Ey2B#H>X%Qh3PYVpvuDjvtWdsNe3fJ~5TX8^!69bmtIlha58#Vvn(7#WJNx7*x}hmgJ#> z-9l_e7vc{{RHLS6Zf(mjmF9<~nH{gLCw9l2J`_wJx?{3hs&6llia^gtaILS@*?s4M zRZ~=}<pYrEOmg$Z+uYh92r*V<|J(Umsy1a%_M}P8URv=ShImqN2X9 z%WLD52delYBEdA48|GW{=^=-Ub~K}*xtbiPLU$LtFh)k?m|V`%L<4{|1kftiu{fMD zjHGau*0sEY*#nq{3q(ZxJ!-ExP7Cz4eR@JLG5{e%GN$NzlGu8h%^vxl?rzP=oMQV+ zvaisacnYyp$v@J*xGpt0D0Q#r3CD(#NM@DMAR{4l@iAf%Ma5;4Y-oGG1+?lZQc|!z zYk`5o%}rt@Q(#2>9G;BSo6c3`CvnJz3G$~;0jyTe+F*)6`!w#owCI~RZ#2)hOl~fA zBg@Qmq7eab3({;aXJ{xEd&EK0)bHb~&-CR)Gi4Xp|8vIu{`e;1B|`!I8u~j95o{Qq zrZWxw(8D;v0K}h3g$Uc`L~B0+jc%8HlFkQ{HO%t;U<6%gjVd2V9b7g3%sSi)q=Wox z1C1Ufd@#G;8s>!~147K`j5OAL>b8*&$O0kQnGw;lg|i3%il2VeL!Tmk9#I@eS*n~} zze6$VCLd+Fr70|4ODV6)Z4?=pY1-P_l5a82uk(Haf0t(;oOQWSN7Ly8M@?-8R6ZcX zP_YdUFLvo~&c*9|bejmXgA~h{{3b%SJ$IGMlfuKiJ`;I=nQbIXnTf*gOtsXY8YS%f~wygq&a}+EaL4 zQitCUa;l@lQ{|kdnnOEClI;^Go5#2>OycRoEmSs&`cPa%2fN;{h7kMPv*-+yn-kpC zW!1eonIieu!wHN+v*<{^`F}m3(3fBo%M6w>FX`-Z6Hd}4R2J}( zLb3jy(eyWTh1l%Yk?qTu5P$+-n8Rz0#2tdJMnJxQE55>~x#{Yb)PPdT5uLGA--69H zT`Pd?1ioXi?e1nyF|q{Rvr%1e%UV)27?jk*b~1m9Qg|guYW&UT9dZ>-P7ppYCNr+< zNu3}w-R_C&kgYp!#C@#C;_?`j5xTt`zcdIvEv(eWluI?|bpoNCz6j`#d-N`d`v>SC zKXaR!XgUyI%)1lxhDPGZ%&63Rqr9sw7Z`8qp_$NPC7xR_<%I1FhVigpPwo0bms>qx z*j^paNVNi^wJZ=Y%UKdcd&gkc^$QiGKU^ZmW`C4ao@^C#w|UQ1!CB7xx`wzR*i8K! z=2a@3`)Fv_g8%@{4~Yc`LDzni$gPE}Hq&F7AM*oX8Cd?by{m7X+NDR4MCl^&6D$^I z1W?0OiZeapk{>C1hL^x&J$1~t=5!AfnMG-B{d)zF$Q59&O;dX(;+v5!2Yf!rBxYWh zy%jkKXdk2Mj!NXK*Y%=W^$f3e!QR>3!{@+5qfTAwSsqqBId{pQ;KtuiYS}<4I!r2e z9S3APWRSZDXWa7x2sPKWPL4Lg57d`=%+!b+`zhxvG2Y$vkXfCd7;+3HoY5{Cp&-hB zP+u{-kFO0{auTRlo3N}kRr!(kW5Jd9^h9S#OW>D8p=^k>4<^-=dHdG&N?5>&$$;&} z#l`9o*H`Z^iL{-<<1Zj<=>q8pNIttMM>inqgc=!fC^MQZt#i6=(Zr;#Yq;_b3Zh4$ zRvqB+0A_s7uCm^@dg$rs5ORcrfq7~Vqo8bweEBrO6UqM}U3eyL;{`4)Ab(J% zP0M+*+Kyi{YDpVhTgYx-$AOQJKa|=k4^qIt++SIN0Z6s*x{@|&gf!1NJ3=G!L&OQZC;;moTs+2y3G3BL3yLYU9Orq{Yfn3E8eP>+6 zhQu^Me5cc_W;6BRK8?s1Zn^{Cwfe=>)jj0TO2=H(@1ur{@GR@BPwa!r`uHsPXHK7q zH=(xM#;%xnqVna3Fi$B^d!ye&e&|{{e5$*rz{UIoCsCv4iyz3AxN2q0p?c@4Yt;7| zm5A!J8W^&Cqmp||nj_CR^TAi7k5r-U4bj0R4pEl3V9)wZG`ulqOdNcZjt!RmRX_gS z4Lx?dMmb(CEUL%yjf+5HKq&%d$^&DSu1F(8)z^OKo&$7C^~?6_ljRhWwk)EJ6PFi) zaBi@;FS@1!JYPN$ZG22{}DF7`1ptfFZE($q58MTk5uP&OvG8vk%ys#kQG88HXMD zD*jJNXojU;``S}^D?ZJ~RX`vGn~LA5p%bg*Pl8J^J7Spn)&A*Q%02Rvi0*?MSK2WG1SvBojl6u2ZX>fO`6-a33zc3R2&F| zE5yK18FGp|Ku0GNA4c{&&zye|^_{ob*8lad_?{c=cYGi85A#Q0QXT70kIb+i)LPu@}~;O$^kPTF9?*;~G>SaM1INc(8_&Y#9ej&3cWk zu!9Wjr7}z9IhS8=LdCD0CPFY^ovYHS{W@Ear(5FpXW?RB*7yY)%_x>2G_i*<_oY2o zwNShN&?w$b*7Fe7T_mO)Zef@O5Q@Yg!0;#Top{iUaA75MOi8Wbmarbp_oHv**gEE`1zxzEV$u#CcEE)E(X(z({`!t;|t!Ias%c~P1LzL+y@aol3UlNCz94UvS!9{fg)H!P@iZ|(K0_juQ5MC*kLnX zK3N(N(%+)X_gSGVf>o{}QT@=H6$Zj@6Hf;BFPEF`&tf;EdfU zPqYD3gz0K4Tp2l#;-xXmA+u?p=yeIA_EW46_i*=S^d*D%5pe}A-Rzyryi?VFRthWb z=Sg$|Dpv!1KKv9u@ybyVJtS^tRR$czpTr@1q`U}M2sB6>*tuN+W1cC!)`X?J9Qk4p z8Xr#@h(ZbU{`bo67}!qE7TOGlo1z+~T@CRzw4>hIk(^fNEv~I2@;#;i%6WqgKw{(v zWZ|Ac@@~&A@L|}ufmIGbx{PE>q^h)A+Pbk~Rq^Wk`z8VLRIX-k_bS)NcM;#k@)K=; zp9w1rfc3ZR{%=wErU!_QGB+{erg?;AduQoFt^F`Gm<88a2Y{|~|6Bo6twhyc1k6uGzaU^^*Py*L7GVemUD*Fos z_1L#v>-z-e@@!TCcF^`Ky~TrvA9cp|qDeuuon_{F-ioonyp|ZZ9sa|HVelYV#D~~G zh>;e|qJZl8rTPujmFonv=taH$Qb2kDw5=X$!-wj<;A9nyO&mi7y26@VoVdkPDU>imAY7@A-q29%Hm@O@IqCD=$}*$7clmD1Tp=e{;$*`o4ZcPs71!N?iBlVp!&2%qg& zsI`URa((c(J!JnjjP7>k_n6H79Qdp8dJSOK(_B6QdHL}AahHi3P4e~aF4m%AVp`$> zOeq_E8bgd)WJ`=opQ09G@C9Kn4i_UK?FHnU9Vu0tzkes}+oo+se7w8VxKy)T1>*k> z&W=`Upm^fjV6)%W_5}x(`U&nm+*9Ti!_g5XfCUlaSF^Wg5nj3S%L8TVi)aWU;u2?4 z*t?*s2Klp_h2n4r0JIbC&lys!Pt>ah8=6|{W#%9}J^>=uksCT7J{d~lFg<91(JlnI z`3vCfKE|<2L>9X~I<8(uOhhh-M%FR-zJ%l_YA<8AZXl{L^gngN-$g@L>6aSn?TB9W z*w5+H>R|j|AiBN2@e95ej-rK#N{c2eGGux^8#T?cO>_7foS+Cui2l;uHFVUuxLvJ{ zGvb@`Awh*&0;ZD-t?Ks$wTt#l!=Y@Ols~4r(FER!yw$)EhpcftHX>>uAw@0X1k6B0 z_D-_xQyJ~71|vR~hW&jK$(ClIs2~`PgNye>-jq)gW#oZGx;d<^$=3sP8JRftL>2zw zgn?#f{^odxN6PV{0vN7}T3}GjLV`ekOWbv+?H2 z2$p=rz?PK46?C9X-owbCwc3rc6t&(hVn0ErXBG^^Ju)@j8B6E1;%#bM#pR98CB2eD zq?9a(hy-w5BX@&CZ#8|Y&)=^uWb+2vd}GJYIpS*pLT%6n&Rmsd)l^ctA{T(L?)5bg zDLa+yT4U!WHskl`KsG;`V=`9YRGo=MN8I#auQAhF$!yqJ%7)6w*nm5W?#*A^hTFd7 zy~`Gsqp|o54@_*JaX&Ewa6*{->*r$Az@kH8pnhS(>2w8O5{^hL2HNPKtf(fzG5_kf z%UltX8wnJ7;?46!Qn`e)Q{hUa^E2{TyQG?d?x}y28Bnr9wlUDu{MjX>usZr)fpjB} z9mi~4EZzg$fsBj zZ!3G(7p8@uo4a$$Gp<@4fnGI}{aqKTmLOq$Zsi@iL-MFCaz(l;Eq&dBj$r6PgMQg6 zp{mbrR=MWhKkCG7u`kV~7>9ovr6NA@jnJD1v3{I3Vd7Dl8Q4(5qHYaG@1&xVh;?Hq zicedg`QTbuR8hB22lbVkaRPK2@xA47RluJwKs>&xdu~7StU^e7SMQ02;?~E<`~yDA zQXt2do9~FMTqI@Re|Nko2nd&|42E^AF4MBH3~o1?BUqUr-@r`jO#+Ct3``D*@7DIb z#3_6dS%q_TiM=sXbI0f0RF6z0P>H2@#Gnx^8~P?yvgP{9t6y1R#b?NV5@xC7MnarJ zyg+tq(HzO+lSl^?1k8=bld?M;13~x#1&!X&kOWZ7E57L-626j!76>GXp;Yf|$VMiN z30=az1)XCyAnCZ8y0bs0bX0ue%-Vu3mZHbiV@I^?Asy+oV?eg7F=^Dy#?H@XfAMOQxx|=Q zSY;ZWM6!N@HZDK$^^TU_LO=-@sYE;fQZ73=*?A*3>s-|H6V9st;5!#zrWWa80)cWi zQ~%i5mpX+&B+}sbaM&+ZoqQNSBA!qtc>*=Q5cKJ|#XP2?6*qTEr5f@Ex5sMy4=6)n zMzc@d0C^5_wMQT_`M{BdaKxTDa8&1ipkFF~5j>EX49LxLx|KX~mrM$_{q3VxURtX0x ze#`m_VEhOfPq4`)+{m&GNl5-`6P2t0He)ZJ>F#2Ca;>Q9+l5C$h7Dfhi$_3q+1eG89749`1fCMaGLE}1 zzZ@JzoGtDSynfJfDW^A%wu~HTiBc>Eod}-Dl;VTBH1$r$R8N$Nx%wT>)ssDu&eNRtc4SPBAQg z3eST2CJbn`A$i>f!}nVJ?LJTCzI}B9$->k9 zZw45xOt0)7K2pBH+WVBIqGE(JV$vbWufwY@k2&%Ha4TK;fOg5JA=H4Cs{( zbUR?WjW(6h^X zS*IZ^45OJMxy4=#Mg&V>tPXF&4M%{pm$SzB78Eg;GK+f?@CMZg& z1v|uV#{tV<<~*m!Iu}Zn9RBR~tS8?%Bg~+rq@+2P=HVY2+Ho*{j)WJ=kgGF@(qYi~ zwo$8h>Y3R$w-addKv@~=YTaL9iIA*4>+1goOE|l|G1x(XgI!SR6>F#L05w@#gR%oC%SYs@0 z>eI;bV_j5Lzf?QU)~!x$iW%rSx+0MqgKkYMIL>Y4Yk7$pX=rWA*7U-y0G>;8KJ_5N2Sjp~pK1eo3^WxDH*lx3f|ZE~24O?fEItb~8DD zP`rJC2(_j((RXK>I! zza2yR^k9Ya^my$%fMy7^py&=6N|w9)1yNXms= z)xp9qK8OIvDgv|V0>uku;4KW-NV6}t?vMce;(3e>rW+VX-Df14k`j!?#~3NwX6wT;EQaCZ^Wp>gqHZ z(y9){Rb|%G7SxNrqvn|LiGCZ`N%WeZ{g&|UW0Re!1NNNKr&YUBy*X$3r>)1AlEQ{@ z#!Q5jp@At&dF?V$$$9Tg=9%9Ry<=jgj41m}bD)4%IRJaD&ZCQ_Mgclf@0|$5E zElODg@F83oJEMJU^kf^~GRA8d5zV)DWV_L$>;_`VgMgkvK$P~cc=?!=;hvtrdZu@) zJLBsgJkspU#L>{u)KAxR(0wNO`tpCIVapd%1m>g{fR zD42=^Mb`WO-02$!giEYWo?Heps$UFU;Uvm@z7m7ZZdf^5TK{mN`Za3VNNTIU+mq0P zs*Odz*U_&=Ktlo3YKvku7H=_u(I^2qJkAR%@3Y_LO)S7)C&g4I0Q5V~4ZB ze}|^mFQ9|xWvoq}P^Ce!ov9Ko00kc$$So&0T5_bBKB-&mF1-9e7Ibyq90u766JCu< zE%9#n*8+Z65P5I~27YMvq|4I~4ggBky0oPt>ya%`gnE4cl*1ysEToz!C@1$mx$#mk zU}96lz;sx-kqsiq-VdM((t6zUS=+?jyv@&+72ZX^ebD5N?g*MdBNG4cfL+*!$EXY;;MqpjQ0>8oNV0F#j$R+ykUye-#O!eK7Rd-}=tW5EY7V!cbtkD( z29GTczh)18r@6KDjsKqeJb86bKbV-pX5j&=^mH`4>wSgum%|FSxPx5^Z{O!z@AS!y zFWnq1oknLnySluEr>=FIi5rF>7dQIyoX?0x_Kq~8@udpiYmHZV2E9vU^?%ToV~>Zf zd!W?{Jk7L7zZXmMjQdXhNLKwmnAqAwL%`V9iZ}$U zV<=@|&-!vPU$19q3DpvN2v5j6?4jW-uVSN~^`&yG!1E{NNi1v^-4Dcrsfl`^uCHd} zdWO0Iu9>rKfJi~pMQd0-egetXNpf%zeJ0O{_pRS*c;TziBPB0R_&D( zA#9ByGp%VDq4Hws{Er#$f-MgdEY<~r*q9b;3mX`w-U4qVsH3e+gE#I=mz4G4M=E&i zpxM3M@ytee=%+g)Za}Sriv4Zr=0uLDFo;i~*^!fnSB76_{*7&0GCiOM1iwQ4!VRPR z&;@_=aC2$tB%?<;E1oONZT^CW^LBunSAOo;iDwQ6|{kK6nr`1-(2zKZesH5dt zy~z3G^?mE0afvLEMSvHv@cI0k1*xM5$BXI z<4g=NXjU6-obNGPdI`q1WcY$ENbx~d+waW8Y{(Cj+$mGU@Uj;+z(M*_Gw@VQ?+)J9 zy-{}j==DpEJ0O8I|6RJ+i(mR1x#0W)5!U}?{{r{JQX6EULn^5;fQn)iYoy|TAxg;@ zUFX|S4Vh_mjAAs5uKB37LVHR`@Bl6nVxwV_Lp|Yq3l!!`q|{a&=pHr9F|;l$wA_ZY z2Ys23*WfX)UZW5>nrnwf17$Uh$CcLrRZ6P5shcoBR6>c}ayDgjEKiBGkNK{ze~DJ2 z{1mOZLyc(WMA~G~Z`+f;usO0#^IatyQ-pCyk3d5i<1FenEJT3Uc9CA)F*T|Y?1&AI zhjLFx@pm!;45 zjJjt1_IuzaP^@A#ZS4*K3Mq}eE6Kb^HaXG1RAthn=fZ&GN7^gfqXnO2cDsBLa)lCD zM`)FSC$t$oxT#L~{=J#FGVUr-5elF6p0T!X-4UH$bF6h~&vDgd!~K|=kf#tZ4k2%3 z(`KeR5}F+hh!#e$RzgjThzgR!b#`;B2bH#(s&hHTmp( ztP(vu`1_c(#;JneEMPz|v-K_CZ*aI|d77h9sYwYyBwov1>y9hjB9Sdt#org+ch8+E zP_F-&Bc9qXnb_?)EAxrN{rBR5r)@`b~}1wpb5|(9otqr#&0DD;3p1S65eA z1Irc9H)wz`$6$JfMlR@KMG3%_#F6KQo9~;aN6dDBTECg&5|EK46q_~k5FilCoC76x zKXGAdC@#OCv?W3<6BEK2L^QO%gnPkG>!DRuF@>rZExD0ymEg}uOlUxp-vj0 z19Mg0XU!Zp5m7RLLf*b)c=1)vjKr!u^#!o6cWF7<8+wyr9oR-nP2AAByB0S&W1=|4 zESN<87hFL*`8Bz11$*sXIeL8D?3ZDq(sXyIi)+isp^*{tOWB|MshAcb$x=Zwd z=iJiXaaU@k7mps$dM1kZbh?qz4&>f4KoI)8X>;zxhN8a$-6wXz{#dX0(bM6s=dCMV zxK4H%ad!fSPsN<=QHj_Ay(v$b=cZCuQZP=ym@x$`QG$)#Dlbf^6ZrZ8SOaF)Zn^O{ z%%>s>N(|OO-;xZrQQ^45k5MJ32Q5&fW6ko(Cq+hBcwyHzs0CvyRdtg>w72ooJEoe8 zZ!~e{JEMEHydRt4Bf3uHt)sgXFy2wBRG^xkDwZ{-b6f$Pj zBXd#@SEeG^y~1>cam62(o{u7nErnP0`p$=+m~$)%1Iyx9A4bBDbWJmd61@1{gWv~o0IA4r$Y*gD=1QJo%sY% zD+(RbY|7LN7;e$k`O>%(UwBG@uQa=a0sYg>i2`o3(LS6*mVMH0T%3ocBS|rC0pXUe zO~{w8O%9;iyxX4@YOhLxgEhk6QOX5g)lY+_C*ec%7klBlA}DtA*;4<+0%)NyTz})y z7&GYtYBfnP415a<3p!^jy@Qh1nt|!aQuI12Eu^IpUJcS?4LO#UZe0|t0pxwCzbSYzyRsi(4~ud^yF4Lxy>*mLT3@CJW3(f>~GQG@#abr`kak^s)uTfxZ3EvNLeg?zp@Ljj?0twpm$Orn8&pw z2(!|86FC^H3U1Zn66q?;Z}X4?Ke#2;{(Yy`MyDX!S?gU#w&w-v#DW%N&wsH-_}l=n z);D}L?69A&aIH;qn_z8&Akxwl22B`ZX9%NU9rd$KsFc*A6(Q|bV_CLXu}4AfJzdNZ z+UD2;=Ip-kZ)WcgK7eAqZ%;)lQZ7Mvsu}jzt2}e+gI+QD!aB}hGyffaLfJA^Q&M26lFK6 z`$=RwR50GtEjuTl2LBw9&Ba<@vZCe9kgUMlPWNSoo-taAEeX?TypKaFovd_kOOyi2 zEkHygQbJr&)~2ULn zurDhzw0F<#E_)My!0#rnw1IHCr`@+Xq+7$9dk~O)8gmtnA8(GAGb(T1hA*F+@A4Ga zj^Mx@EQ*eo0cGt*2|*@%^FS_|(#+=wy^Gz{GK#HX3sXNMl8@zE2Je+pB*ZwWO3wRr zsJ1E@{QB+cnW&2pvx4DD<2K=F6-;{EUB1#M*d2l>*+Mk7igH?mXLU5GZe^ed^X za{<1?SKdAXBqSuYS~EC+Hy(3)Bp}fdCJkwzmpz)udC@&-&i?+Mckp`B@ecuN3>{d> z)q)p8y`qx+iFN%r?06~V=1mwkC-a4s<)l9se|QRyI>54{Q7HCA`a7v0Ua4 zLjASM+Y0mgwPD{8aPtbS=V*yb#>3Oej-nAbNE?38}TB)hTn|7jhj_t?Q&FqefSnKPZjNVUWNI#`Hk#p3f3#d{@q&(cu97xGPiNdM1uFXS zWopEI(<~qlZ`lYrXh0lyT7_IBVpR0?E{QY`>#t;5a7X8}H^nbl8 z8?OC1smIjn^#ZoX`0WVP9L>YIUa}{XsUk9OaxP86nTp___H>s}l8g0ViRd zeSQ{nV-J2L$HOuuKd_{Dp@h>v%7&c(bANjOJL3410W#;fA6I?;J>$$NsinmBJ4}N3 zU5{$PhXjYA^3e}Sm@i^f=j8uOAJTfgf?2N2H?}c-n#K1$+n*$pJ=_5_$r6~4S$le> z%*WtTy^bNcl}1UaslhV{GwAB7zvTEc^K|#egTz1M#P1yURT@^ zD;{iGh{NQBL0?sY+Cfi09uyMd=L!$Icd%b|9xcJf&=othTW)i58scc6&~G}IO~T=N zg+9w_xFN$;U#7~T$GOuye_#qL3=0M>O@0U}N#%5JVi3qYTDuvg>ZyNaT z75^So`<3dV_BQ=b&4e1dZu$98Z}wSY!PuaGKs=FO}x8!{3ZYRG%Kai$V$T@Bc%f}gsO`^{A_NjXRh(x@m2VWjm=H+7Fb z-u3*-=7{6?b8h_eh&M=4uPn?F--!R(QNJGzj7dbC>7TP}Gt|Foq9(AW6mQsJST|pv zd;}*MF*L}J4!jSN=)?0*40YJlp#L6J;Eb?-EikW~{0}d$z3%nE`nX#1XQ9gc#k=|) zh{1r<#RHNxn&v+N4Or!C$-f)H)|!7#9dg|Nsz(3QB>b(r;F~NG5B!GzPz26?zVh{k z{^2LS_EAv(4{`MLl@htyA(<`y&wlznzyPt%=ry=(tH=LqQNhFf@zwbEBslW{WyaIB z|0Pp;RJ}&vi;~|2&P{VHNdN!Ji~b>Ma=xN4oNZ9{k^k#mfpbU|ls6^n8(CRVz3Q{{ zW93$^=izx3I1I#p`a=Kw|Fz^-UJa|~gUR{7*Tn0SSJ`|Br1}TUfM1ONg7e~)yZtxA z5AcD50A6kYG#@qQ?>RI1&GGwW$`R&=D=9kue`f~mb!MoXLuN$(8ei_;g)OX^uM%WFCE|&On$x6h7EsU2mHO3je1DCiHHc{I?jEdg1xhJKVbhk82(H$ zpB?b=4`lp(fB&?#p;w`op<;l9%`m3W`9GU43_KG#^X$V74}R~;SM3z(Lj8hoe-Aez z$SVVCi)-(Hw~pxR+a|KShOuJF7B}|ktK8+Q8~ryl!l@n0b5y1Xxk!J% z(HAISbKR5vwT6Mn82JWCyWR4S769;Aqu+y0>0dSJC?GkR+pnLXUBJBA^jy)(hSosf zb6&=P?`QNUAN%KNKL273OXi9L%Kd*v2G}B_|7@;+*U5jvjcBsLPOKY-DpFp*@u6<^Lw!_Wo$)t#nA(cX0{z+C8%rZ3p z`$4Z8^aC(VA|LktXH-30e~%tJH2VUC+CLebMAIkvAg6yaILVCoq`!k;dn6!NoI-HB zG8y~VM)B}`eb6s_AOdjzD19rqZZ)px4#FH%y_<^hxZ%NsDf;=ZjT_V+`T6xhyIPa6 ze~sPaZ&v-bJ7Biu^ab01Q2?U|iEfs8R*LUkmfxQ{K#v)lZ@@pwbAKY2&uRMWV7+kX zQvE&Tr@x2%78vs5Hzk$}poZZ^CS)ocSsI}{ktosblkA}ZC?LlubgZm>V}zf}bvNH~ zc|8g|ko^b}Sc7~|2iTZ#>ArZ~9Iw8BX?)ZqdJUuR4_~ipjusRQe+N~OP~a6zk;II7 zq1pGrbB*d_OSv1>5tJv1ttlsS=+knmZw#=Yj$t zx=X<6eB4Rkee|qF6Iu#bxBxj`53o8ed!i?y|DL%oUxAl{L0y;!gp)3DnH@K$(zTHM zakkLOJRGSon;KJO+%I}BsffKL$;NLV)!yH@q$vS!BrC5xl64HxUG4Un0025{Ly120 zVQlkTxf2&Jzra-03XnK@WN>q9>I|3V=4%atvX{L3QC8%;H|N6jE~w+#p78Acp-+@} z7wDG1t+VZXw<_Y0aKk*iOh>;O!F0NuG&;Sw^h9nJ|PfhI%!C++cQYeL4jP0 zt0D4RAWP?NFP17VUbdvp@Ni3iY8hL^v3e-b#m?nk=jG9xuYDLBpPqDyr82thE-5Rryg>m3iBLM~k(wGnuwEA#|`GGHLUP1IlS>~XoJkvQg(m)C#15a%X! zYUrGs11I3|;N#gurQ2k0|7LuEFOKl4h(XVB5m!U0>n{)O!8r*5wRH5HY$$-M6CbN83F7tQGK z-+eu%R~FGuvB`(Om+2VG>*Y=2yC0xpi}$-#R+)!IPRu_NdgwE^Ol(O=b|Vjm4jC?O z|L(<+u&?t9g+i)0} ziaa7b-}h$OhO| z>e=^T=<~`ub)`uE)0U#j+AG{Ef+qYPF{QrgOUQDo@wF=nMNSqH!a#&lr5Z}0IU5NW zw&?7YSL3CdNuM>0|17~0i=*}_kT(Wg=v~-2IWvq$@!4F?2negmN12(J_!1d%dR&Bs zg?kw)oNta?DPY*+;^NM(j$i;J{Y#x^a8UO=Q#`KO$mb^>kLpMoC&egy!| zCue=*oAzgbF0{506R7zj-0X$-MpILhjcp6{u*q(l5|BkxX9)x_kU1W|Dq+q8>-_!v z^g6!wcqkhdX*Q9jq#$c|%Iq7w(>SuTv+KUxr7d^6=9rI?N?pj3A-q|oBNc1$_$5{x zc{yuH%aFqBjr8(pFTj%adaE1@10qV2?H~QXKPT6Fs#n&bVIiRE0-s;>Nw;3zVU}Fv zH?`D#rNmb|2hetG~^M5F=<^fvRxdFDiMCRR5A}30QDW-q6D;4^CGQu zX6AsCu$Pp5thtL$p>+DKo7+8GxjEAFN{zs{G-m zTSjYPQM|r;iwWT5qk{SDqP6JnE~UPahRamwxvn?QSDS_+=DH|v5AtT|cHJN0>L##S zXCV4#H^dNAaByG)yF)o8OQ+-UHvkuWUuU{-T7u8@qonSvWn`^NF`-gvKxgyRjTzjAUnRz%e={J zjC*N}vUhUD->PQ#GTSkm&??IuJ{q>NW1=_-AF16Y{6t8LI(rXHX9(=ngp?8#`*e|V zAKwn{+3js#`8siRrQAUDH;w-2uYgYG8%Z^U&W8jj3H%Af)34hISvj^>6t4j-Mc6H} zwK__^%nI-7V46z)rM6y$!D`96-gXoH=46?Sra`l5CiCrRwJD>)SIEOXC%RJE?cs$Z@s+DD z@o0Y75N=NuFanau*_oq&AU!|>LNQ$jxXUAQg{l?q>xsIAfqA|-EEZ7qSb~fhraocr zf$)~H*&@2@jq4{k+a-dbx#a4#Z~z-Ql2|x`!WpQ@ya&Yw^f`sWM40^k&WsI^Uc)(D z91zYyEdPbILokkKn{THaT)2URk1AVvwFo$7=@$ez-!7E-!>2t)p2LcGidy z+Oc``F*9QsX&?);Ri92$=5^Z-P{CUszR16{cKOv@)K&eu8#=t`oDe_&EN-(Ho$|ST07i$f`Q$Y#f!?Ipd%Xn1kwr;rUh7EW2R( z6lit0;D4v)g9&6F+WX;-f}_!h(y=5YI?G*{1?|S<*quriZKFR+W!daB(hon9>G>;- z^(bwQn74`SKFr!?PWXuX0o}fiWSTVwoX)iM3hViqc+g4GZLPsQ3G9R?xHB_#9ZIQ% zdxB|xx4uGOwopM7m)l9Ad79?X0J0!w0-O05p&pgoUp|mkudi_+q`(xgW?$=uczK^E z|BQOrf_h)TfL(h8YaCD1Z)Ngz?^f64aqX>>t7OMw`v)W$jsnXOE}KI(w$K_ETnSAe?#48@L)OZadD9fZ26-Xx_oHZ3)LqfE=3l zCwTifv5=?8^v$DjOE%jvb@XI{;<{B3$j-_|VSK3QW__|Lxjf;uE=Ap;`#Dor6kYLG2N4kC-xs`J^F@|5;F7 zz4s<)Z7J5M9M6Ec{55_JGRe?PkN-FC_jGH}g)>%Qu;#*EZq1&TaDmSnK10SgA^OjT z9l6P0qchhp;ib!ZCyPaKw1veZ>B&`FX@>3Qf~HK@b$!knys#H%B)>Ku#_n8mYcdy>Lr${;SDyK)|1 z8r<`&Zs;Pg~c)nrT5P2|%w5J#b4{V2l*8L`Ovr z96LiOuVV0nK+)1uwM?ogdNeE;yW{V5NyxMZ(+Yg!f`?0HHn&yD?1w+};Jsr+-Z<() zrGA=*1%F3TR*=6w;q(YAAV3yORq2zCFo-r7;A3gwPcC1P&%U7wayWF0j21Mn?rYOW zd%pfbTK1+-K^S#FKO_O++~uAlpo9=dBcZ-~_JTK^&LJt~%IUT+dWKGqe?m5Q}` zvRBDAM^ZT{pHxY<9FEu_6c1|y+fF5iA4_tMnl*GxSM?d(MJzfe&>#pYAMvOy@p^n6 zX&8p}Hi?P((d;db>~!a*j*Cf!q)6}lHZ{l0F%y$ICI)4*vp4uBN3P{jH-AZi-SU_$ znf(yiM8m#-IVC36(LH>lpM`sr;XtP}eDKpR%iGZYt>jWrXAdQ#><4(lPqaU*O~^+; zNcV7`?Ma>&c(hs4VA-4TWx!D|hZE0G2EnnoJ;5qQA)g=Gx>UH!(k-Os#lp|b+f?b8 z==d#`-2DQZeJT>!sbH)-BC;TS^&e*RZg&zxTpw&0x^gXqpZT}Jv)0;oiH>8#J*^0>nr%s$>6!#2C! zL_zz?qYdRR=ff^>gd?w_5FWV`QBtF#v#=-nb|*piRLs>Iu$5|ZWMcyDIY!F^R8oFa zJ!Xv?{Y}!J_*?dUVl`%pJ+AfnfRm$@2H@sWQBgr8srKt)TL3Q(06d1$1L?WZ*g(4iWnp94z*a}Wh`eMV^PthBoykyEdgJH-L^nT4WRE*S#28G zp-Gn>^O3?@3q$EJmk}^-3`e6<=$Xc5F3hl6UeG&PX%Rh982HsSnRs4pG>D@Orag4B z>g+fk8 z2L;$hDr+Gh12vW(uXS3cF9QElKjB0Fos8-HPR0mp8Gpn}N@u+XsKN$g?%)sp1*rg4 z7{=aM{y%ql8rRJ}X@ZLPCK4A$^R`BQDQ!mVnI`xsxNYcPSL~4wF%z)7I}gU;Ut#YE z>B1T3LCPVQ(-e83B)*z{L5$9e;>DbZ#P`^b#i3Z97wz3&avoI=BUAf$$#^|T|BB!%b_L8iMQq?TXjpXi4jDc9M^knpf0kEj_JHIGcib?lvADXJ& ztHfJ@9z_Q?Q!Bz@360&z65&mQyzN;plh#l~o_}!jDE>Z=4PRo30mZ5oq-Ty>^sabP z=(loTO2glcH^znAjGyVPjoY5rZ=givH2KOkPZ|)|YKzTfY+xOPU25}v8dkbH?;rYM6P%onliV#DE#!H3O z0Y~A1dlzo|czQk8VjBX(QFs3%3pE~eI!>_lcAW_? zZd>&pi9lR1*dY-Y(?D;JKW?i(cpIuc{>oE2+sPU`#PmUpF@j$fU3XTt$+uPsEe{Ee z&8j}{8R7F3Lz8FrrlP?LNnb&8?pJE-Y2GS^1 zsIZoi=H$fqrAQx{KKP?>R=7^9AT3fj6T)Ghl5~@v%*+FfzG|lP#D&|>uXN>_7Y$oY zDx+&Zhn85g8UaqyVJx~1)1iY3HfM`K>f8$-0K~j_f+ADZ3Tf{I-sL_3)zd;Qmr2sz#`*I7r*mrWDfp2**(R|1x`_eBLNn3=QJ~chsTNT zVr^<=hMXjhR#xDPUY{WRc6g`+mmoHVQ+A2X`q#a=3T~HG;AWI#)RTvLd2`$}*Kmm- z$SG|;Q>INoYLv9r9sEsVuYt6iC2b}=R1E~gI)ScQOi(};T!$0L2I^4)Iqm8bZg$hX zDFV@6JRhHKgO8S=6PQG4&@X*zYiIc3+;3?rZXzuh@PJxf6e+N`Cj5lVqgLD1UKP)CCldDIZ5`Au8r(MM4$%oV_Yg0VQ2 z-mT8&>r3)&KIfi4s+33L>{wSZkE_3pLXGpr;xku``b%7|iPBFsTT9y2-i_{Snc_K= z4!My5{vh9Kzm&RR5C!LUc>xkL13jr^j{Ah2eb5!#Hc_Y=qUl+2#(**v2u-H7E zy;F7nFXdhCFlk)Pg!dm<$A*jnV!jK?ybH}mSgQROG~JgkpHf4p?v08$U}41}k94IH zvWAhen!{6DwCi;vjx_yw>qws@)&Wxqu{ep-5O>y53h9oGk32^oy}*W3$I!iQ%Ys`g zd07P_GsOLp4R`i-T&^|JLd-iKKil*x@{!IEnbsxHD2G0wOX}8STVqbNMf-p-iQ*VkZ|+EML{7B!{?SmKNyTH0z}k&DGL^{S&9*8 zn$M1VLgR_97l2gR&plr-)ad9rAvB4jxi+HuT#cnUR}x@PEjRJZwR*-gNqQ{I&u4xP zbjq9ww}W(swp?xXeZmb^vQnBK4U1v~r1+vaEC*r?DGMg+&4F|tI!yY2UkV_H+h0=4 zsA4d`ZnW11ncVK@mcO!Yf%jCeofN~y*HV><5FUF{8Z$S)loF>MOGMr09KD_56LGj& zT=>o#eR>8E!e7jq8k?XvjGkc{&*0u^qHaY=k>9%Crg;_L@LfCMjONbE<~2ivG-7u3 ze<^rxu}RUPbn5w20E1-vYYl)S3mOp9hG7F^+2I(Qe)3yCw$DrmrC`B>Zr{2_63ddq zu&b4*6!!0ApR0kWFjr92J+W{?V>YL}y+FG*+PSV1s48Tkj+FoR z2ETDkvkK_%Oi*km?MN!l0~dJwYT5o ze<(EXN_c%PO@oQGXAF6|tNu*1O?PyA+&Rx!ZBLQN*8V`UzByB~Nll_oyYit{fRGHt za}oV}5^8$Y?2~l%ks5a$?Q@HVK=mc64zt(3U;ML#4mKf1!rhyK8F4$|h=k%R|4>;Y zbZSGwqHkUEfNVytT%QQCos|ZA=Msl!tPxmDm$2qS(^M+~4C=?oj!jmdMbuJluI`Zr zQ9`)^si;AK=`Ix=qeyR4T!&~D;Pzb!$Fy`7Eg?Zg?Vj_xD~>Uq z3QpCnEhVDVWYV!YWG^3B_vN2F0jDj^?3shbGVP1WY*#L~D=0!x%lS&pFhFc9+9pFH zl|!Cwe*z{7pRjLi><#coBVJ|41V#!-@mM3`i3U85t4NzH^7J8~gbcKFK9+_y>GCju zNGt&KoX}ED?u7ThKqm7><*p2c`c0SW2`LrGY4uuNFNN5?933Agd_j)v3XI&i*e;sb zpY}3R=#MsA0{T?oT3R@YdO)Z4s~>jI7p2$LteBz(JyUsIW@CNq6TULEzO@y`ah;_1 zd~{y#bRDC#;cfk?xY)AXpoc}{c~v!q(-ZZxuvG7P)3LinMOF%u~?E{hilIR-QnOtcoEDxK{L1n1Pgd~>1XT=%tM(WI+UmGtVKe>EUdSMA@)wY2 z6JmC;HNN`L=C%p=gk(kLTWpJ!{2N`ketR7leTN%dURdEpx5O0|&VGng+2(8MJR1Ev z6i#xBQw>5fk*vDCxRC)u65thj1^r?EpN;Qxcd3_^V;BVh5+Z>1y)x77JJ`bXoh=t_ z1<2;?Y?17)b8Zt03({9T(~#*ZFs$}*Qa`1j*m;Nw*vZQlj*S z26?ZobVJk(sn;#y@t}wCDmO-zhp}SsZru7J@YZKC+0%GD0dl&6Ac~h{0e3wRnYs?6 zt4})RiYnAdHt%N?*1lOLg@~Y)-m`idA(DDCbsI-L zcp4hfIYLN#W0l-xwpbiIRR(5nAfYr%VlIB71MqNFyUeyUjBD)fX7ta9zLzXv z^3;v$4t%F+7Oe%4ji|Kpq3nrn(8e-OpHb$yyxI@Jk5%Fw-T@o@?2^fp5y~;i38luP z9*2VZ=p9Nf{>DTwuT3m_?!*Fc<<9=@VZC~%|M-Q&tV>eO1^j0Csmgm76!%d-Q7;V0 zzS>56hNg5*ic&I5BlQ>-MNjYnM*Oanp#m{UY=b&>TSb=U!Db%0ds=OyM)8p zmmYuEV1#~vi$O{sD=^b?gd?ePFk-aR-f`Ryw)XY#Lj3Q3x9-S>pgr zmJYep)Xsr#h5Mfa;kRkf#f#NMr2_>UuKa_B^V0x8`ck~Q!JGBJ~-QmF!wwaLj`N~|EyOPy|X$3^T)kj_W zw1_y_j$0CLf%C$5U0)+)TiySsx$VXGyJnONIBc;0upD+LjMViC|4N>xUIE@#>K z<6yt`r07*kJ$}$Kw*rA(LGm5|ZcEiI;8lo|D`LidhH5=<{E(t@M2){tB+lGBx|kh; zf1up&Lo`hBEi{LB^V*P~h*u|M78aK+eXH5E{3EZ!TvvCKG_hiclK5d{P=vB*3F&pg z!*`8OT+?b#grXQxalpdA7(PjcoWFKl+a&OJmk@ytzw0+^_u$VGQ?eN_{ca^Z5)IK1 zHQOPQt+Y8CJ;CB-`R3%#6M6YR6ni54>bC(s>%(oQILt|YEnWaUP3_pzA=6%Dyr8Q#MIq&;1AyqZ{eOzy=)No>VyDk+Hj4YU?@?Xyl}`)awKhmCI_56VSNNGk{79n{U9kmI zGC@k(V*TqFqvr3X%@@Fa!Nlp6#yu9-1~-V%JKy%2QYVjp?hsxE#DyXpXk%uT zvBb)v7+p3GcMIG=6t+feBDUnHy&@*#o0TRXiwYQIPB)-c73ucQd_Gigj z(Qm!5%3-AXu6WqIybWtbT(pe9{a_N1KLum}Wl1ElF}@GR(}n^y&W&_$WIV&c-Q)4B zB9W?E{XUwdhE3u|z~O<;Sq|f@kz**n=((;`DBKJX*kl0+auIFMVs>)jnDhc{es{P$ z%C1zJji@eoglT$~$7;0OH1l3Uez*nrNNvt?W}K>HqmU@1VTPv2*D4`31C$hdDJi&P zP0rHm8fujW4UPL#07QuI6=W+4F`|4BBM=H3b?Zb(>$52d zDF8qxi@ReIz@2mx-vQfq8cBKNT9R>N>fyxKyj{hqj z0JsJHzZDO7+}0j1FzTyPb*e7X@A8XQaY)9`HUb}OPQu^084A4}UeNyksUPqe?eZ>S zDqc%?ByArk;-*v75_tQbn|B)0PjZ+js3N4|Ed0nWDnRVqNRYWW%@U!WM+v|In(eq5<(0(7N7Q0V zJKcvyQ8rJPDmx5@&HiW&6f$^M{rFi0!KHD=oA;6C&?(%F?=*@B_Il;@ZO^xNu}_8} z&?DLP)i>rhn}|{zA|{1d^dA;PP1E=bHBp{;aJx9-MeUoJ-nabw{;!^=u@tkBv(+Dc z()CaGUB|Tk?)!afb`-Jt8_xXEgBd1^X@Y>ay~89`Q4I#jhbw(+Bd!e<8ywA=7cmfhQz$bt_y(+j2p zMaBE+nVE$$`27*_@IG#kYAg4IY>#J)65FuCIJbzX1FFM4YCv&8f3X%%u4EQhK${>$ z!oO?Vi80t6Nn3`Zr!RPUB2NC{4;CzH#y_I%Ubh~+`WX(N1!=_`V{SagVQWa2a zI^B7Ia>oiz${JknoJfXp{eE>Tu}p!M0M>_?XQU~(Ma3S8-4^SU*aA&M)8xMvs2d`0 zOSrpS1+}zr086uVr|l6Q2pRLtq5zkT2SC#PDSE60628J`t(gJm7O(>|8O#%OJ6I1f zq$;)k`AaLj$BmOb2e)yrga(lddW?Y%acg>$42`ihuqE?7T_bCZIDS|YmO%`Jbh3bl zoM!l5XGbBGDM%(P!n%lhHc;VR3`Em9n3`M3l=jwV_AZc~%cPC59340f^17AFPh6JD zW1KxUwkN8d7eoO$zie?XRZ8@OW`APMkE`Z9!$vyl8iMSUi}H<26qEibc36$?41;E&T0g?ZNxIPZx*s zSnb$=IGv9%qq(ekU|xp$?@mX!9Nctm)MQ%@(%YI5e_`6Jag&6Dq|p+C6l%EkRQ|qF zKfUu2o7nHJAw3nTUm;+1?yF5VI?Q%yG`}~YKE$lqxtvlSEmL~maafIjux(Qm$J7=b zesRs03D2=`cL1yEt(EJJaZ0Mg33z+riHL&ZzfYV*NdBJw8u|34W>f+INWg&5KAk;8 z-A|zQkU0a*gh7A)lk#z6)BG7)Y?xK|3pK1sWsu9r9o%R*O&=8AVSolTMfK zqu{&u`v&^Pr}_nxAw;g>DOg1#l?poWW<9mG_Ve6>C2o-nNotbFH?<{+Kk^Abl$5=w z(6VtfZFB}Nvz6ONPf_zghTRH{rx$K$#|c?6i<8Aq!~v?jpC&rf#FH5QjSSTc=2+AMyrJisQL)W zjHy#=rPw7UUA>};&@s7O-V`CB1_CPexk;_1mWxVjH4YK@+!2h%Bj+SkECq4ETN~Rv z-iZow9s@OyA6T!Agv~1wVcYS}5<@Zvvp%!->wl{}gpk1DTyqY7FA38ZzNKf)A%Vg8 z1)WQ3M9q9I-0ZPFnnI3qc6vP&az4muIHbt`ZO2k&u(p|6uqfI_vCZul=)>)C`wLA! zBQBHqIJzb0RXbo)e7^QNnX+s{96<0w&ouEH=?Z-Dy&UN}6du&qdlQMkbv zgOy%Ntfbc*#vdFKn$Y-UxPsyBC5C&|s$xlEdlh(|bU~vA8rfqrF*JL;(h|RfOA4Z$ zltjx{6D7!4>wulN`K%2@hoLJrTS5Q9H;ZKmtDVg)~E%Y?|tN|w`hZ`_Ka zffs)6mm`(jw#oe5fLa!$+*Axt>dpb1vF^>IyT_#Zyu(m7kOgppN_rpG`VRd_@`{^TssUIt_Utar5NI=}e%A|k$*R`2c;M`&v(XRzky6sY{J>0v%; z*=!SeriMSTj86g0zC^OQVv0&RV?psuu0#eu&t9)HeAFfoj`rN1+C+)0^2xr{)>z;n z?c4c5Ls@aa@Z9`^pk;#aEv^3i6@E~-XmN&YuAZj@R{a-laF+I%{pB_L&9I=&JMWtj z4!V%d&`s^27;wEPaOe0nrI61R8$BcuSJ(~PsY#%qEIdCnT%NnJ^>gv$zgEpvNg8f(o~O-9Eot zVU{4cPHv`smc5t-%yspSPrJ@Wr%@^;g1D@>jeqC_56(3^z|o|JyUwR75DH@|bhc`8 zd%qbnx{dY>@}``&2sgsxGJyXv#~%^6>t)a%gAsY6nK5Q~0tNdnrc>?`USFo!qMF=x zr=Qe91^bdyNSjc2K-b|e(hz(7pc}Av{mGXY6252nKt-Lj0709!gj5$jLxJ)(Y?U0M zW{#Jt^6LU`Fp?cAAq{yyBmkxDYVm%KLATqX`qU7!(=juG<(-;&cM1EpK=m!hb*gU+ z?{wF)_lW*TeJ!>jT45dEAq`3nU4>}_dh3p7T#|NoQJvI zokd?gBW;ARO=OFLvDVefJqA;CCl`ynd{FF{%LI5MyHg+6%38#uJW!(FEe7cooJn!38DP`#dc zsuM|qxH|DD5R&lxN>(u>y0mw*+WLY6LiUITCHCFZ5T*J1IWK`r-CV6{Z1r&r4{<(o`+@!N14tx5$ z!??Az*1c0Gyetgr`}6T!xlyngmu+hH`?BR?w8_nFL$|ohcbHh>oSXBiw2F63 zflH<`t?^W#THu?*VTWo)b~D34kB;^tk;%!)`Yh);w9)7pE2oH8T&Rgd>|Ljyeu&{t zFm$peyg|_cmG}Zp=VS2ci$5+HEh8*@Izn}!G>r*78+MRd4Nt^dbRm;U@+|&SKRYhSt42^>)`v#m1gLmFWPI6 zbFxIka9b5X`9bOAb^_5*05YxRNcM$HI1xd}PYd>k21mqq@=5<@eg3X2I*W1ncNIc@ zW`O%TsX^`%C&552!FC072EAGi!zT`P#`(HN4= ziLMgkwb>%HfQ~^8eW!e$TO8&X5%KUqvd|W^o1}epEmJgmK=-5aiFf!ujvkjqcS|;I z0JuS2t8TQ+`$E@*dS(}M4AAtzPqPiQ?io$Y|QkSYda_aFBYW@ zM0BgKk!~X=+vfz3^?x6e5ye`=k#z&N@9Y7yZh8a~#6;e!iF3f?&a6 zk7sHww``rLwd%?41L!ILS6yj@dp#}w_bN;^RB2py>dZWhgLRAs6HXMEp4sOQ&8`6m zNb7|xoxMl&C2O~<9)h1e{cuTE!`JTbdpzeOODxq4a+`U9S(rn^R_`xR>w71;@Gc%7 zu``*Bn&}OT=|t!7cP)!%SihRs)Ht%RR{jX00FNISWIoSjwwo8S7fwmk?9gS|pEg+t zQ^i>tHm4&oJ@4<3B4|{}i`6KbNqmeC&Pdhp6)VrZUp@%+BW=t_L90hI$5RneAVl4o zBV+E}N;Y22Z;ysSN$&2Ce-!|gJZ%YKR?EzJ0RQzcl0GwZIE_M`F!gD=rEeH)OZI!< zATJT!TKMBUB4|W^BC!`ucO$}yh8m1vrmUV{ilKNszl-zm*;Ti;x9Mq!q9gucx1U#? zt4mmCjrpwNFFmztlK?>eR&S%5qQb1tGZB+ec&pTOu@>!Vl_{QZ(9bzECnpCkJPuGJ zdR0ttzr`Or_#l}yW#fD*K)YjM$hrQJxOGcab^O%UEwR>Mzz%#Ol4F6IwJ2FTztlvc=P`On6bd?e7&{}`T6*&AR+OI(y-_DFGMC*Nl0W7p=8C+2erTL^ z*6O7bo1077V3v?Y_cv5=aE-vK6Qx1q#EJjf3mf9@PPa$A zA{@D^otD;ZZ;hgJ?BXMEB>V|nARmFF!G!QvGY<8Z2R`rkLPs}NDlW6L)nR2}WYpzj z2&+@0c>w5`p}T)N*<+R-mx;r)Y#SMu-a%}gFsZS)TS2Wp{um@_kPw_)drJUL+Jr9T{_U*?``KC>o(C%s8sZ>7YL4vs!%=$A1$ zB`4f1UrjgL+WA#d$j&h(8VF}F*~tW<^&qIz2d$l5LI0^_0v?snMWW_nZ-PS+oQQIXNR(S{WZJU3EF%Es^JMk8` zhZb*!jaIS<_(HKFukecAU5zU*MYT#!<``D=Iu!I-&vhHT{%kH5Wf}f;fFUI_|MbG~ z`gi)Ks}ZD4cZb_XRMM%g779zhgv;bba#Zr0s;hg%TmP}Evl}$58HsZxxr0OEt)SW- zu~`AUL5j`q(alpPtll!^K%dhq&X3M`fU`D=cp>Oan}5vKW>6$E83NQpd;M_$ue`EW zu%fa}Ccm*Sfp=U|AvO?*Q>j_7OAn(YBM=n4C&DT0%>Z~2s*8w8vkE>dx=BZrLt6ah zl!u7An*bqb9RI@O;yMC)9f=$Hr#A}l66pNHrYp8^Fz7|(v;0>mxO=|drK#PyhWbq5 zg__XT75pScQT>L&kXs$h)?bqg6`*8)E^|c{=}^G5=|$cm>BLhOy(qF)#p)6+$=0Pc3LR^PyYuIC5PzO`|48dGC;ALODm)nKdu z%+=d_9m{2pO;y+8j`Ojt{;3Bblo}AO*)2CkJP=%?>Ly3ZrfU^Ab!!A7@vV0gPoHy2 zO8ZR|E7!-Y?dias@oA_qj}H&IkmNjRH^3F+Bmc_{uJ5;5oxE@AP{JtqR9voCq3WwM zG#$ILuL)`(^_Jiet%?%}I5#@4``0 zsfyI6JTeb}Vth^B4w2X%A^LJfDyN14`M*xaEKgmxyp7CbZ4msqc1?*{z+3xyN=U7U z{#|6H@z);=1mc9w%6-z5ax$Rk^{||pH5qap$~Ss5x4`T+gPUAnP}e?exnW`JEIf*+ zG{oKYfrM_iM`s4AvXt-Qy3b$#GFsTnFE#h{4&AhmVl(jcvDF-AOWdw~KMuzF#?$pH z{M_JtvhCy-0qQe&sSGBV`G(8PFqTid^OZrxt;CH^EgnJMq|iNlFXRqf_9^RglOmXW zKgt2!CzRFuB?cTtK&1uAV(lq#)NjZkSj`_3lF zfKKA_r_Hndthg+N0!jN()u`_{flD5DqR!9uaWpmTWc2CXI{xJ~>;Z8soMdbV!GPX- z-DCu%Zom6EOHZ!-ZH>0i!o;YZnkV!Jp5r@u-8b5S?WuFsk$mnh!)Y8D$UW)z;1*jB z$<;nm#`&(dz`UDIV4T7!kf7q>EF-z!XqkBG?V?mOD1MT}pw4tq4m zF!dCqaCnmUn|C4dPc=G=Ep{*-t;?uoEU3h(hEw~O(T7o!02Ns*^=EHzR!bfahexr8 zUPda&9pc0F@5p*jRH8m2N9@?+t>E?qB2HdjE|I9aX1l3jUpM>ejlG7M9}QdHE=U=F zw)8I#`O)VU3<#T(@A;cw>X=cXWtQ*OSYdKL(Al1oMgVj7R%JPZJ=vXo7$O=>D3cD9 zz0H3}PS7D&uQb}zP>nRu10wK@w7XP7L9JBmTfH-?z!yI^?Dv;=UXZqf!_!w=^{Rb+ zQ-y}=iKMW(!+x3)J4!!)+92$V4?Ev2j~dGt9H_!%`~r^l!@ahAE8Addk@ReM5vUbJ ze3k;E32dv`*81U0|Aj6C|M|$%Hy}XEH0~VkMfd*p_LLB+{c@t1 zyD@+sdMmOh3>HIEFZ_~g(!ZHRA^KoVb@*cqu3Cuv1Pem*^XKs#SI6(_EzlVB#{4(# z{w9`#5Ycb|qLI9_ukQmuZzRQTw;ja%B7LDRv5&qiTbYzle}l^wkD)Y9>9|^wE!ucN z+Xp^jA>CJ7wWp_7(OV|l>pMJLp#O)8en_G3wCQ}?n#9mlFC|E?K)%)Q~C`0Qnwtu}zrVmf)N_A@d5d>%%tq4eXb z8wu5W__(V4fZ{PgSvN@!RqhIf6-!+{e?6Q)h$7(A**a?|W{;zu z5e#8Z8}RAVayj^FxgtA7&NnVplVQ0xP!&mddVUk_-OY4?OW)w7NNZ~-v>B;Z|4p;@B1t9BW25#33}2PkZ_j#rU_ zEHSiu+~BH-RN{tDJaGeI+EzOI0wbzi>YmRweDD0$Zf{s$jpClSs?N3S|A4|Jl($ay8%usL09+F%5Y112Eij z;9x6kh6YluZ;p(YmR4Tuli_v^)6Ml2T-bnAN&_xnH+u?XG7u40KLC>|s8$10L4Y&O z!;qO7l~TlKJq@A{UTeOE$ysS1XO2w)z%d6Z%)S-X)iBN*s;ko@C5g(T3LpG-(l0z(?vI6174rUYSa z7+rQY@#%Vxg85PnvggXrD~QT!$B#|$yisi*b!SxzJ~?%}4qDlLIGCaRwDLMf*HSk; z>>z%lhWV=O#FN7hz7vKNA_hyR%vYd*oz-HnZC&)5IE(0 zfIsqk4hul4-zXjO`(wTTB~!-8Iz%Edeb?4`^qa%!OjoX$%n?z4UkCn}Btm##kGo$B zuPgw!8;PUyc~Bx}*LXKnk-~f@ah@M5`V$jw2f^Dlj4in5LXM1?*Y*Fa&VLDC;veB# zuZ}!_EX9gQ{MW;60G~@i&+M{nL?j|(y%_qhul~HARs73O4=1`ojOe!Of?epYQ+fk}x5#5lER;V*h<) zq$uz}Qe;KF5-bg4rn<-O(U^n(`>K&{!2ZX$O(z=-{*OKat2z1o=N@IIXP(tFdj%Kj z^Z(wXw}?7KJ0Da9)8G7iIqi-Te`GnW(hpsKfrAmAz3<=81@^NY;UjQ`BG5r{hW^h! ztfD#Y#Kp?Egy`_+J5k;o&8;N%b!&@Pcxy2b?X_$;6+Rbbq^XqqmJK znYw1Y?)!_%LN{sxe1-EL;np^%T}1l#nqQ&Zj03xHo>NLW_Rk&qF9~xcevvShp;>T9 zC!0J)JZRt7H~bfL&4VuJ4q>U|3;yM3}e@NGV>Yn~gr-M^gEE+Xq`97!1Bg7 zJ5>Jjwvww7fXeo@*M!m4U@o+J+hF!+ehQJNwD^oKJFwFC?qrMCOL9W!{eD z{~YY%%b_hQr|t~>dvj0d{v29~?%#*jmHFacGOI>j+{+N5Lxw+pdr)M{Nw|vvqN};@ zf7SG%5Lo7|oJ#!8{~G2OLEr_Bxv#zC%RkT5+4jYd##=CaUtM6i3;6S^QMnJDJO6z* zLUiDu^j@z?{CC+W?_TsrXZD{HhSe*JRYE)KF49NTu)zW$*}h8X4DVAKzE1jIRRjd) zV--1&9;(vvNay+Y*0!qvABTHmwEulLeMTuXy0e>YX!9lqTs$Ja+036ej+#6tqV(*B? zv(DlV8*Kwsd(VR~m$pm4wiSucL#@5)-V=pdLjb03`;9%F?}s5b?p`sr zd;-q5p~liD{W}+kfP1(ogy%@JE?jnATAD%&;3r2*oOgs zx%J!c19Fnx(tJ+pP{~A3R3MM9cb_cA+2|Q3-867(sClpu{glDMwEBDwkRbe=f3JUg zw#!zi)k00JbcdCmUX6r_nOh5CFc=FSh$dUz-6fov8#;gkh`WP;`kCoem^+C~x+bMu zHUyCW4OA?WUt8^XUYp3HsVp(k1V#xBJZXLoSshNY`{8n#EZlJC*U$js;qfn7ncyPe zcHjZh&?ZY59I0t(vOuRkDz)M(7!3Mw6-pMOwKn-YabV;+2^1`=A-cJ0E3JMDM4idyr=AtjMIB2sN?UL z>F8*me^(x-GTe<#;zW=EmtD9ldDz>A9AB1c2wg9;g|FYqd0PbOneu14Dic zR2CCKcH#aydb+yw_efwle^@MI>G2VO4<2jhSWd1$Uxee!P<))nj_~@W83T3>)I@<} zlqCJ<#!&vJ+4Au=L=Ud7)&f8$KO)kpcXRP_f8)AieM18lKCesYRLM*>wy9fk z%NZJq=<>_aFCUCm|M$|>2z<=M=&27w*IQ?$gDb|KFv&xJy>b4^ZD~cuu&CtTxTd1iu z977tY?-2QX$oaY_Kig!*53Dl>`#FJ+$^WJH5$J76)_S4Xdi%8QI&Rh90p!zAX+ERi z-H-V|L9wfh{_e})%0WWM{@o*&X)IK;%Za_GS+%xR&7H_unBH6QKZhE866Et9o|LKQ z1F!4lglWnw&O4ZGkr4Ee3X@6Y#n#7ib9P`9a!?7FH??VV7&wfU^h zSl`q%nAKz=QWdo=GAHyj!_!h9m~vM+vfqr)^%W5yzR7bOA4(`+U0(-3CPhY~)ki91 z3o@~qt7fSBMo~ z+Mddj5Sg8y*V`Fg`r+`~zc`HZR(@l0HfV2RG{D3B;=|hh_F>_q zDJABQG~#M1lE7)gfD*tmYl6GWz**)%gCqjDvc;J`;5M%oAW2K%^;X^{MS;@D$iF-L z*M8`l3Td5n2H*)~&iu3*je@N_hrj0Vj#t{A_4GrQkYGTSL72x}t$;2bvhfl1b=mPV ze;EkTn32|5iyc*8G8|FEeA{Q`os3qNr7U={OES zUz@Gm;Ibh7TCH4~3=omg=1V4h!lykZ>~s&3HR}scBeY72!5-15HT|T;>}+#$BKI<& zGUvO+!ZadrFVG2Vvl(c3GlmPQ>KZ9cyJ%OdF)lh3$*By7S!+2z-4HozZ3qUYUd!Is zO|Vc95fyyhi@v#>X`JZVUG3nvSDkL1sy5;P67hkZS)a3i8e1{hZZfK>t9R3EvRXZ* zz_yWdLgMd>48@PgW`6X5=5T}-iRk?;R2c(oh?~05KW{OtRYmeS&KF6-)aK2&ZNENStt-oJ~&yC-06Ca z1fK_?Yki52(*-1Mv{GuCm}3xD)4#3unhb33cK&AAyyd{eAcY3FH(@fX&O{Dy~oFEw`XA`d>^;faE-24^nQJE6S} znl|f0(C>iT1^s3Z2Pv@#@SFwf9WqAc&Rn)L3#-L^tV08T`wN}XJ`KkUbO(A$gRoQr z9qT;}DeEhlqm}mXT7}ieyC8X~%Le0vx6KF$J+VY^KfiBaei{4ht7Sd^Gh~)|8;qJZMEyC8)-bK^xzOdA`&uv*^i-JWxem4usv3rpPDR@s(f9u?n^IJ( zr@)+Ir82y{Z^W;FRzSRZO3ws75j{U#vbG3B8^Ir(?%~~#piUMsr)Nm^Z&7!01=4h} zG%D`_A_^715|pj%R>Y;_tBtwxJ z0j7&|XKLrK^20w7hMUEUMRR)hPjEpxr-{)QRl!y%@yDWh%e_|J=b&1ybPER>7_+I! z9R{FbCSy;aLfaQI5v*-1ywYl^I>k7#8aAn*Fr%;^5v+`Su4)C#Ua!__L-@8wQ>DF= zq>||@!m%1R2`(3Y%!LNgiIEn6o`zRTql+RceV2aU+y`@PEhzuyFgzwFrYa(nX>-Wb z4-6`enx8^zhqGM^YOXPz9QNx``EBf^7aw`n{QqEuu@FtAxs> z>UoXj9uv)z#){v^lN0lOfje{R*N=YdcvWwu!xr(5eu;s{akEue{I)WH*T!bsQ0$!kSARWS3}}8IVt375> z`c|uS-T6RrY*<144mvCED%%G=bGg|2u4N)#V&X1?rc6Qt*x#DcQ!+m2Z)Hnt5N>W; z+Y9mIl&LW0#^jewcC1c&oOEQ;qub%j9zpxU~F&FXzw{N4K>}@XbfnF>JQni znf@_#FFT4t68o!A4i1e*xgChJD&4t*l1u}dTt&`e2Sf3=9e}ZUZBydwd=}H+p@6~+ zX-lRrhoW2G45qf*#u_vE2?>zB*%8rHN=Ny3gxP)#e#w57$Nuk4;~>KlT2djAyg02f z5@r1y-UC5j5W@bg1D1tO3TWPS;}*^8@aqh0NRF1uBhQ%vso_1WN?V@&69_z-Zh|FC zSi%;rbt+Pi*J&_c@?4-Yh`m>?YExDOY2JxPH2o(5ko1@X9c&$;GJ1+Fcn{T2^=p zlIh0+JtBM=RQ@P{SxMZHdtZ^)A6HMR#oH7D<+D$npBWKJg3V#U|7L36Ava-g8FQr^ zU6XlBP&>jNb$RHGhH_}nfB=r7dmxHIdL^$t_2b0*5iX~G-%XmGi>|Hn@pP!{5wBC6 zC~j>No<_%a=j09_Z|-+77NAppR=k3>rJ*V=h8Ow)8Q~Pbi^F5{u!^ll4pDZsR3Oe| zMCJ_o%+(|_mA??#V@;=)){Qw>!eoW`^mrdiTk;^G!D`AK8=X4||EvlQ*V$*f_d<20 zkD*%wk-@Lsj@#`b@7a6>qO0%8{Ibg!&%ex9!-Y!&b(8Z^Mff&=(i z@-mS$`b{P+m_3T;C5P_C3|PHU+@0KYI&U}YXa8W!=Q)T9o@MafF?mSWg3aa&^VYWy8`G$ zvi|zDy~=>*^l;9z?Z?vrBY^*U=^SuAXz2syqdzsl+_h{50`+1sZ;E$NQ`zZvz*uw1 z^>b{caI8z_%kyq%5J0yfe6-l`E2Vf8h~_G7w~8U|#Y1KF^!0^0l^oBgYvOf(0|bj; zXZzvYA0*w>4CNfEHO5d-wHb>*egO8{8#}Y{R`1no5y_X5t z8NvlXr8^o6$Q4G-uFYq0V6`AlxpHTuGQ1s?0 z!Qt{UGdou(%b2aI3J~|2L=;8(c@J|pX3h@K3`W6GL+QqWa(crwBDq~-LWdOxu?YST z_?gMVVDM;u7|!O<;wPVg)qxm_Vu!(?brYM%2_o90ae)Rpeinlv5qtvf+=SuaId|4> zXd-JjVurZe81B^-obfl`CY+Z27nqNOb^xvrr=4J1Zmo#afe;z7PdY=vHk_wBTuLbq zLe#Q_#ZOMxd=Peh^(sp$zPM9xcobv!OWBB~aJAw*hWH=fL(g1dY<&?E$=bBtqzd=6 zO7CcfvQ)Rv*||mHP2L%&Rsh#Nx?f_VOa0EK#vt|&D}5{dWaXY}o*xE`u|&{w*re&~ zsq`tKYMv8}qqE80Oa?W2=J`{MeXpH~9Asv5ut+UKUXArnyB`q>I_(JS^Td794hq&!2!`0Ue`Q@u#y&N8|DzP{jKP6TU+0){ zxlrkP3lsVr(`i)TYphn{c|;syc0?}(&NDwf<_3%o#*Gu_xZDFFYz&61u9`}X96u^T zTtx+Al9dP*%38M|CbJFX`1TlogzQa46X|YlRD*ey!4P#_eiptEHIYkx9Ao>q)u?N=zltxT?0xSCBKOBHw(Y=5i7-(@tGzf)C! zO5C|@lGf$qLV|j)`qd8Bd~M%ZFVn0RfAAZuK>fzxSOo8&q4$8!T%TE0)cT2Y)9HxcC% z>nJiFK8=8aJNiJ}H{12ZHDGRfYTKopJwG21rD>|xJHTaAsz`;zk26Rr!98;)Rh|+1 zeY0)19fYy-JL~G^doQy{?H3M+ul`cDZt?mJMED-f6BtNXp+=W}DErKlYl@N~BE7<7 z7FDj}d~?gw7vytbeb;XoM-ejUH6KL08F*sJhw30XbGkpr3#7wTN<=r;_t>2(FY=kP zPK=59hii1DL>HZkl{i|R*)3U6+4h^d4z>hB51vDmHEaYn;|C_wX*t`nor>aG}DKe~e%cXT)fC;NfIOXqzY$*9A zX?#F$=5D9zqF=?V(XV~ZhWaCBx$WfOG!lxU#mmpy_CWjCaK5YoRwXcK8_45Jv`}`O zL!%9DBxsu!+Aaee@ts}z5J2YZ8!+O`tadDth_}(yRYLSZccUk#*OABZzmEi3w@9sj5FAIwP zc_?imegTsJ#n04;X=;cYCs?(rvXZDg@9PALg0;#Sl8Iut{V7>5Mu|l0mp239KuUgP zIhy0Pln(oO8s1FhS=#fOIY&KAo5`&4Lz(6@1A+RZ|Hyc=lF=WSnEMhk+gfVFnci$6 z)r6hxY-Sm1HoNGQB``vyM$G%He3CP=I6yYX_tS2na*v6?_M7S zPe_VZs}OMx!3v-1LEnl2g#*{Uim!U$wGGw?D~{aDD{g22h1TvN zg8~%A7s_?Yp=jbwOl}eB?XT?*-g|P4DPg!N{V?ad2AK3KP#v9k9Va2q@1wWsXLbNT zLS;WH*RY54@Oo=8gEB#zu0~zHZ@#h&G9z9{B2Zq6G(6_UH(XixXx|njKdzf zyRRGRd9paSuM4wECvH{`k=Cc;7@-N>RhG+fuWiKpa#R16e(sAZSs1wSr%eBxBzTn4 zMEukj%IO}vtLX`MfoC1rR(Omn@FL09Y%0QCU@66v?=dtndX+7{yO@Jiy@T>&LufSIR0ZDfvR$w!;!QnR-z(ptRY}HK3GmD%D*%N{xkRyC~da_3HU8|IruV-kd zJ4k^PzUOhBh6)^Be6C6Xben!E(*&KiJ>zu({oeXgrww-}a}HrJ&m8OJV!BS#>-4QS|)a z153Z4$pCDWTXvf(iN8bNxlx4D=cht&c$O1Q=JrLcNLp36ZBE?mEIKA`kYbVKs*XOj z_62{YQ8)XBmMJd*^>38>z4aTY0*vh_XJwe0@dvFE%N)mJtwP00Vt0-kg81@4c$)9A zp5@@tTp^0JRnZi;_^{rhE}Xc%{xJr!O;uI(Pl9ayk!0nt3%IM-$?SN-okX9>vBp2~ zEDU2$3QG*{&DX-1Dwc;&8jb24EjnZC@4)Ikc`eQ#jmQK!K0pYUEFTk#W06jhP!rL!`JIhN9tVmrWa z_RIq{KCe`Y2?rlCfu;WY&PV5geNdt(esyODaLe}J)ArT$fZiXct8KjE%doe(;c`Ht zKUdnqM~Oy7JR)_QPJQ1P?x9tP$E$!4A(UBNFUUA^bC0nuG6z-~@~zO&FJgvxH|wv1 zl1<}Ml(10*b!CZ}Q3Ts|eyK;X2>GHxrd10lo@IFtoEvdO#%{edn-3Vuv~`ISxl7hb z0XqU;An;e(gungL2biJ9yWCN!XDkpTV9b1=K#hW=Ox{0yQ*G=Jx~SY8fVF>CU2K|T zr$29>4B#Mx5$ea9tmpbY;Yxf=_1sOi$!f}m$%f4=y@_*$p%2CIH87qpWp98a<Xd&ptibVhWY>S3@M&f+CO-}PX z1>lM+C>8^nwa1&C6`Gw+ktO1d{R>gzidD*k#p68488U}%HV`Zr2zw%ld{f`U?Pq-% zK<2dHhlMBLD^8rglMIz*cCz*ac@n8Zor*tFDu1W!nX`I@L~n6>rnE0)lfE;Sq2h1i zdfuzyBHE)C1>M>UC>z3yvPFHg#jpi9QuTge=E`Ke_I_G-k z%1$PuI4SMr2R2z5FY&CX_TE9~<=EtyzZ;OQ`w5}B{ z9wZenPfg7MnZnq!4W3*cP_lNRQk-5IW!4nH?jQ53a+=!g+wG$*At5Vr9#_xddI%d{ z>ClZYuAKAGwiK$uEQAGsV7R}Yw>jWZ(e|&6`<$9JuD(0^anI>VlwJE?5N^X45`o zQ!DqJ$K3@3l1Lc0?MXcX9LlS*)rYhmN~zFU3>CEvlv=rdcel;~6eXy1>JUwi&)zgD zdQTAzKR9!Y#w_JelK9+}4-E^o+vxoaXsB9beL2K6Mf7}e$5qC&%Qnf*qj`d##+j$G zg;)kNVhBXSabvVZ)X|=rq9SGW#PM@-L|lJL3`AhE7=DY#T8o=3QE@BC3T{nL;2xE$o39C0{;*V1ru1BVK6|U!Pa1Y z)du>Ul3(J`$1inO5ZRO0eot+79;83xZ^Bt)lqB4>ohF=f#_)Ufew&KNMa1fdLix{f zD+1^R4Wdzdx$>dEAxco98+wZSX+ zu|X*dJ|e53#00;l{M-T_CU#Dz4g~jY!JT7StHe?Glkw22^C@r%nf>?J^w$Z>+g`QK z;;yAv{k-2cC#n3xbTEoiuD2C6g^IuM??-R5pLr~vh7uS8(k!qg zQ`q4Wj2+z$1qQs&uj6u5SxZZVZG>%|3BW-|BVvbvX~*Pz-Xuj}>BZo`RFJ}Uph4Hk)r1$T<|#l_E!l>ZRuNbTAtT1b|qt1{Yd^s zWewElzrTNGMcs)w{<$jwW#Mpr^==qzL0BmtlBcqVA`gsx{}s}uq+!x#cN4EaehAGo zZQgGR<~@zlmeF1?+;~<(fpR#m9H&3$55x$Zgb1C}3rp+HLN3ea^U4A{b7dDxs>6LgNvR=xuxM zeRkDs`8;0(zq?Rhe<6v4Prbf7J%R_&dA!VOahZYuqKAoEGoz#rM!m2yccBx;07>a9 zEK~ExyD;^vUf)$B-v~O5x{YZ%i&G({SWF@c{oV`K0Q!^D)2U=1MA^IRW191cKw0Bc zC`BkJGCj#0k%-Kngod$^j`$CcY@QSt)QVosksg!SK+(=;OMP!}{GZzW@-kl{|FfgP zJQsf$8(&)5sT6snBv3#i>YJX9f6AB4JTXvXRpu|!lQu^Z!9)Rd*ICX5E;X^XjTwJM z?ejxK$7VC}VW;!>Q~vEgFV<5(t=)zOWaM;3!ja6x+`gzQXnuU4HIj^ z;S<;PilT5tOj08wN!VS{=oA#TJ#T$wPOd#(R|f6RdQSyIO%*szI}zXqQwd;as`WAT zr#C@vLDqCMIez-#dj3XP7C$}W;+f=1?pwOHDcjNO0@yh!4$?>S*2~T1wy{C)rL$S`3TR?5)rvjI~)A(iu@e=2Pni30d zx!=qr!49-z^JmCIPfq2gleN0kU(I6iHQ-(K_wN!T<+O)LL^3KzkB`074RB9mJ4@`- zuinT!P1a*Sm{J*`;OuNo0w@|)1sypEczYY6J(ePHI3j277{4?E z-8AP@8{2H7?KeGWVol{smo&=eWd8S#w55&Z<&l$>W9GIy&r1I2I7&BGe0JdGQQARFDOf-xr>G6f8yHYU9K7t77 z3|(MIdLxQT!()!GjxkZhb9{#x3gHJP=G!d zM_&+5cE=GKrofBU*#Dw%x7TlOP=xAhNk32j62R z=9`3~7p=-+14%SGQi|tG7%w}^A-AHsfl_x1U`Yv+KiZ<(LU@m%*}>AS2Qs1ki`Jte z>JP2Q4|&u9fk4g;3Wmsa!OZ|jC+C*<6qE^2L;o@5&M1CYXM;lQzN~K;;=IlmUCU1^ z7hYcVPZe3XM#{?U%&3e0_7c|C+M&C-01c$+cZwjovI)bFUIbgPLB2TxTrOq7nTYa5 zKznmHkL1Yk-mwrQxL@MxZ@!OIABnqAOmdQ**f)Bpfs%ShY_XI&CihfOb zf=GA@yFd0Y$5{uN(DG6Q8Hs0PB0y%k6=6-NwXzPoV-?BD;huwF$s2maA`*U>$Zk*V{sBrTARJHMk)nJ9#r7^-EyV5J(ByU; zBo^1~aOayV_!@~QkLahx+Qf7=)|SYgYX8|+=(xL5&uyucMzIN+8aDLdX0cXZskZQx z_gh0v#dGJl~f5s=H|E(Bfm6o8>fy1g$r;r#=yU8QP=Hs27AEs&MT1 z;jxA&{nVh9tn7<>I&2`FGV1#ZO){m)7XaF?qF7WcRyr_;A4NQYP{N9*9lEUWC^=B$ zMzB`izZ5Xt6UF#2>U>9Lak78G1y!sO1E8oV5|i^0_e)~FzW{^YU;dPQ8EXC)Agxqu z5{RtKPc}Q92p)~AsOChk;_hGygcj$+vY`k2+)gGK#oC0g#+!k zxR1$dS*|&-%*P`sUXVBMJOGeJ(Zd;CD`MH=aURiNAUim*=vm<_u_TfUotNz%XFgK^ zOpg>iTtsJukj!Zt3==qjcoSmM$2-B(moe*{(~|`eMY#|1<6!Fg*%Oj*_(;Jbf9#}D zXR?v1E2HL+rF>uV{60#Z)Ua3wMLt zAWl0|8!>{Se8sFg{Ahg{3sC1+qG)pI;N>_p*UWVT*9K~-Eerl*Bd2G2RQS=P+`aBHcvghY`6h&kYDj3@{h! zM?&D^D<|knkKUgjyGBEoJ|Zm-i7yV9evAHd?2Gt!El$;~*YX7>G7t0L`IlP|{CD~d zNxyXSpbCzi$GByNKb)jH{AvMHo8fTgj*t>O+4E}J?{j}3+j}j6OWZaV5NM4B{upRL zz%Mj_@whM}IpKsTUTjTd4s)E86;m&9obC0Bq1nmpgCTl+e{waWq!itz^dP{zWUSJn zCGyJ(Bub8ow&8lP@eVi+O}2H;Mk_Lh=)wXIN`yS(*oBw?4n#C8p=?Dh)k&Z1*{ouT zex%JLn^TQv6fb`%5u5XD{6@kwA?gKGUhtx_iVH1qLU)E zvwAK^yN!?2;SeI6fFG+oZNAp?6M!tGWC(xT0$W8Yw@Kn_jjY1G1~*yu83*VA5P-(l z($bPYsi?2dNl=s9468ox=h&xJE!)b_fO8hQu1AunAr;NjNbHoxD<%zE%xkGgb*VyDP zwXLnKRfYdxm=9Dv0k5hx*v7?+#1!UuV4At@yPtqiM2{e%n<7c3wY8c0Fo~u0Jn&tw z3H++IsG~S?F@PS|u${5&Hje4SsaF;Y_AzZaMy}yxHXF^X1%Se@FE42%@*728P&1LN z2|_Y7*zN?JXkmeBnu=ZBeN$cFlCuoV9Pgyj#BRNnSKF6=PoyI{xLja~drAyQB=dMR zI$GxMi@2K?NaOcL`%Nkt3=S09kiu^{<}vYg5AWq{&id_)t**t${ty*Qenx@C*(_63 zfl^r)*4rsD#qArgqT?oHB~@iB;4Ch(3@LHhN^D|stQ2zxN*#Gv~d zk9;X`V<%-qh9d-k43-o0PElpeX&OYy&%1Ye&}_+b6PCN~mm_B~85g@%8{ z>UxCpzN=ODHi*d;^=15(GWx&4u>gY|Jo)JECMRV_9e|e~D|gZ_aD8Cd5bXP_DTKU( zi4x6MM6%`Vs(;g*>hpXLylHto5@5IjyAxkQ1VkZcZ5XTfz?Pck3thn$MFuZ!0W>h3 z+_)OyH+mboOPwLE?h>rUo1$+5-Zi)u2r@Q|tvF)`H)yz7-e5o_qh0K48*vRc-kf#RoKz}fI7-KP#n)Sp6j zU|wt|3#R+ce%gllMv=&FTk<58>1{_%$#=V*QE`+~nbT;sK+w8DtJ@wvHX$#AZ$kL> z0fqV{9NlIhydbgjn*4DrQNW_^)ss6U6a@nnYKLKI035I;Y}M~L`%Lk1zUTE;>EkDy zNK(SmD>N+z_=_#RN%(Y1$^hYK0M?o}i2!V3a?Fkw!BeXf>nIOP*aEE3=~-=#0JYm) zAO{1%`q+|$Z1{F!dMB!8n-k`OvXtUwPJ}fPc1Wb&2$zd2=6e+?!azn?8GOO&L^o#Chvaki-fNbjF@2dJo&T2T&P=VL0+ z!Z{f5*CBqdo6WeZr-H*73Bmk|r8C}lgJk#7P4I7)VbgmTG-SYrH5p=6y|C@sMAY7ZJ%~vk(3<7WF};Je9=T zBy=#Iv*=X9{fGTNUP0u>&an^QlDbI^5(db2yW>Xed}~EM0;xqQJC0I9-z;G6d`46z zgNej-W6h^&y-vJeY|za>;O%n^1q)+Q35=%QyeJVk(NiepSDAcZ4VmkkT{W3=ESXpY zb6r16IF>=<_fz++i1msAkjK2Gt`31C-_RO)m6l{g`}ZcBFq~mkx~1;!xh)qtmAEfs zfT94EgmhOp`_-W)KU%=EYT2^j<#(IF^bZb2!1O@&u#t?#*U!tGIpc-^aK&~b4gjuD zBb=;nh8;_5-F9vmO+;#MvG}{>$fe&$GD+~k0iwvm1cX>w!q>&cXBQY2G>TT?)1S%5 z$8s2)R}++mu`VXl*cZ?kG+<^6#f|85ps{qR>e z;LJa*n#5ZRfBu>xTk8%6X-jk?kp@u{XTn7bwLE9o3)CK#6}{! z9Tu~}sDEjSvffykRF!1LttlqkiBM@3xv}~jzMoyxhvu-P8hoT%B-gP!6Cv_h#%Zj) z^Sq8S1wPEPCc|HE#!seQqBh9h4LZ8NYTKnW6&3BrV*g4B%xFL=m(7&uABbxs^}}7J z6-a|K)>cH5R_(~ylv5llr5ap!otEuXDhc?4j`O4VmuW=8|3lVShGn&NYYWod9n#(1 z(vl+G2uOE>lysMLt8{mFKXi9@H`4Jf^xfy%?>WD`ymUQlt~tjTbL4%GQeQb0Fa7FV zVIHtbjkpi$UtWDj|_Ah+Kk{)^DGa!-!;ndAo?Mw#+pZf>x<%8J;3kL1rc0 zgqyeT^A<|s!%}6v{ck*A9sq& zAmZCB@QMX*C{jVQAh^jsqPn|#2<0vDIkDKt-F z!qOQC5|}K|c^fYPcq~q-NYYHV9Z!7g(}O0oS{q_|{{tcu$e@D_eQnbh$RYD3M(}dl zXcFfpMVT{;8Ucwqo~Z_*=c!La4U zz9R;rMo3m!9uB_uuFDQ00=Q9c*2_*?1E+S*ix_{mo{VsLVMt+|Y= z1VYu%hpwi}o!;GDLgBH)tHBTah!N@X^nJZ>Y}Rfb=-`DeR4%0#Na*WbXTem{Y90MM z&?*G=XRJH&pP^sDL2daDn8!goWiP9MjnyF}yyS4~32Rs3Rf|^g)bOKYiGbA?rl$bK>{5$3Mi5 zQo!uHDgG$~vOC-kVa3GXI1$=qG11nFC*0n0N3)ksDDvYJ$cvec!@agl4iIfhq@4`D zRC3F0@p^*V7XFN=~-OVoBJBrxchyz>I2SCI}fN8oqp8vp62e7 z0~QY)}@Ki+S24P^=uhj9CSmc9$mmCFnS`noTB`q~NqVA*&%0wWB2y6g4b z1iu&bG`mqf6KT{?J@4J$v9sgo@Up8C%ct$$!{7roJbH}=zYCRs@^4X9WQK5}JAUQLwa&ISlxMhqqh-dq~V`2@UW^<~FHGh@G8%3o}%;Dp}R=lF?Y z9z_$UnN7dI!y^Eax)p3|r4vN&t5V3$nmGo{1UUViY20No$Iu1_SRVu0 zm;qn0#J&}nRDK;~7ZUuV0loidz~8ZV<=X!<_U`=#e1pV&Zj8kB;)*7LUdpv^5}A2> zG(!ZiOqo_`hyc(55EJ=+9eyCQ7y-X}V;%;||8U#tCZWr_XzTb=SjJigz&&Ou^~qm( zyH|wcKdV4>r2pM(TVSudTiVYIiYj`5y%q!v#1c#-y`RkxRj5?v z(dPC>qe~cyF?!v47#<+2^$j#m)@+D1Jd1%qt6Us;(h^9?BN<8hCLpM-D=EhF{Bhne z;w~jQxoc<%LI3B^!iW1IwduJxRJoMx(<(gCp<*dg7Mc3L!H&;X_K&_P!8=SC$H(pQ zT#_K^kWL3h(kxrjWJaPBwZe&(fTifl{nDaS-COIoq`&h(BTn@4^F5 z5g=92kR)hD3q;TumF0i!kep8SI_+!FKzIvx~I)jfn^@lci@>puXIKZ zs#H53G^mm(``<`56>7CHo4s%Fs&byYbYBX@!znq!P6&k;U+Rsx>Jlv&W%Ze zVm5BX#Kp_qB0935UAp5K=;>LUH~8z_U2`=+>x4roO;Dvg4(oSp-b0t^-`c;q{L)Lb zeW_K{VOb)y*BsN| zI|C-DQZs9@cPpZ`gCOZR?g>z*?2^OAO)<1%?n20L+ijyo1Y7 z{l)PGs#kyPBrU>$c`!{cW|6;4_}-J~0Vj_PiS*rhgz$Zq{9oFPZ-2!jxcx@@n?J!Z z9Uaq-hZ$b;Vr4R8Ost%;E%9(xjBuoT@gXs_z3l`5fxnh4|F`D zQG+}jlA;!xA=z*g!o=Wyud?z>ocvP zg){@7Znmh~Mp8X_I)BMayCkE*TrO_seIdx;__CWzzxa3xsH%EDCcVN&_et_CK=cjg zrgT92x+qdZ29(hn7JXH#JkWWT<+aZ5ZO7<_@K>Qad}vNr0CPq$I4vIJ)ym9O^`(t3 z1_s^@`Eba2NF_5Xc)aU!8kR=~YDjWl8gV|bm3wA~!CIFmMCULMRvvg#Q5)cy|ITdU zVR5@U%UFzn(*_0!r}Cxk@Z5EH1JX?TIjYofw~+>}CnLj++3?;`Oj0sSRMIAZwecPJ zE76b-@v9<2=^ln-X|gtYzug2ypVT|aYaAAW%)iT(J^P`ptSfHeR(6hu5pj_IdF0gw z04IKn3JVYpEEYPjm~7?S)w`baUmq`*Xk{T38yTkJ67v4`QCl}Cuc{GMR76{8^X}6w zA?#Xz%ZDNHoF;3~9>U@;0X;s+F4XQ}=BJCO-iyks`5 zzU39QMmMR!YisYdTRo7pxJspOo@gC^IDY!|MdON)8?T~*1&8eyvWTx=oVjW1mG$RH zC``h&m}N9hEQwzmLBSA9?pPs4?!I>0KEHm4PbnR}y+j(MwL`$|3Ij0cMAyX1v%~T< zr-kthw*VM4VwiLDO~I6?s2yi*?wE@+vM4ijib9Y1QPEwC@zh$-h$+7V*s6wDSXq1f z#%}7rp1@pS)AnhRYtS{RWcmW{@?7;-w|SAT<*3pj(7;&wMkEsg|~!R4~IB-+4` zjKxq)$sdYJ%bOx9_c`wiW_Bw(#m&ial{))8ABA7K)7}_ZgqKyY#cZh|9=ann>YoE@ zkCFnk@NJp91Uv)oa+{Cc7gLuEV_lGqE(XyKpJ8dV26yx)C&6&$J!bi4?k~ z2fAn-+6o#hQjeM&9HQAw@qP7NaM9@~fYa7Pi|9$k2zyWJob0>QbV5`;saugDK zFW_^&*_ZWUU1YA>jP3e(wXAgld%$TR0Ut6 z0&YFlXwu3w+gH5I5^pUn{Gkkv)E+>!{6o;!PhYmGBIMrw=j>)B|D2uFKWC@l^6v$_ zYM}=-z@l`AgcW1$1GD#GEfES)T|430_2HU9BAg%~DG7&A%h=Xi!St+fiK+# zC@m6}%H&7sm0bg@Z0H%kD%|F&wFO<64XMtPzt)wPw`Yf-NAtN@TLN<-Jq-7PCx>k7 zS^9rW`zpns>!A3b>p*zgTA7x~|Xoe`n{a>A!B)(A-&4F%rDVeStlJGQ@^$AyL- zJ2A#;Sd>sS12KJA9T1jEH8fnD^bI{=c9eK6s%6k_cVR!A@R9A>+S1ZLMF`uh~}>r&Sq-n|E~nPyqC0usW3sdw&tKGOI9eZZ}60q4d8I_yK;fBnPRFxu_9 zf7%jJhCv0uxi=au{xI_}ib6d_^FLegpZ~wg4(wIy{&rSB`hN`NpFQ%}gz(}fp~_;= zZ2kS*cD`zoBKW_b#C;D)AHp|j9khQxhyq6VYG=u)b&6u5vw%54$^Tw(aC@By@SKfN zj%miK*xcOQgUjj(_eIvfG~~a(U>7hE zkv~@ zD}jEM;%o8R+6rODo95u~+hFX`3{#u;9RIBq-y(qz8ApvPod*7hS6(=yuOW# z4hGfvz%*Qh8TUm>9NIvC}>-b9UH`) zxc_CDf$#!&iyn}BsAjuQu}In)icD*p_H{GHubD*fd?Yx%c?KgVFJQ=%{pTIPu6`kZ zeT{6QKS3ARZ}v+Or3d-ILxrG>YyVO|Z-V3M2{23*67YX*|E&*TXR6-0R3#`*LUaq$oU`@$Mq9OmOeWexXQvWNh1?;gVXsA+?W+i5heNeb?<+um1V%Vz7RFt z96e#>$~cgN>v4d69Od5|Ykj-Jr2Y+=SkPnIA*cVZeSd%ds$%IRuZu8JMr2C_FF&V7 zwwM*6uazT8n&<798-lxy^Y>4J=?-WSR&F;GYi$kr@N)S9#=DFeb5vLdz)H%E&f6#5 zS}}w;;rm&Tbn!1`jV5{3e`ZxQ`w3+BunPD_ZlvjjPg1AgAe9-ZDR?im$Tm-#d@H5g zCT#66zR=3{AboDGmxP6{7s(dBo~YmRZ-MsxXR@@!%K?9Vt16PrR0`P$w)KtWs6BQL zh4q=mSq{mddwJVobEFfRuam^5EQU({BI#w@ohEw<0O$o4)yMQsV4Sio!EBsAxSdPp zhyC`z-fSK6N*i+f9!nM~(X-R@v3J!svyE2?@F@!ij&cDTfSErIN6;iwqU4UV4k;GL z@0#GzOKC*wkkSs*AK}PGuVSxgxuOOG#o<%nYlhxSN7JcC{5+?!{SG%&qIaG8UhyZ zYjaCWw*3ZyNw;DUfxVl0FCVTliAjr;YU)t|{*!M7A*g~9RPX3X!QADc_0LaCHGqA} zmOE56U8tt9esPCJr;>mCHOb1#*=ti0_``HRrQE?WkY|ww2OU48oom$gdJ8G-N$Gb^ z`R>^^wQ3>Oe`3r-_7~nZC($k}zq6xr#38~}iJ!wm)7a5GRp>zLfAHWg5>1?0qv@&z z6z%g80`iG&_ap2vyUGLY1w2+!cnTt4*KpRm;^L$8{YffHR)~(56P2R{ZbV`sYPjzn z6NAqD#=pOhH5omZZ4kBAN||cD%pBPqy1ae1LL^!uCKd`b{C(gAE?lC?p-`fUd3!%1 zsy-)KqfXDjpwM7Xu{D^8eSLi$91_wCK#EPzY$re6{|>BjxS)LF6klt->;vQ%X)qcZ zjr&H0)Phn|@#*R5=Wtknv06;|^m}66Hm@QDwMJ$R4&mVx4uyOLVF3P#l#vl>G((`T z!mtmOpm^E0zo!QmfS!|B@6=VV{Nh$9lAM+G7Qcv+pMOc;%q#>1f>~;EA*PZ~6SK5T zW2Y$T1JDu6-G@Q|G&qChMnv<&riKPMEC2`1A3zHGm6av_1OS(jva>5&;agzYe5(Q_ zGG%^_iHqyTf!GAtrMSJHe=TsvNdY(cA0P7A0zidwmIy4=S8Il?R67}=w@I6vHo6V7 zaP_ugSi-}eZ2&BWql7r-|5wbnuNU1nW;ZqRt!!db@4SBI(e5}11}R`*X1eCh(Mh2W zDC7ylFTlsJX6VhWs(;#95(I?5>2s3Ho_4VB8@Bu$%&%N_;tH<(ys}>dC7Dz{72_ZK z4~QSxO$vcqQ8o)}dr(eG9>&I;R^By^CWMt$lT>u^yHUx*+_I)9- zD4S~|Fihl=AvhQ_xhxY5PuvBLLW~cO&nJ0pH>U{$yPK3$<+hCzP_Rd~RedZ78E;V| z0z}r~`#L&5+fFq=1+&%I2QMqw#<@Ge5n&SC=vK1xK`vTak~p}yWLI5UKpznP=#vc{ zGYN?#7(g;f31nr`SUmCg1q1+00qL~c%WP>a9k%*6EcV$^x8*N=#C_syHmBDjj>#Mr zr3=;pYliXQ&RA<+;6Nw3B%$rG#>J`wVWcRp^=dt z3pHM@H}ca5vG8ORG70Z148&u|B$3J^dPxDXliY06@dzJD=Ys>IA|>^OB`5L4-R_#9U&ebr9VCu6_vV{76~nFD6Fjo zfRk9v41+xj!0``t8)}i<55uR@1AK%VfUy2d?*nv_q)TWz1;KtC#y&^@~dKrEZkdd0;a z5T8ba{BZVmT1-JK`M9mL=xCh)0T2+K8@XN>@c?o|d~W zvuiiliVgW`?E~O5MDZ527{r`)B}7qdRJ8*;K%j0o;2(~M*aEQPVcxE`(H@3ndX$OC zhg3R^xs8e7uyyPl+;z=0ekJ%t8e78M9I7T>G_tuCH%wTjx6~K+v!A$_f|-2l1NjuO z61si;VCPTKz;E6^PABWi?vWfp+X-|4c-*&UaRh7~z-B~5;Me6}wFi775@=-g>4Csz z((QbCc1eMSWTE}N#TFjUs^95fA&UXqcQk*w6*iUOAZ)xh)**XaVUWO1WqW<(rqP1O zQOGtJXAT1g*Xs)zW_0(M?e)}{uU^3qFocF7;&ZaVCsUIQu$y1eNhdOf0K9Vg`xBUO zR<}I>Fo(Wjm-zC>qt;bs66#cxXhn4Uuh&OVYO@6OKp#~-Onlr`2yEBR+4af0i3CJso8zdc%Nkffxg2ootuqW;poV5yASg~9t19=H!%;r_U6P_HhV^hd-h zOUShqS15XdrHRRih1zrOK5cP2&8@-`5Nqbq-P?2I40GCCkAK~xUtnInxA1uXv@5U@ z>nFMab+ze&*l;X+M#+qheUM+l$hJGCy(*GR_b5f>+Fs9RNEh=9W_bpCztmVr$}(>s zL`lQ#Fu}zJPLiOe_nI~KkX7BxB8jET{(RY^oIE)`^-i*>*0HF>#9`t}v5;6fw0ms! z9B_&Gk!qtS1_$L5l?DLYmxdt+Ri-G2rLJ7WQZ_DSoQ=_<_*8!j^cx-E)xnd=by2~k zt}{VJNUl!zbmi=7>=Cuw9}?6cM6>@wJ(ZD7xpfBRuzO3YR;sPuXqjxwm0OF>ko@~# z8g--ZYQHLAJJ8SXZGBOVQl30p%nnoh3_vO`0$`sgLtw>++T$+qegQ>;iI8iwdchdl7JciGLm-JDI#9TY&nNQ37Y$ScM&f#6mt=w(ab`e zNiyWylcKR*+y}sDW|P_-P3BQOK4q=}x&O|mg}w3qCkr%tfIfY^&07!P&oLNDbL$lO zaKH?-(eBMx@uABb01#M~0jvd)j*m|_z0|tRI7EUT;g5Hhg_!o)zk{Qq`lbpdlOZvb z?4D&bs*LqT6ojr%wfHIJQYek!KZRBSIo{8m^V*m{OH_(>hJL4EGiu*LOGZADSV&R< zewRo3pAdraHH65vLA)f;J+rL9p-gbTcaAY{`WP%ma9gE+fIlV3?-BZK`Rb)uS^r-l z-M@TH=;{C+szhCqpuXh&=_%Zd(gcvDS@_;9IlmWf z!1e>U-G%h8M}^8u&YIz|MrYNn0b?+>>rc^rWjvk&7@Mj^y;0EyTYKg)Y^7BU3$y){ z_%gg9A3yl2@p;=dSSfPG4cO3O_D4qPO8)GKo~^X-`kv5manH{=Iwyafuk7EzFklIO zosMDSYgqAe`k8(q{z9?F<*PHsb@{p+#`g=)S^#Yqu9BrNdYHZ9gHavd=+kah^jp`ouIs8y&}C z>Rkq-?e_wR)OooVbHFRYqZj;`GbOrd}}sXhbD)Gc9nK)eB3{C zZq&CJl%Y6_`6FR9ZS?rNf9izU-t^*X2zZe$pWeM3i?lc8@h;nx@&_<`ED$j(LCF%& zCxz(WVBW_uN(?WJ8th_pZdXB9IIq8>(ZV7#*;uVX!J3+_4lq4K%UVSugiy z_s8Z~mh@d8FT$4S<#fE#=II;BbB~Vm}dy2*X~23xcN}2x(&4v>qBs@80fUW!2prx{oK&K5yGKL zw`GfMt!=td5$ZZbdPlCoYX_eWP*cGdz0=L0OH3`VgoA@a>x}on_hRgM-{^4%2Ml)> zYs=2Qs-n>!ReXm_vc=8U^!YZJ<@RCdrQC+tedZ1<7BjVUlgoMaUmoC4a|si^k78a{0A9pz3w zJL`tZ=ZtyopH zfRTXNd^WOx?knaAuqXy5voIK)&XZG8N-XfA_&-rifYJtHFl5%{Fsh3NJv zP6Pp&hb|Z=_aeTl?_xA$zUcGIh07)Va1DeFO*gt3{dlF}UEpWX6RLldG!A1#eAncx zQWN<7fQGmMWqo6Vv~n#9~aCm8T-1yRn&3*VZUH%q7MQF7DjRX$}3wzLyNsTHd;xB?e-eQEavL<1?;0-UCBO<{vVY{$; zCvw6sHWbkWh3munuXNcr!B=|`q)BV_0VD+(i6riqmfJ-h8-m0E=0-wA%8b|XOhfAH z0exM{S?(ZHyW;A`@y5OJi9)aVoPS*jd-8qNG3xFLETE1=#ojRl-As783yK9@s_Vjc{#frEIi-YeY({b8{vmZD6< zzJ6DY)M_{$#jdMH)k%}m%X_{b`aQ)W{0BUT6iqK%^VMY*kuk>(tT(2~VTQlmmPix*KSEMt+CK9%dwhkKG5P)8^o);0^EYk- zl&&~kQFrn9uGix4v_R=0#r>Yx#Ub#55ccX3NzWO_vup;C+7J2+{^rmCCEk92uKk;L z-(jsqTV&{lgfs0FXDCoCuo3iC^>)QGm%bOnRzb0HRVi11XjB_18s>x77oZt1gZ9$>DtWY(0ziPNhiI zI6;&{<-$$EzyJ;CR--Bool?DJnp|2+3z2R3RHm!L&1;Ox*imo6OrxQt#nRCQT~T4z z35i>$81wNv<}fhAtnrvOd?8M|yWpROMiI&epRzioId(PzR0u->(*QG=$ug!z@2&v$oe;xAb1m*n)+@~={NW20 z*8NjudN#%_u@EP`jDos$o4yMYe-bm;6hQjk`7sc~zMgJwe#nUm+b;WxER;uMpjLb& z#g76a^EZ&3(duq#d(r62?qJP$U#2U7diE^fd^%rr^BH|Eu%qSSx?(pY_3Mo=;y-zt z?AIEZ8AWamP`c54V@7jrmr;@(K*| zVwDy?h(s!XT(+Cte-o}-)i=fd$rKW;gJID2%CQ_)d)B1$7w#>>dkN%kl*=FXB&Vm(M zD-0-A2NGEPC}Qo7w2Bf8bH@`QY$IB(&Mfz#8my{;nD0Qp@)F`Kfb<;JqGjAK#!%2! z`WE|#a*rOn=9WLR#m{kpBI#)(Nuoq!pmk$NlZ2>B;%P)I^-CC>ZpREGJSD?@CJLr; z#53V2(JI>URl};a9~Nt!HKKn$tGles{B2{#URAC(f2ID-*(`CY@{IDmKoU=3!BOk2 z_7$$l$h}2*7K{bgoAd~K zmDIK8^huD- z<8Ds@FzNM;l-_e2onYAJLS6_azr;TQvZ$sWVY%8GrqPnEUT`i5jHUXeaCl=Sa4n_m z3b=>tB8xa3&glD!S(CH^)bZbX(n1u86e;aLK*CxEo%|FHvin{5tA~h*m_1*p%F^L; z=a`pvo0q6LDa8IrdW*PaCNwgU{$wsgC94zFfcMqJL0|4}>Ep8Fzb4%rr7Tl7do{QefR ziYF?q+IMO2muNwN#;!l!g$=0erM%|jqhGHq?4D?#ZKy;o(<@)KB(S_RUg9ODJ56?{ z_UynNr`LF9;`q?9SgD)fE=~5cjhVmej>eiB%!Bv!F^-r7-EryIgx20Bn`?zbbb_Yx zBLnIgE^nLwazanPQeL^`ynRdVNM;EZljhJ87QX&U^Ig4{Xx%Px3sjcGYe@hMNJeY{ z1tBu%E|fgLla2b-ipA3ynS%9N=(XnzcajJYaqa{+x~zC_`4HQp-EG<$3tzMpGd&Xr zi~BM8$Ip6)25~t6ePf4FJA8WYC6fmJP#Vp?GYkEUkzC$tSCYw}x;t|{eu8c*+o{xP z07t(c<)g=!OZw^kf&u=ponfHRkhHPC2%&w;HSdX97MBujsITu6fjqbkF1|Rjl}we5>k|vkR7H|>II&#E3B{~A zm`$d^Yu+(UO9o+P93){-ha%Tu@*2EbG`=yk#H zm}x2P6Wzu%^*A`(_{P@|R{4Te1!_=tV;ikA0=^w&a*yH*ix=K(j@1nfX=`kyMNM)M z6I`N=myE~Rs9tCjBHgTcNbPbS+>O2A!!%0W&(r%sHg#riB6KzF3P7IOPi^D0{m)&w zmIk_}rXURaBGFE2>$ijK4^{y{^v^&rOjB&SHQ3ODSl8@!zCoc{B3&!?ok`7QBp?6) z*oA|~XFN+2EWxP@sXITjmE0bPPclj=KryQiM#IA!IB^LIX6icpAk~!ZKvKjCo`zcxLWn^x`)cOUqZ$LaJfXaymDMmjxP)YV!?5b?q|T?j}l*WKAv{&>4}$>=*1Z;Wp+43FH+3IcaT%5 z^bmM-e_~INJ*B0a-8Our#Uk6q3yUYqxl6T%$5!9BD@nl8Co2IO*d}}QbVY=kdq6}g z*=B>0_i+E|WDG6c|B^nCp2pv-iuF-~m?Y2eRD)Wt#jH`g$n!p~F*>U`*W;eUd*5H0 zL#v%8m9Gk%CB7+ZO9@D~-r13lQ?OqVN;sDlPqa~zn2aa5o9~jXg1P+UYPN8T$pI|D z4VU|jETyyS=NBKpx%y8JeQp*OGL+d1p^UdOpM1cD^nnEE24qa%u-Qs8O;z<>YESc> zn)-n4xR|sb)Ngd~&Vl%%ocCw=y<)@`Us7{12I-+8cJ>Kw)owFc%*^wRZDl`Jqygh%2aXbiH(6wN#sI@S_}P zePiK=@Y}f*jjCG&=r1!|k7Tc*;->;d$mXxVHM|_bKmy+@p1#iee%u5VQQu6x6Z|LZSEhdT2 z9sJ}ul?4kH@}AHySx)tMO#_jO%23uK2{%%;pp8iYQNDX^SwGmQGm??!_L`(wkX_es zIxt>#?8XHu)fkYi$7x@9&1oZ@O zUL{nAuUC7K3pM8Jr&T5upF2d{l+6TpQ{!&k>7q11O1}?O0x<7@ipbnfGD`gO?WJ`P z(y^F_=nl1S2p4g;0KkZ$k|)QZ{bM~f={Arc1E@p+NaDulSeI412d>RtPk|aFxMa}i zjtv|W4G!36eZ(~Q*f~;-_1tVlXSy32{Jfge1L?D=T}u*_6giKG7l?`i?#@Q|q*fXp zQ>H+825=+_DsX?f@47ZFpXOZE`+d&*P<_qLREiSq^LA!?gec z0GZvL;na{}V{PiqQwnG2pI1q4kpmfcFR*8S5 zu`f!eSC-&<RfdCTpn5StPD3lEqnRA#kTr@qC|P5QrlJN^ZTNU+jrPgjT1|$ZAR)7#2CN z)~=x>-CVdMW3`ha))&5Nl{0I#zin;{1lMHN5%!9 zo*m*|@3@b43ms@VudSZ;v!V01w!IG7`9=m$72$(=xwQJl2g6*-E!#Rrpg#DynV99I zkZ~OfVUNxL&rZ!X#aRwOP)3Wudm7N54PXN^D{+NQ=*=a2%?$nRMEWEF%j2=dM6olZ zR~e(xVcgXN@2x)$U^jtBt+IV>X1dsB3YJ7ZHRwzNTUgJo2&^$Na;;VW;ZVdP+g*^v z=cXh_@SykN_sy06AsUA5x?^E_1V)G_xpRuUp{eV}pnNONT9ruZi05=9ZD8Y0ezw25 zz$!3A$DS<7s*;YWu3vukIYJuhodeBq<7cAc4WpZ;0XK{`Vm! zicD$YU2g$mm?UV!|0`x$sQi;>0CbQA_Dg$VgD2*~`EJwpU;+!*xG@f!bMahpmV$5} zymd!s<{hJ6YbWO&0cuJ8w~d{hK!Bj5;L4zr)8%@9Qj6ck?r;Yrw|_I zirSK`{iaX%a+MK+6Z{SF4@1eVa2v?0V#;N@gmxy{kL#&Lw-3TVzM4^|esOcj*N-=h zptTi9GeAV`-|c4RbxW|ZP(P5gjGoOURhf*UrjBN_iVlA~h>H9ZjmW>YZoUvjN5=~O zmZ`Ote^?1c9pYh4L3!VWz3J}j@LWI|gq$f|n~ctG%0HNZ^jS~eNn<duo_D)sQ zNgHh@JX@dnpx#aB{hk+|+ye5wKz>)m72~1u88;RvJA@IS8=Fng)f-d{!dw-0 z#9bX97lsjjyu-UCFSZAi9YDMh>+ps2pGK@}jrkaX{{r{iJYvXvp&ouwU^zc=H2;i^ z`ZlIdWQqcjkoSXn{n^8I1>QD#UG*;32aB28iu$@yBk}lTy6R>(0dan5>khpdb4>l7 zI4qJpv5<|SfJ%79F!<(t$~tQ`m%ma(-w) z^8+~=6fbZQ2}WlaL%MtlB>M*oU~H;2Og{Zfb#xV#BjV%cNgi*a;iWz4M`sy=1{{B| zOQHNJE6rgDwy%wTT8LbD&p60>lxpiN`n}HfqUvAx@$Q>HneJx1 zHZlXbBT0ou&|57E6l&f>_@h7b@?g41e^k&Is9~ZfCKKyzllQbYIQ=G17(V)`a5S({ zi;&tXc?+f%>5?@n`Eua2m<7Pb0nJ;z&F5BUOE0yZu8$&>Z@%7LA=TrMPq5wu3?IY}X+iI(4ovjOp`CbTWr2 zjpcTf<>zQgiveCG@Syv3s#<3aX19K`9qCcpWh|a%A)18AXRFp*!%_PT3YiHck!>6v zvU$&)0VSMS^`kr$dZ;-2fulA5C}MET%HJB^cSti2cP!pBsyKYs1SiWfPpk5G&Onbu z$2YJi+l_9i=G?C=G=2J2yqG}7Hih`z}0DwCjfd@ zZ9c7iEBmtg^4tj^)ftJ}3HBuGxj2PrvD%;0va(9oj|PW_$Mw7`)VMTR-w~ZW830&$ z;0`mFn{HTzPZC6KJa%*IT%ctx@akp*Ib&_I>lDr(^$J2=N zk&~QUG0&N=HqhJ}R81;cc0N7%O}X_xrP5$K0}aNNGKWzWnPcWP2M=-g89sHpsw8LO z8*E9rjr@9QB)eO_X`pR`5DItQtgljhTZ3i+Nw?(jNdT?lOidN38YjHIJ_Zh6)a0xv zi*-%mTxZ|zX@DV46sxYYZ8@ieY$p}}SKNS}CUD!Gz`{j#DbV_9^6x=D(v)O&w~ub2 z7Rd85#o7~;Th%3ofe9Rh>j}eXSd_^q1I^+~Uoj&0Q+>~6&*Rl*=)9RuCh76{br?Fr z^Uj_{`H|8PMdA&d=_Lvg*?T~^^Bqu517Ccq{AglHzo`ucEY}M8t+|k^&eS^@>+P-*PZ1HpAMVIKH&XL&%;jDA zB^g0`-&a~ag8=&&T&T^x))^QOkYjs{kNPv!DEX0lW#zbIbIfv~IzXd(3Nu0Az(1|S z4?4I$7~n2et!Sjtu18rM>wdOUKtENe{IyOx0`Qk@2hF$&+Gis#zBB1LF(VQ5P`vs3Um6Flout%MKbvw^78ZRhDT^_n_)#N*q*nwq60yi+YMFDfIUO$mG4PfF z);-_(X`4&Vem=n@uTVS^^`A+Db|8^zxTg2^wH>^=ZSqT!iS!CXyrvB<%~rQpXh&yz zMFAqy08~x2$SGxM=gWB`*(tNT%z&S=UnnKc@;K&*vvIxS3)zDFhk4JVB-~qvPegM` z_=cMmE&v+pX`qxm*~0t}nXaST_F|=h0gb$~_bG$Ta*Qpj*3lFb$22A0B&tnl-KGtCqcWC^7M880y?S z%3RDb{?8Qj(vKFmJCrI3E>TO_iy-T$$DVPETezL7IaDA?dn*_IcNn;<$i$_hvyRIb z{QEKox6xok>D%3|g&Cqts@lZuLx(mGg4T>X zJ+w_H5&$&xdI}_On%x@=G&EoFumm{ovxVM`?Scl?O~!?t@A*xN|}jS-NF}BKeqfF0(SX3|SQ)-+_En zUw>iM|56~JPr<{@$>|f<;QB2uT`5`t0KSQC5emIQrsozSUY*e<&xb&#VxJx$S>$f| z7>az70!kV5*&0kp!XMRh3U6O^kzJjS7_4Hop2Nk9`e|Pnl9@tYJ{wO+$b!g^#@&%u z5U2wHMJ`hw-ajQZd}>KzHoFsEy%{q{y_b~e#hjT#Gc~HCQ(VmKe~3{tJQ zS!yk1yG*&@6U4~Z^avnrvZ}b@)Fr?Jck%baP0C4$q0d;46i~J08}=<5&Xw+kvkYD+ zXjR%K=zyHV9rFHF;j)4~xKLcHZ18w{M%M1_*C8AVK<90tZ+;(TIOfpj%B*1b1N6N| zwH)Tp;_OD3Kh``In#CvT08~e*-P#uCb6{2$Y7mb@iY7G74S-uLGst!xY&sXd){OTT z?ii@WUNT!^hk3iT+Qv_&USElzbIW)HFyUDM>RKUYvyKlLf-pe_8hwevRv`tFy_l?K zmHtqSZjYbAKRfeu6YpqS@n**f9wW>fa#KgvVr% zRzkLfxrN7%wwvykmppjfOdS6+j`&3g2-Y#7I{@bAD7R2%6RgZ=dEz2mz2D*1%=RvuB!G0f|Z*3}>QP0e3>BbEEDI4V$A45H;3r7n?;1F-N zU4P(scC2fgn|k6<(UF81mR+B6fXn#8xB4Uq#o%=L867)OM|X`|4x0D^@F#>myg^|q z=TIJ+Y)IV5aAdq8ymkKdPMoQyS_=-?sHbo5KkcP5DZiX@OHzxv@y0hkgR|gBIEqBx zOC6}yVSA*=TD3F!#W;k!Khu0b6K`BQ^SOm-OJ(Fed`7_!R;gGrjEa9T96mtZI6E|j zWtS3ryOiR}Sty$mYB9s4mw*3j=dr`xCoL(!5CWp{3oOihVi$2vsrZ_uh{DKGSkF!I z*Bav2V)XFb(wP0G5u2W_g395$2fy%|ucBXnhBOgTQJV-Z&qXc7lhJ8tbGmwe1Pae) zS@bURG-$2U^8O!6QkVVm>cFj>(X`=59(~3HXbDKo+s1cUzSA=Xj8ITTdMj z_vF(vR$xL|cG|H*SqWW;Qov-ne^?Nz872}ka)O|G27(g6tW6R0)N!!K82VU$`lgVx zISguncY8HFrA{pfEtY3rgNEZO$%`QdJHVaxmZm-Tlx}twREk>;^7fg`CML3oQ9k@C z;)X+mwO9W94Jh=_r=OXJFdf@O@8@w?txUhlj{-y0@nlE?8XCCqUnew`A}oxNB*@K| zIe$!KS2?u7!oni)LEi+K8W~YgaRYMeCFkRPuBt#kL%#8obwSk6pKr<@M&{;3EY@Qw z`*p1%G9*(}GLpkOmO{>c7CyeEh^YHC*P!TnD$i}+yfHREpBS=3w%vDPzB8;2ZMM#| zgg-I^@LmwY*t^DZDxlHN{Q9M(|5Q#sg)X{P2B7R6)cax}9!M2SgZ%vwIGD4(R6@Yv z=-?wn^6-6i6~l*Qt6?=?7MY{yX?fv>!Gj5S9hTFi{WDWHw1Z@IE6(i~&-ndTAeu@Z zG4A;e8)jSl+gm!rc|wP6>Yuv8H(DL_dgbRTwpi_b#2j{}swJzT5(>Nn0cA1opg-Qq zicxp1*9#7g)s%m9ei7}1_ZM&9_$9FDhFqeP9`#M^L3uNY<5Y8n*SYJ(=#{^Nujdyy z?dgcRnQCODQSA0{&eGh=%$)P;^o)`2ls0+@-BtdVz{|B8k`tl(b#A zFYfDtK)vQ{gWI=I1FlxT!W%vwxA@B;4Qn|84FiX*gaE)BX~1~cPh{(+f|+=)@8jwSgpKM(!#`}8u)h0 z?KDJn10BapcRE>T0&S%+i{@-doU=Qy*pe{jVRb~*1$nSP##mqsn4aPf!J6_hRx{fp zN~2Jq3Ij@Yff#;|%F(J(y`;}nTRSzp)yg8xRE|V)nrIU}o~D<|gldnXBZmDz?KLHd z&63kOR?@SvKqrp}sGlQR(=#!No2*Bi-<9$ERRbeXB`&dt$*KrN>J5_BhN2X!BUeCO z9~Jwz!(f_l7+!^eIKMJ3D0MFMQ8CK>Gl`*ZdNSt4x95F@aUX!}2<1(D?jk8VXGp-Nq6pzlsQ8M2k~=SDKS_?N`Ix z)(j~kUiNE@1=9=>(6xSNRjVE5(Q>={z)V^>c!Bj)6_rL4%a|cFF zSze+P`DeY{N(&PK&d_CLmF@n6g#{NKvp40xT-FA~AsOFx_@kfbmfqOVDd38`S*~4e zBDeHlWgTh{->U1!N-fKN^Tq*CRKS=O_S&IG89*6vK7=xS=jTqdZK3UPf)^YJ+Z6>d z(^g~yw#+S?J3xDRhe2zrW2Q|^MGTEnV7xY$!rLE?;g%UFQxaQjrp0>Ko~&y0TdFYo3zGxey65qr#CxG( z+=7zgo;!~v7#&cR$9LTHLgqyB&aY%LwMoBQ55T}!8?QZ6aWcILJ67vH4w*niEWYLI zT-a~OUX0BD-gU1-U6Pga zFB7J-RjOC^O8Gg*y%RB1f!=?vbL zNwQgA$aQCmxd_>czOlUW44k4Oej*Tl&~cF@@Eak??7~R{h(a3$9EH*R!Zbyd$ziOB8ow$c+3 zQgaU&C%o3d$r2mMGNE;r9G2u5O*<6zg&Xu*_7X$ytVv)$D&3+850k3$i_w3plb6_t zcpkh`J;xXyA+lDB7?~0k>;*M2v8v6IbSo!m<~QQw#V6o`TQ6vBd9XsKGE|$*QL)^^ zUSW2#Ja9(&jyfH(IM9p6miPgBep)0K3ctuhut49GT+%q~;V!mmN980;n-LevdedTR zgO<6RBbT^qOyO0 zS1vo29{bJJfB`EEM&vRmd(pHfr6i<<2#;FF7-Bh(6zNuoljFakv6EJwO^+S6yeXP2Gnt_ z5xeK-8HUh6F1m^*%S%2<4S=pe(1WO+)2OXm-W9LjpMC=1_8vI^tfKbMeVrKD@rE~6 zF}6tWKJY;GtTeMu!fE{9!R$dQ$7kxQtKWl_N*iBIcW(J=-2EOB}(q!eYeMfBrKH?=kX9 zMRC;1(-1z|{HU7qVb1cz@qN%p{w`Wb4F@NOpGDGk!x5^&p%`Liv=5G1o3)Eu`HfFS z4gZ*-(YX*v0u2;5?NF-3NwUlO65weMX;{2KYQW#;Qax+S{)Q;dFW?YLogvfI6ul~z zz0niJT7J1oB>^miyGuy{z4* zY+mkA^i;4#zUF zE83yJ!yJhM2!Z*IQxWSjZ&ix{QZw6?8gu;WyZ(4({33-}*e+JlD$@l~Nba>YL`XO| zwAk%OFXENlD%~ElqS%FcXyQouC>N>}penm)%20E@b3jTP+_zG4URQh}YbN|UMS}3w z5lDc0C+J#1Ku`N;Z#~_>ceoQOZeRk0QQVLH8YvdzLreIa|HJS5Per=uaz~Uz(uF@{ zf^k4#i$l^YZcokSb7MCCKh56P>J6Pe706LY#k=FsfX+)DSXIj$x0GVeMl?Q8n&_YCq$*1)7%KW)Hea6z&zQs6BRlyuTr%gqF4 zR+w#{EMQV}#7D_nb#{~zvX%Bs8t_6s?#NSXO7CE^y2hEqGVamc}>+0ga?n7hQejZ?zpn z+t;i<6-+--V6HAh;CESwK-pnTijL`D^GCw=y>a@+MVeFVt?zCtIYT!VLCx*p=C6nx zdBwgBddox`3=gTnqS4k5fTl+ojn1#vk?mX2l!)JwMa`=&Bbv6 zQ7$JNJxfzHK;u^*9K8jAn46I^S!UP;;n2ViC1f_Be(MQ63TG|J@wDjL0hA6^^?jlh zYj?R?m&re;2dH{pu*F(hT8jTv=`3_+dRf{hjGmoUD!Yw)^6stFY9mx`Oa(wp$r1oU zV|6dVujo!40H?~fC-2L|-DSaaHnlCy{I$HKh*E!T&H!SLN5LG%G0Lu++8?>~${w~2pjbdaZ z(k74Z>H^uRTFPoHACjEr8NOp}*kmu^A<0+}>Y7c;3%OWZBX3+hic&%KVGyx^)F%|X zdl5kMfqqd>{!XuBRfa*I=$#CqdJ*W5&$%2@ZtFFv=^&ReST!HnrYxOPNCc)lIDOL= z2>bi~(c#ntkOB_}m<4B4k2m}iv&w(=SQqi!V$u}#53C%zYpL8Tvdgt4GKz| zl_pav&583D9Br0S*uzFP;qGu!~&AdK>E@cxrt{F z!0m!H`<*TrfcgnAX4+sO)%auVTWXf<{ZWlCP=!IU?sG;1SQAi8YqN%dZb`zG>}UO< zL?UF%-*9yW)$3hBWw%1^8zVeYKn^IZ?j0FRos@)PyqE%P!A8rspOqu^ah{?EM9>D&e4h-W&KtKxq?sO>SV&d-WFy z%?a#Y68ls6``|g4p27si2u4yExjIu0VCwHik5nKN*@FpzOco%c-2(}BKHS~XL*ny; zX&JlSPhQ4~oz+20a#aK*Iv$c@yPFHo#U0@wlG&g%&|P?{pX#P z=85L_@cV_9vIWHnrto^*(+{LmDK$D^F1JX9c6?^0QK>8~jb5YwG*hzjSud!pZuk8D z${4NYJQRRB8&2he2c^kMLo%qvuk{TSd{U_~BPa}iMe@nV;8M!x|8%aY2) zUi)ihufvn8SEK;x))!@ay+Su3+oTV^CrZUV6NthVAd0&+5t<~)79udt(+&SMs@AQG z9i=k9hvEE_-RV7r=67JnjzJmP1H8RQdtDkdNAXgp@837Mw-wshtpkWE%}uz07|I}; zl!nrloUL}!Tl9qYA4UJ7aYze!1>MUSR03{M5O9k=L9w0wV~p{t`9CfXras6-L@+=) za$e|;PYRl_I)ORanx{*X6hwz2$IWwFkS~em;O(PVeQBYX&M*U;;2^<=@6mf<^QlhB zS4wx35sSE?A%seqp>+EtpId0N)}s4{@M(l%%cz+tI-Do~Q<(Nc=8KE`T3;U?Wb{NK zmI6atqZ^)ou!KMmz0~Px);i9RI8WOm3S`)~$H*d}VOP|IZ9$2_`nmke#iFYU$u+9l z9HDTa2N8af5EiHfyVRhbbTA>PmRmfW{W7w9_BcIb9jeF6ecOSC^ff#}tfDqJ+{Er(&?VaUC92 ztl`vnYI2LFb5;fIO=J^lHoLId?9x3>=L|7~1W}oWK9PnV=T`)C!?tw6FYr~Xg)VNoJ9txm5F!W z32!?U=?&-)I%S2ICa<9}or?rcS>In`CO~!#5R0V%^M~8erU5R)tuL?S zSN#IxjuL#5>~D>JX!JJ1V$D{KN+&iS5@~Z-kg_<=h}=76Os{~w#fzuo)}U9~t)Vkd zBtpE!f*M_!n5m^;A_ZC|{$IKQdO=~1nP3zv?izohrK4nUx8B$#)R|(9*5rf@f$F?G zOrfwF!RAMB%a>(paXcP@e45azeJ1ZHF}uV0>QpX!N@xmbRN1=H>q6KOwECXq(+j(w z6}DEK3GibgY!6Iu+K0VSQ6cpC3^&HHFI|0g*X($@*Li1ev-P<}pe$<{a~Oi+B6G9l zTw~L{lV9QQ+5F}H4E7Jkh3w(y+AYGmVwWb7xC>J6wYsyT9ax38YYEc|P>;F81T!JA3f%@&2>FPqcr-@9Sx26;AmHvmch5687`7vBFn=kDifiyZ*0(Mvh__J z!CQ#>z=E2FNDHwd)Ri_C-4O#c{hA``5qdT*tlEhhh(pz2+kf7Xx2r6$>tjreNhrPa z!hjkhP*nYTK46tyfCRCd>`I<%uirsb!|M+%@TcSgy>IReFqhoGr5kP>Z)b z&O2{413^Ajx$G>A!26|CI_ql* z6f*$X6xo@aBqWQQpi%;Y=y*&iG{uy3pz81_6cTuGHbI4=Rc(9 z{~`W|qVsqm0X{6gq9_Ihq7~~*rz}=!g7b8~^b0{S#}gJN+_q$DbZrENkx4X+y89ME zakMFlZI+fpL7MNZ_-m#7*b$&oHB{* zq+I5J9&HGhyMCZJs@QhhpvT)&yQ-MF?`Aw-BU724-L5T4tGdT7#&$Wc>*_CDwzIvH zb$ota=?cWN3S-altM;b1pE%W~J4)aySs_8^>5aUiJ zH{VGMw)(>$q`)2-;hGL=+02eiy;FoG-Gy_W56%(Q6i4+<7MPm1-@UB2FDVg-827FT zclFrBivzLm$BQW$z(b{Tvuc*BSb_(;Rl3J&1rm+TBfOAYZid>=j$5`u}v71PafnQJJ%hp>-H zr3QTOIKQ{XAqWKWXdX$bk@hE6My3dX0x`WM&Ve=cZnWMBMKCY?UTbx(MKC7VXPwDC z*Y5K)mM7~xI~UM`$#?qe^gAGBX4U-N0B96al%Qv=V4I@|A50g0@szW%2}bZE+`5vE zXFCVWJhe(<#lG$);HX0s4Tb`xrsC}`J-`r<%=LyFG4b$2KpE%dCA5U?A6YT-*mAfG zzmIP{kof6?!?@xOEIwGdFwpsM-liJvGRxiNWFn1I^!1ybb@&SOP(!&zPbj*#B&~lj z{sY=CUjf4X_-Ecy@9+x-L7c6?m4a{YLa=K1JUo5042;7*BN|aNU6QaADhCFk50eea zrA@di5m?7k1n!Za&MMFK?|rYc0-vR{**Fy5Yg^bX#5f;A>Kp0X340l{$5gT8bvtQ z)e>>oL>ikmn)Vplp43>H<{BcRZ`S^ho}{oJHcZ%bkB)d#I?1CbWwb(smwgJVhyA444nb}dahh;l^}7jHXmWr@ zi63=-jQtFv)ekBpYatsu{wmg`wS!bd1}X}gkEAr*ZMlLne23$(xH~7BkN~$YpNSeL zK!=qru6T7fG?^COB5gTk&rgazjN4X7pTeBaVRz`?oyLTu-6FZcL9qLMM_S`hDurDZ z8ZR^yuAP5}639u@ZufGUgd{*59DQmal<^QwXCx#f%cpusby7~}RL%hAdBvvj=y3jJ z2B16_1^T(TwB(aB$Vgs}!(oTh6LtVED}3$h>MGq}M=ia}A}lP7m`X3Q-3qc;RgB;f zW+g?s>I38~xF1VDK%)=uM#aVko}NO|4?|%pgv?`bprjfMCwFv*B!w_fa0%4$;djRW1eh{fN*{!&@{&mt=!|KrpgtYJVUp8mS``P4InVNH~x zH_PD092>@5N0_(us_7c!5GV*~)t|{AX#u*g@w17!!O`#Yu9vQ1&2|{+*!Kqo>*em4 zZ;2b{0fNRd-5tq_A-DNS@^OB>{>i-Ci;^*eQ>69HYC3@WY9^2u35aa5Xj;g^szne|L=bIvpF=(Uw1|G%QlJ?8T`<1 z@`)~*E3kZziHV6awz_d zoQK=%TaF(3+lqh57QH1o9fv9n*S$xJ2mCmq$l%@n!P@Q7Y1>RcvT#auNCB-UJC$#f zs@s)LP>QhnVi1R_pUNONwj1bup`crO05VCh^bh0d;trrCY|VrS0RiE{;S}Bbg7g!Z z*Vlar#RZjjr2KsZet(rnnj_ZF;DP~>ys=~k4%B~{oa1HvR zo%xTy`D4Jxf=aDKI%Q@`dw$2ZW{BCdzto+UPyK|AB@0Pi9h0E*qpoNI~Iz219-7ewHe;1Dy z;_KBg3;uUkvxND_jna7}1CK~*un2oyUOB2``0#-?nDT#`!NK&8d8aD?^CH&#YsvOv z0?yB>dq|5`42C+k+WfS=_J16}bRILYe;yTB^4XaGuZtg|mYh~zd&O1JsueA2nd{LS zBt0@=mPzR%B;bYS+Jt&M2Cnd>;{Es00Uz8-DGqqU306PgOK>gtKY#UQ7_u}xiqY(Y zv$D#MvPxGk^lNB^_j55qP!H#jm9$U>lm?lM!R}aLNqun^#CKH1IcjwCv6TNX?H?aM z`ni=almy;&MX7g8*k|Bh<{9J#E^aL?SD|f~-Zg$bMY9#dMXjre7?WJku|T33x_wJ_ z^ft5$zniD8$9gi2jCtX!6Xi&#~(CK=(3{sipUuVXtCIMiBJ&r!U zP(z`A$5@{XUcCz@t5*{hb*0jLa~q6v04iz+qJbN z=FhX^ygqx=IrZV;>ix6oDVYokoW`+3Y;5T51ET-^4s_)6wpu~ScjhG(U~wGEaY>`x21I4eI{v-wG${YL*Ck-L=wDLQ zl#)l|`#dW`Tsx{?N;2Nhn{qCeLwt44uStdZ$E|9K{MpOJ|JhxkY6&pqlp)#2>Q;J0 z4JoX`v~5=?%Z4%PW{au+3_Vv{UJsp#o4D5(pfQE!sFs2-n?-Drf*T@vb{3Nax$?Oa zhx;FE>D2Rem+7y@qNz}st_-qSnGDl}aj`r5whqlx_pn%v)01$05X&SX*iyEVthdJh z=Pm{gv9#Aky0_5(`hrS~U~ZhI))dplW3%mbFYtbAG$&+LFZ%!E{39A_QS@`=namf118^5~xh%A-ayBKe=Rc8wtC85n~a%c`ke5D1@ z+mTd4YH~Po`kQbBw~uG`BcGn{Pre4Z=F-RfdREuo(8`g~-xZteGWMUXACYE`WBk7{ zeJkN>%)qGdm&4J=0Ws_t9{Ck(c!^)T=2(lM6mJqO2rm0~nMb`$FXZUljFa8Q#uWzD zB6(#_jO?4{Xw;HfF=UK0;~!5Q+$94h`SgAClU%VH6wo?rO$J$G24Hyr`lA_ACmIg7 zbT}Qp@caN%u?ltCf0y6QpHqSB|9L7} zSKY;n*5&RPD0RT^BYV1D9HR1ZS~!B2M+)Wlx~HHYvxQFxlZJ~Q(^-dgAHzxV2^JuQ zh6Y=|($6+J%0|drmXcYdQ`fC`#w^fhLp7{e**ydp05EY|}luPdE?yWi$mZ-RMAH z+t5HnPL3#>GdyXgf9ia5tiA?hF|1~bosq-?SJej-J21ER`jEHxSOAa(51`qcRE#;g zYr4}1HNHGM|AHVkHa6yTM4~hW=AYi=<=wlp!k`U?!SfW77kA*p!NGa*A_2UkUhg2> zx_|+YtKk9V&h--YhS*gFoZ{l*^<_@M@83U5rafb~ygcVDbF1~fdK(AK*X1dZ^1AH@ zr`nH-q#|03?AIJOk|?9f(sNEvCv|RF9VrLw0H?`7VfN=Ue~#4n2w*Yjdy8cM6eWK`KIE z56)UZRIn0nJHh$sqYJ$?cMevw!7jMT+MLJ3JiWT=+7v;K0Nk;g=Jh~KjYtOv@q9}L)ITel&G!`*GMf5eeta3=_VQ#`nCjbT>LQ^_1DHS$zq_44>)(n zvlzj^poxfyebCn|I15Q0zytsoP%l8NwfSL=dM81(OOF)WdreEOoq<_ zp@`Vn1vA3{F{Q=P{Ku5~!{HL6DT-Xpmg2`G$CG8(HhOSy=uN+-+c{X61}{Lo^T1y_ zODs+RAeYl7ioO8IP&zv~JpjgZfJuAvq#y7fw%bCUiWa8^y-72u0RaJ!@Br!Z?cH6! zvz-QHma<;1^_be9;DV0vHGO=i_b2wQF10)V;_;zlRc5_oF>!o1Ex!7V0*fP4KdkH8 zsF8241?a!w&Hp@>Op<_ODF8T@DkeuF`QRk9tS<@Gr0Txg(7b#gFc_-2J2it+Ybc;qC`N~LLGZ3$^t_! z1_|=w=abI+CU`ho2LliVG7fM~g~*jGpqYBMpn}PK{$O8Js1b8;flj?Cy<5lo4x%6Z zvdQUY`0VlM@bHy{cMa7V3>XZKwVSUoC*tDi4ol0`Rz!3lB*gQ0kA40*oydH-ad-KN zk(>rl%aUPM#2 zNpJIP0+_ua7-j2g5%l%OK}rw`!6I;@(V;gSXKJdxMAf%t-%g^0xaQ7%FFw+70Nuy~V7myzEnjB!Q3c7nOt^OUrQN2cPj2l&d;&J`% z2vck36dqo|oqgqf14DuxhevIk;1z8fnv(kPiTrBnZX1{{R`aqhEXO3Fn&V5JEn5Dp zM$KCuFf|)zEhVs>evG1PEwXnMkf7~sLo))uc0uZR9;wF7YQ7n>HU^vMx}8T&BrE@+ zN%AO}S_|z*HiJ;uvKV9yl>I_}KfBplOGaRHkyOIl`!a6*ryWLlDwmf`sn%NpSQDi5 z$zfwyVNX6lZou+*(XmjoMIl{jDBP0{-RB@f`LZp}73ub19lJ6esCG7xJ5$a+N6KGnY@k z@mK~qAAd)wR-4XL_$w!TgPrMbEmq^{uzV|af^?VNQ3ArrL@q#&nC4dBfd}BP&^=xC zXjdhbfRvR5H=I`9dIf(-XxWG8ShQMdwbngJ~=YZ zH+-~dtc1@vva4oAxhdy90G-BqB`D1+ylGo0q_Z!p1|!zGScEw1PN;q|YkDxBE}?FY zGQ9JK1CfuMkR265N_Zp5N6KcN7IWJxe{1nEQkJqwb6IRd}CDGM3_%|yspGODeE6Rqo6jn9)8M|jEPT!%A z!qFB87z<19PGYRR8lsrLO{A?EDG`#CIQsJp^GwBeEF#cO7Ow_OwQzd-1%0H5?JL-~ zK9bCUOjO+4J3Iw%su15fSJ2p-F5wa#{3P zOW~3(tHkC^E{cd2vX*K+3|V7)tl8#dq7gU>N;zVLHQ5NP32gW^5LAOiXEesIR-ZyX`P5WMmMpl-d88Y%n)_Hd z_&AQtvTpF>`I_v}mARtC4*%+;NVN(@1AEBFuaz10&MDU^hVz8WgucH~8J9HlxHAJ& z(`Yg(?qdKGYjJYt@3kVP{mGW^!oB43%@q6Y^Y45DqrEe|C?jQ&EqN{}+?Ltl4J5z& zGGro{rClWza|iFc40j&0Ij{fRZ;0vj8{e;pAiJ=$-L$>%sX{19?c{FH0`&j(!HERi zP$bOBbRW{5`jx$N)fy)$yUdFF$}Sc~U!mQ<+!0q9tp z12pL$d}tpIN^}WNz9D<@Nor{cN&X5#r-hNil<)xm5Sly~=8c79;&yLXA{mNx9*o8P zXuJnvv^#W|%Kgj${{D8r!U!O9R*0b_1wtP3^AB}ve)!w#ZC-xmfM0O0G+X-SZy8qV zP*!Z93Xt(gX9_Xz`Bb?+&~%}=|A~z?uwIX{%o^|Tje`Tf5?|OJb3ga4>7pbFsdb*& zcA?ESsQprmYqL9QC)+&-f2L7J?Z#!xjFP494r;zm!~>Flr9&Bi(xLUg)1h+H;)WTz zTIC?tEoC+;>dnsI!n$soY?+LGA>k9h>q8J2!L-bEef;B5IMM2ifmnC#*o27dhwo5B zh-r%<$>ei=CJei4wAArwGk$`nT$w-FrY@~Ux9ZwyLeDPyb40g``vd)(JI*6I*C*V^ zQstLp2I#VBfIDE$eChdnXMrj7%QmB8Wzhx%;uxeoewKed6Uw=5IM~~vx|?X}K&Q0a!kAK$YYi| ztvh~%o8gLxTel#0#hg(E7u0}yfdh_3q zWqJ$!B5bD<-#|6LB2h_{DaTxlZS)wm%w3 z_O-cT!+3{@p(FrFNvy|#q=bW;VzEDIc(*h#fJ`ik(gOr{Sq=!u$X=PkFX6V`&xUj5 zm;iSqWqTSf(lRdub7JI|v%@)A@f7xVsFcl=O-}wZPY*W$6wiI%pKnt6>~yZ>MRBni zmjWC?Q4t+b<%29qvlim_jK~o!(Ma>c6CgW*_xS>JCcsd zuxWhew19mHkv@qo;0IimttK*U2PERt4Qv2+d z?K9(~@{xTJ{lNg?xAzH++caoLgVJi;P9se`kxJwM;vA@013#|PrvmMoz01O=jhVz3Q z@1@}7eXI}8%eZ_iZ_qQO@yKy<7ao5#qUys_5$>?tAE=8fCw=P4az`N`B_ZH1mEp8Y z#{J2hd66mi6J}T=jK4zrM&7lYa+3@<6&i)NElGUK} zEbEKG@40%5_x9W1*fk(b-pyAa!<`Cu#U+#eU4wi$LfURi60900@Y|D1#K$Y$;V_` zTpkXHnjN0M$X8w9wgBRGt--9;`XI7WwXQqo$BK31>2O(hqX-vsqpLU+=)2ZceoSdq z6)NpzOD7jW@b{ZvI1E6V6Av(ZALc`nO=J(D+?n8EGYfk{1wa8<`)=nG`RS7$L9|MxSEj_S-Nkrw6O`xpcSzC$NXemPr*f@vT+zd{+`<<}%L7%o=l z*^8Fttun&@rP0LBX1R`1ZMxu5v*I?98?HOZPp(llL}5ol;=2*u}NF?}WF(`|KtkpFz;{c2{|zLz&EbeNF#c)3;hjbs|PCS=~qmCP(2@w<@f&Hb(qkD8L4L$vDM{(Xv&nQcw9Nw&c|!(EgK$!79sT&WvL62Vmh=N|&-d%PT8gjD zt0xT4Ku37&GFb+b#iINiTT>2gt>YRus1sc_lSw58w%03UBT&>l!;R4#NDmWeF;NK& zw!1mvt7Il%ew<>C~lzQ8=# z8#>{HAhgT8eLF}!%=;>Hq{d`F`>FX zeiHQ7Z=iag(s;WW6~>E40gu1$DQ!c z(03wedEic$7tx_%UZ=E|t76L{+k!7db-KE%7GSP|k>V?APJre&^G?89>@XI{le;hEK?v2pV0@5%s%*ylA=jl! znieM7J4Qq%)3UMy-YV>^B#Yh|48;IKwek%twNuC1?&(^v{P7r5EbK#=Ew14VaZ#-g zsq|6^>zM+XBD^f(CxDmfsH%&Q7=h=NU3^2g6_FWskT2tw`?=9*`Ut+~6g*L_f^aie z%Dd5Ke)8-KF``vSW8HK8?fWBP1M$T3;zFfnao_77OC%dvEl!&3?lOFmy|cDtDj8s* z$C`dI7rus4?~3X}1%~c%5$rDTU2djcYH@s&P6 z+|cyY3$ElR>I?QJ(7Pr=$}1okPZ#!GAM-#HOcy9h0a{%Z6fRhsUO9^*KX&=T3!4FPT9=qMePQmImt=vH5a?SWlPYdYj7prnhk%rn3|kNv~e3Y+jud& z(2l+9ix8!N@uREj)v3i^m{>9DDxCbfFS$c}2ofa?=xs%c;_?YWPNG!rfQXBcm4E2}&1&c|6zs zp{ND{iQ((!Sa4KY**YVu@Wr--OuY>`V*L$Aod`W09TDsAbcBY=w!I~L%HAE#!2B;D z40wQMyg;!8J6WjB^G;A{GqF8rS(AH)l$dx8(A6VcJTIwrR$O76e$M zm%)i6*U=OGj=P(yNK8GQe>cI%?qf_No6Y3n>beyVNNJ&rbR{0hAG!74tao{hrt>>v zMtDynRc4QJ7Ya7dU@;r@o(^x0Nn&#n5fND)&RS-s2RuE&hyt@8sGdztDZwsZjKhI* z(|mFxwVRZrGj&=D(8MZ+b}iy!y*D#9CSu8$LV^_y2+=BL7$Mo3SZug42)T-FZhpn_ zyg~jiBu4*FKg-&3Pk%3Q0tXbqs%!bArGNYRst+Z9H3)2r(eAf_aBrDSD2_Y6)^zJ5 zg8=4GTq7`-DA5`qW_hbEOGTKLs|jww%c->x2w|cNu@&#ga~qrAH@79Y_9L!h)ade; z88u#Wzg;86d5Hh4xJpmqbLwqwIXc|)6Gqz#&$lToWUFbnBzRx|aUT^2g|15;O7UBU9F=KvbLdFx6w7`TF3LTL9}3eV0cw*&0x<7>qO@dOQ%(qAO5a{zI+a z)Mf0V+$VXp8}7$xvmfk(@XbqjZOc;X%1XNrjP8DPAH!$GhHKnQBnKJ3(DKsxawkHd zR-tJ?Q-x(G6g1p{c()7Z>n6(}Q2=E=gu(WrOl%Nyr?#RDa*?4;QXdw|`gQ>&1( zpoS#_O!5U1lIV&iLpDuSU9#~;OpFfMIGRbiIWo53!Jqa`m zZnTSCNb<`(Y>aF+jgf9j3x?q>$<#>VKH6JP>z6g+%Q3X6=2(p&z@AP4kC_5!$d zyQw&xeq-Wh8cw_f>}=O~=bp(Pv z&C)%bkPzJ6Gq@AnA-H>R3j}v}4elO-2MF%&5D4z>&fxA&z5~y5-<$jW^IK~ciy0Pk z&grhMuIk!*SL`$~t$n;lprgHgTuNkcA5f5BWiubcH#tPcUlf5wK0Q4p?mQ!*>Xz4$ z3SIrduuXmml-ynuaMvbse&|vZ2_}gmJD)0=3D!zZR^VoGGkKDNK(L(5a(qW{=a26; zOwQmGiXZ*$8+prBNl8hLG4Igv%_$o3447W#_c~sQ$b7Xa8BbwxPQEZ+smX6V7#0{9 z7`1wv3Rw*zKlFmx@6W!&VSP1VxoD+?At07|T8D$}(}jb(XBp)CRw4oEZf2grb9=6e=P} z!h6N<%q^|y4aIN8_VDYXptnk}r<7k&ISV0zB0b{7E6SWJ3$-QtQ^v)$qhm1j(N1{sz#c6P~VnaN>AFZI=CH-io;|)=0v|(u^ z8x#9z(*Eh}l_vW1fz+7!x*;^@X4s}c-n*mxyIhy=&J_tAzcxSgC!iHHVkXn_y3ngP ztGNMccT1wj54!Qu%x>L$H(OX8z4KJFRj!f4T6>QS*cr9=)C+>L0oS-cgpNbXs~nMU zZRy`1OpA2(@QF4;zrGs2M*x54M6t`2OM#s~t?~N(4rSiLZ`h*-?(0{Gu?BmwL;aQ& zQxtZyN$eKK`G6eB7Ep^`_3xhvbeiu8C>4wdXkWM?@=?YT*EIbbNF!Kt_lx zRKmd7YZ6d!2!~utX5F^|KB6Ay2t>G-q}T6>Bw)Z?yz`D?O&Qe9#mG^YIqnTpN6C9k%RK5R+Xg&A-VzSfI!!jDgP zShatQK*l!W(A3I>!34yMhXDFBoQXW@L11J0cl)vkEGAIU3RU^wJdD3P#^})r-2m#( z*L#!T-nVxhQ|+SQo(3zM#Q4I~ zb2KvW^cr*2n4?GTOt@RqyJO8SMr~Q*D1-vmPL1@Pf#LILVQ<#94BiN3PO*g#1=WQ% z=uc!q!zZDYFx_VH+QmoP?`2N8vD>3VNP@J`2xSr8nYqD$S*qvjkE~482e6Dwdopi7 zJeF=#Lu*jP`x$-4W1lPlQ~lmhxa76s?N z){Gj2!Oz>V{N)a-Xn(pirYUjWd57{W+2rWM&;31C2bBI+Vx2cv7b@Lt0EqX{?3^n)DB^ zUk7Z`Hv?MIy#$Wvf~yz1gR_g(LQzf;3OG{*MAcPRzkFKlK&Ow_ai5eWZ`jUolQ?V( z3$-{+7jM4c;QDqzjY7zCn#A08#z$96BWv&FSJ;KZPi zBwn|PjQtsyYO&c>%A$S%xdGsefVCQ~`jqi4p* zE3^-yz*K#4u#-3bCnd1yGde^yP(_tlYBK7}%?U^y8Vj@G-HaV;I7m|wCl6YA+$On5 zDaDhkH$}knSgC^E(Kg#CD_xUR3v6)1=<-v*^6u=+%Tf^UrpGn>0n}VPmBPaKMovUi z1__QNQ&%&d!qMrElaHn*XL<;NM>V1x@~xw+8_`9xX1`54UC5-irQiO@3eR(yj?R;d z10_KO#ExwJDxkdhj$Oc#m_hd7JG8;9Pj!vjF+O5;bo~vG;vCw@zAgrizOxj4@~<}? z{duG4ry4R{=H@HP2W6CMwk;zX*o#bQj{7!lSU&B!DP_x|_>{uF&d>bzN;q9<;_fXm zA147_(2D$$RF@A>ah(m5@#)6Y>4+nEqY)+ozSp6A-$5sTgnxLOq}j{K(Nqq9_`z<> zvUvIoLh+2HE5!WnQJQ7T!oB#~Shzv-POIJqw77Jp-{B9#;kEt~D5@e6e!x3!zDA)> z;}6AIaT?D*UUX-oR<+j01c~+ep6}>Uie41tFd};1I&CqW^r^lT^HTRyJ{Qa1J9oSJ z+|kV68Cm)1!crd)yW>H9vgf(X8f`MAzE_fB;PB-`yy>q(qnQgZh_I8M`hCz^5W#p$}C^ z+|fJq&bLYmbA^IpDZ*QjlgZ)A62d=(N*eA@(-|J@J{N}?{Nv9KhNJG33oq#TZoywr zzC7T5V;K2dI!odk8i+%vz%HIi1Tpr-i+L2=X3Peb;y*l7j=I6@9 zUIKfHz!~C3S)2pz1%QuXynrW5)iioDahl;7#UA^CQy)Md6P=J)_+UJs%4TPEWN)d; zgK*LnG8Je#Wp{6w#=E*>F*gsb5v7s7~p1HH-LJZCp>?d}wS9-?P@1u{Wp+u#3{>R(Tm3oDO)d$Yr(1 zw78Q|?dGSdgo@^MPDZw&p;CB)F~^99bt1ZM5@PCe8o`f#~ciB-AbZh}UhWrl2hPOgwfqCP8S) zUg(rOiPs%*2S@W!p68&3cakbz^`no=HS)RevHjLBU)}!H5k*yYp)YX%SI1iq&L3%D9m6#SSAE-umPM zwLJid8Eph&K8=}t>vSax7OO>Zi`b&~T^yDulBT~iUMdovK-@DXMuuhhQXA*TO0=pi z_4Mri#V6NoMfF9LjtWf5FV!&^Jeb%PxHZ<6fYy3b{UhAF+2Hz(v_{(PkMZ(Qgg{4X zXkB)0{HBYyhoXi&=rMF<&A%9`Li-KQXs-Bv)k&GuNpv&*Y=LWJ>qoN8^hGsAJl=_N z$b^;AcE*CMrJ&Lt%?=@P^GYV#zY5s6Pe?xXVhrGYvaC7S!7Kl?~&B^G^l+v$UzvR96&u(c_%^zJhLZ*oSjQT|Ai7&I`2E zqr-Jk3Q@j=!Ecj`A%-YqV+H9=w1;!XuGZ`n2~r+x2T#oC2h_*nm^aoVCp$_1kas}t zV>}LCWWgz3riy`zyGKW5tR`@{8GncuQ5BYkExZ`@Oe*xZZL;AeY@t|Df)PU(w7g5a zT|3hElU)fc==LyUwMBIBFH%ie-t`@ zvRYKDm?-jZN7h3~x$dT*YeVN=T4%g(8nJm)44f4T%Q7Me3Jr2Iu7c)_-#6-v*CrPB z!F%FrhLkc6%FkWQ+;;g?Fa-eEvGO$P913SRz{2`jXw=vSDH4JkH_eW z1&i(_G<+I#+AbdK4T0*d;fO;8No`h4hsbKq|JD)LIj-rd8_6_FFRE4EDw8UX0>(O4 z*kNx`J#SgfH!hB(^~tSV>;0Hu2%-juJpQ&soCB|Meye(wT!5OQPM`$IX;62L-#TC1 z=Z|pMp4>C>kYtBXBxT5CPB%eB(;tCTl>ir13QQg)R;N-0%3y}J1CrlI1@7f8F7*Qy z0Lsw;(}e}gGpJzHEdFj(EM04s`T$HXrt1_h5l zt9!s!+cjTD_v)(E`gMmu;Nk>UzYHsM#tYzFhv~Vzz2buf0*}(_A6%Q zFBZ({5NuL5Ys0f~D~|d7X8SbZ4fI0%ZLw)qgPDvTb#-#3RhCu(slGoP%7%nsa`#>6 z+b~(8O<*fO63gv%GzJm5?{a1{>_L>oV0zODx`$oLS}5WY2J`7y)iQZ7SuW$gz%eG# z?a@Jb*OPcV3mr&g4{5e4!XT4ejDTO?qtAzoOHDMzifOtW7d_*@x-dF{bfGvFH2tPk zTJrq&poP$J^gFg+Gv(2gTmpcNUo!r#PWF|eg8fQ!&Y941d;&B)4V5?%#FC34;+QB* z#nCGRvanqCb<0YdGRj92y)h&Tvi>N`05oH5tx}k3`nI~Vw)^`WRA+9fhZ>hTGAv{~ z3l%sT1(sKa;V1|Zk6aMJ+$wM!2ka^UoUyN|D=B)BAmNFLQ7BvCF)+wgEK}2G2uRoj zhlIp@eaouCW)Z{jk=XZ6Px`04b)J{($R0Q#Ha3=)ZBGWZBVubL&`onwi`N?=fQ5Wb zoD&x73TvcRAHn7EozuZAm0)lSO-7oM(uB=MH^T0iVCEn^0e}rp7`Y5(N8;6WcX!{t z>{a0LenumFjo0Hfx0VHzUxxHjd}%giG~)2^Kn?iJfI@nq56cEP{NHI z8RO>U;5qXfVaWVW++0sTQAulDDdxCGXUjbk>db3G(DosAEAD&cdq$vj6{(7rQ%4u~ zg&rzu7xB;X*TsnG2rTq?J9oI z#gB<#q9-=GODb@Lyv=d&lDWoSU)?&QIHMyYAwT1@=#nyXzV;B8mGEy#%U1Wt$E_EAyfT?THIEtT)}giOI3=Zq zC?(XfD~^vRwa{gTym#KEpB9y#i-33{`#BnSLW--YBKxbL?W*6rV5+$uL!u0)#Df)=}ht%u{o;eW2O zChpKwK#=JlvGYt%sXz55g2Owg-GJhv30I-uR)(ofLXibgeo}rDgN^v(JSK$o;N^l*Tea4z_M?RIarvY%cb_mkm)m8ZRVr>*5HJC&}2be%zfOnIKbzRO_vb~MgK;4dvPGU zjuax)ji@AuSkfczkj;t_U^9VP7XQF^NBjyKFL58pPd-1B;ckt8{fBxNIq0f!pIJ2X zKF-sg80?=0-?R1kHjTV#+7-JIUsM#J6CFYMv!Mx+7W!QD zFi0^t`@}Zum0Q2t#(UH`I1moOfz*aeKkplAT=eh~<0JQ-(KgjmkaDPG9Qm5q>^@c{ z>+^tAW`n)J#NKzYz1bC_ZDGwh{;8fl*~k>7HH9`+*U0G%rh-2jaKBMD#}Tgf;w$qy(AUxE*r^CPhO zv77^%kPrOnNihkY)f_5@tr*QRmVPe^BGUcHy&i~S@|9)kTxrzpuqOlG++%fsCE}22 z7QsybDDnC%G{FsPh}$E=BS6Xk`aKL|z3nYa$MJG}I?YyZOg6mM(~?F$gCHRo>&x6i zhz+`gZ9SF~WSfO%Ff+v!%@7OUK=_ zwLk@Sx!6Uv`_0BEIR|Ic+Tr!yhHrhO`WNmZ3m{=@CE%6pG@++!=APEy$Q>&5Qs*pC zuWIZ?IM0NZ`G6Kh#E;o@iSHjYtV&EY^eplKe=3nd&|jcbG#x}tWeii_(9lPYezx{- z>kWob;U%ng^G3Bdo6M)@aa)dmq2shM7 z8g~@nD2+^wiRPZAFYsVXRPtmk0r67IBJ?BQj!qOLk)153c`?~7v2;En4|x$-5pcVR z{s7Qi78$PV4HxluCY{rD0m0GRkN&{}4ClL~6cjL4&ToT++-^P3hdxmGP(31e7WPQ9 zPX-RW6al`GKV$zp7!-Rm=YT|;f(Ce}*sVF+h$UPwQio^#Gp|-eM2v;*7@9o@Uf^R( z-$rNNtSbn{{Am!Kd~!b`jY4#}d+Jsuf^{r^pfs9U(_bZ}LT94sD?B~HksnfjW7tE! z0USSQcc*&6{b|+62f!}2dfc(11vo(|7@cvP`h^QO0?u11tc`vd#mZ1zX-kH&r zDmw)_lIzc3;J6*)QU5fyb|d6*ze$N4QTWsl8r ze9@)QC7+a(G#Gg>&f!kc_pG_()3{Md8n-hH3bAQA+3sKd!+a0f2CPB$n78XT*m0D?~zfOT3|=eD>1#s~_!n+T-u z#}n3%zZ1YKn=aMPLyJ^5%?{@5cfKRV1d9OC;L2W?+lUMd=cELTpV+lBZwA%U`3T=W zZIV2PIV`9L>bYY4z6jX*`lt(NYpY(kH|ovOVeA8;s>9rAQdu@R@YXTCcojF6l}~?G zRStA;Ay>{~?IY`n7i04>Sh@ZjxNKmvb|x5sKCpMf)zG-WW(qs&eg5`(R=gI5a%~Fe zkQ)hz@ep-XS)TAO`l6Z*r?V;@alsuC7CT?hdEcDfj=#89=~aH==ObqJ^8h$b-3x(; z!7K)*Rcem7VoDP{ft|gbO9mbuI{+tR_F1v3Hnxp^0;Sp=byQ9Am_bBlOErh>Jd>Wlpb@C82l8 z1nk;1v%ZRDHf3`Ft}GgK)?VkF!Kc1=-pIB$k;iWKsVwpZ1rmx;M)R)K>K}aS4H7Z{ zptz6$;QPbd3<8}X2nNC4J_!?6Pv;;Whh_AyvJx@eJ$-BwQpSh7!nA+1XWocrAW&C1 zn6L^74hJL%bTnzJhAU=J1Yo-Z2pU>NLF%HJeQw?6d%o_X0F7x(yL`FqLbmS@BBE+_ z3SmMOM!K*7Ch?Oj3ehvTMv%tXPNh42Y6Xo*`KqJ6jfmvtUaV3rsi;x@XLX*Or(opz z?$2e}l?FF2jeccLprsfUPcz1T};Kh zA{IedvS+>He^%|{W@N#Vqs4)q?7UD*peX!h;SVH@lz2bz&USK#NnS@oPO(^VDv z}fGp-a@MRW0I&7o&A1@nQ+ioCxRTe~d z?VBu8Rdv|_T2+T^4Qb{(jNK<;fP!vn&8AQR1={g)fy`sdAY|7+e=cVj*HA9wM0vem zK)_YeHb$xJ_hxoxIJ34(5h z24u0Q@Xol4g@YH0@i9W@*AR5qM2RECW3Y^mIfrCiyUrHg|Ez_3SYn!A^o)&M#TZj( zq+8%=B^DgCg6*?&7qRIzi^KFhfKR#KIXmr(P?B^pw0nua%DtWs#h&t+pnvb& zoxHyno5KMID%n|u(eyr=U4B_5Vzx&q!n?(e3dkYaM?rLIs$1xP5g4Jd>1`&R+VjUbovu*NW1e-#ISlCVT$DrO8} z11ks-4~C#oA8cS0E%pjSCN`QL;Fpc-|a8J_~>dc1UOZL&%cG9yH4RD#SY3zc6cY6^_Go zBK#o6vdjKOrv8WwSsoJ^U*nMM7dsPa!?axLht~keuLBpw+XYaA6V+(*QU)+U=)}YC zD`soD{X@_?JHX@nP`+i~|Dl*XDLtp4p4__sh_(@*WcjE57^YcGZL2oj$r7pC8#d99 zC56Ng+u3lq&?=!B|3qUNRTl7FNX(<3xmxW<(twBx_RaRw7-TR$hf?R>iLT+rYuTdiE8Mtx&|vv$e$`j+h`Z=h8bKyZC0V ziV#zmyaAaSnh#!$R)@GKn1i^7+z#lsEO9KjbufexK$}m~c$_;_EUbX&5$fGfQP_N1 zZkZ1tol&_YCXqayre9rM5@2@^AxtGJ5m@b7?>}W{+?%l=l=YJ}lHkzLSWx%md6CJ2 z;-BT>xjf<#LstS@AHfg>@!FFUn-)$yi3iXzGOUObKw}>N$s`5~CkhdEl?hD@lxAae z7$jRzWe++6q=>Z^xN_7kCG<8ZQx^fH{AcRBI-kqQt6kn~GXt;zfZ^>3+pLGqG$ONu zi$I3hRtm>2pmB&%l|5e0kcu&1?<}AWlO!Z;lx@^G%+_w2R&iUS zu*DQLanC2m0d)mR#=$mrI9b^mtp5mUz(1q&-Y2Ijt36BeJf>>b7`iJv+#$1S<7zlW zzo^E5HrD#G-c+1g01OO@gblM!FYc@zj;3!hIQQH|NipaUZBuQgywWDKg08e{QCiE3ux!M=I`&>G6}HKY4&=0=9%^|qfr>HdX!F1f2EY4pXcH`@NnCMOlek$ILKFr1HSQS;t@! ze?IL@Dcv&NRB@#DQ-e>9IeR{UfDa!nvoo2asu^(@%+D@}2R0)rFfY@`fn+?e{Qpq^B2@ z@n?uXRxc7l-rvyebRdCE%|N8LK&cGH)f8J{{xNt3!u6GjA5k1?^h2{6cR4xFJobRVv7zuhq~pj0OR(@1uEvae48Oh6F6;>8`B;r%2s9rtl{V}qpgyEFr8TBI4I zpsLXA*{1)i3zXEfwDK=K^ng&LUFI{+gl;MGl8ALbxzba4_4i*&S#QLn;_B_oXj(v))!{f;;qV0$2s`;Djo#N4Hyh zJrXNeSgMt1Cfna7`X>9IRsk`n_P`l% zCwQ6A01>ZdwnYs&l%oA3#r11ATtPM%fw~6TKP*qW_a7LMs!U39;#*016QO zF4cqQp0cU41W<9YYE9FR}&U8*ZU1-fa!-hpw)c+BiGMJ@#q_ zcH7$7#jb1*X$F7)UJO&v?T=q6w=wEM+89kKc;27Lknz2Wj*!QNvbh4Y=q;h4m*f64 zrhX{0aaR;cKuTCxSe!mq1!?3$-7!m29XYokVvVDd6Q+Jf;>r8Um~;`Kl<~#-MMSMN!T}Tvric!PS%ArY)ZG*Z_^XwXA3=+GZ2H9|42*N2uJduazV$ahK!2;KPaZg7@L9Yk|L{TFpm6b162+;ZGk7qGz)p@(S65e| zbWj31Qoh1?*b?M(g}uZrM*m*9Fh2iNL2N7+(*^}Gv0!hMSXSp8|GOk!Ml70pl_C|9 zgw(hsF%XEsW;2wsgn}t5ogXQDj(6BxiC7HL&fXpco~8i)JrXEpGo2H-AB?$=+mrSp z!opIrj}m9wBNURe3G#~l?d=MNq)V-yVh1wSNC3NQ^4RQR5jAF_&6@}`Ks?z9)YFKs zK=WlT*a(9uq#q$jk&#}9rn!_WXFjitdhrLBXwkCN7Y{T z)t2snhp4-j<3Cye^nq!e35musSB4}o(Xak``G8)B;tLUWmlo66p{8^)UJceZCl2<# zx#fCz%`G`o6?SWaR|c!nJ+tDF z>saj9CC&340b%8O8=^%eUJvyZz26y009O+PoN24m(Z7lL=h6U!0iCluzqy@PGYnI+ z$Jp>uPOCFMH+$?Ql4ck@1Z_fKIda^Y9|OdmSO+5jL+`HRa)EsIT9< zkV-w(m0=+p%$GayMM{1p5}BaeRw5EsvZ09s_50t!{LgGQL%pT6TlWr`#^kgUrLuRs zItuf2clMS~;<&3mTq7n^hA`8)7!*V_Akhon3nxRadD^1BRu6HOp*BC zA>sS@G9(U~!KpXKk00DPaz=s3_*$u3)}y12t0ng z48bWd9n5ZVgps>vzsjZ=Y_5hc$o1Co@4+32iy8|`)oqbgNO;Rg5QvCZfE=0l$?xXf zo40a?G;Lllrdh{?7)bJeHuv(23lg+ExbqJxmEF4WMK14wR!dB)D3Qx{ z;Z1HPeD}%%KUCC}EPH(q+<%Q#T>33)NiV1KQK}5b=8&#h7q9ac$Hs#HE5F$P%-%nX zvf29W+15}P!2P_rhnhVY1LC}B>V&AFt%@3x3P+C=U%9Woyl^vt)=6wO==a;reJC#H z*hetFXm0znDgJ9}|Gi{6r>`)L9U{Tq0y&ay!~6{O(N)Pl&Iwiu%hAovke`s~4}t)evuWLp#LJAl z%uC2Wb}6TbC8E+D1MYCe7({Yj$zR!nZM;U7lS`Md8~Ftp_xE%9u)@fLzl+7;aBkUF ziZJw4yRN3!kS;QTgR11%VhZALogz#1n^IJRp%FR;jGa-=PME$Pr+z$1coPim@j1-! z1Nna)*lhUosu)uQtKsZu!X`F4*n;mWsZ$l}@dmN4c%ZS^pF+f-o*3&#{+bTJiQLEo zllc8Qm&y)ENOb-+u$RaAhW4YkA~mTG1)Y=pl(fG)e2SWQMs-TM^l&|}q`oD=^qL%0 z5%bp=uC|bVjQO%`p3}=1h35XZMXJdHuEWlVTnxBWXWS01cGYbrIO=dvW2W7f8QJc# zQQdPcPrV+i?{g|XDe87V; z9v>=#zxRZ+{O!+ZDS+nz8q$+c+DWfhn0+aPm|DF*oe@u~LrvvTObRE!+=&v~c3NL< z+h6lGD=IzC`+eU%k0myPy&-&*SoLQ+lkw;*_J5zt`sKNF{~yn#Vmc^aiBhRzh7@(- zii*x~a^$MDz=`0DNH3B@)ux92#W+X@^U~P(zh(|hrT88&G=uJbz`)t+GyMIh0l>`E zh6lmWq4;qw%lTuxUtWWYnBVeo4m2y^TiX1}XKu?M(v@x}iL(JS_4+DWM=@V8$(@M* zavClpfUz|jGsH&;{dd`rMuUM*)ov3M0MpHf-d4>)JM&svx$_n26zBQ)mhC&n{c1f9 zbc5ScaqEHbU;pXoOTMi zgh&oX_dO@(-p=1S?f8+nh_$`m&40S{i~rrVtHA%>_qRH? zpZ>OGiJ)g6u!12H>FBdnat-&;Usoyn0WMo>g&AsF<+9QSy@|pJV#R9#8t4ty4G_Oj zny8y{{FpMIuqfgW$MVp_i?Hk_%#M1pD#PbX37?7V1;^esPk3OJ}SkYlH zl1{*CJ}nF&vsTyFzyA2~p4Dn7nf2tF{Yfi5J^i`Y3(yZC2%Sn1;9|qilaBH-CP zJ-GUD8Z5SY-s3KY?dt;}JxIOK{(tQscKiJsWbpW;ca^n%ghuiIpcaPHA z=#=uWv>P1+0HvJ|`kl~ZE7ecdjjaH^KN}$A0s(6_s@+Hie@_y#aXHZCtJnSupvntn zHdBhwK+nLy0Kg{!DLANEpaRr9LbhTI2&e-3R44|$(XRn9+?}Z+EC>jQaG5Ume=RiH z1n@$n^QylC{@xlq-oMyW7Ftv9t!pQBDn~P#cX8U2sN9oC+7;!eYU04_(rZ!MaD-Bx zJ6wD%o^V#27WP6}SdaMOc9RsRbJtmO_ytUVC3YRNI%%)KHt5c)BXyZ8&gOi7WX?YL zJMiERd26^_AdT}^6qMWfABRB-c{Z!U$SDD=&W413_i753mib5$OyUabNg3-api}KtoT;kHjdBJ zO?>QqKx8~2fb3(&-Z(sr@Or$XdJVc?Y;jjj%ae$E2XwCP0Q9GYjm89)uwC11H@X|m zzff@6f#p$67wc^~JnyPYrRFLPPXS^p6%Lt**yqphA08ei-EuAf?ab0rYb}a@Xrb6( zE-;D`xVi5RP=ATqn=0D4+@Glj6fgyagx2==i*9&b&)y0J!YfqDd0Q4J<_I1yw}3d` zK|#@Y4G#}XzLgkrxH-|2&aiq|QRLhLgv~xdhz(P%B%*uAQOK!STx_p#{_96coq%;2 z@`zyT`L|y>=6-Q@Yn`-q@51MNh;s@zHV5PfGZsnOnZzVhF$qLF#>Q~O{w#+J@K06a zc71{@%v4(-GwLbQ9E4oKa-OP_e;B$QRC+A{ZQZk03Z6B z8SnR>Nfm7id)f4(!nb1gU>CeOnyuoVPed}{UOp`AcbPUDI}8_>J!FP46ZN)A4mX)^ zB8m7T|I}C){ZZTVn9GP--P+1&U2Jo{XBxkeJYD^)2(GWM*I%-3`wd9L0`$T5hVyOx zF4k+nZ~9FxzJWsgB=plHxBo4?29&cY)hkI$wHmVKb(&o=liO~$@H}oe6D42u9bi#84st5y$(Dda(ODHaOoQSj z>^v3iXf>+E4cB_vl5WSd#gG7T>o0qg1srY{`D~p#dA0OZeBp**Y=WQ8%%ztS0$1lF z{50xpP6uk*-=h5d{MN6>#E1&uQ@&S9l=1?J_ujUck$9g4xhp#LCvgc@s&su=x;grR zV+2AT?A`GkB^-XKMEVG_IRk#e|18)~&l^Ajdza(?Yyf313)Tp&s27LBiG+_zUFNzP zCV}$~RSeEZCMm9I`n^HXgC8c>^2l;VQ?`W)#nj06e5#}gl*b}b!|PrqbH{BSQMG!# zTxqR1ttCDTtZ9VcqPd+%|4@8}YXcWtgXP9>*;JpDTOFO6KxaqMI`%) zjmJ84W0;NU;qmz3@z|dT_vf}s8^Q;$BK7uKY zE#?{_{obixF=x>Udd)Ema{U4)`ZZ!rqhXL_CH{1xBP=(oE%YgauUa*|GlszBb!xsnHCPQ&me0s6;>x2s9RBCwZbVlTega%sW&<6p3p-QMgX73$#;w=?ZwX2OOHksh#&+y~mLST@5*+NLwsS+&j+CRBW^~bz zGK^#lA$fDOQuB`F-pd@BI}@vmyUb5+n8^rC`YP6LDe@1E=aJz|M^~ttAE|DzrG@f`|4ZvGmHKTkov= z8Z>4kgH&oN-)oAha)hEJzE1xbz#MMY!58R-kVA zjqv;)Q5soO#J@-N)Lw=I_+)}dv{vP4WQ9kN{(83=&hk0JW^wLdwGL9}BcAWv#)LY} z&>{(i3GnGurEvBlvJG~djAw}o`bgN`lMi=c^&MS}8DvtJ@YtVQZ%mgOYv$ubjyOrk zdVskF^ym3;Iuq{82h({qTdb3Ovoo|w$i5B+?#@-^&6kOKfBy6IsJvSRgGRmz?u)yV z5gpk~=TUqT78a(g06_*hnI!RdUk$~mb1~tGT_v6gd3(;g+)`yo9c?h%t_!o6jD9q% zQX{sLE|EJbA?b=<#M^m#yuX|1=;;v)p;vb+RmziXtOfRm!pnX;$86Dv@ueBr>Vr%G zvQk7#de}Nh8k-9C;)uOZIm-UowMp@>hi;Yv%;PwgZ=3wD5cmCSAVKTrhYV2$_x5<8 zC7rX8MrwTt08y;2rwODdJ38D95@c+L^!K`gm@K{c@j?T*l&ympPI8Z3Z9-`^f(U}PLi~=U*>WBi? z4R2@+B(@=-6lxDggrem4^UxYw=lgO)0u`1{QHHPqj?PtPNvI-~q(!0@k5b zG|);4IJ+n~eT;E<)7b}nZ^_d_qazKt@gfS? zXo~EV;96ia9jyiVpvOf1HIN> zTPK1WO9k}3Q)ynKaCq|LYYzK^RvTRJy>}oUoX@C<;WeASGbDgN0Jl6?ZpwF}dvH7R zA#5j1uBF^yheC4wBE$7HTH=ZVO`-M}9id4*82;g4oW0LJ`>D0&nscrP&mYEZ z*_gRd8w@Y_dol6~UF-DNtn!tyRf#cFp3zhUgRqLsx3{(oT`pgV8d%li_yiy-*)Csp zphX>SekG68od!(iC-%RF!WrKeo&e5g{nUILfp_L7#H zVBnBK^=7QnR8g5%+Gquz+oX0lDwbQJ+}GDP*UqH;D3NY#qSjWQbwVwV$&&Y4Hu0FW zD`FNwYzOZU){A~^RB1N;40IvrzBt|-_j(+*BpB}(bJ*vhZ=e!-vj~G_`i2eUuFupk z8U(ROipaQcj-|^4PMU%oJM9av9z5|(e&xubt`)_7?9bE>~3PQX=OW_xvg_0>Dm*t1bw zGuIDUyoP+TS(%sK$Z_;l^L{9($&fJaBDbrSx_Pnk z?DB1snsLXAO}bBG@D0ZC{uMq1pOLuzS-A)cR>aRM3~W&2Sd}g=TWoeTj<6TXJ4SUS zNlMb(3PY29UyTa)mxOp>x{XSW+I+gI6o!Qoa1mxn#&uV3&y2Kq>BiWGvG#4@0LDsO z6Y&gagY8Qp{rS^?;HW{kC%5{B#pqWNnhLSoX2+Xe%Cx}3^ohuJ;oGSy^Mq(F+c?g# zUCm-G$ZP94d|++U5ldaB4(1XTy+);+VL!}%eRns8&&xB8iA*xQx7OGDlG(jdIbhv`KJmA&*YdV|NUi&}fH#!N zBFGZ*TMVekBL^0uc0-^18{cEz1s5yQwkdxF7ykn@eq_G1gOMhY+Rs&keW3g`saW`E zIF_Nd{PZv?jzT9Er&Frf*WK<_=e^FWEqqO5*wJS@tQeKq@%WO<#^!XoZ_beP=UXF=1x9QEp78=QtN_NQ@mNRG>Y-X(+tAGls|J%&fyNe3alWW>E8-{ z-^6UfmHi^57*jNDW(DKtuo>?kQjJyd#d02{g!Qkm@7a zc~hCmp=BkWr%sV!djv$8noU2Ev%KS`t}7H^SJdb@=EoaV&`4f5(Fh#BD)ULG=00J3 zVDdYm0EGMdI449?j3-X#OmkCATu=p5)nYAgI&unR>en8SLY#0}lLEOInGmX$n(GF5+gcAhLp zk%Vp+f~Lz`JW4(p{u5qo-a($cCp0X?Wq863)=6$xS(y51bF?L%{QFh&bdFV3aU2qQ zgGc7P#nDv>`u*JLMO|%8sVGvs4b}a&*=9dw^%{od;kQHemw}Qn=%u5cJ$_mr7mC>@ zwTxeC%w&9Ks(W4WEksL6gz^te7hBzPPt{%F{X;Ll~P|<)gB`rFYyeh33si0%_^KDs3Vfo*fZpo%J9{?v=3XWJ54m z%D3XV33=UfXuF#|D3#8o31K|#kVSMzSO#r0yg$ zud9(C5HjNHjrIhj%MO4z5muVWHFhUBV6k>KQ--$we6kQbk#gPl1x^^NxoZFa);Y0H0ML}s(7Y-I>fo)k>T`~x)Y(%=h zX>2F73%`OBINvyOWy*jeobF9Ffd+M* z*X^~cVTZ}-O3n#4b3bKsD6!;34qt#H1Njf&k{E{(gCpAw3*FLec6Sl~LBWy^yEtMA zsZ={qJjs(we^71pGZ>RrWsN=WP^Zb2x{SQPj)2#lLxs!wpW-*a9$30DFXo?3{t2z` z5u<|Q&%un08HpHgGgBfudjCu$huzR@)nj7K6(T(u>myE8S2wBL?Y!ws=((%thf&W0 zjU3;kj8{lYFUm-tUJmszXc}59jBhL2zWaGkU)pxA!XH}KQuRQgb~-~KjdZH?)HK=N z^!ao%%djBHf~IASBiGz;t+0$4G4w|Li27UbfmMOioJi5ZdT4MmS5kk+4A0Itvj$@< zfqnP6Gv!zn^GjXRsxoc61uusyo2_=1SX0bP$*o#uJ32!WBG2{c73gPH+U(&^3@4=O zD2&KI+CV(eF4HJcAUl(`gYsp0Btf|dhKCAg=f(jf!m{u^{Hh3HpYshqZK1w79 zSVqST%hR&r2LrLx=&-{yUJnf~s^Nc7@|UA%U!ZR)OZ*Z3EF7Hi|1PzNP>Mv6qJD~o zSW7NJ9WrU;o_9?%MCBL}n9p3a{@~5u?2BP6eXASTRx7ACoSaTVD@GQ(FmC>3FgT|c z4@Dmx#|L#3JHVLD|2D5^lzo5$4DF~v_q6uuNak&#T1EC@S%*iHairVK><_K`gbXKV zrb{?dnjVaEOyxpaBx2PLxw{4B?j$6-2thGYad07tQ5oCb==Qq=5MD1fv8|*->SThg zkhankq;TSJkuAW^;HBkuVbtq8SjeMsb zQ|>LC_q=?bA$p63NtMb+j|#Cj8WsX5c$fca|0~;XzRo0gZ2besMHq<|QD48;mmM;4lz2y($|2G$yN^jwd-e`TMj%Z>_H9`6Fybo$r2;(Qk}8UU1p{8p!J# z?zOl{^p*l|!{w%HSwf$bl zw6ggmVtTJA?B}vOM|FUFZ*pN_DP8;X75h~SpUJ+t0XJp%-hYjTprm>SmIB1#{2}CkRg4UcPrIv77qbO=jLYZwSkJkG)hTzW)DXS#2 zGqUT!CC>l65tPqXC4y5qbgpzFD-{)kLh;jU!`r$!PhWnWMN)X^!U3-FF$Qs{|6<)&j4pnIUGYQC%lp}Li{1Y#Xu7~KynFo*U-33)Waq6A5(sCLRd zn0ormsriB8J9r?GWf{;|+d^?zLLFFKhm*gV#TUxFQM-8}^=HyXKR(%;xS{vn*lB^zg82CON-7ZpNWlfdsgSZC0sll`bB{pw z(3gsii$e`u3=V#n|Q zo#823cmDPs`kAF_Wvm@m&~vamiwQ&xq7f6GY^0$0HoN$uOp6Y#~XN2u0; zj}woYH!4QVfMxr$I&SLMfh2CdV0;&A*WFnY719jut|~X;r-(tmx~{6MzuJ~xz25Oc zd?NG0hy$u%Q*nvmUl2nuq>Zd#F1@+z+YaIJeSHsqE2gIpJR`lKLbU}X6c?fq6s1vR z-iNYJ_$xTM(d8hRNBMd1l?%;%du9_t%qM}%5hTbD=#gCJ;Ur&z*`932n8 zm&Y15$KH*kz9B6^lDJ69;xPcpd_}EVoJ^xgAsN>z6dxR;E{uxWUisPO6R91Yc3ONK zX;$B061#GC#6W7)SIHexG8TVO0~#qsab1xQ&B&_I&0LoS@FT>vg$8 z-YXInO)d5l#A^)VQ^wA5{8bP0Abg0xq$tj)usGTswuWK1tZwsAM%&ZBjYM*FH^!eF zX5d9_FY46iM?OLly9Yx*O0vD5VCC1(P!?X&@0PB5s0B^wk`8k@f{|AfodlGq zIjt<^g#o0$Q-Q)7&9 znGhJvGUt80t=T$7hd3Ng<_sffByqO?S5UsoRmgen!f{T8sm-Id&<`J6}cW}Q4^Wh3W zVI=5G-EKNryQe&)zd#Xj)OF~8MU5OQgM>_hElZN}!~@N4^GAg-hy5=^gI-o@b3H6Trv$9yN`zc@AM1}FXTOBS0vNeJ#|Go1&fdA`Dsy7{KOWRuQbQJ(_Gg z>=gK-S+kR?nZp3ipfH^#Hw{a9VEO2yi^WH5aPf9>hYCl(Y60<&NHGYoj$}yOh_#UZ zoOe&e?&e)dGD#07|90|E`RZZMT4&=@8Ge?GMrXSxm;N5XP`UmSI3^bEh9<)Y%t?C3 z`SS~liwu47RUM$rHs}0^)!Z{ohsP}hG0jUPd-!&agAndNx+cFKVCKY3&7(|;{XwEn64wWKkPuNUkYxsuP!fM%B8 z;{v3b*E>im7LbN)MysvQ@45mJT>m51;J^Gnu&CcYx=Xh{KvDjLS;+PtgVb#9UTRtTZ`SA!o)(&v$kWXm zYb>(V(bWm$?qkcCrS+~~nNs|H$)PyR6dbfYLqkX)yXl#ppLo02e*OmM(WK0O?+oj1 znmonk`kR$Den%I=p$1eE?;C6ep#3ORS;Xpx_U)EUN%Fg4Y>K z3YXpM#25eXrn`}Vo5JNgocAYTL!2MwZg3**-`#b?&wf~qKUN*@L0Yom(GJDc>xFN+ z91Yn-AFa|PoKeF6WZ%5>K1~MqbG!L??)>M=2ui&}7j;qZ?uQFcSz-SVTPn1&+l%&p ze5o*Xj{OWhki4G$nb?Xi`~;D58M7DQI>ND{G5~_l-u0Pz=8z`53}K$s z(Zlu!*ZXU;GP9RXJXf-otUnUM{(KDZ|4}d~U)A?Q|C=%3{&$BmKCNO7xU(EfM_qRR zbL9QBiW>|-ls^fiK#a!JH9~gi(CF{f`Z9Ja2MhAq_X_A zPVeTw*JBHM!Dv@^+GQh-Kkf(chdWdDMt|K94BWx+hawSig$QfIS!;SNiH6F(GD)}ySgMW0PUf%YZkYb9 zs>3KK=O_`RM2%HdHY9&t?>uuif!J(^{@k+vT|8y=yE4*T}{OA2tzp1^F>5`i*!dLi_ zBPr*88+ds}>C8CfOCU4ijPxf<@uhbsh?^+Onox`X4YBXWV?l`IH^`at9q`U4!8=fS zp3u#}LsRSJM<)X*L^~CoGTcI%{tyu5RD>2G@YfJ7XoHQ`MHL2N{Oeu+UZB*aRZrh+ z>=%X3m@OJ$4Xvgg7h%crjbt0sCZ=Q{`>y^ud~d?RaU1PssP*cP`?R_NOtz!7{ zu&=fVt@x9N2bY)0QDbg4(Rv7T^BCmirA?xc?4I4(;V%ry>7W@N!~ub)I1V6^3~K4nq)RH@bklA@9W}fTf~on zq`UO(>{T90*@XHcx?`OqA`nJ&_fNiq#~EV!^TmdLTs{*`KompnjqKlgdoziIOvK&#xvItE)v(2o zkI``I^TE$-9YC_a4+?Ph{-yuM-;IE8k<8xS)AsjMh~6!Nr@C8h)mX%DcswoitZ!w& zLNZxDR0PDgZ`{bt*s?_zkF=h5HZ%2vg;P&Z5HVw%$XiA*@OIq5Q-+<4wLsY4A`*)6 zjA_e;F++;pseZ$v0Msndiaz zpgib=s}03omLL`LL(Jcv7!6RHag7-DykH+3?5nHf&cRh#>5;7UROJ&qIu8%RiGma% z{IaMGiIV)7fj765*hDZ=<&DQnyW#hmnqYCmBp|X>T&o2;7?bO&%WKivmNmH#!&_RP z`K1spF|W1HkqwwTTs0iFMB{r540Tfz-G7c1?PJX0E31Vs$cH`642u5 zJUh_OXK2)L@Xb`321z9{F9FX|zzvd5zBUj7^)|UXLgT=v`=DVUkWWb_+jDSCEmsWF z5PRJ;?(OZh_9rmfUSB-7_<2G3;K9<6D2@Er7r|Kc4~WuYov$xgcjmo5{A@T~0MclR zNu%`(y~p$HdHPZsk`)!91@Fl^KVpq5HGpA2?OY-DU4usRHPX9m4BRffK} z*nMkN-_Vc_Xho4Y>Q+`(A4NEJ=9*|z%^5*@V)dSoon2Ka?*IoNOvK%_q|D3@kcoKP z=e>wP)3%(@@I~pnJs9|&lk~j6ocrrY_pTAT+clE^-5+J-YGv{QTB#H58}-pi?5v2~ z0$Gceup#eCgqYUluXGtGA4SCokkLzDQu7=b+A8YC^STm-mzJvToxK0h5JR97#3-k_ z?e5!Lk=E6J<*_?bR*b+oJ{TzJB)I2N!?g>uji84))z!Rv{+|1I;44^xI1F$oD;O2d2442u}OG|!;nO~==22|h9C?h1w&`yGq7 z3)upiCgLwePx8!dhNBS})84;ex@N>|(9dX$M+~d51|TC6Am7^TXL&jfd}c+Msoo7_ z@rOK5;k31FqVT0}d(u&&M97vZ=nh;N5C{mk|Bz{TwEO8kLkU$!pQGH@y577+ef0jYyf-% zv^Op?>f<_aA(xQJlHEn1+^=u}7Xb7~w)ge*8MQ(t=yF~ZJMawNB|Vi^ z$6aM|u@hd1>5f7QWb?U9ZEbDNM^GX_-TWwmD^6isBDMr!$+D6`PqgiRZLzMlKeOg@>aZn?rDg# z-)?|*$Kqj$KF{!$4yX~8vJcHY?VjXW&PB#H|9TMxXEO?0jOX+Gb=^E}C+{vDJM0qS)zds}5(mdXU%4pa zWq{LI(WvW5!Ni%!AB~?MVwW29+FhfN#N^!D7{7cB(6n~YpAd1{^CFLyn@2JClo)4| z=x4+2)5H`O&?QXSQKtIG45b)oq3kOsk!_GX@b(+u>t21Ub?)mjZXmZjdj*b1_fNYi zvVZ13k$0iH+Bwg^;X_T=;)Ywb2v!chQCLM4v<}q~hc*r8hQejQuPF~ot;zwpIhDo* zRHBa<435sIH_sBR{oZ!hi%r>*G5>nMQ7?}v_PQ|oS>hO^L*8BeV$GDp@%9qqdbRT= zZ2R0pAw%ju;}+LB#yql@!RxsM!!4#}A(cjqa!Ril+Omt+#T$W-)6QQia$1fqt(h3+6^Zo_}~m}XU?T?~@7b6ZQ@K8kyLl(>%)0q31=*(hKBfwEC+8-?&3 zH^Hi@^^+m;-s!sPWAjg=KcA#wz6r{jA!?oZN!WFeGZI$ke(6UgaB|P>h7}(;YXeda zK1+jfPt3wo3~QWY!_7d))Lc#Cu(rm7rSp)d7^d&e{4C5EzgXK_WAv}WTeEZdibAUb z-u7(-Ou5b45R^T*aq1XdWUXgC;$KYOF&Wuy7N~)%xQgWTgR7TmHYybh@2=X6H9tEd zWskj`lH7Nuo=RyT{OD~@v~U$RS+CZE9n8Ziyx#b!hq+DuQa$lIIr&!C@JQi2(ehc= zS{YN%ny%N(7v7DDB3fovh78r+wBw}jE&;rUku7o%{2wi-_LPoHCJE|n3g5JvoG~&k z*V3|7zd*^zfn z%31boXvYM}sya+@;Mof!J929hJF~b7Y-Un~J*>yxLt7MVHjvsT1Nx8fCd$+=D^ItE;RNo{7=byH@K!EN$!ttTW!9t zxjvWm>GZuN_zcEOq@>FeGpEFFLBxd2&e&E2S61v{FslQ;6Wpb&s3ZnpYaI@!$mIp1 zun?X=V}3lB^@Bm2cwPtyYE%*7eW8Ekx$f=K#6qlSX3VnD8N z+r;v+fS72#H1mFf_~OHZrT!B>HcPC;C;qyX{k+zSu-IZH>|>21pNg2IslYiRZ`rHG z#S{qx-pqDMV$BIg!myMi?A$ElVa12KY-@C$C()!%&Cy>QmB|=VxiY6Gf;`!ag}1er z78ZO7J5&&uVpZMMiGsyWi#vwq2u;786W68iFeLK?!oy`q=;XC6MF1*zx}e`ki8T>7 z=1Li8)s*!--5vO3#^u$NqW|j!kjw$RN@*I7w|=l} z&EM7tNU1aL2N2ynIrkEWdN{zEX|sjHF)&JWTiR=F(fvM9=Xd(R9Cqh;uC7l7qC5<# zN@2|$d{-k#_wNIbiFN~@x)qThTUo;rK%qmJCe zG_M`%IX@PEq-zmXUTHQ!^ArpM&2T+?Ml(j;t~j{3K(TUahU@Lt)V`=>cUQ8o(Q8o6 z64)*w^@|^Px%3-h+y}XMPz1`%Ul3udWXYYGzMYv>U)q3bzUIIn2KsC;>A z$1$tX@qx12v{oZ#C}X`?0_SC6E&+wpz9O2zKsnc^2ByARB}UT#)Z6|C>^-nGuGPmX zJ5MKz_I0D;78#lcc3DA?F?)kU##_rIa^{Dw_ z0);;JaZ=Z?@(Kz&SSq(Ra(^75-1x-bvY9>x1~xj6FBLx7!NFD@eni@#f5?6 zm-|CIOP>79#PT21c@#4&1Q@kYcDE#(oU*$H69GQJIY)I`R^uw_a(i)&J>|T>L{S{- zsp7)N8+Ev3m}oOqOF)`ylFrs;p#zR z5vyT=o&%Z=A&_Pio1OAC8<5`d+N zP7O*H9T&%Vv9*9>mnpNI(dmsrRsKDJ4%kaQeEj&CZ4F8Up-6}{wSHzSJc3)$yAIK( z@R@uMs1#uO-}9Bxf$FpStBB+8YrH6~ij0a100F#`AhQf&FeN zyrcj?%_m{!KjS+72i6gHk+u5Ff06Z$sb0cr5N%*HG^jv!;4(fCAL4k_vBMv#B){Dx*Pvx~ z1P!skigI>&+_jyxE5*0&D#Pvktm1MawKCNhJtKDIWe5J{gnLKIVoCdc0{Ac6xIO z`uP~>57v4t%ntYRwTl-fyvCQM;BoY`#trCPKH;9qJMIq%m4`6fl*p~F7S!gRURTII zdwXdh-zNpFtiLW^fUQtXbRAF#&6Ie^9gxa)Nf==gG@t~oa1r9Y3i-^lF?=RG^p*bf zm4QIy4ayZ0Cl4r+b~H+#zS4_&mqZulKLt5am zx<-FIgS_BU&JItf4$)*4O8syRx!r4*gS|Oc#5FAu_7+EGg9D^Yi6qdjeNEb+8F7Y0MUi%cPMGP-z6cBFWG5{VaxSs6J(E|U7JH3_(sldQe0 zUbi0h2Re##Q*swUTka(KA=4UYMEb&H78)e8bBcGDnp-y0m_67lz-3FKT6-8t)`Ji+O5Vx zzp^+2fAy!-6CGdH_ZD-RqQ1J5{4RC9x)XszVyFJZu0K4q_>J~x{jbCTu;wKj_(3R> z6A9pq7O~h>maEhd`swvfh^aO6d|yB~{4{)YYQdLzV8f4Gb~<3~meCcorFzTq1krpu zKoeO=K2NdooijR6-pm%}U#a%MtA@qTXfTlx@b<2Ik2er~Jq=y6ppu4#o*h&Cx_+k? z{06VWZdK?g5tB!hx8qD^?yUTMzS`yans7L3I$00Fr^H8jB0;Pk#6}WOG9}m*UEg%o z8PL>QJxn(mp2ETWEQM2X#b#6-f~IDSWgL)k_S`KorBZB5|F!*3mRa|gssyK~X;7wa zhu+poDu!}1l`ZGM=8ETapGobGR7oq|aWW??sjgon==H$>@=iw5E?foEp z?VekiZ^nJ_$R*#!8BTihe&%v98xBVN)Wm+*HQQES{3|dy2)#Q*!4OB-v5UD-{W8g_ z5JIU@S4OXS^F2;BhI|mlT)(4?9}^^Gc|A4ERv{ha?<0~wn0wcj zbJe>?-*n=HWC~n-4En%>0liw8NyBf9#;05ED|~*2VNLYdrH|IeLrot$c-~UQ3ig{io?(STVJHlP|I( zk=MtGmNWAjhph2X_<$9kv2<|;Tb=r*DgE2b6&7JhF*NEvyI6Xg>R&fVzFsY0}66HPfgBeAc@2@r;p>c4tE{ABx${6 zyQnVtV$LoGb3b|`qEzrxWjluSbUw(13}{FQSry#d(l9r@=3lZk-m)4&)g~4AGo^5j zcjNDLKdhwv89(bWk9W0_*yzX4f#37^oIEm-=xxeqxvGI~5k^vSYb^8}784=g;3zFG zv6T-M4r=114>3C3X%cMS`c|Y$W}Ob~J)voQB|E9yQCXt~g%P}cl){62Q?<4g0+%^y z@d^*bjl4{I0QE#kMK+2gEzrYdM}N99Q3D;Vz8RM_KmGlB^x0+!97#VFCwd?Fy7}8_ z{=V!_-y}3c zQ;QH#k{iIy(bd@A>AhEI);zXW%h(+!8vYDNX=Ym=xh#A9cMRM38=(>Lih5C@tz(`w zZg%w^J|WDF43vr9SjzCZb!dlbpyAUFB&1DeXzXJ^H&=s0^HSotUGOo&i4U#ymv7bv zk`Vgsa<2_xH*`#=Uq?PZ_7}#DRk<`5#mYRk>pTNVre)E)L>9}?t3vt{^(Y{o57^8M zOP2NQ(#YRlQB!zxk}~)6NnA*#`zncX3u_Ri zl*^8!RH)~oVzOC`pQWYbKc%xPD^LD@<#MPbD&u2~hpT-%g9h=TuoA-6B^Hg3=!&lz z(=W=}m@JZSu$2uFk%@|C9jWY~;^5>6xwaiznmD$9isb%LT>k4;i~{TTWO!5vdUpi6 zZmD5spK^h)QFp%DW|)hRXA}^{SA41o(-8}{^8zGVc}u$pOmzi%k!P~x_cBpNkrYW* zGa?QUh!wGGtmjdef0+oqPXlh$g69W@#Qh%EH347#Nv- z{lIN*7u`q#23m(}#YF=q_oJcIuxZjaH7K^{gyKDiI@8 zKBIesES#-aCPjlvt2S~<)r_dxn*FAwpt5;-bu~>@EKh;_;bF$dy7eZ9N~XKL@2vC= zBAnVyll`-pxn~9bs+I`M%Kle4#|kJ|IKd3Ai(@fQE)p49TxgmiXl#RN$XtJZKD5NX z+7M!Ayr~zFA!t5am#9W6>Uw341x_PdL_Vs@u7?j1lXLca`@+=6 z!abX6?8{{>)Js_7x1a0|U*l${-Im}r!UKmsj}j?n#1c?A4EZOJkmim!srp(3n(;6* z(sFBWqpF%#DGQCI+aMtq{y2Qvl+8Ep_gYzF5DN#Q(F?i$W=614`_wxcH9^E*G0e*8 zy*cP8NMFQO7)H6FD&d2!`cbYX!mx#a@$}33#0Yv^NRHCVK8$lG__Sdqt*7jy(j|!` z$4cahyeK{90ep}NqXehOQ(~JVJrvKMTMvzXG-`d08H$^#A#ZY0AH9*9O< zBGL;YStomJ<5k9fl~-1348JQ_OsIGjzBM0Mhu*OED~3`-Q&4zl0EeYB;hLJs`sRVf zbY<_$;xYs$Cso)`iY99u^&z#vIz2r)g=G8yNkjIllbve>(BVeq8~;q2sOLW8qYsdt z!_{G6+~Pd0Ogm5E*Ox25toOV`{SblNP|v8kb0gO&1N4KC7zo8# z5M}au4Od1tNP9xEzc4D1x5`xn;h800Qc4eD#&+(#>%2%>^ioG*M~C9T^L2wp=B%7$Y@u zE53z6qd4f8sq_eoypli4N~rXlk6kj^?m0ak9OMcW2(qgxB(baHvAH6e6>bTSL;Mwm z?L%oPw(;Kc%>0s>E^%CTGPRwHRdnbI50#`NTc5PU?rr&4An1U%vK_Kw|3q(iqO0Ea zAanOq7&HD#(M!tn`<)1lfXXiMOupV+O_%NWXb-l&gNThdm^4zG>scjlx^ zRlfH~^H7mDxDND^3&S`4mg%36KuO4R&;#U#pib_vHB+N|48DM>`CR7sO{IFS9SA))-6S<($P6En*NdNgZ^AKb#4vy zTA-AIFEhMIcy_dUXMNEcAPMMB9UiCdc7M!$ZztKiBKL0VKauCoL%+>f*Qd$RkRGmy zcoNDj2Pz7l;F|;X;;#3W3&AHv-7|#dIIMS^vqyjNiOlE_*K3%)zT1m}l zFqo*$VAEFfbTWqcygyy()gb=x?CJgYoAOi_gUvqIlo{nNnM!MOQv`$gS(tcrltO^x zQEDevYgHI4N9yYv9O@n>FKMmcmi=m+qC(Q>^g#Is$Ex+?x)qq6RXXAH5i#JgY;~7Np5Rk;W74Q+VGBC9mn4do5qc+Z%_d&uJt~A>YnG zp?hu8@2z8*R? zaLxJiA_jC1_51dAZCBi5x2P5C?r*%lRFBQHs|bchx&1uCyL_Uh&0}k_YpwPwtHG*Q zdM1p2UE&z!US9`LmH>Ou|IH-`sEp$Wi$s}XiYm-g(U&xum$(lvB8-Wu~ z;XRS4y>KgEd;zL`d_|;j({BiI=GE*z)L^C$A_mco}N@M4wL*s=UsQ`59hsw^s?f5x8BbAG0@rN zi)h$}grnuu-jkd2)xctHuCnfnO0{f>g1TCc49%KioA>YKfE!4ND4~@V12Fg{SI9b$ zzr-Yf*hwMe+&sTt&ncGh?Ul*{T*Wnsw-@Jz0u@GKiwg@VlF<&^G!^j*`3I)J6(>Cg zQof}Xf;Au1!YBjCEU3jgtg%D&K`w%KBr8{tiXFY(`P`AJENCUBn zV6*?4;aGdWWqN(QnH@Q`S-7>r#<}iCfmGNe37+vB(W(BML?(hp`9nn_=<{~N^{N~_ zMuIeqaPfq1@`k}jpFQC;k#KLY8ZZd$efOXwz5%iPrw??3hgHSb*S|5UlP^dq?7jdOhh@&77K#8Ovh$VUHDIR5Z zU!Gs!p&%tcfRvu)?F~xJ4q7>m#WzqN%-tS3?WWin{bG0OI2~wMP5!yJM@Iu%slKGS zqS#7F_C59CLqpr`7;RHWCl*7REp?%++-6pQRoG&@SrxKnBti6Ra{sOt6tq88ZAEe7 zSu<-8u5Yv>ysv;6JA&@Sh^DC`+qK)Wqp*!zvqtkUCu-^KoJx?qZCR9!J>vs6t|z0s z&x#pR&x9(qxewu*eir&B1HYp5BoW5{)kEB9heCjpO+!7w3bjo$QKv|J>FzM{#@(ei}V==5k-tYVy-s{?^ zrRai!f~Q2Qqz@6e+86`|&he7D5`HP0jA;Foh(O1oka>oq!0qz#hGBc&3x)5-`K5>A z@fFpfr1dKnVzFwEoAYTB;h(fiJHR{3&fA>we$x*(Wt%e1{egAM zPeMmQ@`Q9)dq;=g$l8N-Q?gs5{ynE8_8iycwF~j69Q#!#UlZ7ugNJXBi7EFzqC6}t zEPTH>AkNen69mf|N2GxT7{Px386@KL#{T$EYM_hc2f^^}t0H<|jQ5wg%hk~28cX^2 zr#PPtbtQe1&3lB(>T=G71Uv30_z&|7OMHG3_AaH)Z5?&2JRDmB%K?q#L)b;vMNvc0 z+4-=WU}(_5tJ(Mg`2(2>lBaY#h{kSsA+Zn zn$td3v-pB) zH+bvUXv^?r?K1dBqTN{w5fvm6-SswFY>wUX9!2D^EnY0|7iAN!(K)s!_`l z)s-Fo`SSyeUf1<)mP#Mwd|vq%se+~G^(dMA<(F&Pi`Ox~qR3}7>`duF|4i=AP0b!%|SL+~1;WY4Pk091hw_7V6J+7P}^? z!$Z>CxpiP>d32zu=5msB((!e<{aCI%92Dgpa1z5LZr!12(}zJ9&(2qM0Y@=!Nzri& z$_B1055theWwUtMUZLz05U@9eM!Z(6+d_PBxYbc*Zu&Lb!rVEGfJ@@)r=-feKG6OJ ztIjZ-3Yn~fZGG46Xf0c?>CJE&Kya3zQE3%Nb7k*6*L#K}pi$dtuKCP>9RY{qFYRj6 zTWZRjFa&HQrP`a(hC2B#YP?wkH1PLggAwG#SDUM63y=_VOIel!rcL$AHHoZuYLGC zDD-w0{~##CJBuOnr`zyC88JVx*m6;IHHyN>wt47`o;{Ku{^&T@Q^IOOteMhGeI*GD z6=5l{rH=3Jui~fP5?S8;j`$p`iG1ZEnuF?4(3beZ<18JE;Q$9NN`T_Thp2kX}FymJS zP!w|KW1?3JOgU{U|08^>C4sLsN6-E{D};W!=a(G%LrqEFk~s6rOwH4LOs?_w`4Qba zMTH!KpVvK3s~4TG(kma#tsp$Wusi5t`y|z_0yF;1=0J02JmRp){uUTXP*@VKegs*@ zGgM=$EelH~hiN5HAOF#V^W-0)Q6$1cDj4j60n*<(7aWu?kumpQNQNNPkLYcmYxa$Q zzBa3C3>3AZFN8ci>DlTmt0gMpd>p1J3Z|0btX)O|a32*#B=YZ9Eq*)twX2TFz#C_| zZq+Q~((UJ?1V0Xd89g$ZOQ!nGG2w3`=O@lt*dZ|fQ{f`FcQbgE5!|LYm?UtNzMySed_k&~}~>0BQE ztVs~zu}1T!VAyp3<~oiakNyTwJLCUpdE%e1F$>jrYv) z;OLv}YoZq+`hXljR0Jc})lGYdANK&7{s@j6x9QnWObA@92be4E$;SiVeosFV*}0}K z^`gAM4DTSw!U`8Tp8PwOi3HKW%Mm*H+5OFEaMlFBVc0q6-Rzp_LJuRGJm{g6DAg$kZ_ zK%(nD0uOVy+r(Gds1Ifgk+&uoT}l-jJEjNEnMhs{cfxdL3aVvjME~o-V*-89Cv5ZXAbOvE<^Hc1u7 zKCeBP8Xuqzlfi58Ej*C{zwm$l1L%tT6}vwtGLu;@@!^o5bpaO&+e3GFuD?gnyA!N- z_+gxB_aAZ;#BDW`v0ri`q>G2lHO-Z$M*bj71rn%p9xhhXAWSp)->N?(ChpwNH7$iH zb1`Ddq%Ux<%50Hh=kLxGIV&XZK-n!NBhSMjjMU$P^$ue9<@?H<5)xn^H zbyCK226f=?!tXB|FeSv6=dwXh4QaBi8*AW)&1wsM(07p*ZTMvU_)5Q$l#N^%-Hq+f zmDT%7? z7d843{J-Dk1C$gPYG%VjadHe>0$80?`@+@3jQzIvy5l8h|GVNp2+R)^prH~>wQ2GP zZYyxa`zbA$Be!ulA_o`~DR(AE3&~Kvzg6mWw># z8gY|?kTk9@HJ2u)loY||% zHsQEq%UEZkA>nFt7llLVk)*Y*(9NwsNmbS{W6=CwppHhJGZCwFuD*NlgZorq!1>_# zPal4v7`48OWIC8^j%Hzr3=T&ImYvJt?{xZp^5G=?vCVkp2;=PR{BHlt z>vPjpm1^e$1;^TAX#x~%K}-Bf^SNMEW6jH1g!q3A|eV_Ql%z}6_{rx0c zjfqHBD|J!#{%npqC{&u0i0Bkjub92OfW`uh;O;^i<68S0Y;@U14~`)PD?O@EcWigDMAOkrjQ8`+wd4>dqmME9`Age+%eWX-@XzUFDeLC?`QJp;$uK=Rr&#_sJ5b^|WFe)t;ynz(od%d3>KQW7%_@R{2EC9bpkc+39NPrkqmpIPG zc~2C7zHHtg*{HxRy`Q%g#q|iS1JT4(!K(0!)J5I~rz_4Uv|as?A?3iJ#(XOd>uXa! zwF{T)<7H0OlXe&n7bDJ!t;#mHr)$6F8<-IG4j@L-M_IcGTjQu0Y#3)5`eVlB=kA}k z`#EQT7M4UjmkkRr8w>pLgT;BT!KuVz1_@a2&9>Q}Y$+XF9}PB7OsK#}RDK-gPZsca z4pc;iY%#qy+&w(xsw}mbMIUKu&Fv2guJ4~rBuRBdKC7(s8v^3TiUpT!eEj@lv*2EB zbG*S^b|hhyWg;&)mUlvz3a%$>gF1nITs1W{87@cJR4Ov=URqjF@$tTqktjRhJ`Yf3 zGFIyRGcz;kZ=ySZgaaB7bE3N>TRL2&ovhe~_6^8s@TnaD@?0OHqceAzTL(djS+_sY zLG%P%^jkw1IUqpu+!v9ut+R6(Xf{k%?TG<-_0ik{KKs47x*T~mQDmU@(6QJmXsFn4 zizWwxa$n@{9gN7k9!xAT{nPt3+w_^;jR)nAlE}BQvGl06g?9wO()^{EwrB($`j(UN zbnB4|e!#CFK$X1`iYC@kDPY4XBf33Z0d96~=hvcMPS5GJBi7y^0;hU@)1VN3qR!(t zkYBm8)RVcp@&rik+PLi@Tza!3w5oNy2l7&CvrYq#7|T&I!~q7@de4(30}9>PSi>2f zf+J(6C5+4d^89(ro*JL;RxWvKl-BG}aji`w)nLo%KI$DI{ZACviTKrpxMe1j!p+5% z%!01Nc^+ahb(jzd;Sl0DRGhb6ZA5q$=Xl=KN}NZ|3&X+&1p-j| zlOgY?0ihj{$UqVVCAv04D&dhXBMzX0C}c$8OWvmNJMM52vw0Yg3=R)JW;f;YF&)p# za6VY-2{#8A#6AQ+d?zQT$g{v2ZKR#brRZ(sgIEySn<#_XTzFMj=oW zTPmoU_f0u}=4XKU!U35DA)~4)POrhJUtP1>T7E(^rs>iY{-&%ZY2@oyqLsj+Ms>rn z2~CaIC~%Z%eEMLejC8s`H1wt3A8*(>R0h&Xrd=HeW0lO{xNV;#k;aj59hhg^b?-q2 zc5byz)N)`*^rM;OogwaSRBBDBXEQv@Hy)_TxPw=86=CPOTy&~<(3KF@c4aiKS|LY=s(g~OuD-y?WI zM?z|)J9^lC{(C*e6m3K@=f-VWuIP=M15uM_H?XHkS@U=o6iJFW;kIc(HXG4gLdqM8 zd8PBhHcO?Wg|YDi27@TPRo<^(f!9SFiC6s*}2<3WLVbfZ*{%IDf%H|*rtzvmlN!YCWOSZYmw zx<4<_sDfx}CZh$kv?zFC%cVnH>W^fgCnqOi7T=cyarW#l3K2J$W#yz|#9oW!zM}qM zK(Qhok!3ob+5tEde#Ir~{qh5nl%TdQmBJ(B0=2nz)kE7apEGdOVzy2JP*!1+lU(Gu z6{aV+E9zhO%ilGVXdRh*AmNVbFaw$IaBcffABTZ~#NyHt37^|ghqh3G`S*eIDbN|e^#s51fv@Ig`WQVCv=gXmR-ks+@smrP=2#II{kuK_;LzH2g zYyxJU8FjBM-((CMY;#iom^2^3rqDevj&NOYhPFFI#ASPRhkONKzO>jZY*a@bQ{Q7U zdU*LMiKwX`A5l28`!#v7ZeBEZkvnfCh*o=EAW$HheJ!d|>>9=scZ$Y?gootwF6j{t z&oRu^mmP6gzw#Q(S3w43m^RHQ&dyK3Iud9*wQ(+(|8z@%dWNX5^aE@XVNwETn)Co2 z0;van=+&jOxx0Q?sX`%`jQLCky|EsKij@3<&yfkJ>K%88EC7MHKX-ms=<3w!TcinS z+8@Icp$O^a^fx(cmYu2j20>iqMIYo#D*5fn5`CKb1cqcaBgg<4xX++M>3Fb2PU`mh zwNH%G0H6gcPO@pOf-TVZ-3rCw8+v8E@Ip<0A9y(2jFz%DNH~6mqZJb#0w;(q4XN|$LwNBQ`E%v~|+AKYxcHNlNcOn>EmRDTGQPq>pf zllM!0K5j8oRY2GiW9?rDv*#eV`Itd7q_w3$ck(LffVc{&hgbULQ2Kk+n1%M&_`-f7 z6uNhWQP_2D$JU1%Bd?rmrgb|mw^hqe8V7uD%hc%cpd9EJTI{}l`Q4RdqNW0aL->4a z8_~*D7P+Hw8TnX*?bnAnFrY|+GEeo7}?5v31C$5^yvyoMjVg7A_ ziQhFAEPGg~rOnV5Azu`}8A>Ahm>^EnO7W=1a&c&p5E zhoZYSLUvk=j>ZMjA-6;uF7CNxM=30AiBk^jhV$p1{QB3l$2-M?utIb3{%Na{knanU zFRu}=>6Z6Rre4^t@Fr40R;0D;UhXxhV~DhC-#AeJBnt>fSorxpwrX0kW@|hBK(%Qd zNqI+#DLM64fbuE}-8N%6+Gb4wKQ(_PJip4%nd`h(ULa!!hTAq(I_>0Rk!nBz*UOl_ zyXNSqbLT03QjN=?;G{;2_vuJkHIR z8_ePTGsVInfPGx-Hb}6#7&*U|#fbD{CI{-L2e-2Gz<2gM0)b7u&n_7}r+ex_h?0WC z6CPYf&u9d-kaJ`STGpd$H+vDSOvgQ>SJD&c+fs#(_f*w%45?Pk)%af&)ej?!NXDYO zRPQJaB-f4-Bi>e0hW7&H`i{DI#)AOU{8*QZG-khpVj`uTb*@ClGcCJe{d- z8A*6sq}ZMVt0DmxAA2K|z7Zt+7?ySiLh|H&cFGh_{$kBMQu!0TI4f($c2w#S|#f@G!K@?Ag>$>Vwi z3yLHE*@ieMK`Sw5xJB~U`1KL$_U%*{iOGR{ZJuX_2~Mx@r~N2t2BSVTh8Lr|ARwjI zhwQ%H#qM`H`mE>7GlaMt?s{eOhR&c>zbwA zZ(XRLGuu#;v?*gKIbH8j5L2x)bgqLA7T#CWr3DDyHQ@-`6#bK28>+}!fT+$#&H z(eQqc1A8@<5`TzlJ$)OM`!sN^cHR~djKRwpEbrwyttyerEYsg*t=lQviH?6E%fKh>;xSriF{A?>lg!aHtxERD`=EKJEs{O;@8Ry8dIL9~+$7PaefWedBw zD|GkXfQCN9ueOVFr*tWscT-&oRIA%i)Ba>&4nAN1m?7XA$9K*xYpUx4K&lAaJoC;8=<^L3A555DdB$V>(X1)*FB zYkr17^Wa2a@Z)dOtqWKxdB19~b7NWKSr`k)%@ZZ&zZi^0{7XTY0h4i0Zz<))UVf$- zI1={lks`Mc1}5_j%j;GuljpZcspAK38rLHktdV3Jp0UBjfWs658HaL}73xRZNW?6sBTq9WnuWj4$S2JNTxc zPnND~7)qWIPKlkykcdQ)h{!d%bH~t%22+gKiDGd`PiT8eq`!faVc`rr#$>5?J#|NB z>Fmvx*05e`qi&^%z5?qyzpFjE-kbO4bG3+_5jnPz{t6-yuSwXF_>BgV3y_^Cj+Vp+ z32_q52a;VV^o=N-6WB1H+0xreuL(2R{dO>95gYjMm@t@61eWA@#-6wbJEXbR(Lap^ zLu0uktd#sC>^aGAIQNEH~U~{-f&hpM3f}NJ(Zgd)+5r$0uir7O0D94dUqY{UI_^ zlODd%IWJ-GaQEpJpGPQI=50(L$eHiD{3zyWy;=$Aj(^F)((0;qKm{47dK;2y`+@e9 zAmUZp=C^rbmX+}()b{=F`?%ar^q57@{WdzS4!_f{A<|_EC*y2e2I4b*gKlROv0T`UPY9#=3|&!^s$G*eB37C=iv|$LHvdgcEMTn)Mu9zMpmV8@EluOoc11Hu`R_Y zGCDu!bGeb9plHHTN4r7i3LQ4GGA1~`FvL`sF1)!?GaOqy4Nu#BH#9q@qaleLaM#KKFZ^q~&dwbP$|ERwMzdnP;_60W|CBZWp+0 zbOJCT>3mrdq-!`V`I6c%e3w3CF&w4ef3PXgg{XYaKju(5L#T4V60V5iexpbZ`9zeZ(PX1MEt|Q>9d{!}kB|)vvmy7p% zVtxugvbzoam<&TKmgaB{_v%O&dqSJTG!?M5u$ zt#vS)%}S-qOhzhU-CiI{t|JH=oYSMm_M-Z96kyJC4pJer*PTS)% zAT>qr7wqp(mFXvxo(Zq2Z~XMiM$_J%gKym@VBF}8;v69r(^g?fZu_|DVJ#ad&?}fUstIj`|!(S;J2vaa+$R^0nGG(JT{SV(A0)1OPc#ZI|(Us_mA(l9J~-2fLtH`_EzjZiBz}exO9C1PZQCX zU(}cZmAc@`8*bGkeMDvpQlNpTuSw!$NPzhV382ecZ;8e&h)t{2*oN7BO~?ssQ!p`V{!!6V#{HL4^_kfORUQ22 z!cV^4r=_I-FVj-sbWbEWIz)+ss7tkCFh=3wBIAoc<>YFz=0XMTXTz&S7X}wf-&yu0 zqM%28X0Sv;VW>}c+t?yPUu1g7pHa&KM2GH*H1Rt3Zx7k>_rKunwDDQ?dVig^e|GXV zr)@AdkV#Iv3+>sDpfa2syr0wVnNj>a9JC*4mWXs* zkwxIFqS9_d+Ya|g!rCZXU9jiZg}b7(5$HcM{QAhOY`9z8l}YAT@ZPL2h@bV8WQwCA z{;-zi*t};`p!@X*Ja?)2c22w8*HFAH)e+Y=X*J;Lmz;+Aj4H~YCoi93PjgUN2K!Wo zdX@ie-4%I*%0QMjfyuAc6@{1l<^bEck+8Rnh}nb2Uo;IJtVv!+wmCn(z-de&gj`# z@MAyU&d30`{xwQrq8b(Bq7J7 zmuoUx!A-IR9L2FG1z;LOx+XotPEvVFjT_>oGxdw!NRZ*oFS|4_1FRA1TMp7Mno7xh zjnMEpr^7R!I1!T^+Gh9#2HHs1i3JF*Su7V|IJPJWv*~!CWQ+7B>o4E#0LPbH&$05~ zRBG91o|CmO8n=kZ#@()nVV-QZWTs=zqRy)L%{$y_ILd}DjL4_J!-ib=vtWsfT^^RS zTU5?hh#y=$lWytMt%9)0giIy1l_KE_2U%Z@850alDGn4b|NVnb{qY46>S=r~YZL|S zD3wf6MB<}eG71X4-Pwztr{YT2SA>FsI`u~bGZNpK=;F9te-G$-(FONx*Sn)a>o~vTq?b+34B) zb{^ChuUqbj@%)Lc?Y=o@SgUI9r-B$}L8#h-b}TQlQ(dxgrz@q=naBrU@;0&_lCW_4 z7fMkwrEuF}0s*I?7e~=hrJ(o(>PzPT3O4_fy;J^^HZK3?_RTHHZ(GV6mJ9=Gv|Yn~ zWwn@?jJ9UJLGEM+f$Rceoa#RZbz6l#gQ+<%=Q~>^%A7=P9wj?alU7@ zpKPoM9(%tO_>KM3woC}En8LUx(a2r}u{E4<=`wrpG&x;;p4ui#MS*Z-4lJq!?g4l% zqzTyKDn$GV^X4mGTN=6})eb+^7*rC%b_Ozrc2@a%7$MAr+$2(+N)k*%2h3s-uk8kF z3Y3Vt_>D+UtT0nT!VwBi4FVMRpoD}fXfRu*4_0k-oGHgQ9OW55Y)o%p$U$@fNM z%{N&+22OWM%)3WpEu?^x$;?9&Y_JWce#PzvF63k45d?M<%u&b^{^a<_7vwu!$(J{i zOvK~c6b(7yg-r}j(dm&#Q7}4_1tQGX9^}sp{Bz)>YEKW3c%y{AL8vDXj$}5cd-i-W zOmGF3!2*gyQ!G=|TWeaDmXgT3q_p_;ZG&bWOu2ohw0kX&Xoba0b{H4c-WNXd=SaBb zm0sGg-07T*y0ag$MeRP#v-ort3C-TPVINu|dZd6oG<3DoH!cuMnI7Le?bIW)iX7aS zEvOIA`nrX?Tt1x5s4#7j685OLVhRapGuB(~>hIT$AH4F_VXdFv7KE?Zs%ZJP>~_#T zHkQo6Lrh7z&0fxJu>UTKam@uED}5NKyy&UMtTr$R$M><49`$!g`XfZg_0{O@e$xAR ztQfV}0o+|h&bd$<-@-5`{EE^YZ@h6#iV6rWD?=B!G^u8(a5+*Yc1>nu$%i*{+!#W= z-ftzy?QvC!J&Gg}a!Eayrd8)C6ilb4HHB)sGW%6U>fJk>7hn(N)2zoU zjJ(rV^N*bY0vP~he9I{Z_NB#@rKP3MRRb4C>us~a!I9RC-8pD#bYKQM!85{bK(7BNRqWGF<~*2ziCz#t37kb{J*?RZZDK)NnZl&%}E zZ4S6|%c>|qAH?oS3h>7NX^@%!-PeaT^%d9S%0bBUkFLI-&Qc?=-% zD(qH1A3j?{d~+fZ86E9n!w{^=(UcO_d@Vgp0XsA(PAs%C+P(8>u|#{rg}aQx+Tl|w z@&E+t05iQy^K-z!iqeFzcn;fOVxKTf%7G}m`A}U&+Ww@qJh!?1YVBY$95=5qoGbxz z+V?`FWrkl>UIf7?mftQc4X8?4N!EK$NjnG-)TWrf$IUZa;JRiix3~208qJ`h`kQI1 z2Qo-b8h6O~e@Pn2359!2$sc)&AA$i1zd+ISKVqw&fONU!e0wUeOYqL*=g!S=+1ZfI zwS`jLsK=+2K7bE&*ONsqw_3`nSDp^5@#T@_)$6PKs&rnyjySKsxyqvcS?9&~5x1Y> zyob~5b!0u5c9_~m6VH0D@}xLbQslDW5O4Kmx`u7oO^i+*rv`v;EQ^u>^TZdCm%OOE zgYbjFP7?l((erv?ms;!-iZR{RRvzkgi|x4DRIBSy)Id_5T=+ZXcyv0jGBFvwbkJ;c z#}V@JPpHrjiexoDNwc=M_RJarCtB|70=Dj<&fKJ@LbTF=xf3oC_ zVa8)`Qsj$54f94<+zHec_0IRxX%ME`g0dme1ClEsEv#B=?^l@A6G3{k0wnoA0yPRE z4vv$Q7>G<9!%GqEFDa4A8+qY}=>ST${` z0l&%f#+4>c&fdNpCWeD@6rWp`PhQ4)JIk3t^W4xVTTSc<1&H`~BHhOiAV(|rSSH-e zd%y=s{8Sumggxp!oGLZNR;OQ4v{~16&{w_4!WAJ zvT%%kfmJ$(uiHloY4GJt6iJYRnUq5S+{{*76qAKvg1jIuL;dJ=bM?VqO)mLPnI2Vx zlxyQ!ej6$EP&--HuZ>MAR3k=`-ei8;XFSc#at6#*EEnNMD^a9&@E&6|<%eCcjZrk7 z@nyt*rSc&eiZSF@pQAvvY4?JT{cgC(I78Sno~jHIZkambR&Wr|(NsCnuVUPp99j0R z7qM^t9qx2@awMx)F=yiVsrT`hnwO~*mRlXe9+ramx@b$%!ORU`u}SN#B|?&A4r@=W z3X=y}x?)I=R4dGO!V+aMl~Zk5R0a;b>{fR;x?+yj@GK5aXROS2qgV|Obe&x<^=JIm zKw|UB+@X{}n%Sgi%+2kn%%Ve7Pdl3Kvb=Ay%~Ng2Mt`rUU72Fn`$JRC`iotv)hN3w z*WxJY)&R4(!FMMu5sFddU>9LG86p}hIC>(J%oP+AO@X7F(7>p|fC83zW{tQ;CJ~rv zAU-&R)dYNuA=SCx`HZum!xqV(CF!>M2YN^;yXf1ecV$_#IqsMchx4Hj|D@OwLDO2e zU&S$|>S)%((y`>^hKN>eNIe zPcPvI6IG>>I7SUTt?Gwy0pk&!co8Wm`BnCv+bcM3mZq@i=;(bpxK`k>4VPdnzIHHG z>!6XXJ7|9qm=*WwSh;j%Yo-b-7>kjjW!fXNtv=M;e*3403tYlca8E9B@0>DDA@5?9g+d{e zbz>v&{HPJBy~tQWiLu-9_7@t8l4wSZ=kVQpvZfj)1Icl+E=*l=Xn!)D8 zPYvfx=Ug9m>e}Qw%m9bFy?4KNg|%3tW$6LMqN^WIRoSI&Y{RkJ0Kx8A?9N*wh|JM+ zWjrgpaffPlw!XyP(d+v3NM^2l4BSR!`qy<{M>HQvL7H%I zWY6A#n~ZcQ4nOXAo>KcDmNRhM3Qtj z`vd?bnRH>q)@#PSrUktA;0t)GWTk5B=3_rxX1!dvH>Xp^N7m~yzMj{pq#uQA;78(m zpO4sm^|-3~j5P#`0ZN3t#E^)WyrT6-$V`@+iIG{XR1LNU@l2aREEdyrS|wA1fKmer zrLa~Rj-tzWxOp09&+4yjS?}|laJojSal;LLE~G+f{fQ?DFdnfq$~jiFcALOQO1$33 zk|s3e8Y!`k%!+!fi^*}KaE|Fc$`P+(o+*yTh*pY6up+4; zNbe4Dp5%MADwEx$ELczUNTfkTwQyCX#5JyDq6H~RXyr|mP#ajc?~X>OiH&*QRr?iN0bvCRjTrU6@a^3T}B<8P?fU=OmjtL=4xuy+|zn3Dk@NdP&=1l}RMBEG%MdCtw0&)Qv4 z7WUlMM&FylMKr>m$y2K%x#eK&2A!SP5X2qVcv2kOW9bS3w_wDIhYLYbnExS6r(JEE>Kjb!gW)@`4vb#*+|5w^MS>I(yvcfTqHt zsy`DCN5BP$g%tVgAX_#E5BeN0_a;pvs%L66;cyQ`>Fb+?B=mY=&EphKR=Yp(yuPI+ ztvhU<>%}_T%?|n%`+Z_)<8ycPejB=MZq^xXBg@OdVvEZw-PY&uR&g^l_6gtJR%6y^ z76v|3zZHpRd|M*tIso-N7FGW@hHh(kH^ZgmT0&HfHC#}@5)>n0NgZv*=N#=aCuKjCue5#Pj_k{iY&KIa+J5eesj&UHAg2N zeRBrnEBATEmUf31ZXU=i^=g;-O8F`=PGNm_Wrn>~oB7hIX>Ns>>Xqi+K)OeNsx-?3Jck419^s0B)OY>(zp-b=X`HO+^<&5>#{E^SS+s(hihFiHa*(bwJ@0%a=! zaJe!;C4VRVAHka3vuzydV#x3F{uLi zAO7$%=d`N7PL43G(Y2xy^?H8|Z>%+dW~aNB^`XK+LM_Hh%=HqgzbRiZa-#pFeqV8u zgZlTo@plrURU^HtPZw7YuLMg(lkGpgR@5w|YBD5)=~_9CcFS?a^$Th};b|z^uuyN& zb{q&zpLeds12{bF>&gHWcyp#}_@{;pTD)L8e6L_Ewx)tx@{0!PnpiWoja(%4n@7Wb z21;xPLxcaz0FV@*o5}&JZwmwR!OeG;Ev?V`z|7YE$tMIC4wHcw4LLE#{uk!f?@#z= zT&YePqBvH_&+JFZHrb7R-q02;()#epjn1D5Cp{;TkEsS2vy1O;vWAmCJXs;uzZX@Fd&8b;|6=bgluR<`ZK1f^TH$J@yUorXHr+XAT0{-UW^bc< z$rA>Kf%D?KN2v2XT)r{pSIuIQ2Z|^D)efZCteuTv@f%}8CyskzuQS)Q#+p4`Bk-rJ zR&q4tYIjCqkn8-}Lppx#y$vvA&YB#q9JOY1uL*&qeN)RG1l`UtYq!Zmo|C8Q>+S*&>K#)bZZmA9HG!aW64sz}cL$1>*a3>g8=F<_Q`( zViCI}0g|6k!hQxPOMUZ#Hk^+|dO7dE!+2Eb>zn10@33=T38zN!EYx9}BNv}{B)6`P zS7BWZYV+s-8}V{aNEs47z$&^n8?_Fo86`$^&dMAv)=>*-y-dyp#0-aK3a|Uq4&C|ax#gaSzZEQH6v(bQ)=+MF@cmvS z@=bF)dkgTc0fgcH%RXuysX{!%ulxn-$PXiS_z8l#pTjQKRoSDFh{AHg6d@!~Ze*)w z=0_LwFq2I*?bId3S5l0(iVCbBe_Cn_miKfnEDhMAd6pth{BCk)`b%JP6ZV;>9vsE@ z>hPj7oB;M-YB%mzMhJF1XUD%C=te_)H)jlENyRE-2q+e$=)xr)TSBz?`iZfV+G z-;_#F){tOdp&q=9-D`cp9E&iel9!u_;%9ENQY3Dl5?}EpH_ca+lP4lPOvft>B=|W> z)PGM=kZ4Y>FBQ*e<3AB2rl5IJxSL9%7#nw3ZDViEanKbddL10|x+zNX*EVB4G-1{+ z%Qk5~aT))l9OUE_xct&qw+a2ZxaH7}@;}>fln)Yi721pZF;wWql4^W+H0o|@Dz&vY z)xnWwj+Ks>u-vDo;n)Y21?` z00ro))Fp;BxaZI)`>Ose2leX7&p#>u%Ksq60bOo*&e-H1r1(qZ_O(>#(((p2_va)E zQpnXTTU7jd>OT)4Z){%eEWIN|GACI2IVXI5z0ObEA&l#%%Tj)@7INyGqlOBF>EvIw zj~l7i=U@}rs4LrXA-_^|H^X*}y5ygUMwH=aZL92zGbrEgH+g@|FZHTnhz$jKT0p z;f{XRtbC|RrwiV7N%Y!cnIC2O|PqY-PYY^QaVZQj>3)v()nr9Q& za_OI0HHEtvxldm#%9Gi3m`^5=CGj1S`{iQlH9cw9ZA^*|D|OmN~yQUKb4rd|u(-15F0=Ub$NqIVI2A!xesKwgd$g=rpA#g97Sp2`7v!jZM$ z;l|vn&pB!l_-c#`t0i-@!(Uvt_`dE9rF4Woat!TJHAe3r>08kGWLWk9mda_ZI463f&Z z=dnK9_bJ$ys39Z_o0(u-8cIDmH@q{N>f0yj6*p>Yn%Pp z?y8(Ax$q6lL|bNszNqcOYbg(BXSg_8oAx$e((^u>7&Gzpak5@|wW1x$A_lG6FXLwP z2UmMu?@CeZZEe|2#~w*1)>9o;_q79ShQ*y7GCq6%<@Ge_)-;^yn3j;fg0~f0qa?_o zRcI=D`uZsN?r&D6r=T|rqKB0#w%+DyRD~a2Mp^96){u5wz3_K&M!2f2?B$<|TST7-X?ym_S4nuxfgU$1Ha00=_bcgr z{;2uyw=O%A@=FVA2F*+gD~NEIE~eoInVIj1fLBB`@#lW} zC(WbYt?5{AYieH*I8yOeh}2Ifv?Cp74cXfCBrg2bA(o1PsiFG*gCqq7}2rpHuoLZa}Ks*m?T{P4`24O2quH{9tHa#MwtekVtw!qAK+Z z3VYB6KWOd?BcU<(g^>c2!ek*sa78z2N0pMc3fBz()yA6rg693i4w7P6LD@~3Cm?uqm0q5s7l1g4Ua`lywm zyz*V$@zS2(zilcKhRtcL_Kcm*&G;`L1lStVA^o8n#-iN=vqJHi6;}BY1oWTZUT!l= zN|&U)faT)mUW|_DBqbtxWcKrWd*$z$0xn$XKM;0dn9ilZ2w4=Df|de*A^$Tvg|^PE zWmSvEY!7|?l<%33`o;GPZ6f^IztnP`k=ytbF3ht`rOq!l_iW6e;8(x^df#)QQMFNz z+pf^$nUX$lWhI9ylh)BGO52=zhV3`l^$htqGvBmW% z*?jlIB>4TEWqKetj4Q>ukEZe;UgUR?AM^7_Z{BqOWRb1IMfpYW{!tVK7^u>FN`Iw% z3Gst0zKP6zqvC%W#p=J9!o*7DE+YO>Qcsbo4RF~v%rmHoXcubqe^->LJdKT%7YJ? zVVQ|aEk01ff`Jfc2`Ul=%k}64Tigdlzc2Tg_G#9yA`iZ|ivc{HB9G~J1k(XFxc~2O z0Dp%=0RQ~VD4$FIub=V;HapKEfpI z9=zA9CoW(BWm2skQ~q7>-^ae^5g)9*1w%pgC#3#??fv<4<(@z4()cX-uV;}z^rh|t zAxRd5@)x$hjr#8A`}+MTU0_;2Ahbp>dw0roQNL7`u{ax-Y*wCfoUVdX#(h981%!4X zFqVfX0s$0%(Q2J{VEG&&Nc=`DUh4(@lt;sAk`J0DizK|K-A2^4aR0rq@iF}Ah|zzw z3P%6C`xx#O6W}AcA6l9kyaHYH{2d)V!yneY6}8Ige^a@6?_V;UU+nLf{4-8XPyc`U zW$TIm#T>UmCm8%wh>Y;=#3U1aO+)=%;sU{Ue1U zO|c7b2^7@2rOA0M&$&y@tV3AI!G$elzeHd(@Z{ViGZ5scMygTwgQ&idvi0aKX zc2pPW6$b0mk2zX=AaT6KoN9`8hZoaFUb1gt4!JBtf6(9x=Y40-gj@gFOa2Yp2=x1D zvFG@&W{a8Wz+fd8ZcT$fC@UsT{y*iA7yn0VMT6CD1U|=4jJJOW2X`8$B5I!7|CRIK zCuX@{ps%Q{H9BDbCNNzGfTzRGtp6%nXrS9wXli4ma?!Ju#6(5iF!S2b?RJLdsr{|~ z@#@J%BDiF`H}EI_9iczGfW`pO2AG_Nh`+8XV%`V?rOk0#az5M&w(Ey_&Fup z{BLhd&{*(&V-+ux5&r7OANu!lB^`L$Lw=ZMgF5=N5eEO+a!)y;j~>duiwf0!e0ZY5{SjUHqjeKDr=FQDsTFc1DABoWBUa34 zw4E9MP6w^a35t)8kI8w6$i3o_R{1cQle>x-gUwx3jRIL_pwP-W!Ep)~iuJzcE`nhx~Wec=p-F3SH2wfriP zw``H>pVycI=M+lb&y)TSCgFFX`;pT8H-u(L?50jS-V0oCE4tT|L)k58B0aWD`+i?eFizgmQ$yW2g0C9PuWf|fRQ)ju1yt{0>wiE|_} zZoMwAMsT0|&1^Cc@JyeQ%LZ`D2_6!{4Tosl+EWE?;}6?)$DIupTmuoBVj9A6$}i3P z-*Zj!8p$Q8y9)~7`qc47zfZc;Gc2g3KbZf(UG2n((CSwIZk&8T+W}1`yfB{QQd*NI zj%J$gZ3EB8kN!*A_Mw-dm}wEV~Cg;FTB^8EG>1bDy|s&DhF{a%}?plgDC<-?Jv+=4&{| z-V5%idj9WzqhV0kvm;1TbFzASix9WLjFC`w;P>SMd+)B9}K$QSv)t zUje9s^Z7pUt}rF3fWWg|OUHfdlDCF#o^>H-#<+yRmq4&A{o+OB=j%r?yw8lgiR%y_ z^1GLXdUvLK-`91@cfFrI7diet3cWs<)+v@Lx{i_X#?1_t+sRVr1@zh*iskN)zE{lW z$C;dcKX2mTkJP)O%mMx0FPpK!S114(c$TgSpzU&4r~@a_Jiy`)MiaZo!~ibiyY+#) z#q~5V_R_iX_vmaUafuj{>SmaXVLuf1I@kDb5wq+k zhV2T}|CBL5E%+r*-82l9%Ag8&Q0OlOo1LG^B?fd}S)?3z0zZxNvgYf4P9|ghWX}Q& z+hZ!*8&R`}9{`@ z6N3)LOx0>`e%~T2!|bvDnvlGYPPJIvp+Kc5{4h0YkkcyL#W4odn4lsGB_=h>j8d>! z*>j?Oe4g?+y5kx4KEIddRjqgaHjFXvURHn-oT}`g<58i`NU#BLPS2%Nc%C8?z_hO| z^!E0mKZW)T1fW~$qA!AXd?;Vo3lu=N%Wd$}`0o@C&7vn*_j z#dYI9#VL^0ril_8XzlKnhb1n(p3sTV?F>&5owKv^EUF*Dci7@%Mia02sA@n=Sa?d{696+hG-AjmfXJN5Ce*OZ6k3}yxUC7Slg3SVu@5uOE9k*-u zqk$kuFgR1HqB8t*=lgeqeT5008a$@T8UHgKZ;}tkYCT9NCggf6URU<1YjkmAx}jX@ zR~hrfT{EoUtD`*fvVx{ta6yk&N%R*<{BQO$^~#3)%4JwAM8@Yi;?P~IXSMAroIe8M zhygv=C7a{vNiSY^MWMgs`qBiGqV&Hs;$9h;j!ZExGU3p-leG>>EcZyAboYxx;A0Sc z=s!H=u^jf3WK50y`goM_{Ps$#R_|o<_isO7vp~zd7A;Ewvlqc8Z?rM=TEcnQ&ysZq z8-q>NWt~o|7L3T~IRAgh$nIi=aDz#!{|9Y<6_(esbb-Q2AUMI@0t9#G#Y1qn;1b;3 zgS)%C2MF%&?gV#tC%B(U*52R0vY-Fr+?-q{dFPznUDaJZYSbv;$9J8uV_AYB313BO z^@my+f5vE10_g)p&Ci5Hr&9?+_B;51#Kw4=2XA+Gx6aP+ybwN5PY4D|xb>js*U?4cS< z#F}&iD?PvFQ!6M#F^)$2foRq{aWTmDS7JRE?ri4-R{vUsEIf{#r3!f4BN|O}*u;!E zrs{G0+RTxP<*m~kp(rkd7Eo)KVcr}D=8;?SYut7DC+ps1BNQb>edox4(DC%0=}pD$ zV(5+7_37O3+vAkm%-A-j)FT)|M%wrJqr$p_2UbcOJ;b&*()Deuozwsp0&<;TBfk)^KU%Fp#))^& zE*lc-KfglYO9}6isoh|*vcEuwJfR4LY6;IkFzZB6libZ5ii**QNCn82LOwt6{OZ~@ zByjHmL8bJ!n~K>waUAt{Hb|S0tdnEToy{%ea?Ec`(IfbeAtDDbXJ%CHwXu2hWbuj!M5b2zD@!vbJTcIyfEa?FH^V#(j zXtmJ-^iyb$CwmASR@V}M5rIafh=|XJX9eUZ(eQ`M8R^SIVPZ&k>l}{1cTrZ|G(Pef z0uo4wWTxFD=k{Ep84-iW-71OfcKfHifVr}@oWd> z53gHXg#cM9CN%@VM+Nc(bgXZ4C3b(4^Gh8=a60{_ZIo`%WnLYT4_@ErV@qvA z3IM8si8*K`)fr4~PrIt5(fMB-TE$|WODiGe1okp=Jo0}+OwRbPm#s}{wQ` z$igl-tJ(IXE5P%=4~a z^i1k&^kBni zpVuNvXyFuP1W5HqW$k?0U=8VX=01HN^3yEnv z?&vgji*3QsbOlR$kU%;;0;5j4^D0;@A`m9q63HTFAcUBueWK|`;RKI$Q}wO25iCGt zBK=@){An0y9I2CtSS1!;_=$^T1Igin(d_tWZC6X^>S=eFE|IA<#vRT%2gN4WXme9Z zY|B2V`zg_=-sKKxGY)+YU6IIrf=4oe>43}b&XQjI*sOz!cIR(3+W(4`z-dHM2DuoH7-8xj2t$wsV{vaP(_%*{wGS zJ1Z!spc@xq2i}`?=T%1_PgttU*H^DKtV=p9O2F~xZ z(lN2%527?v!7C;&R%7NT`Kr-$f-;^dNEanU&BzE|A&XS5=Q25z7ojeZ%lJ)VN6I9^ zFv5beq=#;%M1$%e@3FhPLpUNf4TI9>#dK|h4{*L7Gz@znLkKvsG9$8ueS|^1vLwODD%*5ZzX6Z9R^y4 z$I11aJIi5%(iP3e@V_#Yna~%l=R9MiwZj&N1SHlw{F_Ipwp^T)6hLgNXtaS zi`mQ7nCJsdLf-+sbvj1VT;#4MNRU3hYy8LuP&f+YB#8LH1K$9DA>avx_?9RW>n*Yk z^JeaO0iA}?ud8Pkh{7B%;@_j*pJ~CuvZ-J|QC(uKPw~pesQmKli&_B2y%&IUMf5 zYH?xvHe+voR9Y8GGm4e4Rs_7~N&&688FAeYfA@lXrqBV$kTbZp)gSd!$D}_^3NG3k zM#dlkD)5yjoRD|s;lMIThSB`J*Zao|>tUizU zh})D+&2`RxeSNLms8>Ore)6jZ$5IYdO|x~0$&Rr)Ymdo}PydjUAieIW>5PsZnO3s?MXa^qEyXP{`16p=-@XkkhNC2+q3$o2hvs0j7o5L3Y z<-k1$Dmio?ch!+@1e{dfiZGA^>Clg-JrL4W%8dXyiemiek&n_CL1634v~ftO1pUP9 zWFO4wvzT|(r!CjVTOM;-?QchH13U_HZAl8`q2YOR$Ht;28A4yM$>nC zLMn7Xv9AN5z(LBJh#XLz1EmbraYyUK+)0B>xWGUFV8M&GfDs#!U{qcni#Ih>iykk*sDkG+^~c5OfLDI>jkCy3$4G1Tt;cSTS-q!Dj(9d1x;xdyd-B z9XVdWb5#Jvf2o=IU;pFEXNBM*{dWr9g*%6;PWj$nIfydAiEsxxj>|GV|JIi?NG6BZ@O;Tf%6 zUBQOI`eN*Qt@Oucf)9h0d$3h|-$jnEDeY0ZtO%4@7dm%WR7em7-jSgye>k(}*zt5H z49lQO$I75cJ$wDfEf-So!-)P2pxOBqy;b`gUsou>)z*VaHDk4YEJouNM5NLeH}E2K zxVTzs9^vOoG@;?`O5LY2v2X;@ep&2ds}2on;>I|6BT>*Xw;3VjtWnVHGs4-{`qwGr zB{Ty5m9>O*NhA!M5@t8of3T_fPD4LuDOS^x}j#eFsI}T~`LqeHX@ATH#)54pFz(_ZfuBvyr>wlw5xTwGb0I3;e`nkYu28J()gVdxt)p{Q z@6Z^i*|W4vOiqTGh0P#tU~$sG*31x!@!?c#wA&wc^40y7O)~tMJty0Rx4$9n%?yT-IcbPStuZ|m7`oiB&Go%jMU2uQq8V+s)cDP;b)ITXhocC(ep7^VY=PF+2Wz0){XBj>`D9T-K4O;>9w(%^xf27ssi3PlMc`p+r)pCk3p85>{dzRw?0dQ+yql=LCtt8n-aSbd@XUp2&r zn|dw^RQf&YxD|6Y%!rH9-IY^n#;XIXMRMwdx(VD}D^-JZ^qkD}-EeelQSQahGFJ{b zWs0OmGCkNN%fsJHx)l%|&QNu!Zd&xSLwh4VURphH zs)nds=irR!$Q&v4_03T_-Tfiu2BooI z?2^{h7^2<~LW8(kROF(6-y}zb7-kQfkt*i$gh~$6>#t{YRzyc4(oLwEYo4Rnb{>^Xz=0OPWW>ec^iF5YLMV>#{(JG7OxkrB5T57fl zZLo(BlkdsjIy?8U5#M)rozc*yJG=Nc`TLyGrL5EUr=~=?*m1w{>Lytf(ed=fLn1?M z+yuv?PUV<3J0s+1tN99hqody*0h3$4)$(JP$z`z?qpJR>w`AQ!aa0#%1U3kH`lnlj z>8$0qnPL^n3nr5_i--g|t;&1)#h!7+lH`yn6W~NbFRcjOSzO!|%;0J9<*TyZG*~wi zWMt^;Ljps1 z)%n)@fbx|NL9!m=rfaf1kC5U@55wJUZr_moz<;@xvT1LfUa88eL?EEEpAHr0 zeSK3Oshvr7LR>=7>@Lnywrgx3;$UkqyuUIWn+IN{UD!YD3#PyGBY|2lVQC~QC{n&I z8~?th!qDDkSYRY!D#SpsAH)~|a-MT%?`^mm}z*0sjw3Co8b&o5!3G&OlPc^!O+ z=Y&16q@b1?jyrkAJcn*T%@4fjAvPPo^r&Tby8vr8fE>7IL9_X-T9r&~PAyZWgU?>o z$T@H%d2#BpN}upG7_7ak3(S1+(G$S@buSYuDsO|y7SL^+%*vv~Wm?6)$O@B!K+qj; z>DEsj1E9%G^-mCcxJCF!Pi-H5PT9%g=ASeR`FuFr<4nt&oa|awGpx`PqORZ6M&W1HaoJ24CvR zd)x`?w>Co1`kyHTs3Wc$Pn9wu?E%$II!1>xG=6gx7Ry4`c*w}Z;HY0aVjOoy%?H%= zTU1OqitOYo><@Pc)mfT(M!mow^{*^|d_@p}hZz}oj$jBn6*TPVs1!8-ZpX_}qvNoe z^`@{<=oA+gLUTBtkPBqNVm$X|c;KTvOl=9Lant}R!){zoXELPf=g#QpJH`0Y2!zNK zz3cY&IyyS-lOyJfwO}_V+>!Q%yU%ku{D3@#%+D#u%BwqQX_npH8`M1Ugl^s!b%U`r zggo~iSXv2ZCp)Ck9Dw;%d@zQaPdylrtla^ZO^Q+){(l~}qs zJMYB(;aT9`r?@GT3C(FYvC@y}CM5$G2Arg}kd2+)G6*-1IaV3a6a+-bD$DT^ zMw%lK7I%_SDulb3C=xJ?9lrUBn>SH249-`aOXuQ%958KI=y+jP38L05?51bAQ)&xR zFdAg19*~FF1fDBmAEJow1a?|NVZ5H}HnMsi3{Aoiyx!fQWQr1cyirU9boA}nPBD1_ z181g1=M#jz0)N3746bW|mH5qTjQH$B#=>4usr(1ZuXg*z{?m=t!3 zOcOcm8EyCX3lHUt)!N*NQDD;xt8s8}7?29sZ2jj#Vs*K$5*xc_SP}f;af0{u$cNK7 zrAaqLbb-Wt(3L=pCx9 zqtl5i=2dUEPXGv{3Lm9#2Gz^_II*(or;{tk!p7ElxW<#{|DJ1sS%U=Z<^b_wYY&mu zkM!@|91GaZ$-wxMj%R8Rf9Lg$QA?Xgl$ox;d>uA5MOB;yIO7FX?0EL{%PmVQ3V}gb z_n2K1X~*JmClbj{<{F>3SXn%8YL%25w7x<`_MS^?zUYaax)uz}Ip;YzscUH=#*;!6 z^Mn%=#l#e|OCEzTFgaMtbvs3HzmLEvaP9=CLY!eKATpebk}p0Lnn?)pkCyJ=$+iZP ze9(aF3=|v`Y|m>Qh5@xQ;Q?)s-`8fiMhp~n>bv~f%5}B}yEHNgu<@huMMNL#4#SBc zu5eO%^V~o-7W7*5o#&p?u>XGMN$J6mtZ5eJNOKZ^9ZIAv>?{(cNop3RYu;Fql`f&>fL-hGzT9m@Ar;i9QUSZE43A!yz5TTt#&q#9@#?{u!J*SL84BJ# zpPBB;FUi@W`ji1_b=KPOr0kPZRU-{u`br%9(9cNSlv$+B-6Is^f$W?wJ-g4xJj2$N z>gZ}5qSE~o>eGGF!`YmupFmZdHBlx{`)m?2^E-P=Ukx{Sh0*BeCT_DFOl{@9PdAMe^K+y>=8 zi>1`m)*_cuz{0{FExYg)_J%Y2^8~#?8s`Q;GP|)v#s8va|B3gFkpLHpz)uST7OuhaZ8-+hn5$BSL0p?QBGYq9LeXO{0YMy9Tc`6xi zE#-b(5y;sWncmnaJe4;7?Sa8Ec+gQ)Q&LdVH91bfVS0-w+TUPD2eY z8lw3#EN*@z9mbPK+?hbO8KpD4aw!h;J>2V#Lr|_}QYU#z-yrG#A>hD-X!<1n< zM32vpeo0cq9@)+eW09*?lZzMGV;-`ZMb?2qcItdqIHE}&;&uhl0cF3gMHerz;h7ROC}HR_=PJiCb;N_|q5zSMSCyZ5 z)}!DFu5{#+{EXh9ODmuif_`V1df@IcDQWNU(`F74pZ!5>bh^Z3tl5Oy^VKX?Du=zQ zF#<^2;{}jf2Sl=Q0cpLyg_@sZ>G@fQi**P`M`qI;DX|uTv;d|&PVwp{!3o~L9pGOe zvzHxoWMm9&`mp1{A6&{Xz2MpqZsdU~)V6}377Mo#)n9v)2OB@m{FH}IEf-}sg#ijP39=~te#SxOitJ(`3&Xj5A8Z_fg zd0;#OB-tjG4fPW(Nera+j_54rJEu-13T1g(IP#a4hn{$SV0t|#clgh#r@o!x22Q|uSC%ti?|!!2!x|fy2eKZHu$@cOg5 znOIADFg1MQ?)ZCpX6DX~q!z2w9Tj6(6|oY<{0c~p>-0_&gdXj61q4|aVVm)#CO7;L zkE__8U9#v%6x>S~0F2gVagpl=OXn#$Y@a{!YX*(tuX>vo{pjD3yavGuCv2=yEXI%i zM$Q&F;d1^}v5Ttk*jk9`_}tBErb6+;9fd}i|2ZW6B}hiwERDzM{>(qn+)T$`w$03k zNn9EOA3xCA?dQOCuXL`0G1u`TOokWZIi;n$(j`_a;!gaG@~t?VN>h%lY-rjxm0ZuU zCNEh2voc-CE@%$trD$1EJ)dN&Sh-bwsv2n7{-IR8g*2sTU;xl@7l2O$#DId=xg?c< zrq&f!>qOv?MKEiwG9l%*)HO8c1cgmLq+%ipqC! zM7Vxp>fRYqOM>02ppM;|f`*0dYqUSqe^{p&chbN+xpcldU{Neqrb+3NUZ^z>3r3@& z$A}<6NvaLGG;L0sjat8GN2a1xsYLbi8YH46AjVq?ngrNe3}l}^fdgBF?hVBY-bkO@ z=o1jiWdf)|fkqR$Zc-?~=7SE%sP=d^Ztva$xL=*~4R4syN>O(wK>((_Kmw$P9GvBt z|LwVNi6XIp;7`v#9zwUxE=zFsVfIbdXuwpRk&l=G3!}Pl0{>{u9xOXr16>#Q*;}f< z`E1SiI1H?@L3D)c(B<@#0VR1^A{RZTt(U3KFm&?!2wj|s+I(ulvNt`@x`J1`L z8{r-0yW4Z(8GQHrPlIRgT_JWqA7a;Tyypb4j6swAxgA!1cN}7Y9+C=Fjb||(cBU@~R$0>eq{fzdwWx~I83@h0&L9fJD-}Z>n9N%9pz3c%_|?T0km>hF z8dkVKI;&?mEXo`2v$EL$C+HxV)-ei9U<8nLXUKP=$Gk-G1rU_DN#hA5QjyLM07ubl}9}O(OcRYS-SEa>-~?(GUtVl>XR_ z4$;{y)tCcP5rfq>aX6V{{Uk^F#e}vvzJ!d_;5!W^g2|Qjx0vhW{h^WawKi^-dfxCD zLQc&HUY6SJ-6_p?>Y4eLo&k<8=o{V}LZ5`C+7}HaE~96C<#4;k^25wXlc}jMzD+!K z6`!?m)21$YjZl#WJERkt?`?(A>)ll99y8}DSOd;XKrr-tyhj*W5*IQ}HqdvlbmrFL zae>OzTdhO4xqVW3Kf?X1^Z`5`TY{jXLRO+5+E37e6MYrx7lSrETUBqEN4U;*^Jg8m zXJRV;4;gG`gF><7Up0y^mQK-&x?;M)D>mp20kOWh=P78AU~3UvnXGtZ%5e~HQD)uq zM*0z^DKRn1lA-rZwidUu)BO61JuY_bflmZ=4<1Gp(LDEdjU!Si^-8Zw0g4UK8pwCP z$^e?-hYNzV3YVmm)A#Cu7_uM*H2@_;m9N*$6N&0f3{kv4ktgPl|5h^STe4yrpQm%;mZN^iF`!P7Y`Qrcixu|oKf7jUj4aFJ@)tP)NXE`C?vBe*C~G z1zwnoAG33|z-E3p2lKf-5)C=xTjv1S3;AO$F`^OyWym{MK-y$L018%t&is8qbFWUC zz>dhePWSW!l0+u-*Ocrc3qfv)qZ4wute#JwbRQW#@)o#wXW011cAVGyb+B=^Iv3ZR zjzD<6OX&ATs?+{?A|bMcBVEfhumPYh$fua8xyZyg9pwEu26W%c#Mz|Lhabt*v`Xy+GXg&gx-7t9)oTI9lJ-fU*V~ zL8idzef0smI=k(#ge}uA+7D4#pX=|&tdEv=Xo@})oAV>v!@2pV<)uYjNb(6xLP3cJ!sP|n@^AfYm z%wzRn6#dEstIyQcJ?-v(s|x@f9jk`d?ZW`9+-ljGDlt~>)R;eq@e=iwP*_@vVEKN8 ze;~LzU@Xen4Z6@*L!sNPheytqb!oZ7cFKltWVi=E(pGT5eWNOW@dfWYsxW!O0m>cQ%a-RrYvSv^ z8nV>3d5<|b4@{Sz-^&jd@g(cbT%16Tpmsyj_LpS$?cliV_`blXoT(p555sE1@!KSX zWcF_9W-5>ExcZtAZPZ-z!x04mf9)(Lk;;)2(JYJ zMD^8=sdhBJuKVXTp`1hOp*C1ulLm0bmnD_B#8fC<(VgWy-8kn2+Z6GY$N@WuUa9<< zMFEft)puTbEREg`D=Q#}mffxBjjw@cQ)HpdjcUPmE}#rKNL~`cxlq zxf1U_GW-1V`pP(9jPt=M=)`|xY+}IcWjoLJF|-=Tvk#v}irsdCU2` zC!>U|Nbc$U&^bunX<>K)6&KoTo^O@w+?Uyuc71{E-6P@Z>Z;sqpAnba0kyT&g^9q^ z;Lm^q`D#%B@M1A)();fr^9cbnfdzA%y7%X@W*Sg=ofFGg>b;|gS51&8$c0>s;<#D5 z_lfmA$vaSxXI9o*WMpL9iFhMAP~samEt|8htP%h<K8?@w zAH+m!sJ;e!RC6^7WUJaST_32pao@+?vET0lCGtHd&I#4`pXvE8U&)mgh(=v$e^=A~ z+jZU`y#Wrm-*tR|NNNH;@JM^)@Fv@z$+P}xpP#9F5X4*qhGY!~{9;JHGY9?qfgi~L zizg%FL0dri_w(B8_+LMfcmE$oR86Q_{yGd!oB+dSCApFMyPfYl`=w}cxPFG#2xsIc zTDLu`;*Qkz{}}OqZP$w~3|LXE1(ckpzy85h^7SzOJvsFUGo58TF@rVC$v~*Rzb>Du z@~%j)P-deAeLxz}hLVEe6Pb80TZE9o z```5dEa@Y_MF~&!Qygvm=Y9aA2gCpGe+T|EU+wEOt65L#{QW(ALBK%7*m<7#|18|g zB(yyE-w)>l|NmsLuD1N)rX8K{JYSf2Ne~wrGjA7&M6Lfds6Qj@r+D>!3Tf}_&;K6K z84dF7jxVkTrxf{f-R)T5Nz%Vf$`i_U8Ti>kbLW{mSnQG;gX#U=tD|H+U>zv(;fs~r8~*5(-N>{F*1vuo=z0D6 zsct_|qyC;nBQd}QB}I+d!+mk#KfAr{`X2{8VW$MajBVhr$u6~>g|sD{C?5c!?jJ_u ze@3K*4UDq2noQ{7uPpR!@~f?H=Zn71jk~{9UT9I~PiONvtgW$$gU>;yR-gmwlU|L1 z3D@NR@jIK>2ih#4WIg>oZQ_5XZIbkL+Nu~*TUv<^RI9yn;1!~`JVRTQ23LtF`3+LDJb2FFO4%nE)r^XI=H$ zQt^;KC({3!o|iNsAOC3ncuvFLU%fW@x+KPr3I>2Vdw&vU{SVT?Gqh4FsMTKB@0Ou& zt{MwPj$B?@+1ZaoT&}$d!R2-@Q4uoA@^QS?jdQE2u5UuW&6Gab|G!ppTM3x>g+4Fu z0yp4B|M$Z71j#?A4DahgD?qNBU0!6>9q+*rD_v5*P59~DQ@@sv#J+1Qf1FC`Cf-@t zT(Sr2is}yw`|_1js=`{b90BksD%M{hq<+PB@$u+DKNPL?NM7DonY1>kjM15^HvgXH z`%9r1Qc0`r!LP$foHwTRB#n^&om`+8j*v`}dFrp-jSt*T>h-gT|CqBqqLe(FR7}## zm!KcCI*^vn@V&HS@+OXxI_~zUg@>Q+H8o*9%Vx1^Hc@d{?6~#Xpge5wo*n0#%RQJs}CPy+51C<#G`z zl}f?Y)YO1FE%jwYl@vce5K0Mlmeo2xJsX>_tLwX!W@n#QI_Z4XZl!fFnZG}Tjz^xD z8`(7m0YSv>*wbroZ=V#3uDO5jjzNC;@};6} zcT8&ysL6(cgHyzQg$n59O%!aA(9pu>t6HWnW&Q*>A9r6IZg@{Y`~J|LBLOIgje`$tBaOlJ=+0k6A& zi~y02ZcOB2=Nm->i0tF;X#`Am>AP(M_r(bIx@t^h)Dl+=R3Q7J$R$7y@Eh$iYA6F(hFv! zWMj8aaAsy{<>u34>@KcF6J}~^IJZs@?Vc3Ly1n6DfTURLJ(I1riAgXtN-q@B&KPe* zP@y_hdEMA6dyvYnvbNSgL5Xf_)SSZC*VB`a^gAZ87LUu;U>vOD$x=@^UQ^u887xnk zSezKSS}h!>bRiNt^6$5RIFTF{70cFW8V*n)jie)CX9xF+_%yPzvhs8NzFH1YtFsXA z>dDW_;<>-RLe>#hS8q=2()i~31n`QBHJZ{ZXpYHVBr}GE(Ity;iHg_sm`8IbAc2FMFQ>Bm}io2GS zRJhaKZ#1AgZO-_Y{MW6^EA@2)PrZ>a68^uP>@_-A{Qw+})sMxZ{l6GxXacCr6yBZ8 zw6FyK6ekR@Y-h8Bf7~F*ZAt)ahZ-{(lBQ^pkeL!`a<;Ae**2*10c+19*y|*mo^`xS zZgp0-Q}$ZRZNo{ zuj^FVqHL~{{`JNa-gR+GP>}}#5?F(HzHS)>05s$vV%C#DbWhp*Zl9{8rIwl;wYsr*-QiFmG?`{&D?MME$f8p3`2*cv zb!KU`b|OWQXytt)r6iz!b@{`moNW!r&jVe<^w!$lH$VXHw{g7aXyrib32?=(kC&4U z3qlbAj?!ExJL&lO7aLe>Fa1$m0+Nm#l`7H#%_o0}?_fUQadd*C(9edv<>KOijoJYc+YVZTqhtsXQslDP^-xI2 z$ejW#V5H33qcZ*L#f=Wf-hgduKU`}}piey7+1d)0X{L)vNVLb(EUxX|NQ7>W; zMW+U;kx_FeE5}7d{&@{Z2Y=2eSJKzWlJu;a`rkXn8>DZ4f}Ppd{Q_*-roM5fM5d>? zR!G(!sP_!w2$__HoLm-{!mt^ou@wYB`(n=D=uB)vKbMAzpB0U?X7Qz0dou?Dl`5ah zzV=5gXU0p}5pDs-i1Q@?&cCgVS11fWCoXNqYz|tZ*}NL~vo?#E zQbCB)*r|5!+zjOtsDY4}uy1e>W+#Jp5UF%J z*IMC>R2oNE+jBJMlT&h4g|0BeXF{Zvl?%TvK?Zk#U6ZX)7<~&Urde(EXH?RXkwHG5 zeKCD}I5poRie@KnSv(8t_>O*IuVfjloMm(y4uessp2-g}ht$r%#&$ zLq^oA7=bZLF{laAX^zHvzN2ABYtgIVLPvR~-;CZ#twQyT6~!IeoV z_@O=M=rQER^nSgh@9jcDVi2{;I76o~#r)P0=`B|`VaRLAo7xypZMs?G=zfxfQ!}X2 z#S=lOeEkW$YRVkL!cY760YaekH~1GkHn#Fcyk0Z@pSp`dH6TAxFN_Sb4` zdmXINkB3}Ws|=$N1m~C-^jv+qkSk8r5nXEs4sk@^B{FN>!$pV+_RHOcgi&u%ZMSBLZ!=z$YA#Z3O*^X&OCg05Wt-(wYykaz8|T|8m=9qu%~=^2Ig7szmV zxWc-yj`FuJ?ow`6RgP(4WcX_EC>1}MgNwzMc*&uec1?6bK#QI`Jw5W&GSrP1?IP%z z(#+YxlJ@RYn(q7ND{UIjphf_xj%<-AqO;{D1hqQKCPUP|Jf72;5wM|S3=cVV zN_o&c%qg?Ao#ITnaib@ASl}%wn51^!-gfjRB z2&SSzxe5>z%vS5ALSZJwwWC^VHU4{l_NOg7V1L@WBolb}3kA7N{&Vxq7k|ConSj)_ z7K6$s>SoA)mincZ=gJhrrAM7;@(A`D%cr!+934{1rM&m=+5F=4?#Mh;d&D<^T`|RA z1I8my!N`>urBN+Yc=D0x{REI!knQ@xr>Tos&MawY>Be`e@>%TRDs9`7@jZj}VnOI+v^429O?;wMHHki?^X*8&&l_aIG&h@EuJU9B&{oxCNO#J_WdZ==2K#dfwr ziSZ36EWfoa|K^R&>z&AFpg$nTm-J%_Il1MIWCmYqzR1v4cdAU!kE=LimSCgs@Ps!4 z`6gG{a_`YbX{(BrUr!x*bQP{A7>qyD|c1B{qBID;LdHH|=qH*g$URY!_a0Tt8#g;5e6`rs4B@YZ0?@AlKfd z`UN8Qnj9BjV)^DH#F?hK21lY?t`O8-(?aPQuSLhi031|TGY<#}`tSY}UDyNHyx*2! zFq0BHORPY$ySqJ;i-F-m?IYrSRyzeY-^*57T(VRqe-KH|0?4vH10zSFLgBk2(->*E z%@0WFJE8dqo5CI57TG%^0H^nk#5RRXp6X+ulpn!TmfQ~s3epYS&N!-B)bFv<05|*o zavxer2_sHHA0Wr9S>S4VIcr|Llb7~s2qiD-r?Po%@fYtAQHvD_bUgdC<4V-Fl5T3FSjBY26S@5n+ee6*d0&2 z3zoYySRNnFfVM-a+%8g3vU0OGPFRQrIOgC&@+|>~-j8#1631Mo zWU$I0wZPVd*>`J)_Cm+?Q(-{_C>0;A_zw*74yBQ7uE(qeM3rn)o6)_WS2~DF^X7$% zaZf!5d#KO%^ifqqor%@TKI^*yok&Rqa5K!yDt2Uw_e}3wcnsjJ{6My45~{}$X=EsR zw#i>718zRDB`Mn-{f0CHp@;X;>3^9@*Nas(*Fk`FVzq657NAK{EpcJet^OGWnU~ZQ2&)b)k=Zo5Vfjc5)mPfT zEXjq=dvttUVK<<&j()bcK$FKXJys0$36_;{H#i8SNQW70`ZC=sipDxJB(}=tLuPw$ zwz(F5$6iaS@~{=XCt?X0oqZAvh||O)NYW$k!jOZuqo4i&$4Flq^GV zLky$qj4rJDZw~r>!7d|#FIFnUhk(cR1k%H2mnKQv!^08`+MUX;S4H6ne2hszy8Htt zr&>G@jb8*QLrF;qyedXHV^0#M0T;7kXMD7WlRPrR}Pr<<~fh;tGbjqy;z<=h=9&E-aV5O}Ng zYCI^R4dET<^z5w2J~(cvx4EML`MxnD_NzanF&pq#kgZ!cQxfs*e}M|%qT!kY-XE2- z4fzMrGyyGPx>$x0Q^J6fr3OPIyL7)phKjOX0+esLr)1+p&2KT@RCz)w+@VK_2vO8| z$`SiEN<;Ue&ayexEa6(vUfG8}mR2Wxrqv~B7#^$eUv9LvZJo4+k|C(m!BQ`h&&1a& z*IBHeTk-t;IEkkk?SSnQq9G+)LYqmw6@$XIFmUat4~c*bmFAoOSIE_GC+H10{x|t# zTcHPf{?F#AYcD(y@BuB+ok7w!91w|2nVE>;Cf7F@=3F(4YYqlR8bgW>mSsL0RtNWx zk0&@Ca-$S}h!_B+_Ve)DDXV^4fjlEU9jO2a|HSoeh!v%g!v?u=poYrW3VK$3@BfMs z1M@QO0|A!T+CTM(FB7jV!!Y{={38*v_7>~Pm^Jp9sNM}sm?QW?RT}w|ovd<{0pUu> zx;$Hs)4-|q#UMlR8GZqCv!@z#Xi|l6uBuv>Kz0!fk*wl11vCgCLq%vD{-7#hnctLeoMpG?4Bcq(^{qOA}{-yOAZBztJI)6@eSGOCW z!F5U{Z4Wn(>n$cAy&88VN+vhRw4dxjN=nM0y&H+hgSt4K2E-0$$!yK><6$^;%G_CP z9X%=D@0Z7$otbvVE0Am4++Q?Up1vKhyxyH@H8_a5xKbPZ;&Z9eMQuSDT*Fv(PUmuA zJw(Q|ZhulUJQ#q<@nATsCj1RAMiL7juD;%C!xf6&Rvq;JvO`HivavhvpJ$)PpB7W8 zXr%jZ9Umn$RN?9V=~s*LD(3Dv{ju_!;@?{5;yPf`yow$L-7n9V4!BUT2&<(xu{UKZy!=0wG_(qmVBW_5Ki_0B`fE9-RTBhyq{s ziVCetoV0c4HDOvB&mAU?lr)~81+{7ddiqPN?MUjx)lo&OElNGJ5Ms+;g(OO8WP>TwK9uCd(d=|?g`*YKFGPs-^ob*xA zfn7O@g$PhfYf^e;QRC7d)=RydS%{LO z1K(9vwMs|N6H;Wn&a(LuHPjLz4PptaIPm1uhM|bduYHsgk*6T+z};?tN;(H~(j<%R-{ zgy04(G5nl_fZ!&ng!$QuV07z1K&Es#8qZ+E<_L@Z$C-sRszG8%)>6^#V;NV-L`7gT z%ow}d{p^fv!@ax#G9r$C^P<5lmYysVC)gMC{j08?4yAB+ZP98^Ebv6Xi11D@Yn$$7 z5XqgWc^xStf!kIWttbBK%8qX~ajKgz=h8)bzSaUmME@*2zPv(Wz$ zdRNC?w)nD3Y5ak4TlmFJ?Jpxju-R$4`s_w6SLum})zjAZPLMEM+Dlb`~X>A z=-OITJI6;A+PBM8$(_NN?%4!Rf73T-$F9TQ0@Xb*PqgL_$M-(oOzARTXalvsKv<;OYW&%qo+aN0ce^Aj|l1^ZO72NSWdxE({fabVS_EzJi)h z$+a6)cW-l|+iXg9AmrEZG5BRg3(q%}H1>XRWZJ3)UQI}56n)!rvf+HO-{8rB#Q}F#_{!!N7YpGXmJX3VV^VP#$ztgW{W`m%fhe3XbPB(PO2~qD2 z-1$pNMk!nqTi!H39Rz0A`yX z$(1HPr&Fm9NmL{sDwmPDviLetbEQ2*Crk5Cd0Q;5Y+-x1iWp~5ZUW&Jo&>#ieGiYQ ziAQF26RH{l=##F9JR$4dZ)wcmiNv5}SW@R=0s|;NrAng=Q#q#rY-(WlBs(WxYsp_> zx)1HfnsBM%H>2K45MdP<|B+*29+Xp= zhGZ$T2$mV~%)l+G>v)Y=c9MpP$s3wTeN?@dES(e7ZVs3gw(P~$Pb*{>$t z@%fDT8vyEh?Cx}NVa%fc0RS<7_zeK*{0#u@#8>m6oXleIPgI6k5$;fk3u24fQCBmg zXleOyb(UD`-$-S$e_;QonFoJp5y$0b9h2dQ?Ld9aKoh5n~v zi^5o9w>`fS^52rJz5hSS7Ou|8rT=)0qF~6OC`2Twj{83GlJ>7W^UT<{stm=ZUvUQZ zJ{OgEBLK4bXUjE<_o#W2Co{$KsC5KKR&5^X*9W~8 zeWt}i-#U+W#so7CFc|Dh3RI7EuFJ+sD#^d@8)vQdj|>E;ej=}&?N(P%Dm%?-Ebay%9b1%0{%ZwnpFu1F1*Kz9Dj=3!F&Pw!V$#@$>P~xUcyO zv#6zHT5XEAqLc%7updqrYPzCIumE-en_<_g9fIM&Bl!L%pk4={nn9pa)}E_MBD>qc z@PWt0-`VU>dB8+UYSrPb@oVcyJlUjP-5Oadvjqr{-CEi(5MY(XGd+A7AWH$C2ojl* zo{jHL700b@7YPT6CrQoosU)rzk#z8?bE9!^IyDar$c{g(*nDN+%y1gc@CnbNHT+Ej zcvlbFqTf_A46*-Jl7ssE`y-ktOO!Is!!t;{GIq7zDnPhwx}cnHV@)WMm!Zso=an7&^#*Ap=_Y^n^s4F)<)3VG*{^2wX}oS zx+C;otf;LcF61R<`PHLgLW{o3>q^hELrvR57{_#6Y!a+_yB4Jp&~QCpO4l_n9aa@c zu!ScwQl~6*^jmO$8VEC5zIEDv@0v0uF*zuas1l&PN1=}X7DGF8=)-)Cv*=_>l#3of z*ZI=Fm|D)+?Gx`O} zVD5O30~vGj>cH%50IO$J&TE4tw!3br&M0KVkw;ZimZ#)w=xH2rC}WKu3L4sw!ZnQ~ zJ%4}X_4c>NQax-8fPLS_QV{k-|6RE+8~??$p?YN6yNsT72Xyz-^;POJ$n zRX}E8^~8Qcz!tBmp-hZt51JI>LJZ4P;r^V78fNu6-q8Pe0qA=5!MCo^%HGFZ0)Ho@ zK)4G=byp>E$X1>ChYx@GQtA0c#L)EiwC&3oWX&`|}IOm&e+omxgI|c!pI`)at1F zvy~mcn{z?|QAVQ~$E(gawr5mm$-nyh@t#pWGdxVfM0GQSJ=ABo2)RIb)ZlP8Q;JD`KZz2_Y> zWC-~^?zJD6V{M$5U;dy+E@QrbWSHTy{XOo_C}+P%C`HalqPjfI#=p?5f#A~k z$)Y@-uR+l1Lw8L0p5rO1-gQYO1F~V`?NZnG%~N^%IAJ&R#TJ<7vupu%%!j;dh*;52 zjIExt&~^1b+JKxNu@g%AP%qQ7z$;Capt!9JNf&#UDX~@628X58YNxG6(A?X(5tjO7 z8!0o@JxNsDPtrl9kDVy>*KuN?pAb|d=Pf<253{!)Z~*RYy;=i*Au0cs=>(Lm0xONR zdbYruK%HHma#v+zUUb?OVh$omKrmpjWkvVgAZPbQg&FXSp4e}ePHBJ+Y3nggjjffW z;y5!k^axK%)M%e)_#r;5Y#X{kbe-0T(Dd+IrGr}A=2NY^FG>TTVjpX7%At*fD-q0zCCY(?nVnT*}6@~L&eS*f9AqLZ~) zKSDo;GezgnWc;tgy>lRY2eeaoKJCapj1Zm=IK>FhF1pyb(Ip-Exa-2`2(q z#N(LFWL~lw4p?mtdQMGA+JV#GkpqW|@}qvmjz>L%2OOXEw7$ogcOA`20=f&I(soBp zj+UwE;6YC`B-g9ed)xil!d1)g^C`d?AUYpM){^}2;Yt1t66AqSX=$nEQmPIXi}~vS z8=FV(Ab{}vP!kKqQT0CAZu5nI4SDtM98hu~A^n$4F@+S?j>%<6AnY!fv`D#|vbBO% z>qc3NB_ow`XlZHnJf=UjXaKw%V~RFHYn1qR3HyK2!~7R-N}s>m!GZEgquP%84>GD6 zqG<>`Axns0t&}>e13yDWPI#zwdQIxHvskAUf<+gICLa3Eb^U=%3^&#s^I<4 z!z0L9K8`#=4k!q|*u)+(QlUa84*LmJHYRa3nlQXBoJ1AxX%UH_n)Y9}Px$s~!KAb; z?rS+S|4XtGy-WFZ|8VE-HPNtFp4f#fXhRa&+9Br-YrTn}b&YG1`jU*hiDTc_JK-DYOeip;4PuMn}&0#!96fDxB4aTn|0`h4~Pqr@}mNOJ)SowncX2 zP8*lB-fyiajw|ot(hcU0-|ex)HT&vZff3!t+?wJNx^WfOrIJD`_5s_wPC#+Sfc>JX zU_(fMW4XpRC0kS$GtD-g%3!#R*kmt>-|YpGw5j?HwC$COWSNOrB@Y~~0nkw5auTQV z&Fpl4(S>)}==ApwZoZM1usCS9RbkuDx8+=%F43jN+nD}o<(}O5^fB=4bOqKt1_;PD zo&9>lXCAAM(y9UYV#KD@ugav(d~}1jOGWtW1!@f#(Wo2f^tM!GE9Y8?$GeI zwzfydLPDyb(0yBBc9>k$_j5Ca7)tVk7)xf>LcNQ{ZnRMzJJ%;^Jm1itXU;&({2UDp z4S0&t5eoxC%`*!?ZVQ(S4wbY!av>rlTy69HOcZcgdd-X$`ysX}!KZ84u&;)|husE9kS%`a4Y*fiKK#JW zUNT#pGLy*$&E5Uur=+6b@_gX4l&m27an^nk~436Zi_lP5}{vb~_Y60X2 zuw#L(mhN{psBC}o>@)j!` zgD?g6J;p!1;BtRr$6ugQ8j-n9*ZH@k!|P~ieOv%&Rn(hY`*nZ42ArrT+_(gAc(y3g1?fEo1WV3O0O#>2>9Y1jsEce7_dQu4h^fTU2N zNy+T9Y&Hu-U0dx4ocVD*Rh|fe)G%j4=pHlu84*zNbBREwWh^Y+Eo_IAr$ z7X;vP^%y{%W#bQ*@k84Kho9~3&JavrFRykPf~|}(7xPQ} z9~Cn>pu>X7;r(Of5Ffm8 zI&Ft!ivRml0nIuSjFU|fC#?$gfB(U~_)&eJD;lV2WV(&PxOK0NKvY&ZGVY{%OF=3sgIDC6dT0h2zx&s;4~jQC<&;-xdc<%X)lt zn0s31xds-UbOz@yOi*i<21Q>#TXjK1e<1a>7dHL#DLy@WB>CTo_Tc~Zmj3zcCu(r# zqatKUYoEG>h)FR1L$u-db@EYRV_X%0^~ck{eD%nI*bpPc6CC)^^Itsv8NlyNG+BCP z`ERm3-!l8-p(&mj2n`sY6I%W82EY$GkCK5h7wGPP4|)Xm!&vYPjTW8XSdPO)_W#MT z2IsSVFxq;~Dc?c-GfjTqS)(>s6LTC+fEyaSS8N9;e(;xYu% z*^WotRi3$Y!A!kjxV!3`ORxrE`hQ(02obX^L*$|q`}gM_Eg)SUcwq&^3f*uP8*&*I zi#Pn|KdystgZ1@xRlJlxFBa|h#bz^7dKuKaFs>2(_oI3G_>E8KDF1^;Ed}P3IGG0B zY{5QHVAkCbiG2Kj-Rt?|5_U5^*83k<171QC1jGNkuNjX^e^lJ_kT*M z9)%Z@C#FV9uSaZej5-SutvX=I(;^JTrngvix*H$#aaDp@d?9G3=onIn{v4d{i9qF+ zm{>)y4*&1#ZN7LEr!gK@RY6n6pQ{hlOc)J{PeE1=C^dJKgemPc`a6WsuLv$`Zw-n>Q`iM`!XuneZZivS>x1ayEdba$LtOd=?k3@e)vyv=tgfG>_dh>Bi@{2jIb~X&-4B5A7&Dl=v=KZ)*l2aLc z{=HXxeZ5ZdI8$==-|7E3Q|N!sl(tG>BVO@f1iE)trmqeRj7Qj|nHp_>wJZ+Wt3yF@U7RPmdp zrhOYHZuQ5t0Lj@S|53SQEbTvr(&LIbVD{n*ni9i)Q@n-#w2Er^B#D_6W!nio{jtD0 zeT-{u`E!UmuZR#Gk zmBz=Lch<)gul|FE6PErsayRyeD-hE5&nJS|c{d8I_eZ826MJiaas=SH5h~MCE$gBW zQr8t)i+4OdFsIU{yuMBLSzaxx7w+m|Hic2}YQB{CC1x%DqAc>m!Mt89qcJKLHdcw= zfYPnihG=w=L?EKOnRoL%Yq@03XS}_IYIxvfa4BaRc<@_W8s-7U5^31yKX{;Eu+lxg zt+$o`j3s&zHGh zQ73y-ty)T+v_nVNoDKxjtKDn-A z^9%CtGEOnTszvY{aC7E1T)(bnXw3;3thwXBTF_fJNgW&;L&_A6){uN5Sn6_fF4~~% z2(KO_%&0^Y1VfWGmKw*s@tvI5fw}QhLg5;apFH)B zf`Vh>r;UwOD&Xpp^l&|=prAPV*@p&1=dYKFRcfPmI2K#(R(6r84*(^H9sTWF&6zTk z*Klw`R>|XvH6@sir&}Q^wO;|+vjQ5^Qu2AlSy`RS;SxX+a=DpO&f;<%G+GJ_{&=Fu zqBM<-p;c8?y?lIDfjcXU%?<;XTZlYc*X-<@vz-ageB-BjOX!G*D?w-4z-b1GmbO%Q z)7%Xll>%G6p$R}~3uFKYx26kq!6GqK~7S`6@2*$7+k<{_uM&S9|=kUc9AaZnZ^)v3i@En1G1ab(2L6cPdS}-tO$* z3B@fD#kFpY*VVcQT_s^9PtgubTke$c{h_R74!DrwSf0O}CG zTLyyVUXsepa~JH(s~-Z+KUO`@lXkO~uhMtbOFdSHHKmzS?YHA=T#reZsBkl< z$q3|duuKQZ63c0bW3I9GNbe_Sfa3tthqMgM+(yf%hV6cuK&|#hdz5_LdTSUiQ#cC2 zN(#wxXR5fbJr|?37+BKdF)^Wrg!p?vHg&;bGG!uIVPWl!WJ^%vEqnR;($f-!`Ab)= z&US5nLx``zzVd`t2a1-b`aoUuQeB-J9RmZ3s1~?gKHNXwn<-a!4Pe>48PAioBJ$ey zv%$y5U*7AF+g+V3WM{DtlPb#kDgev+xWTcSigf=`MYtdO{Vpd>{#l*fDp{F5wZIm_ zta`#gx^4~u_sXn2aJtLzI%3Q_F_VB|)ptnh>qfcyJ#~$z(P!I;Scs~x{HoBHav&LX8Yu~+;m$e7nHBoK!(9+^u+b< zI6bs=-XE!}rJq9b)27zT(9>aU%?Lxt->gu;ho(>>41z?#)D34)q1joek3m3{E4+T` z>KgFu2hI%zE}Qjh6O#hqZXj}d+*OLKjhrZLiAPhfU%I{V1jkXsXAhc{27atAT6E<6VzGYjS?>W3Ieg(Tj-5xnteYjk`1Xtc2PfgL< z9acCcU2(FN0UFb!{VT1ZV1eOq?I*~9gKF`E2mW+4YqN@enn1wR1$vGS5yd{IT0#eEeN^ zmH)H0sDCM{GK7|4D2yv|h)1<7;!kXg^Q#Ed-n7QsyrrfxFQIGR(8i^;Q|@KpQuFK| zX{^Fla}DB2A6cY3Sjr0{sQ<{AWWdv%3nVrICEf18l~rS}=Gf?%fe}xTZn2=voZ*n> zjpT@CENdO4$8%&{0g<&WS&(YX8o<$bPL}VT`08v|bGg|)?s{QWt7km-p$#n27F)xI ztI_Os2k(rI`s3CJ)5O+U%}0C#$)<`_wIy*r#?t9VAm9TBLm<8J!gVDfN;9=T4Za`&W|5-o${Uc{IY5^M(O|sVJ-~yD(cI`50e+>dMDr%>X8=@p@9=@eL zMzv;}Pf2NX(5|w%q+nc_G~gaEm@Uc#&6H@Cd$4ogE4&5%n4S6Z#ifR0a*8R1g_VAA zP(a`a1_er*O=2Pzt-+9y|wI({znRAP=r8cya${1#{D^ir|U6IW0xx z?-#e&&e63P)^4Vfz42MkJ276@nx>`@dp~XSpjDxU7>he02}(!0Hmhm zu>t!PAeW4_jjj}xUHu|{j@H<`jR{#sq~@frbjXn-Is42u`dVY?1ff}~TJ;Cgvmw}z ze!<#6Qc_)w>XnO^5Vkk%@=F@3$&I3=QFd|i6?aX1Yx@U?)c)!@i8dN1t0#+Bg!Ey( zy8D)FTXXZTVh(*v4bn4ilBWld@03_p?(@wnG%v`pf7lLs6RS{nY znSJQi$6`AS<*)y?^OvT?5jOZb=vRZfPz(!A7b3hGzsA+#=NB3CI5R9)-*Tn1zKh0< zvRM3jKUt(oyhJ!SCY;G=G3bg+qVh`re$j^$Q@?j3577CWw?!U2Jgrh?b{mi>6;Z@y zVLvuL<8JOAMh=(6v%gwrI{%XV(rxKI;%BAt$(EO2>cl>@Xz6Mi>2)*;;f8)-I#?=) zFu(oWd$W2%Heuq;7VOkII3ruo;+nrvT(R`$QqfZ}AYhOz|8d>ZG`{!=;;JAh(-xaQ!hl3f;M#<2Q25*+!aS%-Zea|(s_V&ye-||u|vgk9e1I7Le z;>JPGgw3N}q7Iw)Peom^U6VPqf~up6KEt0WpZQAPd)w|5h_g}34UK={Xmo_g1*%(`sh0r2S7s)Og zU&79WD*dF8vVfft#b~BbWx!?o?bQiwX%@ll-5m-aUQZ6Ph4ucd4XVS5?|9Jp{#@Cq z9pUlC5;35s5h}8CuWTP@iN;a1rvf=oK+Zewr`8`|SRw$J!*WZk4r9C#5xtjKhD=f1Xv(QYsDc_(mOpZbMaR_;QFEzi~ZRywcZD!JbJ` zcoeZmZ`zE;L=|rY@K7-9bSx$4PmT88jZQhk_@OYG{F=q!37rsQu_J!%(r}A%%enQ_ zFQP~<%}0b-!pw`G$e$eQ`9poq=g0}cEdz4CxZZ;eKYc@!A}(Q>cc^>6+V0T&;fWmr z%I?aOjywb3rmd|WUu!d*;Gsd$Soq&q{?vS3HzZon(JEG)qyeir*J^NRl5F%*(? zCHxp9n!RAZ7kB)=kmrM=ciKnYc}a`YqG^$q1&8LocqNd&|FDS9!g1zg&=zZJtP>dV zQf!8Ey|r$MTVtgt10p^T=uF}DL_e?jawB#3TkX=iQ#|G+*h*$(AX=ZwclGj*_1 zD05YF^UE%BI%VUT|K{9~3T>>SVW#=@DAr!320N&-38Du*fIsq9q1qI?V48RSE%$&9 zJm+CHdMYVi_S{#+9JMCsMUEy>A5-K!>y3>RnoHq$&))KCMuVH;M3%}Wy4D$P2 znK;`kgqef{tEvSh-I0@z9yGs5`TXL>#j8}&R6&W=KT<};V7A^c+JuAKaNd?Cz7iL2 z)c)m5*AFuG=oBW{np%mzoRM(NJ6E2u=GG#bgWcP6p9}0|i2~a3hlen)3*cU2)GFGj$3s;R>J_ou(%0wg8$BD_<#oJBu{qH({%WPABS$xv7-sjGmB-wzt1Vi2HjMhE2P}*Fl+{geODftOz9&H)R7DEj@$I~CK^xQ3Gr0vba}Z+LD{!c zC{}H~1D+MFs2A#PDa@^rut+DT)@QLp7kt9pLJT|eX0P^=pj#J9zfM!T4&C1`ULKv3 zBE8b@o6ngWfBvOT0{PJY!+EmbYe*08;OFFOW&>WU=qAdnaLiN>q?jo05TiWD7k zlII8yudBXDyy4Lih)e`Ja~}-Gyg(ePotm2UED8II}EzX9|*2G^H8H|upan^X4!g|hH!+>x6J{qPanHUkK}S_dbz6c zP%hj>Xm)%1lRo-dOnkAxJn1do=8-dTO?~X@c<=`prC5dCchx;;1qthZQ2dr_+x4#* z0G?N+)&Nzfz?%0J0jNSW(n>o=KxN(Al(M?s1|JCycENs#0JneIP=` zZo=?N>f$}7ICe-kQTc@=n>&$pN(Ux^pfgkmKX-Ap{VkE?@(NGyygSvf?h30XT9YuM zthKGIE6Ao>x_nhH-cCl*cX&5M_|ogh;xvL=p!k!sf*c8TiJr4s%{1oP(Mz`=<)~kH zN{4zrTc5iG_(qN}V8Tm}YtCtq^Wk1BpO_dZtl^zfJRREliMxUn2CY&uOS^!m z3;k0Z5vZ8*0L!=BA5Tf9oDULJwf$ugkf1N-fj(hlOBM-*loV(rY2XYLf7{#sLYYWhh0_f^ zK|UmEV4G3rY2LUg^ipVXiMf$rT=!V!g9OmsCm`_MGnZiC6TIQ)$#(XpO@_qv@gZV4 z>mtIM-XJvjSz6H%c5J*XmBSDb2*=Tz;d;_2s_?68QWojx=$w@{+a|`sVA}CJf?`%^ zPU!IW3>qr1su0jDH^iQte$>*uF1piC74W3mbGLpWa6v)Y`H5Svh=ugvc)#yPp}{f)~v=u8@67La47B zM0Be+E{|?EOqbdQY*`JeBe!ufLznss?wY7`3NiVnWSqI?$dofW!YjW&Fh09|8g_8e z#kM~j2Wj<6)O@zq6bd+{rfYz@fs;o(yK(Guy`)5R1o;zXxsaIs4*1NxHtlwNIqSt& z@(K(i(XwmnV!X0Ol~d8vGK%x5QezPlrl4 zx5Yjf=N?UvlL)I9S4C&GOcxXBl&g<9&`J$WZ#+kH?_$W$pO-r@o?{!)5+G6m`20|T z@ytNrB{)}AC=JqLcwBBAzkhg&M)a9mFuNQ zMfpqsq-1e(x@iQi6!L&SQnvZQy!ZM7X%QtwzxPr5%&&3VUlbsTml7W@D${$-r!(kn zOIij+spA=&^JN(FK%5q$5rCgWAK|CPCrZixhMkw|fA^=V)yH`Js^;YB4~m(X7fm(m zHoKu&x06m8dElyRhwYr>v*-!dKb;xtnYsqFQ^X>(wKHevC= z7K5;0R2Y3lA+2y--11JFxng7h&7dom@_JCKgILLZHw49W95QU%&Da~uPFtpy?~{Y} zC!|nkf{r2^?Z$ITj^HcLs{>jzh2x;KU8{1%7R3?nphF4Q$xKFqs#$KebaYo zQaDY)%`N5b)0Pypy}qG&JT>}RFJ4ckX68r)EH>F^~g;SiWSpKFLTFxoL z5xdK+pVFB;mp4x^#$svk-ONjU0-01wVmaQ?YPny)r_{YX*u%RQi@r9x8%6vjO|y}$ z%JX5mTo+Z1Q|xx59vSxZeqXyEXG%qr1I;0ef}?j7qeD~Y;!PP>apk6_=C+?F#hZ;6 zfqvoKSrjbzX__IpN`XtZgf3a;c)rktFfdUuJU`%-D&S&N(_{Uf7mBpirxK^ zjsLzB8-*XQyO<$&c_dDYGF3t7)=F!(g1o&IUxgcRYf5;$caK6O`X$;9HaRe~g;X7J zWtrejjmu6S+u0hwM9|9nmfrhloP_ z{MD&$?o1HB3?!~nuGC9Z|52D92hhcc={uhZ*SE^LJKxf0(P|QW!6Rm1h@AMUNTwVz zWw)XQ$88@#r^ekiH>b=8Uun7!u^-BsKW4?tm)1>mXV+>x1=Kr0bcAR)VqZ>%*9EdJ zt+z)dKk7&luA`UvvEm>~XMDnYiN?djqgYqjU?QI>E=&wijMl$ z#l=D956~lLWf85aIShQSfK%(smcWH?SsUVhK+RoV?-DVbzrB0TC(M3Mi^mQHP~&9I z`f_T?_0}QoV)xJ?Vny7f{~K^XfWUIJet7CNYkWxe%!Z*aZeT)r9reicRW+_F=i9<} z@7_0MyG@~e6*F}az5I#d%AS2$Xe4B^yxp}E@)^f;tjQa3N4Dat(^DjLOd1p~Y!1Td zI$m5L*U45Xu?4dumf|AaoU6H01){D_ou(-8c>o$s+xZ$-Y%Qs%smv_T;NaWy92pAu zdS-6hP$Bre_s=ivYW(9FWg)oG&h${>%lZ0tm?)y8pB?;IPW$wgharMN;$Ai?>|~Z0 z3Rb|OPvT<-uBGPGDNjMjuzuw3op36315e0p#z8R5ND%*aq=iV(=#2d-!N&e;G~U@N zL(d{rhr-upK^U9@uJL;ug%B6$7(z1vK)& zm%0!%N!lNs10CO-EY6Gfg?G0;F%b=LzDqM*C!o&Yy6=5S@Z5#q$ki43I`vuS{b|16 z-rKSKAP<)28^V>fgJ5Y2!nqHN??d?Th@bMZp@hfl6hkwqA(GZlQet>0%ZCQqCO<2X zYdf%i@PQOvLwQNW^&#&pf&*VrH2S8cBWE6!LKiRpTieXK6=iZ-2#9u_Z?sM1--QWJ zfc=EX)jPVFdFD#8r-qo$QSIq_yl{IVQMF+k_bIdF0|r{2S0b*ke3u`ove#$&EPchy zXw)x_oj5hhT4TRPYHW}FOhiGdrpd4yF$Y~gUNI(&q9Zp1F|+mFejJarXlU4&*M#f# zTs5`5Q5hbuFOj;y&$A4nHSgCIR-46ICV{A8lBHTN0h?TcZ#0+gJ2(T0B}9|R7ZubL zPvlS0t2-Pq z1I5RW_9Z8bF+R%7>_8eRz(@P)e@ zoP>IUCI^7r*W~0PE4x5tu?$?rRyith-{2-Yn)acdvxo03Uw{nQ*QigUAubLU&?+r! z>{aTl4e?4&*jR5Be%RM~e2?shQ3X7!$1K+>v!|>d{va%1KT<*l5{m!s8ehmK^ce&v zewEj>w-7jf`lG^r@t3VRnJy(&69~$)4o-BgDhRt&5X5sd-?Udvj5#E*xU*n!#U)du_Q?(T?jhB2gY5 z4ttkqX6l2$al_)2CE1c+W519}tgJgy7oZitBveAV^imlyZS~^Q>5=V98GuR(+>$#UAZ|vA6hw!j87=t zW7z|xczA<|g~`KE87(NOF1cbzfK*5DTR#S^Ois#`jjOx6KNbD{>5XIrrYw-Zb%&?8N@0h$rAyc3 zyu;p~tJX~8CH2n1$PH}*l^&3xwsNVzNyl)w0fT;dZ!ZX&-F9_*Z1aZ@7HBOtMg*x8ozB2Vbsk1SDw9?~wp;WHdkn<&jX1NJE`5Y7> zg9RHhTBmso8zbpm_z}K>TEKjpc;GH-V;G$G{&KDSFRH|m^%m3KjQi7@h)lP-#=YDC zMJj!Lv|2C3JfG~n&4&-mzSS4Lkx51dBesA|aVOt~s@Kc+8*i2ot&fQRcr|YHPv@F9 zfigFMr!t8@F#>IJ4v;jGa>^ zYGUxgWItQrDWr&~Mcg`f`(F5TX-?{yVKu}eJDlj);cb=p`opXFW?xR0tve~`I0uXy zNu`v@laf8k9~s!TU&;KIKYHL%fo$xSHfBWocCX0qB|`5y^R5&oI}!+chloT$=TwJNmv?J(O* z%wDP;#@*oahAcn7M2SV}NLa)#hicqFp!D$VOg}2DO}gw1z3KxWq60ndVuP!fU&*`n za3L~~w;aUeR`{kE2`6)r6Wk_JFu0ZQC zChwVx*nx2=45K%?wne7AC&0(|P_K@lb_!$*|=lr3`wZR*`RDXy|E`1|4wrwo;xWWjS8nqOu`XdUY zdER{z>n^RXZUdlhtRHHKZl4?(-$e%Dy7j^Q(~iJ;axu@&j(A`hNawyzztMjj!T(aI zajTRgo@2*9kclHNIJ>0Y@r(>9^;L6AOLAtca;uRpseqfX6=1>pMpG)&xV4p-E)YeH zFw|NllrmUF@iml9Pb&~1aBAILMeNV<(c1MyphOrt0-NoK$9AcuayRL}sWa|}UccL= z2LIbGt)0!H{5(>jSGQrP1T-HOaQgTWqfbb!b{5Y)MEi$3?4u9%K9^bRk6vOXsDc&@ zJhYvY_QDEDuHrU1kaRW_bCMzVzye9j0@%C-*YK^6h8M^v!Kj2zB3Xy>e0W8&|&gpV)Y0afnrkpY=(A0ZN z&#nNdur;v)Xyw?Cc75KG#e^A6@dBjtSRnkO-OBjPnEwixz*%BE`11(r#A)aVo(ZcCD5-MSN5PwgBFc+U~A(#;1g1p3q z67-d`tCY-M_7x5j3M!m-3VQ5?epomIjOMo(N1SS_GcOO9xWWg>WmBnMNhrjm`XvIF zf>f7!Tl*so!MnU`clqt4ju=P{Rhu%W{S<2mjKJjonFr=d+g zzJggsRNyqTHPWUyOG93uREcq0{^6cPHtz+SwSg~aMEZ53<`z4i<+Y;W#I+L0MBwuJ z!o5lBnk<}~nQw(NWm`_dC< z;gU2s4yR4tPkdPMq4F@;q|F|S;uNG5qoyeP5s^C^%+!LB=8SDAG0$nV6t%=W_>BFz z9cHPTmNQTr&u?3b{PYZ6XGL9e8MOSS2I7V()dQ}n=O;56y%oL0p3T>12ieyg1w!u? z!W@llR8Jqw^=j~fQ+b8ysLd}+t%9cfd} z8ozF&V{e!EE2Y@BmPVDvXIBijChzW_Lfi3o`{V)5>Es|f0sDFfee|2+?deD^b%iz- z#gZ^#R8s3-N(gd$uwvnK5*t|Tq*56)8-sCe_z3tTeQmsrBDPeJRBqr9&RdV#JA2hL z?k%{oCq=Eio1@zJ9(-gb#p(Hh9SLS-$e*&N>m5+*b^AijAt`rOGL$)BM(h;H{S!x*p)&sc9?0$zljHsL z%WOJwz`S?UMA%_eG1OhJe;MesaZv^Z5IFW9>_g&55vGdqo^^SEcN z6a2cn5rPhtg;JA8h9AE0`O{T?7d?tt{Kj>dQhuST{qiDjV%P8B`KI2d?zh8DxEM?( z-R&`gM~O_TEk${O?Z_Hb1%v(1Xd|ItF4Wthz+YVP-k)ND!jH|yDD(|sz*@V%^rOX> zhhpwzAF#>^O>48bOp8Uuj;hsmj0D?!w& zbZSgux7F1MbuZ5*;OaUuLMz%{i!TL^I`R$BF z^nBG9;uFGcddjQmiXvDT7#i!X4|oNH5+X5^96JQhW*%Aod-xK&e=@?J3KwRbUwr+G zLb%^duVL=BPl3K6)h}7xA4KGx7)w2(u#(Motzk`!1Y@{H@gj7f%Nd#PISwIRW(@sa zKdkwg1xek&<@#g}{LEZZN&BkOqCFX@F}9k3^ln03BynX!$3vdNW;TKo_ju~3eOyD` zPaB$YH!eH|6$f;W1A3IEs{!L*TiY8vPD5xlH@AUchr06gXXFR5eq)|zDHn*E;k91F zbcNL@81hx9+82qxI>yiNpiVN_Sb9hMEuQX%$$ykI^A)@f0-41VO+txOS0E?sz2lH1 zGAuIc!;JUN>jGJg&20QlG$CVAzs+w~WtR^g(I}NEi z0M}KOY`xTm!KICG@thYlif#~wZ>lXRvscmwsHN)d)^wLNU+9q%PER@~U~Sn)iq0J% zN!Hr3S4}y7CW4z`MQvVx>B}FCa^IXJDEq#h*c2u-Xp2sRi0g+_(Pv)0UJrsarvCCS z`Qwv$S3iI8pwA@owTK)76uR)CuqBqJxEw!R#4isbi)n~Tx^V%n5UmspDN~Q;Z_7)7 zWbU4>aGYmW&T)-ZUA}MP>aMy$+$HZ>qC+H*0OSBO|8{ zOyHW2r2BJXauRv~I@E%_PnT;m-I|`09)BWCwQf&!{apMiaP{XHCWVcn8=rs|lxD8< z;Wmjpp=4x@xh1o0k;rvWPfi%Y1P$Bqjia}<&Aec!CbVR&9_EWzuFX3R&zJA6Y0Z2d zJDG8{BCm~lmdLff&WE19W{p5V!`Sf+77<+8_9SohP%iydAU6sXt318dTgy9LW|Wwy z`~9$6H|KL9Ycj_rTA%Aq>acu+tG+cRjq+=xdS?68B{w*~d$P*8G_i~00+NP8zsc~> zZefXFEC~sTNGjQg%uBWqn_Xs-g4`XgcE8j#s=n*9g!a#BcA2INvlJcEP%mC&ApeS* z5eW@@MBh%gM#Ns%?Z$;he4!FQa-?Nrt5c}8j4Du7%GMt;1(`x&IcVi}hic)`!P$gs zlBDw+--w44C=*pjj~LXYMGB)QGeF9)Z2ma`FeB+UV(>0=z`{i-&?jN=3?a*R;uj#fF>Ff|PHQcjk-0<_+!t zPovV?Sw$E;O@cQ-3jhaa0u@14hin=65QS0-GaMYaCiF`Rd4n8rn>t-=m)Zs1vD6%jZl;=vA zhf)+3*p*}W$HE3e*l;{WbK%@Onu2Bv6lTRkVGK7Vz8PbWNZRY9scYxly8;F9RwK79 zO@?P3MG>S=mRm#zt8cwAK2dBc$?J8X5#lU&A?0;P7Rcc4Bh*bbQI%YK7f%mR|3*yr zRrct;KZJw_1qSu!%vd4!V5S!xC)l_|Ma-;%&W1^4+_&>_U7JhjG#q+SF34p@&lHE1eGCj9&%L zi7CG~?q=S7(#%3U zDQ~o!@1F0PZ??j7vu>d6ot{KF$XrWQ0u+X5aF!O8UgANvZS*tw*X zBDHlv6McLTMoO_ew0ZlhW35P?bjJIV$Y-};u2XKm4AX4+zC*&r?J&Rd4xwVXx$nJG z5Md<`gwR;u9|gt2rSB%B|Ng|o9}&b_XWjyf6dKFjV72H;mRySk%*3ZwgkDoMl|_!g z$4!t21(-o$9O2Q1fo&FL|{ADnPUkuVTGRdccYnq&O7DF7iUMI|rT zrLQ;;)dPrs@n$8GjuD!1z(Gh^ft_3tqU*P!#i?aX0x>u3|osapndJWE#g#J48p)& z9w^{nAI^48PJ%$AQR~W)C>xj+a!eRxSfr;b*c{GUVDqGs!CnN$^ZPB+xz!~U?N_}3 z%Z|G>;HUOg={)Rnc5|z~jfTD09i=qAJ|n}yEsc2^h#_S~qI(D~NUW%!Be4e!;twGq zIkHRDw2ShECf{eDrrtiAw&&vSo2h;07U7O+GPb$7sX~pZ9wkprK>?M*Vxg1_1Tl4P z)Vez#Fq@fC0x?qUUhjagf%mxgF;9{1a4;~v0dVBK)+T_)2d#rJd>bxZxD?1A0)n!P zUaPG%Dz*UC;e53b?Pbbq-xQ$aVR=*X+`L3O{i)=6`|)&3`yx$^@uC;=)>tGvrKa0e z?boN>ItBsYD_>nX>+O6oic3;3UP7;c@HrPlV1h~$r*RgPyex_zFth5xJva=s;(r9~ z-CT3alnjMLMW7VV%k;qUu(vS&piHWVes~B$-?zm2(M|dxUzD3r zB28&<1y_zBMPoopn0Pge<+sR%bAb8NNkN^BKE>a)@r?d6D)`tPAUUVORIbt`pUjf~ z%Um5FW-n-?j06Ay$Hlb4@Bx*gWrC(IZ{jJ{fqTm8V=Y<>G+a({xn$2F?*A)aK|M`#=! zBe|*x=}qdAoO`)AD)>`dA(U37hDyyIlh_v(X^Ts;CFF~91+S$xyU&^Ln6@irdS@Hg_px?NrRT6#a#sgg>sCEP27b6 zt3CilTH5oH13Vbo^OzSHiUJu|LEL^+^~8ip8G1)U34#jUE;2t3%{2;%Rh>{Vf1xj#lu<(*3#*KbKZ+rp&fmyL}NG~=H>u+8I`5(6sIDqH^m1;u|SII#7 zec*QGOs?7xCYDAs7?Kf{{|E{Kq66q9SRXHjDb6u6Ga~@Cd-M^)N9W`!G+M0+h@2P* z7^&bV$(ar>oM`l}?1<8y{Dqtn=cq8++S>7`r<+TisY z;Ap(u;t~VNE`vfkiGncD`R0`BOc5yi!_4M>`Ikxt)$Bh$xHKKR{h%T8J-JW!l$DWy z&%EVR_q`CHX;ilH=1d(X%6q(U!~7mH9NjikZr(K~xJ8yCVjwr7@~N zC@$ky6W7|2`TRz~bvI%<*J)A3;;%4X)itJE1{gSR>xPfj8{lkhF?{k*9Fj58U(1O4 z|5`@=@4l^Ia&HFR5P%jKiuilA6icn%lDN&I{o6)YXmX|taE&LCjH24^B0;qD2Xj!X$CYKpkOxxk?b=i!rgpuaX39`v5Y<#VuE*4{5&E_Msh5tiCG!;k=&3>|={p*j#f>pHqUU7Nd029Sq$sF0uVSdeQ?XS#YNUUl~;bJsShfqcg| zJ=9w!?(eWa8bkmYE#j^EXZTNts9tSEiK+0%qM_7`E+sReQCGx<+BR>Yg90bQ04fL& zcik7+`J>Wh*ifU*omk!dr%;f09a18v5zEulQ-%G4twlS^#b11-ND|;`lRX0eHeqJJ zy+=l^e~OL8qZ&5d!N#Vmauzq+FgVZ)A%M|xJLPvO#5Ov}!-K2hIAQ30aHEpwRNoO#9ux+gP*qRL1|@3AtNU5(YpCf&rhUA z>{20lLDufzOe;l%|7G=ae5)HZ{~s}0qo*MMyk4^&?1-^KeKjN&S6Gay)7J%XWx_Ez zWNH8Zvkkq0a{Esv;O!d|komOcpZj4<<~_spaDbSxZmRMrk0Z~d3?&NQN0~n2oN1Tl zZH(Fd8wvQ{f)KD+*BztZ|KSllqyDSF)m-?OO&CzNP%#?in!%ZAa&FJk2V{n4s<4va zf7F4*hX}kC`u3Dhb?Jxt$DscVqWWg=EFqCT{9|2!_aOpi15u^yaMIr|8g)SbkHDa} zSxohoFd4s@*Zb#(>J0)60%U9b7yczbRH?@dmoELk7@_|)27O8ZMUM1Wr2OkYpUQ^_ zJT+RSU`O(AsSdP0!~X^ZjDHI!l=$C)fIh&GhGJvz%KY0h&81WIUl;sK8OZk)XpE8; z2Ti#G3&ekk1wX&J`NgQFZS(%F6M`{d;Dh|@PbA*r!g3q_jTigLx896otD&(1>djiN zMdmF#2ezP$CRX*YK^n*>{pRSFUVyy#547{|_r2~$bhZM?zKA=Kb^PKL(XMYl{^xc1 zmfrmM$DE=sfB15Z8G+Afy2b6Eh4TN!S^n31HHxJi{R4vV8JKC0ZxK-*Mw4Tm@yGsnl!m*he*)KN=LNb5t7=L|#|H;cRCVy{aB+}E{@*TRZcz$2i{9}9q_pYpiN4si-$rbN0UxbSsae1>$1dG8J@QL0QO{>8 ze9-sjZ4jZav0LsW0e_B4@1{5RB2BdZzc_jB+`zZcmadx22xKfEMSOK=`qP}=Ci4Gm zP6r%XI>w?1?50Z!zzg?hSo(y*S`sLp$E=gR7m1Je#QV5z+QZHj*chLA|0kCe@K`f} zXbc2}hkxPo-c=|zaZO9@i+}ppfG&D_N5Q|8Z(<@Z<2r1AS#xTVPP3T(y<{Hzvt&At zPP@s6#Qb~a=llTX%f7duz`s5w|F?f?Sn=i!Hbza_l4Fw^UL`QBN9!4~v8*wfDsNm;)E%M@ zlt%CfqY6SHk-xVkt#B9_B13} z4QvUV+6Z9K`@Xv?3j#E6zMtmHuvwqgV;|r*0(3?`VTguC6v1&^?n?7}TTn1CgC5XP zd=NG9&k8;_YOdqkj;5fgX=yJKE~t@XRm>i(&Fl0XUz=7@W*&?MaOYJ$Q5i^wha0!2fK}nZrQ8! zt$gcYZcL0UBP4aUC3FIx>!Z6vBtU+pj2@#v(-(miFce3*F}**f(&{QDZ4>eY3=lpT z&?4pX@QrT&O`wS=nZyC6qr-k_Ckj(9PEr&94w6Q#*+Ffa4ai`qQQfBa?;+K}`i9D5 zwkrJRHUNVmVbwTQ*p4z=W$Q^d49@98#rRguKXX#HMpL> zZ&|+zfeKSW$A%TE3WJ;VHS%lAfM?2OARxYAfREql&G?WlB&OO<)=C%wa$(fl6W$IP z-sf0qOXfp5>`~58!EYUIk{BA43OtI%a|kQoV@f7lqnDhCx9yG;{T^l?{?q8$8tfic zVaaapvn0%dW5_Vi4K(;tzc z&q5=Xu*s~=V+2sgpj|F^QPwDegM$%o?KXQ%K*7N&L*r=fxE+2+KJ1P4NAO5WYLmjC zm2+@3%4Q2Y#8N0BqN1W^g)Ox^o$>&^*$7`>B3={{l4j%i3Z5dR$0+)s-cm}?TP zgTup2%M}i5CtCmIM!S&8a_#m`pbi6%UhtG2iz&aD$io{aohwZ6^CO5V{x&)~de`i% z+Ex^=X~!3mKi6cwc*Fr4e%SHWR7zzUXSe)|X^t4@8ciVd&Tgt-=b@mD#4pXywWOTE zS}5=Nx=YVOg_UIbTE7^z_`}m-tsJng*(`M$Pgr(mfgnguD`?K>_*R^0{L!Uy$>`|3~59^aEdvf z)9&wOf{VbdsIKn)4kn?PT&PmXrB-K7)0a!K4M^_Z0nIU#&2&JS3fx^(1Z3CIF*ECc z$dds|^^DESg8;l>hNv9w26O~$Y=RzSyGyQ$ni^!sm%A;Qw=VZ?MqC^*kc&Xk{ME6m zmxPc|zoYqPPz;PXtxX=I z2Ofqrwb6+Qxu@(DU+f*XFZ|)QH2W3~|Mb>1Vt|s@`nNOTmOzixJX&B7Eqmhn!zy@8 zwxHdQ84xaXx8qryjhFI$O3b+Uf`j+Cr)z|4%hDOmy{hu8ej~U66l;^{Jh>>8iUJ8m zUcbS8s2^H0j88Y?9|#Kl)Tt72q3VjaSfQc@_b{{XrN%G4APzHObOrUBp>ww{m$$;L zn@LcZNZ6(!p)hF7Bd>Ed^WAE@N9e*9BHo=uTT~=rlXv?P_U zkwHAPiFefsZ(a7~27G|@_x&yVy>U+AiqvXdP8t~VhFOvA@G;KF$h;TZgQJ28t3VyM z->My_?|8{1U+8$HnN&kV1N3s5z_y{I$K&yAgK()oEig0`aSKSQ^}pB|{tj(wHcKW9 z4Qa%1{{u8Z5+rATpP+*m>+aNCP%u{5%l~YlXKE6uyD|%#7Ay5{)S`Qp$!J41nWlSp5*ZkHk3h?jJFlwTOa3J zOtH(-C0xJB@JmB!K*1E zVtWGw$V2E}__4?apqJpz&&8t1xGoHUB#tbrS*mj6Iu|wObS=P=Vg0cPHvM~e{*T4> z*Uu8UH+(wI$BWFWn2l-;W^SR z;;O~kGXlu90Jvo9`f#qQtwvh(r&tK=wE^>cT+Ke-4_#dZDi8fR_KgQOKVI9|*x7?) z12m9x|2V5S>hW>SAC304I$j7}bN(KkE!o& zPiO@n%NcQG+eA;0_0n4-1g9!bQ5M_#lzF1&cF&J2d`yW)b>c+ejuwb`Kzr8D?%$Ox zXCPl+rX&!GHzbpn^wLJJ8SP2qOtg8p-He{^B{JQ6k@rc!)M7dcTtB4gY(;A4bV5Zy z^c@;GS$|7GbLVY~_aD?!KJ^F(lrPRw#(v=*dEK?v%`k!+PhQ!=eG z65AA2d>S6qQ*B<|396}$XAT4V)YfXOtyA(@dL}xZHGbH>nC90i@DcjEO2=kJvuz+i zK&fI(c^w>RKM*ibfYN3x_qB~yub&>Kb@AfFs}=S0(KaL$9?=Ylm)f$nz~nj9r8 zZs6RZ425mY@5+@W7hTS%x@~0*p)mynN9o*c!NgNdLnk5}_i85?D{DR<9j~6cIGE9SKFK?gT;9I)AkNG?2}$RHWFK#_9BCcF!?fpp4MkI6Pgbj)262GdM0x zJF_>{XbCjRPy*bq2>YbDm0Z{JfXWTPSh~;$-ChPXOy$dsHssjdTi-Yi(}g|BZbZ|f_P>>|AgtfdPERKa zZGl$v!o|(a%g=cL&+$SvJ_zwRxQIFWa|!n;A?kQ79dCyqf9>rYDakMa{&XtDYm1VJ zNn_i3Jd|;0*R&bPBYUA-iYYP2`o;~m+l0EWCO^e5E$U7;3pOG)l`sOqUTSurbMp=j zy(*HkJyHeKudj1#2p%=hHBhH(Ft*kz(K|Cy#^yhM{Wgzhpwp)DZP3gNf?E?OwHe8` zP3r~f#j`YW$}dPzsz{}ycNIdK#ou`k4aRB>-Q5t6-(!~q)!-HHGBYs*QoJGQO=)`4 z4keO}jC$+A5q8s@mS->1FLfuzP?HeC+7OetvG$t9?D9vKaAj zedKovI9OWf`hoE$aLNxYmj<2>y%lOaQHiz08hzgYiX)k77TXKk4Qa7Qg({#Hoh&?M znl}FxMnCffI134i_;)YzWH@)=A#R2^g!oqlJk@XKdqZPqsCp#R&01Mn_Twhs@kI!I zJ+t#%Zn|KdrN%9yBL!>&@}slAS**k1qwbOs@W0U2*XhCOQ>h+jq918I@}gGSs2I`` z^20{F!(i46XeeKE9P2;tKJ;tV5wy0T6gs63q$)|5TIv4r#mr08#uld|<$kcH+Kmy0vTu%W$jqH>`jrTso@3^yAK3W%hjF&&jf8YtSUj1~Q> z*D*5s*DofIz27ZG#t|7Gnsn6(jL9>vLgh@Q!BlLsD-NA zMGWH3K}UL!mb<1)rSxX!T15QO%VamO6sR+P)iZjgc#7V$1RAk7oRLDDDwHU;ZSiix zw+W0inoUex`O;skMad*a;}|e0?s-J#xN75DTU#4S0IVmaJ08(w!1+hzIS_)lU%+Yc zp164F0_u%QyEh4ki^6FC`v8g7KY;i^(+9#_yyI7+EFfWqx|17G+nI$v)~ z4}fx02o_~ir5Q|Q3C*uz0D^o`nrVO-y@T8pp|8AP_IP8VVlM-k%5Fz9BU%to?V?s= z%jWq+Bq8DijYJZPG{JATJEA~0ci@(M}dS9QdcM=}0G;8b7Xu`j@ z_}pRUeBLDA;)qh-m^&yi^eYm@%;bV+faSky67LXX(cZs+38i;_f7R4b&P zo@aes9r7!g%nS8oC|j(1cpWtU;)&PA^T5I3M4i^J;oO8WgB`zA>vl5G7EwfmZ5S1{ zYDJ`b#BA2TJN+j4y-~ZKFBcdR&m+@&?ejGW%evWJmFtNLYfKH^gObb_z&LgB4VBofx&v7 zfgh_YOLa_vEB83ey;RF)e?hysNB5cFmIz_l7g6IMiv?>Zw0iFZrqkmKN%DiVR3I}8 z~ftD5IX{iZFEy@($BqDd!b&oO>=7#@H$cPwlLU(@6~ zO5zqO6w5gQMz+D#kj4s=1u~6SR&oZNkr7QYjS@sHo>m`}YBxd>j&t=+L_l@=E%dVB zb2FMJ0VEoIr5sy}9m1Ok_V*r$a(U;aZ(?KrqH*anyP3Hr+`UhujgGT@CamtR}l z^3it+%yF%@3P%8T-njht)=q#lDvw2CCL>AQcX*r#wE=iz4Fru^T^%|O=gykwxgLH4 zl{?h)7#YEVZNSwE4*S%Xm3}g8E>4&C~U7HBjw zJhmeu<$?f~b5{fdLf-I<%Zh8y=ZC6xqQ^>lGnJl(N}yb9TH9#1NBL=wIfO(Ko$>(O z#bp!?LFnQGkPi6+_@5Bx(n>f)_bxyrr!Ca`^sLC&mos}K$XBaVuJs(UD&7~Cy?4#f z-@Ug}V`x5$A}SJPC&F`g*c-N9a+pAfu#^oV>2@JFe^le!E!WqW=lR9g_|9j{hbiyWl zc_nSseyLUAGY6Ce+oCC~G~C6^wfAZ$_(oG+<#MY;v|jefJemet>0ie zv+8oM<-KY4dRB37g3>S+v_*I9$jRaQF~pS4Ct^)|f}#Jelc{VzS6fs)l2h zY(&rP?j}*pL9Mv7ns)fR)k|jLP^c#`v`}iTJk=;+)NDK=FGgfEXqb6JcO^K2y=8&Y zRl6#4Ct0=mNZ<$Qc#55qoQg5vc6xHq^aezjuA4XqvmxE|aTfy^F8z#5`qMvRua?9f zmh^tqbk7t!69+lTW4bLr8HnSxo;1Fy3(hee8pAKth$GPU$+(KS+^D#2OM2R3I!Lvs z2B})LSObQUMs3ROZq4O<&#IWN?ktmu7OyEMi9PgP`?YL^!o6$465rGz>++HNS|(v6 z8>7+m(R9pNodK!{>eiJO7lo$VRX3Fa6@CEYBsVk8b?JNY>;6n2WQ^6?3R4MyQ5j+nSh$A&PKsMIyG8;5|YWBjEeDUQaWhOXoVis2h zQTb@0LcU636{;;&cQ*IS9i9@y9kE0_<`J5X*=7$U1=9*`J2t+X&T>uVITXmMFdzS? z*+Qgo0+UQcO-+joU+WlI%V+_~E{kmMcM$*c;FBW#yvnv2M>xSvlvB?r9_NQm&)=mW zIuE1VF!k63zj=7Wxh3#5j)&qfN@0~Am{tvoghnSJ4rG;@p%;Aj=1PbjjU)+GlF}?J z`PB}NdchG;y7*h5(3pPydX`w|g5}bdCH$c<&LdA}GDx-zwov=^)9wOT$D_feh5k># z;#qA5>Kr?LfeDXx_VAzuJx7i-$%)t5iv*Yu_W$YoFc{;h( zpcl_#9{;ij$0hq>)fl>Ep~$I@{p*%hv*Z9dZNwy!4T=*2DxTYhUi0#4@qQ8&F03$xrRnZbG=?4VTxVZ4d*LTl$_ZHjbb7re{#|L zJpy?7++>NMeZSWP_b&ngs|79JQJ+1-fjvN3o7r_+w0pG`DQ+%y8}H#}X~%whp!^{D zt6*N9R7yb`P#NmjL;zTu@89DQeEx1}Z%^bUQw@^|ugUDkr{zP~&bfKN73}9xR)zKa z{l907HzkB<59S~t`E3^(jkV|t!DaPuQRp5rJ}C)_Kc1rS8Pp&Z?1Y}ipf<`@PH8zb zew(G2(zrIj+GG)t9t!%zQMXYtx`Ab1EO5=)SJ)H`!Cp2NHAUxR{4jN0psA-Y&pHTg z_oTsM!J8_^h-xS+UMR{6zQ?>3Fam7D1;>(OWIuZbNo(tKAlaKE41YDCc~0FX)XFL0 z=V#TedWHDPnCTM>#u?-j;^)g{3t#O{zZT5LI<0s_jFuU?+ax|cefWLeymU$N^$~7HGt4Z0Lj+SE<80Yl~RN<+&A@(1IBo0icyh_m0jy%Q-i}v z!-b_pg-rkrrKXJupOlj`Ej@i+YDx2C&JAy)h3#3mcrqOu{Z?jN?7X^N!D9=|er~ z_S5kwiemW}=;AGj(~7ISK2%^n>&^(FmQsaq17H$P`-eN8n_x_Xg&d8g9i+Mu zt{Q30(bn;wOZ#Qy&V;vl~ znoMM2tk{if0(M)MXbqmaUT@OM%SX?eqNCHHXQQNk<2lCd_#=UeU z{+Un=MnF23Gdbgb3#dFtu3>jFt6i_v7OQ%Z`($9FBc*|@BM|WiP`k+_bxgP&Dv%Ni8snv+xqGOn<`lPG$1o2JT z)1~SGuYJuY6he}U=MFt)BK=YcA?OTH^zjTZ@FQK78jR=X76`T8>b)AC)f5Lf}qKvH8b z(`z%=qNB>G|Ee2_Bzbc_ZL-~ZqM4NXf(!B0%@cvg?Q=IsflStMeVao1ghz<)i}Hr9CNyTsK@D*+LtsSE~^uXN(#v^i)- z3mF+#7OJ>7Sfa=C3Pz6`T*;TE6Jr$>)~-*LA}+PY^sO)c$B`azHt8tjcy{S2sYmid z7^m$f(}dFvLLwX<=DgJE#;BaQn@{Xj3{S0dc(J?w3{l+@ettreMOLf%TkS{FhfTLl zajsVw!bunjFss}T?^`BPFUy$B7Q!}F=7Yk6wl0sy;q)2e0>&nOEL{mKIUWc+v!O|9 zc>?Q(i~O4QYFLWMYgC!Z>KB|gt2x)jrOl3XTY&nca|Z=o86;q?W*p5%;+7CRx|LJ{GtOzjHI!rmzJ+PD7n@^ch0Mfn(05y z4sd43Z4Q4K^SOK$pZefpaWpc*^l;U_TUiVHxW*NwRxh)?%-=rAsx3X^IEAC|GFbe4 zDC??*xU;u-LlpUgSUJ*HJLaN;@pb1^|D6Z@qm?O*>E7w<;5{anIbuNk;LnJB-fU&f zB_}3mZvp2@0jN>8Qx^}f{jg#c#o(YYqf8pVv5b4A@-i|wli#o>rJOf;F9M_$A!jIK zFa|h|RcpW83aH(VHQ3yIAkd#TO>fdGh2hoc$0D4-FmT!i*$^n%0e?Q~-c;Ef2REmj@~)7W&HW+{%jfvG5QFp-T_ zUc+lOHM}>g9&moGMO3QUtTj6RVjjxOy5=5p(EvRChv%~~SJ$0%OVPv4arP#3xk@E} zIZ#t!Wj39TbUay>3V=&_l?^7{Es!I;d#I7EoWhD-jI4{6A)=#;JeHBC0zA?ZD@_i$ zxe~AW57aZDR$qY0A9$vgFa7-iCUVB>gklqn2>P+A+U~yovgfQVtB*XP zZM1tB&@%e$Ys<@sW;At@y@MpqO}wo*JzJLg-C!t?ZpiTcVd1{ZL+bFNHPN={5-;~7 z74N6({6ecXlS`f7&oV8&gA3gz*ORLL?ktN9*{7c22eVfE0dVHI*TX!sjxmHj*%bWY zJnyUm6YZXVa;o+i_j!TJtHR%fXlbJ0=c7PlKU*d9`GMUdcdRn>3JKL*71x)xNa@pw zTplrh!*)2}3FNk=nM|;I$y>^!U%6V@I~u%dZPR9{X)1zZhe@f))!D+$|Nf+rvaxjp z$*xDjCF4-mC{Ww$ zf>vCL!>szT66vMr0vU(BCT-T8Q=K=Dfj_)&Hf~wS8MC)bO-}CpX!3)|ik3xpdZdEf zNgQ{3GS0=rKz9ULrq1Xha}D6e9i~WGx>%R%@)z4*s3s*QC7n9o>YsT8z@Eh+RT*9^ zL4}*G_#gqu=gUz6uRG+?61o#v#0S3sB7Jd4%bI8NfUa@8(q$B(NP#;ofLN0y5Z?dL zvjpTBu~7I<9?Y1UA@9#J*}kukmK~SYBuRr z9WlDTDt;Y6uu43SdHE70S8IG4?(l?JZ2OM**)bV)#AAl8u4Q8PzOyEO{s-}rY&myo zy}mMKSvpkxR}BrpQ@ilb!-%N3Qcl%-Qr~1n~BZ4*65yp65w=Eplss_Yrb{4zE=av91qtWG7t19D7MRknn-n? ziw(Yx@k5l%zh}j!v!viCBmS;F%)GzoLz3Z=&cx4TI;|eR-~Dxd+F-zm0BPBJjk*X( zIB?jk`IG>Hf?z3CeH`9ivKgk0adlq2=I^CVHEIw!Evtmzr;#x{jkP!Ak_V4#$f^J0%DR`A2l(hBid>^m34-hy>Dob;=wL z2Ln2Ni$&HpfZMK)<7hV}pvp+<=v0fF^xD;g&>{kl_Y3cf@r&f}d)rYmTy|SI%J#v< z-eZ7xz@DQKcwx&B#B1m#bOsPLXamk$yEew+i*0~q`pE(X;Y129USb-o2Aj|^wzohi z1l1j9NyueyBW+2MGD z02+sU3v_5uK4AOkaDqtXa*_7~zs3pCA%Ck(Bti~w7daR0*={GqBHP8nULwS;*(OI4 z;`0(Alr8fOgaOGOPd$r}VEiF^fn~c}&i1#{tB5XWVE14N?1_|qso$gB)#C3}lUPE3 zZ;7xH`(Jz@&tm4ZH95cC1(#h>t5#v8^Udmh-XB;1Nq+ESuTiyUn;2G@?9z-x&~rx z7no=abv$%k8BH~xXc-a+3e-^ecaPY^sfdNk%M6uFB;OcY^!!x z5r5fSh1*=F4QQWaLm1S*t zFxMFNSTo_pGIc!KC_5X{C#>Pl-pREGLs~@pH4iBZS6BgZF_Uc)yy7i7RL~BcU-_jC z8kBdRCeSJZjUSA$#y)SN-cI^shI9)9!bLg)R;zSW24gs#v!LKDttm0^tMMNg- z>m9NhAOTEjzv-|{iKR@lSsHGTBm)gHX|~~6$8L*>Szks2h{xj>Y_F6kC1z+W8z9#`4Dv9vo*WzjYF%hE5#P(~XX zzH~|bKyC1f==YZ|U+lYKJlNNyS`Y(RtX{sH($b%+rCaH>txIJB7@LFVpwpu;t>rEt~0r#W>y?^xZXCkckkB-KX2GpSC2xgdRTp|KnLafNrD*Z zrhVRrkS2(&sgIw}7y|5R^x|KYhhTh5?Cs-~!{2tZ(~*c6vxftCTWV%#OLfD&GGyEE z9lRV5m%f>vn@^J$gQBgzABP9XGb`YEgFjs>XzLF?UW`3~h_ldKks|o`YoQInwe-Z~ z1(xZ@$qFX&&V*kmud}&fb;g%AEqZIx!Q(fpq#zEk#j7pWr{pq_)GoG7< z${#?oy}g?tYnsRI6n4JsGyGx%tTLju+St4{scjDj zW+u8y57m)V$#_i?jnU~PxCFvBo|xx1ks6}8 zd_=hNAxC|adiwHVgX>g7jp>xRDb-!;R5&GnyBVMbsbTCFB4Z==!G#fl#^I&iIk1*$ zT%>!pn190MNlLEY%@NTM^p4g*K|q+G7)gY{xcKQc+ythd1f8GN<#$EB%D72QxGF?I zk@Msze4Qrio$cTG?i~uNnBW)1@+ib;qH-w{e33s9_?*>Pb{g3PWbLGzI2HokreOX>Zst`Af{na+2AIJ~}9%Etw}zBcfX()ZYq z?6xh=$xO!9wTKc#?SeWwK2Q=w>0Gww^#D?CCn)P02Lq!Qzt{i5h?UPTV_<~Ib6d9Z|!L;P0Gca$aewggi*uQH&W|{1!BujPEO9$rf&XWl%xSGcHc%( zaoMERXQCWoC1_vNeo)QP{`21_g<%&VnLbh3!`LMZ{L&W__76I03vgSrhBR5B(egtf zA3}Nv^W2vBf+HYh(+R5vUKv6d17uJqvtUw3Xd~j#uyU0}JQL^GBM)X_bJpKwam{{? zo(eXeN(WgqPK)Qerpw94(_HfD4~G58=jEv~MlhNrA#`;N2WEd&VuK9?&e7NC%%x}F zU#;q13h)|1uru%pna6Dr3P}8}`{FZQB==6Js(=_frVM!^!VTKwnx*;|5zu|vov&1d zuRhBr+`HIMD<8RxoLReF>(4A#F;C}NAu={TrqbTQw<``0I?!oz{hgj5fHsbsU5bUO zM%<9#nB1}PDFS;GR*Yz^jj^1kTvG%R@&4$UpldL5C53Hnl-rCLN9?+v6Q`oT)P5^aePI}6ADjb zyn!wul7LT3daUD;y07%pp}ncrH+NA=CkYBPlVurSwpJ%ytjA6onfi z`w-$Gf&&;{Zzw?;|8(U-il^02PEI1u)r$w2`16F_3G zRc#((0e^-HLJ%n|?0>not--w)OKLvV0w6Bk&Nqo+%jXA?48KYKwfXs&^6{0?r?n=x zVt(sH1}0Zatam)4{D_?&YxvcnBEASm>`YR$esx7fBJC<|Jm-FSO|6m!wc<}N_32hC zZI>3?WH?hk2em%3t#XZ_%xCBl<&P2u!sRCY7|v<+VSS z#VH3jADtbs2~YgUD0Nk($K2MuNwNUYI=&}3+XnA+U?D2Vu(~p#dO72NPv#P#9Q=w= zA;Q*kZg6JUBg;g`pXG94p<{Ww$G)R^KNuI2?7HAY`feDg;+P`)0QFt=O>uNu{t=sn z*6b-iY<2-i$W8ficJ6s=NVv&Xw|emmHVEqQiN@rJt%((7tlwyY9|(ly8nk^MKkh_T zNmLnY&gxQ7(U84_CtHUCp>|n|drC!{Hw&95brFutPd6(c~?! zI#>@Mq>b6`W^Zsp@@ThBS8C;S{DkF5S!;B2ngobWjO;=uQ#DS|(_+ zr51yx6Vur3W;5ra9J#c|3VjGwY^tKw>}4$*lAY zapU)3J7s`{g1>1C+GO}_6@$@Oq24|SXHK8MKsuC0{wxuVCi_8y32Y65ZBVfPudO2i zXS)Bhp&>6rA}Z#X`^Z}?%RN^mq8y1NM~-5$h~(H5(-11xvR7(K&bdN4Gq(!4b2TNm z+*ALbsq{YYf6wpvJ$v?SpZnVP`}zJpK5pcfe*8}CAH+zqvU0RiTe9|#q3>su`>oXS z#3UoQ4$XSkb~FC9&qWIx)bPwmC||6DxW9jSczU1=fA&^MUz*;=H)|er(E7Ksx>bAd zm9acO@aK*p1&ZS*yPMI$dNZ_vlON*8TW!Aee%;y|uWUOn%=?dLCpFH5l6E-oetE>w zKR(elSScg_ZtbfDjc#~vpE`&x!Bq<(IH(y4JXf>r7`%+KfWYcSZGs2w#19}wlf*yD zu~$lu@G|nlR=$9-h>lxg1qiInJahb^!^0Oob;8E*ddD%CoaPfgk+I_42Kw0N`0Ks* zQ=)*WNzE>}t;|8SV`Pq8uYEm)qY+EjnyUhRgP)Ayb`2a7VDnL8Zsb<+8P$)9!H-o4 z34dugcnh7&ta)@FR^;F@AH~1RQT@cutJL!qyhzJWCQVHzGUo2MC2~g|#J?Zf|Mko5 zA{yOL@$dQ$zi>7##I6=2j^Ww$FdY8#RBy5rRT#x#2*NR^HV^rSA3IYx(vv@yK(zmE z7oeyYNzkbZ3&Bzdk#TfiN%{K{fyl?nxXbPqd`TP}QLDkqk-#%!?bdlJwwGy`n9Ess zY+DugS#GJBJv4;r)?Yp%x#()3)ufzGeCw=Bm`iw^Vr~vi)h^bLMC{i%8zgz3QPN62 zqH{caizI`KxTozy1@>rNyETJ3YMYlk%JsI!6@tBO*wLfn^qDu8_k0-|*WNR2tS2_^ zfA7@_AjpaKHn^+&YG$$*zIXXuFbyTv_DU znB+J{`4f$1Zek*P+dBNA#U7#?=ayj~js$}DuEYi)ZmZHbK%&DMEE3V)c6MI1MLEx3 zT+fOXe*?_*J1$u+FDpBOU*Ug7eh4Pj&l^^C1&PGWl=T^eDv&*_4?YmgQ*~8MQ$&v5 zd-XPJ1qy*c$RzfT%2~TA7>~D9X!6=1Cdc2rXwk7a^BK%!ZCW`-#w+!34l+p7Z_;wD zwJJ_@_##LO3OBfW?>+{F%g4;eCe931n7nm}L}X=Mo=SS3^=^|S#^^%OZWWEaf8PLZ znwl*XUoQ`Yq98(uaa~rXzDWE^Kmq!a&yv2K;y9H>KaGQv^9-NK$&+XH&VOo68gnOs zHp|z=X)nJ!(cLY|JBXI>Jfa0#nWcZ|U*q3Te38sD{lc9F=zi-8*?^Q9)R9X!iFvii zCxvywUPJZu^}z5@M|-a1r_(iZ5dQ@2OPADvBTsU|Id+ZdEiLjfeHO&(@GyFSj{`E2 z9RJ9nD!Ox+v%GQ#&nFYIuW>Aku$IaI zG_c|6oJan-xl$B0F3F9ED7d+XPPRpyJ)p8n7td4Z065xr>X3!_5TaGD?=k zrg|cQwjaCKH1jhFx=sBA`$OFB{QBOD25S?p#kI$dlm=IfHJpmghM<_4uZoH~J{O18 zB*?sijk*nhz^Il;i}_l7R8-gIh2UUKzb|2C9{a>Aws)}~LhisRST5jqK<2wD&Lbjn zqR74e8LJ5^E6$`YPWsY}>=i!rC0*M#k>0kZCUdnnK%Po0!7!FCukNjEKhw$OX|x1R zHXBS|=bvzNaYdDZ4dhgvCsF|nvq23YV ze5)^}%fGCw>>91brin_;J8YxW?@AkSj;I!aLm|6&Kkn%fY||6SzL9|PK_C#OlY=)0 zS2s51@8)9DA?D#vo*uU@l^5`RS=tVXX4%G2#8{36jwGhai{RZVmL;l{dC1i)Fmdr- zRbVAEmL7BY@`FKM(^lTHb2t$pbr{S}#;QQ!iXqUrK50-#y#5zfK+O4dX>!bFTxGdb zlTri>d{HI=Cex#2CY0iE+NdnSI0OD_?j*e2~U#$ z-aY*M+8y&>3OU9Avy89H!QuEHug;Hr&3)YD7j||F*RU=~N2hu!wWj?+_(^=+ zA=SdQ^)A)+#6iVzh*czTUcHIZWnD0-A}-$SLZ3e}eR5{`_VO&IQAX#7Vs(`17HG!x zOp@6HT2llf@l0s;ncapplE`O|k(o0@2>-d6tQH-d_F^=elm)NOH@ zvFuIsIekpf>$q2S{>b$;qu6E7U2{e z92}G(_y)Qg)>)&LFR%65j9mo%$Z{~M#i~vuvL`)pYRYp@*tvy?^nX)T&TgdcRHm8u z75(Cpi;IlX0x$PR^#DsMR1h|*k3!8y6+-Ue17!a3HXd*-bA(sV9aMT^JepYW1`&XR0lG!T}>ZvERJVS|cVU_nX9u7KucXJ+0&KPY%|SaO^B7 zIMfAGb4WVwJe}ai#jD%4;DXS4t^ER%u%Y7^3St<{w%IU&a$uq#GDsvbx>BkhIqCE@ zs#ES&SU33cHR6ZNaFm#pAbIQ7CHzNfLxbn1&c z(Du0Tp^e-Aw`Sd8fMzq+IXSz1F|?%I@AOpT=j%(7q)ae)bIpP0<04Cb2%qP}#}cNA z8);LX?Kn?m!SOe0Xs`lFZv2rzzD~pgR4`i0+S)o@KW*}J+PFcBAjdBa0p%ewG%wHW z)d6m^599YX4076wV7A!C<4An2R;7nd95DQ3pL{@~u!v2P)DL;o{${5W06iVB@5Cwhvr^M@ll(bC!|@*WGg5ogYq{fD~t%DHhYA4d7A>RoiGNxK{|qnWQL12)yIzxdzEfq8lkIt8{F4nGEkC8zz)M#)?wW)D;EV}$LOZAN)5~+;_v}f^ zspsTeycGkY?I*o&I%;mOG@fvnxifs*T^M)u_>d(%=M@f>wp6^cL-%4qwjbsEG1{zK z1R4AXuiH$|xqx`k6>C#fM|Py(EzImes$F5-{S}djd%rSa*S*_(JI?;HE=IWU0II&# zKB4f+9T5y)19gYpp5@rr?JL9iV79F&JBf5)N5R|iK|B2@Pe`oA{x%rx4|s`E7Zie4 zpX50n;|??m@(o^g3E$iS{OgNdX-wGE$>0bTtsQdnj#t^z=M>^<`+@^4mR(mDrbsx29Nd0Zj+9BB}o{vzCY_AeO-J7nO#n}y-J79ksg)%fGHvANbks}!b z?lb}!sMM+Hb7!8Ct-uO_fQ35`NRb6zZ;DB^~8o!gP_+)ona?Wy8y6D*C3gug@Fl&)^*d zKP6nH0nw(;6Be1|etrN1<|;0hXi&&Xl&FD*SLnQbzn9H9;!WrFGGyhj;D0HYG-T2> zUD9~^aJx2O@ZBSNfuEzo3Zl|ya|1TyasBdfg^wD<_)l>?Z2yOccYt{$NQJHJDA8;h zV$I1u1ixe5?-sDHqTT-ZSBh7IZwCP=jDCm2-)}6jfU2W9YVvmg4EwTV*R#&@p|ZU? zl>;BWc%5z3H|90PZ14CPA-4Bp0#d{VT8gn4^LHi;I}$=#GQ` zVIV{mP=OB{>KpTpC)*oVNgMb-FuFi#lq`pFOzwn`@8WsEY_&(XaQ_CH5l8Y2Jz literal 0 HcmV?d00001 diff --git a/screenshots/example_app.png b/screenshots/example_app.png new file mode 100644 index 0000000000000000000000000000000000000000..588725e512b1bba18cfa9b395c7a62849ba97e56 GIT binary patch literal 259584 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

R;j~&EN+FDr&JLRG$rI?a4)WNckj2kkp&?4N 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 literal 0 HcmV?d00001 From 367ae2704d1106ad772326b9e51eae1d074c373b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Sat, 11 Jan 2025 01:17:14 -0600 Subject: [PATCH 088/141] refactor(framework, hooks, test): Rename `DispatchEffect` to `AutoDispatchEffect` and add `initHook` method. --- .../reactter/lib/src/core/binding_zone.dart | 12 +-- .../reactter/lib/src/framework/rt_hook.dart | 38 +++++++-- .../reactter/lib/src/hooks/use_effect.dart | 8 +- packages/reactter/pubspec.lock | 79 ++++++++++++------- packages/reactter/pubspec.yaml | 2 +- .../reactter/test/hooks/use_effect_test.dart | 9 ++- 6 files changed, 100 insertions(+), 48 deletions(-) diff --git a/packages/reactter/lib/src/core/binding_zone.dart b/packages/reactter/lib/src/core/binding_zone.dart index 0e5b62ff..25935a79 100644 --- a/packages/reactter/lib/src/core/binding_zone.dart +++ b/packages/reactter/lib/src/core/binding_zone.dart @@ -12,7 +12,7 @@ class BindingZone { final _parentZone = _currentZone; /// It's used to store a collection of [IState]. - final states = {}; + final _states = {}; /// Returns the current [BindingZone]. static BindingZone? get currentZone => _currentZone; @@ -48,26 +48,26 @@ class BindingZone { final instanceToBind = boundInstance ?? instance; - zone.bindInstanceToStates(instanceToBind); + zone._bindInstanceToStates(instanceToBind); return instance; } /// Stores the state given from parameter. static void recollectState(IState state) { - _currentZone?.states.add(state); + _currentZone?._states.add(state); } /// Attaches the instance to the stored states([IState]), and if the instance is null, /// it adds the stored states to the [BindingZone] parent. - E bindInstanceToStates(E instance) { + E _bindInstanceToStates(E instance) { try { if (instance == null) { - _parentZone?.states.addAll(states); + _parentZone?._states.addAll(_states); return instance; } - for (final state in states.toList(growable: false)) { + for (final state in _states.toList(growable: false)) { if (state != instance && state.boundInstance == null) { state.bind(instance); } diff --git a/packages/reactter/lib/src/framework/rt_hook.dart b/packages/reactter/lib/src/framework/rt_hook.dart index 1942b4d4..e4a8e4ef 100644 --- a/packages/reactter/lib/src/framework/rt_hook.dart +++ b/packages/reactter/lib/src/framework/rt_hook.dart @@ -19,20 +19,20 @@ part of '../internals.dart'; /// void toggle() => _state.value = !_state.value; /// } /// ``` -/// > **IMPORTANT**: All [RtHook] must be registered using the final [$] variable.: +/// > **RECOMMENDED**: All [RtHook] must be registered using the final [$] variable.: /// /// and use it, like so: /// /// >```dart /// > class AppController { -/// > final state = UseToggle(false); +/// > final uToggle = UseToggle(false); /// > /// > UserContext() { -/// > print('initial value: ${state.value}'); +/// > print('initial value: ${uToggle.value}'); /// > -/// > state.toggle(); +/// > uToggle.toggle(); /// > -/// > print('toggle value: ${state.value}'); +/// > print('toggle value: ${uToggle.value}'); /// > } /// > } /// > ``` @@ -61,7 +61,33 @@ abstract class RtHook /// This constructor calls the `end` method of the [BindingHookZone] instance /// to register the hook and attach the collected states. RtHook() { - $.bindInstanceToStates(this); + initHook(); + } + + /// Initializes the hook. + /// This method is called when the hook is created. + /// You can override this method to ensure that the hook is properly + /// initialized and to bind the instance to the states. + /// This is particularly useful for setting up state dependencies or + /// performing side effects when the hook is first created. + /// + /// For example, you can use the [UseEffect] hook to execute a side effect when a state changes: + /// + /// ```dart + /// @override + /// void initHook() { + /// UseEffect( + /// () { + /// print("Executed by state changed"); + /// }, + /// [state], + /// ); + /// + /// super.initHook(); + /// } + @mustCallSuper + void initHook() { + $._bindInstanceToStates(this); } @override diff --git a/packages/reactter/lib/src/hooks/use_effect.dart b/packages/reactter/lib/src/hooks/use_effect.dart index b4cacfc4..e83f17af 100644 --- a/packages/reactter/lib/src/hooks/use_effect.dart +++ b/packages/reactter/lib/src/hooks/use_effect.dart @@ -71,11 +71,11 @@ part of 'hooks.dart'; /// ); /// ``` /// -/// You can also use the [DispatchEffect] mixin to execute the effect +/// You can also use the [AutoDispatchEffect] mixin to execute the effect /// on initialization: /// /// ```dart -/// class AppController with DispatchEffect { +/// class AppController with AutoDispatchEffect { /// AppController() { /// UseEffect( /// () { @@ -186,7 +186,7 @@ class UseEffect extends RtHook { _watchInstanceAttached(); - if (!_isDispatched && boundInstance is DispatchEffect) { + if (!_isDispatched && boundInstance is AutoDispatchEffect) { _runCleanupAndUnwatchDependencies(boundInstance); _runCallbackAndWatchDependencies(boundInstance); return; @@ -314,4 +314,4 @@ class UseEffect extends RtHook { } } -abstract class DispatchEffect {} +abstract class AutoDispatchEffect {} diff --git a/packages/reactter/pubspec.lock b/packages/reactter/pubspec.lock index eab3a9df..24dadc12 100644 --- a/packages/reactter/pubspec.lock +++ b/packages/reactter/pubspec.lock @@ -61,10 +61,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.18.0" convert: dependency: transitive description: @@ -77,10 +77,10 @@ packages: dependency: transitive description: name: coverage - sha256: "595a29b55ce82d53398e1bcc2cba525d7bd7c59faeb2d2540e9d42c390cfeeeb" + sha256: e3493833ea012784c740e341952298f1cc77f1f01b1bbc3eb4eecf6984fb7f43 url: "https://pub.dev" source: hosted - version: "1.6.4" + version: "1.11.1" crypto: dependency: transitive description: @@ -163,6 +163,30 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + url: "https://pub.dev" + source: hosted + version: "10.0.5" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + url: "https://pub.dev" + source: hosted + version: "3.0.5" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" lints: dependency: "direct dev" description: @@ -183,26 +207,26 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.11.1" meta: dependency: "direct main" description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.15.0" mime: dependency: transitive description: @@ -231,10 +255,10 @@ packages: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" pool: dependency: transitive description: @@ -308,26 +332,26 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -348,26 +372,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4" + sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e" url: "https://pub.dev" source: hosted - version: "1.24.1" + version: "1.25.7" test_api: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.7.2" test_core: dependency: transitive description: name: test_core - sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93" + sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.4" typed_data: dependency: transitive description: @@ -388,10 +412,10 @@ packages: dependency: transitive description: name: vm_service - sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "11.10.0" + version: "14.2.5" watcher: dependency: transitive description: @@ -425,4 +449,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/packages/reactter/pubspec.yaml b/packages/reactter/pubspec.yaml index 9caeebe5..7aaa5321 100644 --- a/packages/reactter/pubspec.yaml +++ b/packages/reactter/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://2devs-team.github.io/reactter/ documentation: https://2devs-team.github.io/reactter/ repository: https://github.com/2devs-team/reactter/tree/master/packages/reactter license: MIT License -version: 8.0.0-dev.15 +version: 8.0.0-dev.17 topics: - reactive diff --git a/packages/reactter/test/hooks/use_effect_test.dart b/packages/reactter/test/hooks/use_effect_test.dart index 4a05b73c..c5f51e81 100644 --- a/packages/reactter/test/hooks/use_effect_test.dart +++ b/packages/reactter/test/hooks/use_effect_test.dart @@ -128,7 +128,8 @@ void main() { Rt.delete(); }); - test("should be called with DispatchEffect when it initialized later", () { + test("should be called with AutoDispatchEffect when it initialized later", + () { final testController = UseEffectDispatchController(); int nCalls = 0; @@ -146,7 +147,7 @@ void main() { expect(nCalls, 0); expect(uEffect, isA()); - expect(uEffect.boundInstance, isA()); + expect(uEffect.boundInstance, isA()); expect(nCalls, 1); }); @@ -253,7 +254,7 @@ void main() { Rt.delete(); }); - test("should be called with dispatchEffect", () { + test("should be called with autodispatchEffect", () { final testController = TestController(); final stateA = testController.stateBool; final stateB = testController.stateInt; @@ -389,7 +390,7 @@ void main() { }); } -class UseEffectDispatchController extends DispatchEffect {} +class UseEffectDispatchController extends AutoDispatchEffect {} class UseEffectTestController extends TestController { final testControllerInner = TestController(); From 6b9c1956f5a7f7cedb1a90cdef0b1a94f5759143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Sat, 11 Jan 2025 01:36:15 -0600 Subject: [PATCH 089/141] refactor(example): Update dependencies and improve code quality in example files --- .../3_shopping_cart/views/cart_view.dart | 2 + .../4_github_search/github_search_page.dart | 2 + .../lib/examples/5_todo/todo_page.dart | 6 +- .../controllers/animation_controller.dart | 3 + .../7_animation/hooks/use_animation.dart | 141 +++++++++--------- .../flutter_reactter/example/lib/main.dart | 37 +++-- .../flutter_reactter/example/pubspec.lock | 22 +-- .../flutter_reactter/example/pubspec.yaml | 4 +- 8 files changed, 116 insertions(+), 101 deletions(-) diff --git a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/views/cart_view.dart b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/views/cart_view.dart index 958ac995..0b37617a 100644 --- a/packages/flutter_reactter/example/lib/examples/3_shopping_cart/views/cart_view.dart +++ b/packages/flutter_reactter/example/lib/examples/3_shopping_cart/views/cart_view.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_renaming_method_parameters + import 'package:examples/examples/3_shopping_cart/controllers/cart_controller.dart'; import 'package:examples/examples/3_shopping_cart/utils/format_currency.dart'; import 'package:examples/examples/3_shopping_cart/widgets/cart_item_card.dart'; diff --git a/packages/flutter_reactter/example/lib/examples/4_github_search/github_search_page.dart b/packages/flutter_reactter/example/lib/examples/4_github_search/github_search_page.dart index 46802038..bbf42015 100644 --- a/packages/flutter_reactter/example/lib/examples/4_github_search/github_search_page.dart +++ b/packages/flutter_reactter/example/lib/examples/4_github_search/github_search_page.dart @@ -1,3 +1,5 @@ +// ignore_for_file: undefined_hidden_name + import 'package:examples/examples/4_github_search/widgets/info_card.dart'; import 'package:flutter/material.dart' hide SearchBar; import 'package:flutter_reactter/flutter_reactter.dart'; diff --git a/packages/flutter_reactter/example/lib/examples/5_todo/todo_page.dart b/packages/flutter_reactter/example/lib/examples/5_todo/todo_page.dart index eff1526f..3e719ef1 100644 --- a/packages/flutter_reactter/example/lib/examples/5_todo/todo_page.dart +++ b/packages/flutter_reactter/example/lib/examples/5_todo/todo_page.dart @@ -39,12 +39,12 @@ class TodoPage extends StatelessWidget { ), ), ), - body: const Padding( - padding: EdgeInsets.all(8), + body: Padding( + padding: const EdgeInsets.all(8), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, - children: [ + children: const [ TodoFilter(), SizedBox(height: 4), Expanded( diff --git a/packages/flutter_reactter/example/lib/examples/7_animation/controllers/animation_controller.dart b/packages/flutter_reactter/example/lib/examples/7_animation/controllers/animation_controller.dart index e69109e8..1785b777 100644 --- a/packages/flutter_reactter/example/lib/examples/7_animation/controllers/animation_controller.dart +++ b/packages/flutter_reactter/example/lib/examples/7_animation/controllers/animation_controller.dart @@ -11,6 +11,7 @@ class AnimationController { control: AnimationControl.mirror, curve: Curves.easeOut, ), + debugLabel: 'uSizeAnimation', ); final uBorderRadiusAnimation = UseAnimation( @@ -23,6 +24,7 @@ class AnimationController { control: AnimationControl.mirror, curve: Curves.easeOut, ), + debugLabel: 'uBorderRadiusAnimation', ); final uColorAnimation = UseAnimation( @@ -63,6 +65,7 @@ class AnimationController { control: AnimationControl.mirror, curve: Curves.easeInOut, ), + debugLabel: 'uColorAnimation', ); void resumeAllAnimation() { diff --git a/packages/flutter_reactter/example/lib/examples/7_animation/hooks/use_animation.dart b/packages/flutter_reactter/example/lib/examples/7_animation/hooks/use_animation.dart index f989e7ff..0427a581 100644 --- a/packages/flutter_reactter/example/lib/examples/7_animation/hooks/use_animation.dart +++ b/packages/flutter_reactter/example/lib/examples/7_animation/hooks/use_animation.dart @@ -4,87 +4,26 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; -class UseX extends RtHook { - @override - final $ = RtHook.$register; - - late final uCounter = Rt.lazyState(() => UseState(0), this); -} - -/// Defines different control options for playing an animation. Each option -/// represents a specific behavior for the animation playback: -enum AnimationControl { - /// Plays the animation from the current position to the end. - play, - - /// Plays the animation from the current position reverse to the start. - playReverse, - - /// Reset the position of the animation to `0.0` and starts playing - /// to the end. - playFromStart, - - /// Reset the position of the animation to `1.0` and starts playing - /// reverse to the start. - playReverseFromEnd, - - /// Endlessly plays the animation from the start to the end. - loop, - - /// Endlessly plays the animation from the start to the end, then - /// it plays reverse to the start, then forward again and so on. - mirror, - - /// Stops the animation at the current position. - pause, - - /// Stops and resets animation. - stop, -} - -/// The `AnimationOptions` class represents the options for an animation, -/// including the tween, duration, control, curve, start position, -/// frames per second, delay, and animation status listener. -class AnimationOptions { - final Animatable tween; - final Duration duration; - final AnimationControl control; - final Curve curve; - final double startPosition; - final int? fps; - final Duration delay; - final AnimationStatusListener? animationStatusListener; - - const AnimationOptions({ - required this.tween, - this.control = AnimationControl.play, - this.duration = const Duration(seconds: 1), - this.curve = Curves.linear, - this.startPosition = 0.0, - this.fps, - this.delay = Duration.zero, - this.animationStatusListener, - }); -} - -class UseAnimation extends RtHook implements TickerProvider { +class UseAnimation extends RtHook + with AutoDispatchEffect + implements TickerProvider { @override final $ = RtHook.$register; late final uTween = Rt.lazyState( - () => UseState(options.tween), + () => UseState(options.tween, debugLabel: 'uTween'), this, ); late final uControl = Rt.lazyState( - () => UseState(options.control), + () => UseState(options.control, debugLabel: 'uControl'), this, ); late final uDuration = Rt.lazyState( - () => UseState(options.duration), + () => UseState(options.duration, debugLabel: 'uDuration'), this, ); late final uCurve = Rt.lazyState( - () => UseState(options.curve), + () => UseState(options.curve, debugLabel: 'uCurve'), this, ); @@ -99,6 +38,9 @@ class UseAnimation extends RtHook implements TickerProvider { T get value => _animation.value; + @override + final String? debugLabel; + @override Map get debugInfo => { 'value': value, @@ -109,13 +51,18 @@ class UseAnimation extends RtHook implements TickerProvider { final AnimationOptions options; - UseAnimation(this.options) { + UseAnimation(this.options, {this.debugLabel}); + + @override + void initHook() { controller.addStatusListener(_onAnimationStatus); _buildAnimation(); UseEffect(_addFrameLimitingUpdater, []); UseEffect(_rebuild, [uTween, uControl, uCurve]); UseEffect(() => controller.duration = uDuration.value, [uDuration]); + + super.initHook(); } @override @@ -263,6 +210,62 @@ class UseAnimation extends RtHook implements TickerProvider { } } +/// Defines different control options for playing an animation. Each option +/// represents a specific behavior for the animation playback: +enum AnimationControl { + /// Plays the animation from the current position to the end. + play, + + /// Plays the animation from the current position reverse to the start. + playReverse, + + /// Reset the position of the animation to `0.0` and starts playing + /// to the end. + playFromStart, + + /// Reset the position of the animation to `1.0` and starts playing + /// reverse to the start. + playReverseFromEnd, + + /// Endlessly plays the animation from the start to the end. + loop, + + /// Endlessly plays the animation from the start to the end, then + /// it plays reverse to the start, then forward again and so on. + mirror, + + /// Stops the animation at the current position. + pause, + + /// Stops and resets animation. + stop, +} + +/// The `AnimationOptions` class represents the options for an animation, +/// including the tween, duration, control, curve, start position, +/// frames per second, delay, and animation status listener. +class AnimationOptions { + final Animatable tween; + final Duration duration; + final AnimationControl control; + final Curve curve; + final double startPosition; + final int? fps; + final Duration delay; + final AnimationStatusListener? animationStatusListener; + + const AnimationOptions({ + required this.tween, + this.control = AnimationControl.play, + this.duration = const Duration(seconds: 1), + this.curve = Curves.linear, + this.startPosition = 0.0, + this.fps, + this.delay = Duration.zero, + this.animationStatusListener, + }); +} + /// Provides additional methods for playing, reversing, looping, and mirroring animations. class _UseAnimationController extends AnimationController { bool _skipStopMirror = false; diff --git a/packages/flutter_reactter/example/lib/main.dart b/packages/flutter_reactter/example/lib/main.dart index 4d3e1638..b3b7ea50 100644 --- a/packages/flutter_reactter/example/lib/main.dart +++ b/packages/flutter_reactter/example/lib/main.dart @@ -1,8 +1,8 @@ -/// This file contains the main entry point of the example app. -/// It also contains a small example using a global state for the theme mode. -/// The main app widget contains a list of examples that can be navigated to. -/// Each example has its own page that demonstrates the use of the Reactter package. -/// For viewing the examples online, you can visit https://zapp.run/pub/flutter_reactter +// This file contains the main entry point of the example app. +// It also contains a small example using a global state for the theme mode. +// The main app widget contains a list of examples that can be navigated to. +// Each example has its own page that demonstrates the use of the Reactter package. +// For viewing the examples online, you can visit https://zapp.run/pub/flutter_reactter import 'package:examples/custom_list.dart'; import 'package:examples/title_oute_observer.dart'; @@ -11,7 +11,7 @@ import 'package:examples/page_404.dart'; import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; -/// Global state for theme mode +// Global state for theme mode final uThemeMode = UseState(ThemeMode.dark); class IndexPage extends StatelessWidget { @@ -29,9 +29,9 @@ class IndexPage extends StatelessWidget { IconButton( tooltip: "Change theme mode", icon: RtWatcher((context, watch) { - /// Get and watch the global state - /// This will rebuild the icon button when the global state changes - /// and update the icon to reflect the current theme mode + // Get and watch the global state + // This will rebuild the icon button when the global state changes + // and update the icon to reflect the current theme mode final themeMode = watch(uThemeMode).value; return Icon( @@ -56,14 +56,21 @@ class IndexPage extends StatelessWidget { } } +const indexRoute = '/'; +final indexPageKey = GlobalKey(); +final routes = { + indexRoute: (context) => IndexPage(key: indexPageKey), + for (final e in examples) e.routeName: (context) => e.renderPage(), +}; + class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return RtWatcher((context, watch) { - /// Get and watch the global state - /// This will rebuild the app when the global state changes + // Get and watch the global state + // This will rebuild the app when the global state changes final themeMode = watch(uThemeMode).value; return MaterialApp( @@ -76,11 +83,8 @@ class MyApp extends StatelessWidget { darkTheme: ThemeData.dark().copyWith( materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - initialRoute: '/', - routes: { - '/': (context) => const IndexPage(), - for (final e in examples) e.routeName: (context) => e.renderPage(), - }, + initialRoute: indexRoute, + routes: routes, navigatorObservers: [TitleRouteObserver()], onUnknownRoute: (RouteSettings settings) { return MaterialPageRoute( @@ -93,5 +97,6 @@ class MyApp extends StatelessWidget { } void main() { + Rt.initializeDevTools(); runApp(const MyApp()); } diff --git a/packages/flutter_reactter/example/pubspec.lock b/packages/flutter_reactter/example/pubspec.lock index cbcef412..7a5683cd 100644 --- a/packages/flutter_reactter/example/pubspec.lock +++ b/packages/flutter_reactter/example/pubspec.lock @@ -117,26 +117,26 @@ packages: dependency: "direct dev" description: name: custom_lint - sha256: "22bd87a362f433ba6aae127a7bac2838645270737f3721b180916d7c5946cb5d" + sha256: b09a939c3bb062fdf072aa4c8eb5447231338854c4fefb23e4c9f7cef41cb3ff url: "https://pub.dev" source: hosted - version: "0.5.11" + version: "0.5.0" custom_lint_builder: dependency: transitive description: name: custom_lint_builder - sha256: "0d48e002438950f9582e574ef806b2bea5719d8d14c0f9f754fbad729bcf3b19" + sha256: f1430048ccddcd82cbf7722fce7701530fd535b82e91f45b0c81ed07814143bf url: "https://pub.dev" source: hosted - version: "0.5.14" + version: "0.5.0" custom_lint_core: dependency: transitive description: name: custom_lint_core - sha256: "2952837953022de610dacb464f045594854ced6506ac7f76af28d4a6490e189b" + sha256: a39e98cba1d96076e2e5c96aad582a776909c78b4d7480d0efdcc3791b225c1b url: "https://pub.dev" source: hosted - version: "0.5.14" + version: "0.5.0" dart_style: dependency: transitive description: @@ -170,10 +170,10 @@ packages: dependency: "direct main" description: name: flutter_reactter - sha256: "24f56d3e520d978e20fb39e20fcca9365a4eb16eaa0eaedf6827855f996c2814" + sha256: "1dbc4e1ee3ff45a0c6adcde6b98bca43a3c653564b52172de2213a1326e44fda" url: "https://pub.dev" source: hosted - version: "8.0.0-dev.14" + version: "8.0.0-dev.16" flutter_web_plugins: dependency: transitive description: flutter @@ -327,10 +327,10 @@ packages: dependency: transitive description: name: reactter - sha256: c2a9f823b1570f42842aed54f4765dc86b294ce7ea8e6004c3772de1354feca2 + sha256: "69fc36ffc793fcb17bbea5fc498f7de72210694fbafea011ff3aca2158b1792e" url: "https://pub.dev" source: hosted - version: "8.0.0-dev.13" + version: "8.0.0-dev.15" reactter_lint: dependency: "direct dev" description: @@ -521,5 +521,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.0.0-0 <4.0.0" flutter: ">=3.7.0" diff --git a/packages/flutter_reactter/example/pubspec.yaml b/packages/flutter_reactter/example/pubspec.yaml index 1d218903..2931503b 100644 --- a/packages/flutter_reactter/example/pubspec.yaml +++ b/packages/flutter_reactter/example/pubspec.yaml @@ -12,10 +12,10 @@ dependencies: http: ^0.13.6 url_launcher: ^6.1.2 intl: ^0.17.0 - flutter_reactter: ^8.0.0-dev.15 + flutter_reactter: ^8.0.0-dev.16 dev_dependencies: - custom_lint: ^0.5.11 + custom_lint: ^0.5.0 flutter_lints: ^2.0.3 reactter_lint: ^1.0.0 From 5a6f50596def1aef7ab3ecdea07eac63eb446a78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Sat, 11 Jan 2025 02:35:13 -0600 Subject: [PATCH 090/141] refactor(example, devtools): Update pubspec versions and improve README formatting --- .gitignore | 4 +- CHANGELOG.md | 100 ++-- README.md | 12 +- .../flutter_reactter/example/lib/main.dart | 1 - .../flutter_reactter/example/pubspec.lock | 525 ------------------ .../flutter_reactter/example/pubspec.yaml | 2 +- packages/flutter_reactter/pubspec.lock | 189 ------- packages/flutter_reactter/pubspec.yaml | 11 +- packages/reactter/pubspec.lock | 453 --------------- .../lib/src/widgets/tile_builder.dart | 1 + .../reactter_devtools_extension/pubspec.lock | 445 --------------- .../reactter_devtools_extension/pubspec.yaml | 2 +- packages/reactter_lint/pubspec.lock | 349 ------------ 13 files changed, 71 insertions(+), 2023 deletions(-) delete mode 100644 packages/flutter_reactter/example/pubspec.lock delete mode 100644 packages/flutter_reactter/pubspec.lock delete mode 100644 packages/reactter/pubspec.lock delete mode 100644 packages/reactter_devtools_extension/pubspec.lock delete mode 100644 packages/reactter_lint/pubspec.lock diff --git a/.gitignore b/.gitignore index a715b98c..50b445e3 100644 --- a/.gitignore +++ b/.gitignore @@ -77,4 +77,6 @@ build/ # Flutter coverage **/lcov.info -**/coverage \ No newline at end of file +**/coverage + +**/pubspec.lock \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 12bee5bd..0a7a6349 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,68 +1,70 @@ # Reactter -## 8.0.0-dev.15 +## 8.0.0-dev.18 ### Enhancements -- Add Reactter devtools. -- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtInterface/removeObserver.html) methods to manage the observers. -- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). -- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). -- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. -- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. -- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtStateBase/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtStateBase/debugInfo.html) to states and hooks to get info for debugging. -- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. -- Add [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtStateBase-class.html) abstract class to implement the base logic of the state. -- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtInterface/createState.html) method to create a new state. -- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. -- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. -- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. +- Add Reactter devtools extension. +- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtInterface/removeObserver.html) methods to manage the observers. +- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). +- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). +- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. +- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. +- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtStateBase/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtStateBase/debugInfo.html) to states and hooks to get info for debugging. +- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. +- Add [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtStateBase-class.html) abstract class to implement the base logic of the state. +- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtInterface/createState.html) method to create a new state. +- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. +- Add [`initHook`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtHook/initHook.html) method to `RtHook` to call when hook is created. +- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. +- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. - Enhance event handling and notifier logic for improved stability and performance. - Enhance state management and error handling. -- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.15/flutter_reactter/RtComponent-class.html). +- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.18/flutter_reactter/RtComponent-class.html). - Enhance dependency management to ensure to re-build when is detected changes while building. ### Breakings -- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/Rt.html) instead. -- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtStateBase-class.html) instead. +- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/Rt.html) instead. +- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtStateBase-class.html) instead. - Replace [`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html) to `UseAsyncStateStatus.idle`. -- Move `asyncFunction` to the first argument of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/UseAsyncState/withArg.html). -- Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. -- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtInterface/isActive.html) instead. -- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtInterface/getDependencyMode.html) instead. -- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/DependencyMode.html) instead. -- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/Lifecycle.html) instead. -- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/Lifecycle.html) instead. -- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`LifecycleObserver.onCreated`](https://pub.dev/documentation/reactter/8.0.0-dev.15//reactter/LifecycleObserver/onCreated.html) instead. -- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.15//reactter/RtDependency-class.html) instead. -- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0-dev.15//reactter/RtHook-class.html) instead. -- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0-dev.15//reactter/RtInterface-class.html) instead. -- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtState/notify.html) instead. -- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/UseDependency-class.html) instead. -- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtAction-class.html) instead. -- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtActionCallable-class.html) instead. -- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MemoMultiInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/MemoMultiInterceptor-class.html) instead. -- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/MemoSafeAsyncInterceptor-class.html) instead. -- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/MemoTemporaryCacheInterceptor-class.html) instead. -- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/MemoWrapperInterceptor-class.html) instead. +- Rename [`DispatchEffect`](https://pub.dev/documentation/reactter/7.3.0/reactter/DispatchEffect-class.html) to [`AutoDispatchEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/DispatchEffect-class.html). +- Move `asyncFunction` to the first argument of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/UseAsyncState/withArg.html). +- Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. +- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtInterface/isActive.html) instead. +- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtInterface/getDependencyMode.html) instead. +- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/DependencyMode.html) instead. +- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/Lifecycle.html) instead. +- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/Lifecycle.html) instead. +- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`LifecycleObserver.onCreated`](https://pub.dev/documentation/reactter/8.0.0-dev.18//reactter/LifecycleObserver/onCreated.html) instead. +- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.18//reactter/RtDependency-class.html) instead. +- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0-dev.18//reactter/RtHook-class.html) instead. +- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0-dev.18//reactter/RtInterface-class.html) instead. +- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtState/notify.html) instead. +- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/UseDependency-class.html) instead. +- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtAction-class.html) instead. +- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtActionCallable-class.html) instead. +- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MemoMultiInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/MemoMultiInterceptor-class.html) instead. +- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/MemoSafeAsyncInterceptor-class.html) instead. +- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/MemoTemporaryCacheInterceptor-class.html) instead. +- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/MemoWrapperInterceptor-class.html) instead. - Remove [`Obj`](https://pub.dev/documentation/reactter/7.3.0/reactter/Obj-class.html). -- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.15/flutter_reactter/RtProvider/RtProvider.init.html) instead. -- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.15/flutter_reactter/RtComponent-class.html) instead. -- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.15/flutter_reactter/RtConsumer-class.html) instead. -- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.15/flutter_reactter/RtProvider-class.html) instead. -- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.15/flutter_reactter/RtMultiProvider-class.html) instead. -- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.15/flutter_reactter/RtScope-class.html) instead. -- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.15/flutter_reactter/RtSignalWatcher-class.html) instead. -- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.15/flutter_reactter/RtDependencyNotFoundException-class.html) instead. -- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.15/flutter_reactter/RtScopeNotFoundException-class.html) instead. +- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.18/flutter_reactter/RtProvider/RtProvider.init.html) instead. +- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.18/flutter_reactter/RtComponent-class.html) instead. +- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.18/flutter_reactter/RtConsumer-class.html) instead. +- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.18/flutter_reactter/RtProvider-class.html) instead. +- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.18/flutter_reactter/RtMultiProvider-class.html) instead. +- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.18/flutter_reactter/RtScope-class.html) instead. +- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.18/flutter_reactter/RtSignalWatcher-class.html) instead. +- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.18/flutter_reactter/RtDependencyNotFoundException-class.html) instead. +- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.18/flutter_reactter/RtScopeNotFoundException-class.html) instead. ## Fixes -- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/UseEffect-class.html) causing dependencies to not be unwatched. -- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtInterface/register.html) method. -- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/RtInterface/batch.html) method to work correctly. -- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0-dev.15/reactter/Notifier-class.html) class. +- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/UseEffect-class.html) causing dependencies to not be unwatched. +- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtInterface/register.html) method. +- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtInterface/batch.html) method to work correctly. +- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/Notifier-class.html) class. - Fix to propagate state param to `boundInstance`. - Remove listeners correctly from single-use listeners and handle null cases. diff --git a/README.md b/README.md index 079e60a7..f688db42 100644 --- a/README.md +++ b/README.md @@ -20,16 +20,16 @@ A lightweight, powerful, and reactive **State Management**, **Dependency Injecti - ⚖️ Super **Lightweight**. - 📏 **Reduce Boilerplate Code** significantly. - ✏️ Improve **Code Readability**. -- 💧 **Flexible** and **Adaptable** to any architecture. -- ☢️ **Reactive States** using [State](https://2devs-team.github.io/reactter/classes/rt_state_base) and [Hooks](https://2devs-team.github.io/reactter/core_concepts/hooks). -- ♻️ **Reusable States and Logic** with [Custom hooks]([#custom-hooks](https://2devs-team.github.io/reactter/core_concepts/hooks/#custom-hook)). -- 🎮 Fully **[Rendering Control]([#rendering-control](https://2devs-team.github.io/reactter/core_concepts/rendering_control))**. +- ☢️ **Reactive States**, using [State](https://2devs-team.github.io/reactter/classes/rt_state_base) and [Hooks](https://2devs-team.github.io/reactter/core_concepts/hooks). +- ♻️ **Highly reusable** states and logic, using [Custom Hooks]([#custom-hooks](https://2devs-team.github.io/reactter/core_concepts/hooks/#custom-hook)) and [Dependency Injection](https://2devs-team.github.io/reactter/core_concepts/dependency_injection/). +- 🎮 Fully **[Rendering Control](https://2devs-team.github.io/reactter/core_concepts/rendering_control)**. - 🧪 Fully **Testable**, 100% code coverage. -- 🔬 Fully **Debuggable** using the **[Reactter DevTools extension](https://2devs-team.github.io/reactter/devtools_extension)** +- 🔬 Fully **Debuggable**, using the **[Reactter DevTools extension](https://2devs-team.github.io/reactter/devtools_extension)** +- 💧 **Not opinionated**. You can use it with any architecture or pattern. - 🪄 **Zero Dependencies**, **Zero Configuration** and **No Code Generation**. - 💙 **Compatible with Dart and Flutter**, supports the latest version of Dart. -**To start using Reactter, check out the full documentation on [The Official Documentation](https://2devs-team.github.io/reactter).** +_To start using Reactter, check out the full documentation on [The Official Documentation](https://2devs-team.github.io/reactter)._ ## Resources diff --git a/packages/flutter_reactter/example/lib/main.dart b/packages/flutter_reactter/example/lib/main.dart index b3b7ea50..e2d51515 100644 --- a/packages/flutter_reactter/example/lib/main.dart +++ b/packages/flutter_reactter/example/lib/main.dart @@ -97,6 +97,5 @@ class MyApp extends StatelessWidget { } void main() { - Rt.initializeDevTools(); runApp(const MyApp()); } diff --git a/packages/flutter_reactter/example/pubspec.lock b/packages/flutter_reactter/example/pubspec.lock deleted file mode 100644 index 7a5683cd..00000000 --- a/packages/flutter_reactter/example/pubspec.lock +++ /dev/null @@ -1,525 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - sha256: "405666cd3cf0ee0a48d21ec67e65406aad2c726d9fa58840d3375e7bdcd32a07" - url: "https://pub.dev" - source: hosted - version: "60.0.0" - analyzer: - dependency: transitive - description: - name: analyzer - sha256: "1952250bd005bacb895a01bf1b4dc00e3ba1c526cf47dca54dfe24979c65f5b3" - url: "https://pub.dev" - source: hosted - version: "5.12.0" - analyzer_plugin: - dependency: transitive - description: - name: analyzer_plugin - sha256: c1d5f167683de03d5ab6c3b53fc9aeefc5d59476e7810ba7bbddff50c6f4392d - url: "https://pub.dev" - source: hosted - version: "0.11.2" - args: - dependency: transitive - description: - name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 - url: "https://pub.dev" - source: hosted - version: "2.4.2" - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - characters: - dependency: transitive - description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - checked_yaml: - dependency: transitive - description: - name: checked_yaml - sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff - url: "https://pub.dev" - source: hosted - version: "2.0.3" - ci: - dependency: transitive - description: - name: ci - sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13" - url: "https://pub.dev" - source: hosted - version: "0.1.0" - cli_util: - dependency: transitive - description: - name: cli_util - sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 - url: "https://pub.dev" - source: hosted - version: "0.4.0" - clock: - dependency: transitive - description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" - source: hosted - version: "1.1.1" - collection: - dependency: transitive - description: - name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" - url: "https://pub.dev" - source: hosted - version: "1.17.1" - convert: - dependency: transitive - description: - name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://pub.dev" - source: hosted - version: "3.1.1" - crypto: - dependency: transitive - description: - name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.dev" - source: hosted - version: "3.0.3" - custom_lint: - dependency: "direct dev" - description: - name: custom_lint - sha256: b09a939c3bb062fdf072aa4c8eb5447231338854c4fefb23e4c9f7cef41cb3ff - url: "https://pub.dev" - source: hosted - version: "0.5.0" - custom_lint_builder: - dependency: transitive - description: - name: custom_lint_builder - sha256: f1430048ccddcd82cbf7722fce7701530fd535b82e91f45b0c81ed07814143bf - url: "https://pub.dev" - source: hosted - version: "0.5.0" - custom_lint_core: - dependency: transitive - description: - name: custom_lint_core - sha256: a39e98cba1d96076e2e5c96aad582a776909c78b4d7480d0efdcc3791b225c1b - url: "https://pub.dev" - source: hosted - version: "0.5.0" - dart_style: - dependency: transitive - description: - name: dart_style - sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - file: - dependency: transitive - description: - name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" - url: "https://pub.dev" - source: hosted - version: "6.1.4" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 - url: "https://pub.dev" - source: hosted - version: "2.0.3" - flutter_reactter: - dependency: "direct main" - description: - name: flutter_reactter - sha256: "1dbc4e1ee3ff45a0c6adcde6b98bca43a3c653564b52172de2213a1326e44fda" - url: "https://pub.dev" - source: hosted - version: "8.0.0-dev.16" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - freezed_annotation: - dependency: transitive - description: - name: freezed_annotation - sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d - url: "https://pub.dev" - source: hosted - version: "2.4.1" - glob: - dependency: transitive - description: - name: glob - sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - hotreloader: - dependency: transitive - description: - name: hotreloader - sha256: "728c0613556c1d153f7e7f4a367cffacc3f5a677d7f6497a1c2b35add4e6dacf" - url: "https://pub.dev" - source: hosted - version: "3.0.6" - http: - dependency: "direct main" - description: - name: http - sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" - url: "https://pub.dev" - source: hosted - version: "0.13.6" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.dev" - source: hosted - version: "4.0.2" - intl: - dependency: "direct main" - description: - name: intl - sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" - url: "https://pub.dev" - source: hosted - version: "0.17.0" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" - json_annotation: - dependency: transitive - description: - name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 - url: "https://pub.dev" - source: hosted - version: "4.8.1" - lints: - dependency: transitive - description: - name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" - url: "https://pub.dev" - source: hosted - version: "2.0.1" - logging: - dependency: transitive - description: - name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" - url: "https://pub.dev" - source: hosted - version: "0.12.16" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 - url: "https://pub.dev" - source: hosted - version: "0.2.0" - meta: - dependency: transitive - description: - name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" - url: "https://pub.dev" - source: hosted - version: "1.9.1" - package_config: - dependency: transitive - description: - name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - path: - dependency: transitive - description: - name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" - url: "https://pub.dev" - source: hosted - version: "1.8.3" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d - url: "https://pub.dev" - source: hosted - version: "2.1.6" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - pubspec_parse: - dependency: transitive - description: - name: pubspec_parse - sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 - url: "https://pub.dev" - source: hosted - version: "1.2.3" - reactter: - dependency: transitive - description: - name: reactter - sha256: "69fc36ffc793fcb17bbea5fc498f7de72210694fbafea011ff3aca2158b1792e" - url: "https://pub.dev" - source: hosted - version: "8.0.0-dev.15" - reactter_lint: - dependency: "direct dev" - description: - name: reactter_lint - sha256: e04dd220eeca470f94a74360344d2c9358c16f87adcccb407f6f19952b819d20 - url: "https://pub.dev" - source: hosted - version: "1.0.0" - rxdart: - dependency: transitive - description: - name: rxdart - sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" - url: "https://pub.dev" - source: hosted - version: "0.27.7" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" - source: hosted - version: "1.10.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" - url: "https://pub.dev" - source: hosted - version: "1.11.1" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://pub.dev" - source: hosted - version: "2.1.2" - stream_transform: - dependency: transitive - description: - name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" - url: "https://pub.dev" - source: hosted - version: "0.6.0" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" - source: hosted - version: "1.3.2" - url_launcher: - dependency: "direct main" - description: - name: url_launcher - sha256: eb1e00ab44303d50dd487aab67ebc575456c146c6af44422f9c13889984c00f3 - url: "https://pub.dev" - source: hosted - version: "6.1.11" - url_launcher_android: - dependency: transitive - description: - name: url_launcher_android - sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" - url: "https://pub.dev" - source: hosted - version: "6.2.0" - url_launcher_ios: - dependency: transitive - description: - name: url_launcher_ios - sha256: "4ac97281cf60e2e8c5cc703b2b28528f9b50c8f7cebc71df6bdf0845f647268a" - url: "https://pub.dev" - source: hosted - version: "6.2.0" - url_launcher_linux: - dependency: transitive - description: - name: url_launcher_linux - sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd" - url: "https://pub.dev" - source: hosted - version: "3.1.0" - url_launcher_macos: - dependency: transitive - description: - name: url_launcher_macos - sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 - url: "https://pub.dev" - source: hosted - version: "3.1.0" - url_launcher_platform_interface: - dependency: transitive - description: - name: url_launcher_platform_interface - sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - url_launcher_web: - dependency: transitive - description: - name: url_launcher_web - sha256: ba140138558fcc3eead51a1c42e92a9fb074a1b1149ed3c73e66035b2ccd94f2 - url: "https://pub.dev" - source: hosted - version: "2.0.19" - url_launcher_windows: - dependency: transitive - description: - name: url_launcher_windows - sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc" - url: "https://pub.dev" - source: hosted - version: "3.1.0" - uuid: - dependency: transitive - description: - name: uuid - sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" - url: "https://pub.dev" - source: hosted - version: "3.0.7" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 - url: "https://pub.dev" - source: hosted - version: "11.10.0" - watcher: - dependency: transitive - description: - name: watcher - sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" - url: "https://pub.dev" - source: hosted - version: "1.0.2" - yaml: - dependency: transitive - description: - name: yaml - sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" - url: "https://pub.dev" - source: hosted - version: "3.1.2" -sdks: - dart: ">=3.0.0-0 <4.0.0" - flutter: ">=3.7.0" diff --git a/packages/flutter_reactter/example/pubspec.yaml b/packages/flutter_reactter/example/pubspec.yaml index 2931503b..dc3872ef 100644 --- a/packages/flutter_reactter/example/pubspec.yaml +++ b/packages/flutter_reactter/example/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: http: ^0.13.6 url_launcher: ^6.1.2 intl: ^0.17.0 - flutter_reactter: ^8.0.0-dev.16 + flutter_reactter: ^8.0.0-dev.18 dev_dependencies: custom_lint: ^0.5.0 diff --git a/packages/flutter_reactter/pubspec.lock b/packages/flutter_reactter/pubspec.lock deleted file mode 100644 index 251f9db1..00000000 --- a/packages/flutter_reactter/pubspec.lock +++ /dev/null @@ -1,189 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - characters: - dependency: transitive - description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - clock: - dependency: transitive - description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" - source: hosted - version: "1.1.1" - collection: - dependency: transitive - description: - name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" - url: "https://pub.dev" - source: hosted - version: "1.17.1" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c - url: "https://pub.dev" - source: hosted - version: "2.0.1" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" - lints: - dependency: transitive - description: - name: lints - sha256: "5cfd6509652ff5e7fe149b6df4859e687fca9048437857cb2e65c8d780f396e3" - url: "https://pub.dev" - source: hosted - version: "2.0.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" - url: "https://pub.dev" - source: hosted - version: "0.12.15" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 - url: "https://pub.dev" - source: hosted - version: "0.2.0" - meta: - dependency: "direct main" - description: - name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" - url: "https://pub.dev" - source: hosted - version: "1.9.1" - path: - dependency: transitive - description: - name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" - url: "https://pub.dev" - source: hosted - version: "1.8.3" - reactter: - dependency: "direct main" - description: - name: reactter - sha256: "69fc36ffc793fcb17bbea5fc498f7de72210694fbafea011ff3aca2158b1792e" - url: "https://pub.dev" - source: hosted - version: "8.0.0-dev.15" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 - url: "https://pub.dev" - source: hosted - version: "1.9.1" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 - url: "https://pub.dev" - source: hosted - version: "1.11.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb - url: "https://pub.dev" - source: hosted - version: "0.5.1" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" -sdks: - dart: ">=3.0.0-0 <4.0.0" - flutter: ">=1.17.0" diff --git a/packages/flutter_reactter/pubspec.yaml b/packages/flutter_reactter/pubspec.yaml index 1c152595..30863dd0 100644 --- a/packages/flutter_reactter/pubspec.yaml +++ b/packages/flutter_reactter/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://2devs-team.github.io/reactter/ documentation: https://2devs-team.github.io/reactter/ repository: https://github.com/2devs-team/reactter/tree/master/packages/flutter_reactter license: MIT License -version: 8.0.0-dev.15 +version: 8.0.0-dev.18 topics: - reactive @@ -12,6 +12,12 @@ topics: - dependency - event +screenshots: + - description: "Reactter DevTools Extension" + path: doc/screenshots/devtools.png + - description: "Reactter Example App" + path: doc/screenshots/example_app.png + environment: sdk: ">=2.19.2 <4.0.0" flutter: ">=1.17.0" @@ -19,8 +25,7 @@ environment: dependencies: flutter: sdk: flutter - meta: ^1.7.0 - reactter: ^8.0.0-dev.15 + flutter_lints: ^2.0.1 dev_dependencies: flutter_test: diff --git a/packages/reactter/pubspec.lock b/packages/reactter/pubspec.lock deleted file mode 100644 index 24dadc12..00000000 --- a/packages/reactter/pubspec.lock +++ /dev/null @@ -1,453 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a - url: "https://pub.dev" - source: hosted - version: "61.0.0" - analyzer: - dependency: transitive - description: - name: analyzer - sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 - url: "https://pub.dev" - source: hosted - version: "5.13.0" - args: - dependency: transitive - description: - name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" - url: "https://pub.dev" - source: hosted - version: "2.5.0" - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - characters: - dependency: transitive - description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - clock: - dependency: transitive - description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" - source: hosted - version: "1.1.1" - collection: - dependency: transitive - description: - name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a - url: "https://pub.dev" - source: hosted - version: "1.18.0" - convert: - dependency: transitive - description: - name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://pub.dev" - source: hosted - version: "3.1.1" - coverage: - dependency: transitive - description: - name: coverage - sha256: e3493833ea012784c740e341952298f1cc77f1f01b1bbc3eb4eecf6984fb7f43 - url: "https://pub.dev" - source: hosted - version: "1.11.1" - crypto: - dependency: transitive - description: - name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.dev" - source: hosted - version: "3.0.3" - fake_async: - dependency: "direct dev" - description: - name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - file: - dependency: transitive - description: - name: file - sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 - url: "https://pub.dev" - source: hosted - version: "7.0.1" - flutter: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - frontend_server_client: - dependency: transitive - description: - name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" - url: "https://pub.dev" - source: hosted - version: "3.2.0" - glob: - dependency: transitive - description: - name: glob - sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" - url: "https://pub.dev" - source: hosted - version: "3.2.1" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.dev" - source: hosted - version: "4.0.2" - io: - dependency: transitive - description: - name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" - url: "https://pub.dev" - source: hosted - version: "1.0.4" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" - url: "https://pub.dev" - source: hosted - version: "10.0.5" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" - url: "https://pub.dev" - source: hosted - version: "3.0.5" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" - lints: - dependency: "direct dev" - description: - name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - logging: - dependency: transitive - description: - name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb - url: "https://pub.dev" - source: hosted - version: "0.12.16+1" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec - url: "https://pub.dev" - source: hosted - version: "0.11.1" - meta: - dependency: "direct main" - description: - name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 - url: "https://pub.dev" - source: hosted - version: "1.15.0" - mime: - dependency: transitive - description: - name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e - url: "https://pub.dev" - source: hosted - version: "1.0.4" - node_preamble: - dependency: transitive - description: - name: node_preamble - sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" - url: "https://pub.dev" - source: hosted - version: "2.0.2" - package_config: - dependency: transitive - description: - name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - path: - dependency: transitive - description: - name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" - url: "https://pub.dev" - source: hosted - version: "1.9.0" - pool: - dependency: transitive - description: - name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.dev" - source: hosted - version: "1.5.1" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - shelf: - dependency: transitive - description: - name: shelf - sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 - url: "https://pub.dev" - source: hosted - version: "1.4.1" - shelf_packages_handler: - dependency: transitive - description: - name: shelf_packages_handler - sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - shelf_static: - dependency: transitive - description: - name: shelf_static - sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e - url: "https://pub.dev" - source: hosted - version: "1.1.2" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" - url: "https://pub.dev" - source: hosted - version: "1.0.4" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_map_stack_trace: - dependency: transitive - description: - name: source_map_stack_trace - sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - source_maps: - dependency: transitive - description: - name: source_maps - sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" - url: "https://pub.dev" - source: hosted - version: "0.10.12" - source_span: - dependency: transitive - description: - name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" - source: hosted - version: "1.10.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" - url: "https://pub.dev" - source: hosted - version: "1.11.1" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://pub.dev" - source: hosted - version: "2.1.2" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test: - dependency: "direct dev" - description: - name: test - sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e" - url: "https://pub.dev" - source: hosted - version: "1.25.7" - test_api: - dependency: transitive - description: - name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" - url: "https://pub.dev" - source: hosted - version: "0.7.2" - test_core: - dependency: transitive - description: - name: test_core - sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696" - url: "https://pub.dev" - source: hosted - version: "0.6.4" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" - source: hosted - version: "1.3.2" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" - url: "https://pub.dev" - source: hosted - version: "14.2.5" - watcher: - dependency: transitive - description: - name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b - url: "https://pub.dev" - source: hosted - version: "2.4.0" - webkit_inspection_protocol: - dependency: transitive - description: - name: webkit_inspection_protocol - sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" - url: "https://pub.dev" - source: hosted - version: "1.2.1" - yaml: - dependency: transitive - description: - name: yaml - sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" - url: "https://pub.dev" - source: hosted - version: "3.1.2" -sdks: - dart: ">=3.4.0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" diff --git a/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart b/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart index a775cc06..2a04098a 100644 --- a/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart +++ b/packages/reactter_devtools_extension/lib/src/widgets/tile_builder.dart @@ -25,6 +25,7 @@ class TreeNodeTileBuilder extends StatelessWidget { visualDensity: const VisualDensity(vertical: 0, horizontal: 0), horizontalTitleGap: 0, minVerticalPadding: 0, + minTileHeight: 24, contentPadding: EdgeInsets.zero, selected: isSelected, selectedTileColor: ColorPalette.of(context).selected, diff --git a/packages/reactter_devtools_extension/pubspec.lock b/packages/reactter_devtools_extension/pubspec.lock deleted file mode 100644 index ff734f3c..00000000 --- a/packages/reactter_devtools_extension/pubspec.lock +++ /dev/null @@ -1,445 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - args: - dependency: transitive - description: - name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" - url: "https://pub.dev" - source: hosted - version: "2.5.0" - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - characters: - dependency: transitive - description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - clock: - dependency: transitive - description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" - source: hosted - version: "1.1.1" - collection: - dependency: transitive - description: - name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf - url: "https://pub.dev" - source: hosted - version: "1.19.0" - crypto: - dependency: transitive - description: - name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.dev" - source: hosted - version: "3.0.3" - devtools_app_shared: - dependency: "direct main" - description: - name: devtools_app_shared - sha256: ce205ca88895e2d44321e6f92f33db761132e2927f4edcc63221cc10267c2f61 - url: "https://pub.dev" - source: hosted - version: "0.1.1" - devtools_extensions: - dependency: "direct main" - description: - name: devtools_extensions - sha256: "109355b1e66c6a806d9166953c0ce0cd45a008c5b53b5a0479dfb428887d18c6" - url: "https://pub.dev" - source: hosted - version: "0.1.1" - devtools_shared: - dependency: transitive - description: - name: devtools_shared - sha256: e5fa35a08b7728bb835982784f4b261f35cec0eec897698cc91e815caa92d10e - url: "https://pub.dev" - source: hosted - version: "8.1.1" - dtd: - dependency: transitive - description: - name: dtd - sha256: "0d4a51ab223090d2d6b86477f414052db78cad1b2de020619f454a2a39369fec" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - extension_discovery: - dependency: transitive - description: - name: extension_discovery - sha256: "20735622d0763865f9d94c3ecdce4441174530870760253e9d364fb4f3da8688" - url: "https://pub.dev" - source: hosted - version: "2.0.0" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - file: - dependency: transitive - description: - name: file - sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 - url: "https://pub.dev" - source: hosted - version: "7.0.1" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - flutter_reactter: - dependency: "direct main" - description: - name: flutter_reactter - sha256: "9a6c6a67bb39245b2ef183685e859c8d3664b6e474d315bb36d1e8c6b37bc966" - url: "https://pub.dev" - source: hosted - version: "8.0.0-dev.8" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - http: - dependency: transitive - description: - name: http - sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba - url: "https://pub.dev" - source: hosted - version: "1.2.0" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.dev" - source: hosted - version: "4.0.2" - intl: - dependency: transitive - description: - name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf - url: "https://pub.dev" - source: hosted - version: "0.19.0" - io: - dependency: transitive - description: - name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" - url: "https://pub.dev" - source: hosted - version: "1.0.4" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" - json_rpc_2: - dependency: transitive - description: - name: json_rpc_2 - sha256: "5e469bffa23899edacb7b22787780068d650b106a21c76db3c49218ab7ca447e" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" - url: "https://pub.dev" - source: hosted - version: "10.0.7" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" - url: "https://pub.dev" - source: hosted - version: "3.0.8" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" - lints: - dependency: transitive - description: - name: lints - sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - logging: - dependency: transitive - description: - name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb - url: "https://pub.dev" - source: hosted - version: "0.12.16+1" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec - url: "https://pub.dev" - source: hosted - version: "0.11.1" - meta: - dependency: transitive - description: - name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 - url: "https://pub.dev" - source: hosted - version: "1.15.0" - path: - dependency: transitive - description: - name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" - url: "https://pub.dev" - source: hosted - version: "1.9.0" - pointer_interceptor: - dependency: transitive - description: - name: pointer_interceptor - sha256: adf7a637f97c077041d36801b43be08559fd4322d2127b3f20bb7be1b9eebc22 - url: "https://pub.dev" - source: hosted - version: "0.9.3+7" - pool: - dependency: transitive - description: - name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.dev" - source: hosted - version: "1.5.1" - queue: - dependency: "direct main" - description: - name: queue - sha256: "9a41ecadc15db79010108c06eae229a45c56b18db699760f34e8c9ac9b831ff9" - url: "https://pub.dev" - source: hosted - version: "3.1.0+2" - reactter: - dependency: transitive - description: - name: reactter - sha256: "2d0df33feb1d79b077e3a664ad22d87a94a20c33415bc67e63b9f61d2e20a39d" - url: "https://pub.dev" - source: hosted - version: "8.0.0-dev.8" - shelf: - dependency: transitive - description: - name: shelf - sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 - url: "https://pub.dev" - source: hosted - version: "1.4.1" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - source_span: - dependency: transitive - description: - name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" - source: hosted - version: "1.10.0" - sse: - dependency: transitive - description: - name: sse - sha256: "8168874cdbd42c36ea118ba9f88a656ad97f604f28c976c61cb6d5b281c5319c" - url: "https://pub.dev" - source: hosted - version: "4.1.4" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" - url: "https://pub.dev" - source: hosted - version: "1.12.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://pub.dev" - source: hosted - version: "2.1.2" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" - url: "https://pub.dev" - source: hosted - version: "0.7.3" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" - source: hosted - version: "1.3.2" - unified_analytics: - dependency: transitive - description: - name: unified_analytics - sha256: "099bb7f0d7fe4d1cc551a81cb1d13ff45005443f871a1429d18ccdbdabee4c9b" - url: "https://pub.dev" - source: hosted - version: "5.8.8+2" - usage: - dependency: transitive - description: - name: usage - sha256: "0bdbde65a6e710343d02a56552eeaefd20b735e04bfb6b3ee025b6b22e8d0e15" - url: "https://pub.dev" - source: hosted - version: "4.1.1" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - vm_service: - dependency: "direct main" - description: - name: vm_service - sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b - url: "https://pub.dev" - source: hosted - version: "14.3.0" - web: - dependency: transitive - description: - name: web - sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05" - url: "https://pub.dev" - source: hosted - version: "0.4.2" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: "939ab60734a4f8fa95feacb55804fa278de28bdeef38e616dc08e44a84adea23" - url: "https://pub.dev" - source: hosted - version: "2.4.3" - webkit_inspection_protocol: - dependency: transitive - description: - name: webkit_inspection_protocol - sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" - url: "https://pub.dev" - source: hosted - version: "1.2.1" - yaml: - dependency: transitive - description: - name: yaml - sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" - url: "https://pub.dev" - source: hosted - version: "3.1.2" - yaml_edit: - dependency: transitive - description: - name: yaml_edit - sha256: e9c1a3543d2da0db3e90270dbb1e4eebc985ee5e3ffe468d83224472b2194a5f - url: "https://pub.dev" - source: hosted - version: "2.2.1" -sdks: - dart: ">=3.4.0 <4.0.0" - flutter: ">=3.22.0-0.1.pre" diff --git a/packages/reactter_devtools_extension/pubspec.yaml b/packages/reactter_devtools_extension/pubspec.yaml index ef025406..6e5b609a 100644 --- a/packages/reactter_devtools_extension/pubspec.yaml +++ b/packages/reactter_devtools_extension/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: devtools_app_shared: ^0.1.1 vm_service: ^14.2.5 queue: ^3.1.0+2 - flutter_reactter: ^8.0.0-dev.14 + flutter_reactter: ^8.0.0-dev.16 dev_dependencies: flutter_test: diff --git a/packages/reactter_lint/pubspec.lock b/packages/reactter_lint/pubspec.lock deleted file mode 100644 index 2fe03a08..00000000 --- a/packages/reactter_lint/pubspec.lock +++ /dev/null @@ -1,349 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a - url: "https://pub.dev" - source: hosted - version: "61.0.0" - analyzer: - dependency: "direct main" - description: - name: analyzer - sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 - url: "https://pub.dev" - source: hosted - version: "5.13.0" - analyzer_plugin: - dependency: "direct main" - description: - name: analyzer_plugin - sha256: c1d5f167683de03d5ab6c3b53fc9aeefc5d59476e7810ba7bbddff50c6f4392d - url: "https://pub.dev" - source: hosted - version: "0.11.2" - args: - dependency: transitive - description: - name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" - url: "https://pub.dev" - source: hosted - version: "2.5.0" - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - checked_yaml: - dependency: transitive - description: - name: checked_yaml - sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff - url: "https://pub.dev" - source: hosted - version: "2.0.3" - ci: - dependency: transitive - description: - name: ci - sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13" - url: "https://pub.dev" - source: hosted - version: "0.1.0" - cli_util: - dependency: transitive - description: - name: cli_util - sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 - url: "https://pub.dev" - source: hosted - version: "0.4.1" - collection: - dependency: transitive - description: - name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf - url: "https://pub.dev" - source: hosted - version: "1.19.0" - convert: - dependency: transitive - description: - name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://pub.dev" - source: hosted - version: "3.1.1" - crypto: - dependency: transitive - description: - name: crypto - sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27 - url: "https://pub.dev" - source: hosted - version: "3.0.5" - custom_lint: - dependency: transitive - description: - name: custom_lint - sha256: "22bd87a362f433ba6aae127a7bac2838645270737f3721b180916d7c5946cb5d" - url: "https://pub.dev" - source: hosted - version: "0.5.11" - custom_lint_builder: - dependency: "direct main" - description: - name: custom_lint_builder - sha256: "0d48e002438950f9582e574ef806b2bea5719d8d14c0f9f754fbad729bcf3b19" - url: "https://pub.dev" - source: hosted - version: "0.5.14" - custom_lint_core: - dependency: transitive - description: - name: custom_lint_core - sha256: "2952837953022de610dacb464f045594854ced6506ac7f76af28d4a6490e189b" - url: "https://pub.dev" - source: hosted - version: "0.5.14" - dart_style: - dependency: transitive - description: - name: dart_style - sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - file: - dependency: transitive - description: - name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - freezed_annotation: - dependency: transitive - description: - name: freezed_annotation - sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 - url: "https://pub.dev" - source: hosted - version: "2.4.4" - glob: - dependency: transitive - description: - name: glob - sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - hotreloader: - dependency: transitive - description: - name: hotreloader - sha256: ed56fdc1f3a8ac924e717257621d09e9ec20e308ab6352a73a50a1d7a4d9158e - url: "https://pub.dev" - source: hosted - version: "4.2.0" - json_annotation: - dependency: transitive - description: - name: json_annotation - sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" - url: "https://pub.dev" - source: hosted - version: "4.9.0" - logging: - dependency: transitive - description: - name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb - url: "https://pub.dev" - source: hosted - version: "0.12.16+1" - meta: - dependency: transitive - description: - name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 - url: "https://pub.dev" - source: hosted - version: "1.15.0" - package_config: - dependency: transitive - description: - name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - path: - dependency: transitive - description: - name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" - url: "https://pub.dev" - source: hosted - version: "1.9.0" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - pubspec_parse: - dependency: transitive - description: - name: pubspec_parse - sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 - url: "https://pub.dev" - source: hosted - version: "1.3.0" - rxdart: - dependency: transitive - description: - name: rxdart - sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" - url: "https://pub.dev" - source: hosted - version: "0.27.7" - source_span: - dependency: transitive - description: - name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" - source: hosted - version: "1.10.0" - sprintf: - dependency: transitive - description: - name: sprintf - sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" - url: "https://pub.dev" - source: hosted - version: "1.11.1" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://pub.dev" - source: hosted - version: "2.1.2" - stream_transform: - dependency: transitive - description: - name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" - url: "https://pub.dev" - source: hosted - version: "0.7.3" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" - source: hosted - version: "1.3.2" - uuid: - dependency: transitive - description: - name: uuid - sha256: f33d6bb662f0e4f79dcd7ada2e6170f3b3a2530c28fc41f49a411ddedd576a77 - url: "https://pub.dev" - source: hosted - version: "4.5.0" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" - url: "https://pub.dev" - source: hosted - version: "14.2.5" - watcher: - dependency: transitive - description: - name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - yaml: - dependency: transitive - description: - name: yaml - sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" - url: "https://pub.dev" - source: hosted - version: "3.1.2" -sdks: - dart: ">=3.4.0 <4.0.0" From a7a717bdcd49c9f9c9c6d157a285ec1f2a8934b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Sat, 11 Jan 2025 02:36:42 -0600 Subject: [PATCH 091/141] build: Update pubspec.lock entry to improve file exclusion --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 50b445e3..0594868c 100644 --- a/.gitignore +++ b/.gitignore @@ -79,4 +79,4 @@ build/ **/lcov.info **/coverage -**/pubspec.lock \ No newline at end of file +pubspec.lock \ No newline at end of file From a91e40ad435921ec398af0cc95acbbf399fc4233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Sat, 11 Jan 2025 02:43:14 -0600 Subject: [PATCH 092/141] build: Update flutter_lints to version 2.0.3 and add reactter dependency --- packages/flutter_reactter/pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/flutter_reactter/pubspec.yaml b/packages/flutter_reactter/pubspec.yaml index 30863dd0..8e72616a 100644 --- a/packages/flutter_reactter/pubspec.yaml +++ b/packages/flutter_reactter/pubspec.yaml @@ -25,12 +25,12 @@ environment: dependencies: flutter: sdk: flutter - flutter_lints: ^2.0.1 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.1 + flutter_lints: ^2.0.3 + reactter: ^8.0.0-dev.17 scripts: test: "flutter test --coverage" From cd3d94c9b91ebfbb1c1ef9bce18521f476439cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Sat, 11 Jan 2025 03:00:05 -0600 Subject: [PATCH 093/141] build: Update flutter_reactter version to 8.0.0-dev.19 in pubspec files --- CHANGELOG.md | 100 +++++++++--------- .../flutter_reactter/example/pubspec.yaml | 2 +- packages/flutter_reactter/pubspec.yaml | 5 +- 3 files changed, 54 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a7a6349..d57fed15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,70 +1,70 @@ # Reactter -## 8.0.0-dev.18 +## 8.0.0-dev.19 ### Enhancements - Add Reactter devtools extension. -- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtInterface/removeObserver.html) methods to manage the observers. -- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). -- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). -- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. -- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. -- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtStateBase/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtStateBase/debugInfo.html) to states and hooks to get info for debugging. -- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. -- Add [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtStateBase-class.html) abstract class to implement the base logic of the state. -- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtInterface/createState.html) method to create a new state. -- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. -- Add [`initHook`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtHook/initHook.html) method to `RtHook` to call when hook is created. -- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. -- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. +- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/removeObserver.html) methods to manage the observers. +- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). +- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). +- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. +- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. +- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtStateBase/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtStateBase/debugInfo.html) to states and hooks to get info for debugging. +- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. +- Add [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtStateBase-class.html) abstract class to implement the base logic of the state. +- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/createState.html) method to create a new state. +- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. +- Add [`initHook`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtHook/initHook.html) method to `RtHook` to call when hook is created. +- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. +- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. - Enhance event handling and notifier logic for improved stability and performance. - Enhance state management and error handling. -- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.18/flutter_reactter/RtComponent-class.html). +- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.19/flutter_reactter/RtComponent-class.html). - Enhance dependency management to ensure to re-build when is detected changes while building. ### Breakings -- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/Rt.html) instead. -- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtStateBase-class.html) instead. +- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/Rt.html) instead. +- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtStateBase-class.html) instead. - Replace [`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html) to `UseAsyncStateStatus.idle`. -- Rename [`DispatchEffect`](https://pub.dev/documentation/reactter/7.3.0/reactter/DispatchEffect-class.html) to [`AutoDispatchEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/DispatchEffect-class.html). -- Move `asyncFunction` to the first argument of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/UseAsyncState/withArg.html). -- Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. -- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtInterface/isActive.html) instead. -- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtInterface/getDependencyMode.html) instead. -- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/DependencyMode.html) instead. -- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/Lifecycle.html) instead. -- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/Lifecycle.html) instead. -- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`LifecycleObserver.onCreated`](https://pub.dev/documentation/reactter/8.0.0-dev.18//reactter/LifecycleObserver/onCreated.html) instead. -- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.18//reactter/RtDependency-class.html) instead. -- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0-dev.18//reactter/RtHook-class.html) instead. -- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0-dev.18//reactter/RtInterface-class.html) instead. -- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtState/notify.html) instead. -- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/UseDependency-class.html) instead. -- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtAction-class.html) instead. -- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtActionCallable-class.html) instead. -- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MemoMultiInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/MemoMultiInterceptor-class.html) instead. -- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/MemoSafeAsyncInterceptor-class.html) instead. -- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/MemoTemporaryCacheInterceptor-class.html) instead. -- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/MemoWrapperInterceptor-class.html) instead. +- Rename [`DispatchEffect`](https://pub.dev/documentation/reactter/7.3.0/reactter/DispatchEffect-class.html) to [`AutoDispatchEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/DispatchEffect-class.html). +- Move `asyncFunction` to the first argument of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/UseAsyncState/withArg.html). +- Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. +- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/isActive.html) instead. +- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/getDependencyMode.html) instead. +- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/DependencyMode.html) instead. +- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/Lifecycle.html) instead. +- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/Lifecycle.html) instead. +- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`LifecycleObserver.onCreated`](https://pub.dev/documentation/reactter/8.0.0-dev.19//reactter/LifecycleObserver/onCreated.html) instead. +- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.19//reactter/RtDependency-class.html) instead. +- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0-dev.19//reactter/RtHook-class.html) instead. +- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0-dev.19//reactter/RtInterface-class.html) instead. +- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtState/notify.html) instead. +- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/UseDependency-class.html) instead. +- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtAction-class.html) instead. +- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtActionCallable-class.html) instead. +- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MemoMultiInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/MemoMultiInterceptor-class.html) instead. +- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/MemoSafeAsyncInterceptor-class.html) instead. +- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/MemoTemporaryCacheInterceptor-class.html) instead. +- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/MemoWrapperInterceptor-class.html) instead. - Remove [`Obj`](https://pub.dev/documentation/reactter/7.3.0/reactter/Obj-class.html). -- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.18/flutter_reactter/RtProvider/RtProvider.init.html) instead. -- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.18/flutter_reactter/RtComponent-class.html) instead. -- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.18/flutter_reactter/RtConsumer-class.html) instead. -- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.18/flutter_reactter/RtProvider-class.html) instead. -- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.18/flutter_reactter/RtMultiProvider-class.html) instead. -- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.18/flutter_reactter/RtScope-class.html) instead. -- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.18/flutter_reactter/RtSignalWatcher-class.html) instead. -- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.18/flutter_reactter/RtDependencyNotFoundException-class.html) instead. -- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.18/flutter_reactter/RtScopeNotFoundException-class.html) instead. +- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.19/flutter_reactter/RtProvider/RtProvider.init.html) instead. +- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.19/flutter_reactter/RtComponent-class.html) instead. +- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.19/flutter_reactter/RtConsumer-class.html) instead. +- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.19/flutter_reactter/RtProvider-class.html) instead. +- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.19/flutter_reactter/RtMultiProvider-class.html) instead. +- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.19/flutter_reactter/RtScope-class.html) instead. +- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.19/flutter_reactter/RtSignalWatcher-class.html) instead. +- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.19/flutter_reactter/RtDependencyNotFoundException-class.html) instead. +- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.19/flutter_reactter/RtScopeNotFoundException-class.html) instead. ## Fixes -- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/UseEffect-class.html) causing dependencies to not be unwatched. -- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtInterface/register.html) method. -- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/RtInterface/batch.html) method to work correctly. -- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0-dev.18/reactter/Notifier-class.html) class. +- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/UseEffect-class.html) causing dependencies to not be unwatched. +- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/register.html) method. +- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/batch.html) method to work correctly. +- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/Notifier-class.html) class. - Fix to propagate state param to `boundInstance`. - Remove listeners correctly from single-use listeners and handle null cases. diff --git a/packages/flutter_reactter/example/pubspec.yaml b/packages/flutter_reactter/example/pubspec.yaml index dc3872ef..c424bcc8 100644 --- a/packages/flutter_reactter/example/pubspec.yaml +++ b/packages/flutter_reactter/example/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: http: ^0.13.6 url_launcher: ^6.1.2 intl: ^0.17.0 - flutter_reactter: ^8.0.0-dev.18 + flutter_reactter: ^8.0.0-dev.19 dev_dependencies: custom_lint: ^0.5.0 diff --git a/packages/flutter_reactter/pubspec.yaml b/packages/flutter_reactter/pubspec.yaml index 8e72616a..eb4ff2d4 100644 --- a/packages/flutter_reactter/pubspec.yaml +++ b/packages/flutter_reactter/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://2devs-team.github.io/reactter/ documentation: https://2devs-team.github.io/reactter/ repository: https://github.com/2devs-team/reactter/tree/master/packages/flutter_reactter license: MIT License -version: 8.0.0-dev.18 +version: 8.0.0-dev.19 topics: - reactive @@ -25,12 +25,13 @@ environment: dependencies: flutter: sdk: flutter + meta: ^1.7.0 + reactter: ^8.0.0-dev.17 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^2.0.3 - reactter: ^8.0.0-dev.17 scripts: test: "flutter test --coverage" From 02c7af8ea4bcb04e54de134e0f7884bc7b390b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Tue, 14 Jan 2025 23:54:02 -0600 Subject: [PATCH 094/141] docs: Update README features for clarity and consistency --- README.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index f688db42..13e79a9a 100644 --- a/README.md +++ b/README.md @@ -16,18 +16,19 @@ A lightweight, powerful, and reactive **State Management**, **Dependency Injecti ## Features -- ⚡️ Engineered for **Speed**. -- ⚖️ Super **Lightweight**. -- 📏 **Reduce Boilerplate Code** significantly. -- ✏️ Improve **Code Readability**. -- ☢️ **Reactive States**, using [State](https://2devs-team.github.io/reactter/classes/rt_state_base) and [Hooks](https://2devs-team.github.io/reactter/core_concepts/hooks). -- ♻️ **Highly reusable** states and logic, using [Custom Hooks]([#custom-hooks](https://2devs-team.github.io/reactter/core_concepts/hooks/#custom-hook)) and [Dependency Injection](https://2devs-team.github.io/reactter/core_concepts/dependency_injection/). -- 🎮 Fully **[Rendering Control](https://2devs-team.github.io/reactter/core_concepts/rendering_control)**. -- 🧪 Fully **Testable**, 100% code coverage. -- 🔬 Fully **Debuggable**, using the **[Reactter DevTools extension](https://2devs-team.github.io/reactter/devtools_extension)** -- 💧 **Not opinionated**. You can use it with any architecture or pattern. -- 🪄 **Zero Dependencies**, **Zero Configuration** and **No Code Generation**. -- 💙 **Compatible with Dart and Flutter**, supports the latest version of Dart. +- ⚡️ Engineered for **speed**. +- 🪶 Super **lightweight**. +- 👓 **Simple syntax**, easy to learn. +- ✂️ **Reduce boilerplate code** significantly. +- 👁️ Improve **code readability**. +- 🚀 **Granular reactivity** using [state](https://2devs-team.github.io/reactter/core_concepts/state_management/#state) and [hooks](https://2devs-team.github.io/reactter/core_concepts/hooks). +- 🧩 **Highly reusable** states and logic via [custom hooks](https://2devs-team.github.io/reactter/core_concepts/hooks/#custom-hook) and [dependency injection](https://2devs-team.github.io/reactter/core_concepts/dependency_injection/). +- 🎮 Total [**rendering control**](https://2devs-team.github.io/reactter/core_concepts/rendering_control). +- ✅ **Highly testable** with 100% code coverage. +- 🐞 **Fully debuggable** using the [Reactter DevTools extension](https://2devs-team.github.io/reactter/devtools_extension). +- 💧 **Not opinionated**. Use it with any architecture or pattern. +- 🪄 **Zero dependencies**, **zero configuration** and **no code generation**. +- 💙 **Compatible with Dart and Flutter**, supporting the latest Dart version. _To start using Reactter, check out the full documentation on [The Official Documentation](https://2devs-team.github.io/reactter)._ From 44e57b9a5923619422f02beefcc234a26f17a552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Tue, 14 Jan 2025 23:54:50 -0600 Subject: [PATCH 095/141] refactor(website): Remove unused font family and adjust locale settings in configuration files --- website/astro.config.mjs | 3 +- website/tailwind.config.mjs | 1 - website/yarn.lock | 5605 +++++++++++++++++++++++++++++++++++ 3 files changed, 5607 insertions(+), 2 deletions(-) create mode 100644 website/yarn.lock diff --git a/website/astro.config.mjs b/website/astro.config.mjs index eda4a7a7..7fd6b587 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -26,7 +26,6 @@ export default defineConfig({ src: "./public/logo.svg", }, customCss: ["./src/styles/custom.css"], - defaultLocale: "root", expressiveCode: { themes: [myTheme], }, @@ -39,6 +38,7 @@ export default defineConfig({ ThemeProvider: "./src/components/ThemeProvider.astro", ThemeSelect: "./src/components/ThemeSelect.astro", }, + defaultLocale: "root", locales: { root: { label: "English", @@ -46,6 +46,7 @@ export default defineConfig({ }, es: { label: "Español", + lang: "es", }, }, social: { diff --git a/website/tailwind.config.mjs b/website/tailwind.config.mjs index 49c576c6..55f6e485 100644 --- a/website/tailwind.config.mjs +++ b/website/tailwind.config.mjs @@ -35,7 +35,6 @@ export default { }, fontFamily: { sans: ['"Inter"'], - mono: ['"IBM Plex Mono"'], }, }, }, diff --git a/website/yarn.lock b/website/yarn.lock new file mode 100644 index 00000000..56536636 --- /dev/null +++ b/website/yarn.lock @@ -0,0 +1,5605 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@alloc/quick-lru@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" + integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== + +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@antfu/install-pkg@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@antfu/install-pkg/-/install-pkg-0.4.1.tgz#d1d7f3be96ecdb41581629cafe8626d1748c0cf1" + integrity sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw== + dependencies: + package-manager-detector "^0.2.0" + tinyexec "^0.3.0" + +"@antfu/utils@^0.7.10": + version "0.7.10" + resolved "https://registry.yarnpkg.com/@antfu/utils/-/utils-0.7.10.tgz#ae829f170158e297a9b6a28f161a8e487d00814d" + integrity sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww== + +"@astrojs/alpinejs@^0.4.0": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@astrojs/alpinejs/-/alpinejs-0.4.1.tgz#38d957cec24fd46bebdf6063a249c7c409e6edf9" + integrity sha512-ifC2dh0eaEMFwqXrx5y1pXhuKjqlOo9OmcLjNmHSMV866bH5VLGcpjy+VNf0MR9Qeax82olDib/o5Vmmzg6brA== + +"@astrojs/check@^0.5.10": + version "0.5.10" + resolved "https://registry.yarnpkg.com/@astrojs/check/-/check-0.5.10.tgz#1e6aa4d2392bb34ae9938f894b6765bd858363b4" + integrity sha512-vliHXM9cu/viGeKiksUM4mXfO816ohWtawTl2ADPgTsd4nUMjFiyAl7xFZhF34yy4hq4qf7jvK1F2PlR3b5I5w== + dependencies: + "@astrojs/language-server" "^2.8.4" + chokidar "^3.5.3" + fast-glob "^3.3.1" + kleur "^4.1.5" + yargs "^17.7.2" + +"@astrojs/compiler@^1.5.5": + version "1.8.2" + resolved "https://registry.yarnpkg.com/@astrojs/compiler/-/compiler-1.8.2.tgz#f305d5724c45a9932a8ef4e87b2e7227d15d1c2b" + integrity sha512-o/ObKgtMzl8SlpIdzaxFnt7SATKPxu4oIP/1NL+HDJRzxfJcAkOTAb/ZKMRyULbz4q+1t2/DAebs2Z1QairkZw== + +"@astrojs/compiler@^2.10.3": + version "2.10.3" + resolved "https://registry.yarnpkg.com/@astrojs/compiler/-/compiler-2.10.3.tgz#852386445029f7765a70b4c1d1140e175e1d8c27" + integrity sha512-bL/O7YBxsFt55YHU021oL+xz+B/9HvGNId3F9xURN16aeqDK9juHGktdkCSXz+U4nqFACq6ZFvWomOzhV+zfPw== + +"@astrojs/internal-helpers@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@astrojs/internal-helpers/-/internal-helpers-0.4.1.tgz#ceb5de49346dbdbfb6cba1b683c07fef7df56e1c" + integrity sha512-bMf9jFihO8YP940uD70SI/RDzIhUHJAolWVcO1v5PUivxGKvfLZTLTVVxEYzGYyPsA3ivdLNqMnL5VgmQySa+g== + +"@astrojs/language-server@^2.8.4": + version "2.15.4" + resolved "https://registry.yarnpkg.com/@astrojs/language-server/-/language-server-2.15.4.tgz#9c2eeb64e4b9df9a52f19c6bfdce5397b8dba094" + integrity sha512-JivzASqTPR2bao9BWsSc/woPHH7OGSGc9aMxXL4U6egVTqBycB3ZHdBJPuOCVtcGLrzdWTosAqVPz1BVoxE0+A== + dependencies: + "@astrojs/compiler" "^2.10.3" + "@astrojs/yaml2ts" "^0.2.2" + "@jridgewell/sourcemap-codec" "^1.4.15" + "@volar/kit" "~2.4.7" + "@volar/language-core" "~2.4.7" + "@volar/language-server" "~2.4.7" + "@volar/language-service" "~2.4.7" + fast-glob "^3.2.12" + muggle-string "^0.4.1" + volar-service-css "0.0.62" + volar-service-emmet "0.0.62" + volar-service-html "0.0.62" + volar-service-prettier "0.0.62" + volar-service-typescript "0.0.62" + volar-service-typescript-twoslash-queries "0.0.62" + volar-service-yaml "0.0.62" + vscode-html-languageservice "^5.2.0" + vscode-uri "^3.0.8" + +"@astrojs/markdown-remark@5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@astrojs/markdown-remark/-/markdown-remark-5.1.0.tgz#1d99b451fc73c5ef775d243057a80a66638cc5cf" + integrity sha512-S6Z3K2hOB7MfjeDoHsotnP/q2UsnEDB8NlNAaCjMDsGBZfTUbWxyLW3CaphEWw08f6KLZi2ibK9yC3BaMhh2NQ== + dependencies: + "@astrojs/prism" "^3.1.0" + github-slugger "^2.0.0" + hast-util-from-html "^2.0.0" + hast-util-to-text "^4.0.0" + import-meta-resolve "^4.0.0" + mdast-util-definitions "^6.0.0" + rehype-raw "^7.0.0" + rehype-stringify "^10.0.0" + remark-gfm "^4.0.0" + remark-parse "^11.0.0" + remark-rehype "^11.0.0" + remark-smartypants "^2.0.0" + shiki "^1.1.2" + unified "^11.0.4" + unist-util-remove-position "^5.0.0" + unist-util-visit "^5.0.0" + unist-util-visit-parents "^6.0.0" + vfile "^6.0.1" + +"@astrojs/markdown-remark@5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@astrojs/markdown-remark/-/markdown-remark-5.3.0.tgz#fd1f8874f2bd1e2c33a7447d069fc75005b677f2" + integrity sha512-r0Ikqr0e6ozPb5bvhup1qdWnSPUvQu6tub4ZLYaKyG50BXZ0ej6FhGz3GpChKpH7kglRFPObJd/bDyf2VM9pkg== + dependencies: + "@astrojs/prism" "3.1.0" + github-slugger "^2.0.0" + hast-util-from-html "^2.0.3" + hast-util-to-text "^4.0.2" + import-meta-resolve "^4.1.0" + mdast-util-definitions "^6.0.0" + rehype-raw "^7.0.0" + rehype-stringify "^10.0.1" + remark-gfm "^4.0.0" + remark-parse "^11.0.0" + remark-rehype "^11.1.1" + remark-smartypants "^3.0.2" + shiki "^1.22.0" + unified "^11.0.5" + unist-util-remove-position "^5.0.0" + unist-util-visit "^5.0.0" + unist-util-visit-parents "^6.0.1" + vfile "^6.0.3" + +"@astrojs/mdx@^2.1.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@astrojs/mdx/-/mdx-2.3.1.tgz#638e28c29f502de095e7fa4a46fd674be05c0a88" + integrity sha512-BOQFKD2Pi9cRntNQJlpF2fh4xV8doNpmVy9NKI95r4jsitrY4X5aTOhAowi+fkQgP/zW1A4HwCyQ6Pdam6z8zQ== + dependencies: + "@astrojs/markdown-remark" "5.1.0" + "@mdx-js/mdx" "^3.0.0" + acorn "^8.11.2" + es-module-lexer "^1.4.1" + estree-util-visit "^2.0.0" + github-slugger "^2.0.0" + gray-matter "^4.0.3" + hast-util-to-html "^9.0.0" + kleur "^4.1.4" + rehype-raw "^7.0.0" + remark-gfm "^4.0.0" + remark-smartypants "^2.0.0" + source-map "^0.7.4" + unist-util-visit "^5.0.0" + vfile "^6.0.1" + +"@astrojs/prism@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@astrojs/prism/-/prism-3.1.0.tgz#1b70432e0b16fafda191ce780c2820822a55bc46" + integrity sha512-Z9IYjuXSArkAUx3N6xj6+Bnvx8OdUSHA8YoOgyepp3+zJmtVYJIl/I18GozdJVW1p5u/CNpl3Km7/gwTJK85cw== + dependencies: + prismjs "^1.29.0" + +"@astrojs/prism@^3.1.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@astrojs/prism/-/prism-3.2.0.tgz#e8ad1698395bd05f8d1177b365a8de899655c912" + integrity sha512-GilTHKGCW6HMq7y3BUv9Ac7GMe/MO9gi9GW62GzKtth0SwukCu/qp2wLiGpEujhY+VVhaG9v7kv/5vFzvf4NYw== + dependencies: + prismjs "^1.29.0" + +"@astrojs/sitemap@^3.0.5": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@astrojs/sitemap/-/sitemap-3.2.1.tgz#ed3874861fbca83f9ca3e66ac24a0f7ae3f9cf49" + integrity sha512-uxMfO8f7pALq0ADL6Lk68UV6dNYjJ2xGUzyjjVj60JLBs5a6smtlkBYv3tQ0DzoqwS7c9n4FUx5lgv0yPo/fgA== + dependencies: + sitemap "^8.0.0" + stream-replace-string "^2.0.0" + zod "^3.23.8" + +"@astrojs/starlight-tailwind@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@astrojs/starlight-tailwind/-/starlight-tailwind-2.0.3.tgz#6ecb07e922b6d25e5c9daac866ae657a0e01194a" + integrity sha512-ZwbdXS/9rxYlo3tKZoTZoBPUnaaqek02b341dHwOkmMT0lIR2w+8k0mRUGxnRaYtPdMcaL+nYFd8RUa8sjdyRg== + +"@astrojs/starlight@^0.22.1": + version "0.22.4" + resolved "https://registry.yarnpkg.com/@astrojs/starlight/-/starlight-0.22.4.tgz#c85e306a71132ead4cd5e120b90e729c776ca6a4" + integrity sha512-AgiVEVv2ZkGHkoJcjY0azXG2K7892i+z4FpKtasnESTciomO91I/X9vAfKfHxmTxdVP5BGPxBFVi0Bp2X4Lxvg== + dependencies: + "@astrojs/mdx" "^2.1.1" + "@astrojs/sitemap" "^3.0.5" + "@pagefind/default-ui" "^1.0.3" + "@types/hast" "^3.0.3" + "@types/mdast" "^4.0.3" + astro-expressive-code "^0.35.2" + bcp-47 "^2.1.0" + hast-util-from-html "^2.0.1" + hast-util-select "^6.0.2" + hast-util-to-string "^3.0.0" + hastscript "^8.0.0" + mdast-util-directive "^3.0.0" + mdast-util-to-markdown "^2.1.0" + pagefind "^1.0.3" + rehype "^13.0.1" + rehype-format "^5.0.0" + remark-directive "^3.0.0" + unified "^11.0.4" + unist-util-visit "^5.0.0" + vfile "^6.0.1" + +"@astrojs/tailwind@^5.1.0": + version "5.1.4" + resolved "https://registry.yarnpkg.com/@astrojs/tailwind/-/tailwind-5.1.4.tgz#7c2ce5f1c9e20a84839e39bc7488bbfa84b872fd" + integrity sha512-EJ3uoTZZr0RYwTrVS2HgYN0+VbXvg7h87AtwpD5OzqS3GyMwRmzfOwHfORTxoWGQRrY9k/Fi+Awk60kwpvRL5Q== + dependencies: + autoprefixer "^10.4.20" + postcss "^8.4.49" + postcss-load-config "^4.0.2" + +"@astrojs/telemetry@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@astrojs/telemetry/-/telemetry-3.1.0.tgz#1038bea408a0f8cf363fb939afeefed751f1f86f" + integrity sha512-/ca/+D8MIKEC8/A9cSaPUqQNZm+Es/ZinRv0ZAzvu2ios7POQSsVD+VOj7/hypWNsNM3T7RpfgNq7H2TU1KEHA== + dependencies: + ci-info "^4.0.0" + debug "^4.3.4" + dlv "^1.1.3" + dset "^3.1.3" + is-docker "^3.0.0" + is-wsl "^3.0.0" + which-pm-runs "^1.1.0" + +"@astrojs/yaml2ts@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@astrojs/yaml2ts/-/yaml2ts-0.2.2.tgz#eabcb75a57a97c5a2f0422a0a03ca14f000f4f5e" + integrity sha512-GOfvSr5Nqy2z5XiwqTouBBpy5FyI6DEe+/g/Mk5am9SjILN1S5fOEvYK0GuWHg98yS/dobP4m8qyqw/URW35fQ== + dependencies: + yaml "^2.5.0" + +"@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2": + version "7.26.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" + integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/compat-data@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.5.tgz#df93ac37f4417854130e21d72c66ff3d4b897fc7" + integrity sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg== + +"@babel/core@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40" + integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.26.0" + "@babel/generator" "^7.26.0" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helpers" "^7.26.0" + "@babel/parser" "^7.26.0" + "@babel/template" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.26.0" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.26.0", "@babel/generator@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.5.tgz#e44d4ab3176bbcaf78a5725da5f1dc28802a9458" + integrity sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw== + dependencies: + "@babel/parser" "^7.26.5" + "@babel/types" "^7.26.5" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + +"@babel/helper-annotate-as-pure@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4" + integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g== + dependencies: + "@babel/types" "^7.25.9" + +"@babel/helper-compilation-targets@^7.25.9": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz#75d92bb8d8d51301c0d49e52a65c9a7fe94514d8" + integrity sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA== + dependencies: + "@babel/compat-data" "^7.26.5" + "@babel/helper-validator-option" "^7.25.9" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-module-imports@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/helper-module-transforms@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" + integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== + dependencies: + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" + +"@babel/helper-plugin-utils@^7.25.9": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35" + integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg== + +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + +"@babel/helper-validator-option@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" + integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== + +"@babel/helpers@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.0.tgz#30e621f1eba5aa45fe6f4868d2e9154d884119a4" + integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw== + dependencies: + "@babel/template" "^7.25.9" + "@babel/types" "^7.26.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.25.4", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.5.tgz#6fec9aebddef25ca57a935c86dbb915ae2da3e1f" + integrity sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw== + dependencies: + "@babel/types" "^7.26.5" + +"@babel/plugin-syntax-jsx@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz#a34313a178ea56f1951599b929c1ceacee719290" + integrity sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-react-jsx@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz#06367940d8325b36edff5e2b9cbe782947ca4166" + integrity sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-syntax-jsx" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/template@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016" + integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== + dependencies: + "@babel/code-frame" "^7.25.9" + "@babel/parser" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/traverse@^7.25.9": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.5.tgz#6d0be3e772ff786456c1a37538208286f6e79021" + integrity sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.5" + "@babel/parser" "^7.26.5" + "@babel/template" "^7.25.9" + "@babel/types" "^7.26.5" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.25.4", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.5.tgz#7a1e1c01d28e26d1fe7f8ec9567b3b92b9d07747" + integrity sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + +"@ctrl/tinycolor@^4.0.4": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-4.1.0.tgz#91a8f8120ffc9da2feb2a38f7862b300d5e9691a" + integrity sha512-WyOx8cJQ+FQus4Mm4uPIZA64gbk3Wxh0so5Lcii0aJifqwoVOlfFtorjLE0Hen4OYyHZMXDWqMmaQemBhgxFRQ== + +"@emmetio/abbreviation@^2.3.3": + version "2.3.3" + resolved "https://registry.yarnpkg.com/@emmetio/abbreviation/-/abbreviation-2.3.3.tgz#ed2b88fe37b972292d6026c7c540aaf887cecb6e" + integrity sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA== + dependencies: + "@emmetio/scanner" "^1.0.4" + +"@emmetio/css-abbreviation@^2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@emmetio/css-abbreviation/-/css-abbreviation-2.1.8.tgz#b785313486eba6cb7eb623ad39378c4e1063dc00" + integrity sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw== + dependencies: + "@emmetio/scanner" "^1.0.4" + +"@emmetio/css-parser@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@emmetio/css-parser/-/css-parser-0.4.0.tgz#96135093480c79703df0e4f178f7f8f2b669fbc2" + integrity sha512-z7wkxRSZgrQHXVzObGkXG+Vmj3uRlpM11oCZ9pbaz0nFejvCDmAiNDpY75+wgXOcffKpj4rzGtwGaZxfJKsJxw== + dependencies: + "@emmetio/stream-reader" "^2.2.0" + "@emmetio/stream-reader-utils" "^0.1.0" + +"@emmetio/html-matcher@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@emmetio/html-matcher/-/html-matcher-1.3.0.tgz#43b7a71b91cdc511cb699cbe9c67bb5d4cab6754" + integrity sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ== + dependencies: + "@emmetio/scanner" "^1.0.0" + +"@emmetio/scanner@^1.0.0", "@emmetio/scanner@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@emmetio/scanner/-/scanner-1.0.4.tgz#e9cdc67194fd91f8b7eb141014be4f2d086c15f1" + integrity sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA== + +"@emmetio/stream-reader-utils@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@emmetio/stream-reader-utils/-/stream-reader-utils-0.1.0.tgz#244cb02c77ec2e74f78a9bd318218abc9c500a61" + integrity sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A== + +"@emmetio/stream-reader@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@emmetio/stream-reader/-/stream-reader-2.2.0.tgz#46cffea119a0a003312a21c2d9b5628cb5fcd442" + integrity sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw== + +"@emnapi/runtime@^1.2.0": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.3.1.tgz#0fcaa575afc31f455fd33534c19381cfce6c6f60" + integrity sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw== + dependencies: + tslib "^2.4.0" + +"@esbuild/aix-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" + integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== + +"@esbuild/android-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" + integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== + +"@esbuild/android-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" + integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== + +"@esbuild/android-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" + integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== + +"@esbuild/darwin-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" + integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== + +"@esbuild/darwin-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" + integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== + +"@esbuild/freebsd-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" + integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== + +"@esbuild/freebsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" + integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== + +"@esbuild/linux-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" + integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== + +"@esbuild/linux-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" + integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== + +"@esbuild/linux-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" + integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== + +"@esbuild/linux-loong64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" + integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== + +"@esbuild/linux-mips64el@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" + integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== + +"@esbuild/linux-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" + integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== + +"@esbuild/linux-riscv64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" + integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== + +"@esbuild/linux-s390x@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" + integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== + +"@esbuild/linux-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" + integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== + +"@esbuild/netbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" + integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== + +"@esbuild/openbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" + integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== + +"@esbuild/sunos-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" + integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== + +"@esbuild/win32-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" + integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== + +"@esbuild/win32-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" + integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== + +"@esbuild/win32-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" + integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== + +"@expressive-code/core@^0.35.6": + version "0.35.6" + resolved "https://registry.yarnpkg.com/@expressive-code/core/-/core-0.35.6.tgz#7fd2f6b5c130588acd956c0d8fd042a2d8d096cb" + integrity sha512-xGqCkmfkgT7lr/rvmfnYdDSeTdCSp1otAHgoFS6wNEeO7wGDPpxdosVqYiIcQ8CfWUABh/pGqWG90q+MV3824A== + dependencies: + "@ctrl/tinycolor" "^4.0.4" + hast-util-select "^6.0.2" + hast-util-to-html "^9.0.1" + hast-util-to-text "^4.0.1" + hastscript "^9.0.0" + postcss "^8.4.38" + postcss-nested "^6.0.1" + unist-util-visit "^5.0.0" + unist-util-visit-parents "^6.0.1" + +"@expressive-code/plugin-collapsible-sections@^0.35.3": + version "0.35.6" + resolved "https://registry.yarnpkg.com/@expressive-code/plugin-collapsible-sections/-/plugin-collapsible-sections-0.35.6.tgz#4a2143dc80083479579a2f469dc35a91ef985d3c" + integrity sha512-PciZoBynxp3DCrK3dvcc/rxkj2HVbFxX992yqez1pircmPj0g1STySslkOBVMHh9COy6whJ4Mbq2k9DPV1A5/Q== + dependencies: + "@expressive-code/core" "^0.35.6" + +"@expressive-code/plugin-frames@^0.35.6": + version "0.35.6" + resolved "https://registry.yarnpkg.com/@expressive-code/plugin-frames/-/plugin-frames-0.35.6.tgz#37566970a7e71cbb793cde8d9aeab8fd5e6c6abb" + integrity sha512-CqjSWjDJ3wabMJZfL9ZAzH5UAGKg7KWsf1TBzr4xvUbZvWoBtLA/TboBML0U1Ls8h/4TRCIvR4VEb8dv5+QG3w== + dependencies: + "@expressive-code/core" "^0.35.6" + +"@expressive-code/plugin-line-numbers@^0.35.3": + version "0.35.6" + resolved "https://registry.yarnpkg.com/@expressive-code/plugin-line-numbers/-/plugin-line-numbers-0.35.6.tgz#38980019e5d170861306de1e01ed40ce736dcaf4" + integrity sha512-WVPk1ghCP1i1CfW4xDVQptlY1Py1X7u5tWhFZnLAvcuPtSo6iYP3DPOGtQpmrucnmvYIwWTdruiNFUlDUUIAcQ== + dependencies: + "@expressive-code/core" "^0.35.6" + +"@expressive-code/plugin-shiki@^0.35.6": + version "0.35.6" + resolved "https://registry.yarnpkg.com/@expressive-code/plugin-shiki/-/plugin-shiki-0.35.6.tgz#9f52f5e557a12fff702b94a414d3d41a5b0aebf8" + integrity sha512-xm+hzi9BsmhkDUGuyAWIydOAWer7Cs9cj8FM0t4HXaQ+qCubprT6wJZSKUxuvFJIUsIOqk1xXFaJzGJGnWtKMg== + dependencies: + "@expressive-code/core" "^0.35.6" + shiki "^1.1.7" + +"@expressive-code/plugin-text-markers@^0.35.6": + version "0.35.6" + resolved "https://registry.yarnpkg.com/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.35.6.tgz#bcca76e8eb90dc44fa8fd937f28d5eb78a4bc0c8" + integrity sha512-/k9eWVZSCs+uEKHR++22Uu6eIbHWEciVHbIuD8frT8DlqTtHYaaiwHPncO6KFWnGDz5i/gL7oyl6XmOi/E6GVg== + dependencies: + "@expressive-code/core" "^0.35.6" + +"@iconify-json/mdi@^1.2.1": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@iconify-json/mdi/-/mdi-1.2.2.tgz#757d5780470c9e539dca9dd97b94f0bb4c8dbfb8" + integrity sha512-84aznJXzfGdbOXGe8xB7E5uNAb7Yo5IABwTgq2X3kczb819qZeS9eL31bTVn7wJdCLK5ieaoUc2GTS3QYIkJ6g== + dependencies: + "@iconify/types" "*" + +"@iconify/tools@^4.0.5": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@iconify/tools/-/tools-4.1.1.tgz#40cf7b49efd72b2ea4e332aa883ed78365231ea8" + integrity sha512-Hybu/HGhv6T8nLQhiG9rKx+ekF7NNpPOEQAy7JRSKht3s3dcFSsPccYzk24Znc9MTxrR6Gak3cg6CPP5dyvS2Q== + dependencies: + "@iconify/types" "^2.0.0" + "@iconify/utils" "^2.2.0" + "@types/tar" "^6.1.13" + axios "^1.7.9" + cheerio "1.0.0" + domhandler "^5.0.3" + extract-zip "^2.0.1" + local-pkg "^0.5.1" + pathe "^1.1.2" + svgo "^3.3.2" + tar "^6.2.1" + +"@iconify/types@*", "@iconify/types@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@iconify/types/-/types-2.0.0.tgz#ab0e9ea681d6c8a1214f30cd741fe3a20cc57f57" + integrity sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg== + +"@iconify/utils@^2.1.30", "@iconify/utils@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@iconify/utils/-/utils-2.2.1.tgz#635b9bd8fd3e5e53742471bc0b5291f1570dda41" + integrity sha512-0/7J7hk4PqXmxo5PDBDxmnecw5PxklZJfNjIVG9FM0mEfVrvfudS22rYWsqVk6gR3UJ/mSYS90X4R3znXnqfNA== + dependencies: + "@antfu/install-pkg" "^0.4.1" + "@antfu/utils" "^0.7.10" + "@iconify/types" "^2.0.0" + debug "^4.4.0" + globals "^15.13.0" + kolorist "^1.8.0" + local-pkg "^0.5.1" + mlly "^1.7.3" + +"@img/sharp-darwin-arm64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz#ef5b5a07862805f1e8145a377c8ba6e98813ca08" + integrity sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ== + optionalDependencies: + "@img/sharp-libvips-darwin-arm64" "1.0.4" + +"@img/sharp-darwin-x64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz#e03d3451cd9e664faa72948cc70a403ea4063d61" + integrity sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q== + optionalDependencies: + "@img/sharp-libvips-darwin-x64" "1.0.4" + +"@img/sharp-libvips-darwin-arm64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz#447c5026700c01a993c7804eb8af5f6e9868c07f" + integrity sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg== + +"@img/sharp-libvips-darwin-x64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz#e0456f8f7c623f9dbfbdc77383caa72281d86062" + integrity sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ== + +"@img/sharp-libvips-linux-arm64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz#979b1c66c9a91f7ff2893556ef267f90ebe51704" + integrity sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA== + +"@img/sharp-libvips-linux-arm@1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz#99f922d4e15216ec205dcb6891b721bfd2884197" + integrity sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g== + +"@img/sharp-libvips-linux-s390x@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz#f8a5eb1f374a082f72b3f45e2fb25b8118a8a5ce" + integrity sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA== + +"@img/sharp-libvips-linux-x64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz#d4c4619cdd157774906e15770ee119931c7ef5e0" + integrity sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw== + +"@img/sharp-libvips-linuxmusl-arm64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz#166778da0f48dd2bded1fa3033cee6b588f0d5d5" + integrity sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA== + +"@img/sharp-libvips-linuxmusl-x64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz#93794e4d7720b077fcad3e02982f2f1c246751ff" + integrity sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw== + +"@img/sharp-linux-arm64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz#edb0697e7a8279c9fc829a60fc35644c4839bb22" + integrity sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA== + optionalDependencies: + "@img/sharp-libvips-linux-arm64" "1.0.4" + +"@img/sharp-linux-arm@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz#422c1a352e7b5832842577dc51602bcd5b6f5eff" + integrity sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ== + optionalDependencies: + "@img/sharp-libvips-linux-arm" "1.0.5" + +"@img/sharp-linux-s390x@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz#f5c077926b48e97e4a04d004dfaf175972059667" + integrity sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q== + optionalDependencies: + "@img/sharp-libvips-linux-s390x" "1.0.4" + +"@img/sharp-linux-x64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz#d806e0afd71ae6775cc87f0da8f2d03a7c2209cb" + integrity sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA== + optionalDependencies: + "@img/sharp-libvips-linux-x64" "1.0.4" + +"@img/sharp-linuxmusl-arm64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz#252975b915894fb315af5deea174651e208d3d6b" + integrity sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g== + optionalDependencies: + "@img/sharp-libvips-linuxmusl-arm64" "1.0.4" + +"@img/sharp-linuxmusl-x64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz#3f4609ac5d8ef8ec7dadee80b560961a60fd4f48" + integrity sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw== + optionalDependencies: + "@img/sharp-libvips-linuxmusl-x64" "1.0.4" + +"@img/sharp-wasm32@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz#6f44f3283069d935bb5ca5813153572f3e6f61a1" + integrity sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg== + dependencies: + "@emnapi/runtime" "^1.2.0" + +"@img/sharp-win32-ia32@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz#1a0c839a40c5351e9885628c85f2e5dfd02b52a9" + integrity sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ== + +"@img/sharp-win32-x64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz#56f00962ff0c4e0eb93d34a047d29fa995e3e342" + integrity sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg== + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + +"@jridgewell/gen-mapping@^0.3.2", "@jridgewell/gen-mapping@^0.3.5": + version "0.3.8" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" + integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15", "@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@mdx-js/mdx@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-3.1.0.tgz#10235cab8ad7d356c262e8c21c68df5850a97dc3" + integrity sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw== + dependencies: + "@types/estree" "^1.0.0" + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/mdx" "^2.0.0" + collapse-white-space "^2.0.0" + devlop "^1.0.0" + estree-util-is-identifier-name "^3.0.0" + estree-util-scope "^1.0.0" + estree-walker "^3.0.0" + hast-util-to-jsx-runtime "^2.0.0" + markdown-extensions "^2.0.0" + recma-build-jsx "^1.0.0" + recma-jsx "^1.0.0" + recma-stringify "^1.0.0" + rehype-recma "^1.0.0" + remark-mdx "^3.0.0" + remark-parse "^11.0.0" + remark-rehype "^11.0.0" + source-map "^0.7.0" + unified "^11.0.0" + unist-util-position-from-estree "^2.0.0" + unist-util-stringify-position "^4.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.0" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@oslojs/encoding@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@oslojs/encoding/-/encoding-1.1.0.tgz#55f3d9a597430a01f2a5ef63c6b42f769f9ce34e" + integrity sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ== + +"@pagefind/darwin-arm64@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@pagefind/darwin-arm64/-/darwin-arm64-1.3.0.tgz#f1e63d031ba710c98b0b83db85df9251a255f543" + integrity sha512-365BEGl6ChOsauRjyVpBjXybflXAOvoMROw3TucAROHIcdBvXk9/2AmEvGFU0r75+vdQI4LJdJdpH4Y6Yqaj4A== + +"@pagefind/darwin-x64@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@pagefind/darwin-x64/-/darwin-x64-1.3.0.tgz#10aa3c5988daa464c5c0db5c5aa4bf72e9bbfba1" + integrity sha512-zlGHA23uuXmS8z3XxEGmbHpWDxXfPZ47QS06tGUq0HDcZjXjXHeLG+cboOy828QIV5FXsm9MjfkP5e4ZNbOkow== + +"@pagefind/default-ui@^1.0.3": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@pagefind/default-ui/-/default-ui-1.3.0.tgz#e3fb585d2fb08d463a8abc3c8f430420f0310109" + integrity sha512-CGKT9ccd3+oRK6STXGgfH+m0DbOKayX6QGlq38TfE1ZfUcPc5+ulTuzDbZUnMo+bubsEOIypm4Pl2iEyzZ1cNg== + +"@pagefind/linux-arm64@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@pagefind/linux-arm64/-/linux-arm64-1.3.0.tgz#cceb0391901736427738ee1232ff326a985eda8a" + integrity sha512-8lsxNAiBRUk72JvetSBXs4WRpYrQrVJXjlRRnOL6UCdBN9Nlsz0t7hWstRk36+JqHpGWOKYiuHLzGYqYAqoOnQ== + +"@pagefind/linux-x64@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@pagefind/linux-x64/-/linux-x64-1.3.0.tgz#06ec4c2907780a75d2fb65a22203c5a48abe7a82" + integrity sha512-hAvqdPJv7A20Ucb6FQGE6jhjqy+vZ6pf+s2tFMNtMBG+fzcdc91uTw7aP/1Vo5plD0dAOHwdxfkyw0ugal4kcQ== + +"@pagefind/windows-x64@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@pagefind/windows-x64/-/windows-x64-1.3.0.tgz#ce3394e5143aaca4850a33473a07628971773655" + integrity sha512-BR1bIRWOMqkf8IoU576YDhij1Wd/Zf2kX/kCI0b2qzCKC8wcc2GQJaaRMCpzvCCrmliO4vtJ6RITp/AnoYUUmQ== + +"@parcel/watcher-android-arm64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz#e32d3dda6647791ee930556aee206fcd5ea0fb7a" + integrity sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ== + +"@parcel/watcher-darwin-arm64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz#0d9e680b7e9ec1c8f54944f1b945aa8755afb12f" + integrity sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw== + +"@parcel/watcher-darwin-x64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz#f9f1d5ce9d5878d344f14ef1856b7a830c59d1bb" + integrity sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA== + +"@parcel/watcher-freebsd-x64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz#2b77f0c82d19e84ff4c21de6da7f7d096b1a7e82" + integrity sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw== + +"@parcel/watcher-linux-arm-glibc@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz#92ed322c56dbafa3d2545dcf2803334aee131e42" + integrity sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA== + +"@parcel/watcher-linux-arm-musl@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz#cd48e9bfde0cdbbd2ecd9accfc52967e22f849a4" + integrity sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA== + +"@parcel/watcher-linux-arm64-glibc@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz#7b81f6d5a442bb89fbabaf6c13573e94a46feb03" + integrity sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA== + +"@parcel/watcher-linux-arm64-musl@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz#dcb8ff01077cdf59a18d9e0a4dff7a0cfe5fd732" + integrity sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q== + +"@parcel/watcher-linux-x64-glibc@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz#2e254600fda4e32d83942384d1106e1eed84494d" + integrity sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw== + +"@parcel/watcher-linux-x64-musl@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz#01fcea60fedbb3225af808d3f0a7b11229792eef" + integrity sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA== + +"@parcel/watcher-win32-arm64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz#87cdb16e0783e770197e52fb1dc027bb0c847154" + integrity sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig== + +"@parcel/watcher-win32-ia32@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz#778c39b56da33e045ba21c678c31a9f9d7c6b220" + integrity sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA== + +"@parcel/watcher-win32-x64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz#33873876d0bbc588aacce38e90d1d7480ce81cb7" + integrity sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw== + +"@parcel/watcher@^2.4.1": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.5.0.tgz#5c88818b12b8de4307a9d3e6dc3e28eba0dfbd10" + integrity sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ== + dependencies: + detect-libc "^1.0.3" + is-glob "^4.0.3" + micromatch "^4.0.5" + node-addon-api "^7.0.0" + optionalDependencies: + "@parcel/watcher-android-arm64" "2.5.0" + "@parcel/watcher-darwin-arm64" "2.5.0" + "@parcel/watcher-darwin-x64" "2.5.0" + "@parcel/watcher-freebsd-x64" "2.5.0" + "@parcel/watcher-linux-arm-glibc" "2.5.0" + "@parcel/watcher-linux-arm-musl" "2.5.0" + "@parcel/watcher-linux-arm64-glibc" "2.5.0" + "@parcel/watcher-linux-arm64-musl" "2.5.0" + "@parcel/watcher-linux-x64-glibc" "2.5.0" + "@parcel/watcher-linux-x64-musl" "2.5.0" + "@parcel/watcher-win32-arm64" "2.5.0" + "@parcel/watcher-win32-ia32" "2.5.0" + "@parcel/watcher-win32-x64" "2.5.0" + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@rollup/pluginutils@^5.1.3": + version "5.1.4" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.4.tgz#bb94f1f9eaaac944da237767cdfee6c5b2262d4a" + integrity sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ== + dependencies: + "@types/estree" "^1.0.0" + estree-walker "^2.0.2" + picomatch "^4.0.2" + +"@rollup/rollup-android-arm-eabi@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.1.tgz#14c737dc19603a096568044eadaa60395eefb809" + integrity sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q== + +"@rollup/rollup-android-arm64@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.30.1.tgz#9d81ea54fc5650eb4ebbc0a7d84cee331bfa30ad" + integrity sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w== + +"@rollup/rollup-darwin-arm64@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.30.1.tgz#29448cb1370cf678b50743d2e392be18470abc23" + integrity sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q== + +"@rollup/rollup-darwin-x64@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.30.1.tgz#0ca99741c3ed096700557a43bb03359450c7857d" + integrity sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA== + +"@rollup/rollup-freebsd-arm64@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.30.1.tgz#233f8e4c2f54ad9b719cd9645887dcbd12b38003" + integrity sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ== + +"@rollup/rollup-freebsd-x64@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.30.1.tgz#dfba762a023063dc901610722995286df4a48360" + integrity sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw== + +"@rollup/rollup-linux-arm-gnueabihf@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.30.1.tgz#b9da54171726266c5ef4237f462a85b3c3cf6ac9" + integrity sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg== + +"@rollup/rollup-linux-arm-musleabihf@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.30.1.tgz#b9db69b3f85f5529eb992936d8f411ee6d04297b" + integrity sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug== + +"@rollup/rollup-linux-arm64-gnu@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.30.1.tgz#2550cf9bb4d47d917fd1ab4af756d7bbc3ee1528" + integrity sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw== + +"@rollup/rollup-linux-arm64-musl@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.30.1.tgz#9d06b26d286c7dded6336961a2f83e48330e0c80" + integrity sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA== + +"@rollup/rollup-linux-loongarch64-gnu@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.30.1.tgz#e957bb8fee0c8021329a34ca8dfa825826ee0e2e" + integrity sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ== + +"@rollup/rollup-linux-powerpc64le-gnu@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.30.1.tgz#e8585075ddfb389222c5aada39ea62d6d2511ccc" + integrity sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw== + +"@rollup/rollup-linux-riscv64-gnu@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.30.1.tgz#7d0d40cee7946ccaa5a4e19a35c6925444696a9e" + integrity sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw== + +"@rollup/rollup-linux-s390x-gnu@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.30.1.tgz#c2dcd8a4b08b2f2778eceb7a5a5dfde6240ebdea" + integrity sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA== + +"@rollup/rollup-linux-x64-gnu@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.30.1.tgz#183637d91456877cb83d0a0315eb4788573aa588" + integrity sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg== + +"@rollup/rollup-linux-x64-musl@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.30.1.tgz#036a4c860662519f1f9453807547fd2a11d5bb01" + integrity sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow== + +"@rollup/rollup-win32-arm64-msvc@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.30.1.tgz#51cad812456e616bfe4db5238fb9c7497e042a52" + integrity sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw== + +"@rollup/rollup-win32-ia32-msvc@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.30.1.tgz#661c8b3e4cd60f51deaa39d153aac4566e748e5e" + integrity sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw== + +"@rollup/rollup-win32-x64-msvc@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.30.1.tgz#73bf1885ff052b82fbb0f82f8671f73c36e9137c" + integrity sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og== + +"@shikijs/core@1.26.2": + version "1.26.2" + resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-1.26.2.tgz#05fe0d3fa88b2a2008d883457b8d243688378835" + integrity sha512-ORyu3MrY7dCC7FDLDsFSkBM9b/AT9/Y8rH+UQ07Rtek48pp0ZhQOMPTKolqszP4bBCas6FqTZQYt18BBamVl/g== + dependencies: + "@shikijs/engine-javascript" "1.26.2" + "@shikijs/engine-oniguruma" "1.26.2" + "@shikijs/types" "1.26.2" + "@shikijs/vscode-textmate" "^10.0.1" + "@types/hast" "^3.0.4" + hast-util-to-html "^9.0.4" + +"@shikijs/engine-javascript@1.26.2": + version "1.26.2" + resolved "https://registry.yarnpkg.com/@shikijs/engine-javascript/-/engine-javascript-1.26.2.tgz#28b0278130d3a76093da8be0c070df6fa2d1c7d0" + integrity sha512-ngkIu9swLVo9Zt5QBtz5Sk08vmPcwuj01r7pPK/Zjmo2U2WyKMK4WMUMmkdQiUacdcLth0zt8u1onp4zhkFXKQ== + dependencies: + "@shikijs/types" "1.26.2" + "@shikijs/vscode-textmate" "^10.0.1" + oniguruma-to-es "^1.0.0" + +"@shikijs/engine-oniguruma@1.26.2": + version "1.26.2" + resolved "https://registry.yarnpkg.com/@shikijs/engine-oniguruma/-/engine-oniguruma-1.26.2.tgz#a8a37ef33624dee783e79f7756ef1d0a2b907639" + integrity sha512-mlN7Qrs+w60nKrd7at7XkXSwz6728Pe34taDmHrG6LRHjzCqQ+ysg+/AT6/D2LMk0s2lsr71DjpI73430QP4/w== + dependencies: + "@shikijs/types" "1.26.2" + "@shikijs/vscode-textmate" "^10.0.1" + +"@shikijs/langs@1.26.2": + version "1.26.2" + resolved "https://registry.yarnpkg.com/@shikijs/langs/-/langs-1.26.2.tgz#0880cd47919b507585f312ca805bb16aaee9bc8d" + integrity sha512-o5cdPycB2Kw3IgncHxWopWPiTkjAj7dG01fLkkUyj3glb5ftxL/Opecq9F54opMlrgXy7ZIqDERvFLlUzsCOuA== + dependencies: + "@shikijs/types" "1.26.2" + +"@shikijs/themes@1.26.2": + version "1.26.2" + resolved "https://registry.yarnpkg.com/@shikijs/themes/-/themes-1.26.2.tgz#b5385fef2f3d26a68a61144331ba301ce28a832d" + integrity sha512-y4Pn6PM5mODz/e3yF6jAUG7WLKJzqL2tJ5qMJCUkMUB1VRgtQVvoa1cHh7NScryGXyrYGJ8nPnRDhdv2rw0xpA== + dependencies: + "@shikijs/types" "1.26.2" + +"@shikijs/types@1.26.2": + version "1.26.2" + resolved "https://registry.yarnpkg.com/@shikijs/types/-/types-1.26.2.tgz#6180cdb5023d21dfe2d2bd784afdfab4e2bce776" + integrity sha512-PO2jucx2FIdlLBPYbIUlMtWSLs5ulcRcuV93cR3T65lkK5SJP4MGBRt9kmWGXiQc0f7+FHj/0BEawditZcI/fQ== + dependencies: + "@shikijs/vscode-textmate" "^10.0.1" + "@types/hast" "^3.0.4" + +"@shikijs/vscode-textmate@^10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@shikijs/vscode-textmate/-/vscode-textmate-10.0.1.tgz#d06d45b67ac5e9b0088e3f67ebd3f25c6c3d711a" + integrity sha512-fTIQwLF+Qhuws31iw7Ncl1R3HUDtGwIipiJ9iU+UsDUwMhegFcQKQHd51nZjb7CArq0MvON8rbgCGQYWHUKAdg== + +"@trysound/sax@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" + integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== + +"@types/acorn@^4.0.0": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@types/acorn/-/acorn-4.0.6.tgz#d61ca5480300ac41a7d973dd5b84d0a591154a22" + integrity sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ== + dependencies: + "@types/estree" "*" + +"@types/alpinejs@^3.13.10": + version "3.13.11" + resolved "https://registry.yarnpkg.com/@types/alpinejs/-/alpinejs-3.13.11.tgz#ecf938c0f4678d617c77933961e9385e45ece148" + integrity sha512-3KhGkDixCPiLdL3Z/ok1GxHwLxEWqQOKJccgaQL01wc0EVM2tCTaqlC3NIedmxAXkVzt/V6VTM8qPgnOHKJ1MA== + +"@types/babel__core@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.8" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" + integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*": + version "7.20.6" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7" + integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg== + dependencies: + "@babel/types" "^7.20.7" + +"@types/cookie@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5" + integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA== + +"@types/debug@^4.0.0": + version "4.1.12" + resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" + integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== + dependencies: + "@types/ms" "*" + +"@types/estree-jsx@^1.0.0": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree-jsx/-/estree-jsx-1.0.5.tgz#858a88ea20f34fe65111f005a689fa1ebf70dc18" + integrity sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg== + dependencies: + "@types/estree" "*" + +"@types/estree@*", "@types/estree@1.0.6", "@types/estree@^1.0.0": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + +"@types/hast@^3.0.0", "@types/hast@^3.0.3", "@types/hast@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" + integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== + dependencies: + "@types/unist" "*" + +"@types/mdast@^4.0.0", "@types/mdast@^4.0.3": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz#7ccf72edd2f1aa7dd3437e180c64373585804dd6" + integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA== + dependencies: + "@types/unist" "*" + +"@types/mdx@^2.0.0": + version "2.0.13" + resolved "https://registry.yarnpkg.com/@types/mdx/-/mdx-2.0.13.tgz#68f6877043d377092890ff5b298152b0a21671bd" + integrity sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw== + +"@types/ms@*": + version "0.7.34" + resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" + integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== + +"@types/nlcst@^1.0.0": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@types/nlcst/-/nlcst-1.0.4.tgz#3b8a9c279a2367602512588a0ba6a0e93634ee3e" + integrity sha512-ABoYdNQ/kBSsLvZAekMhIPMQ3YUZvavStpKYs7BjLLuKVmIMA0LUgZ7b54zzuWJRbHF80v1cNf4r90Vd6eMQDg== + dependencies: + "@types/unist" "^2" + +"@types/nlcst@^2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/nlcst/-/nlcst-2.0.3.tgz#31cad346eaab48a9a8a58465d3d05e2530dda762" + integrity sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA== + dependencies: + "@types/unist" "*" + +"@types/node@*": + version "22.10.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.6.tgz#5c6795e71635876039f853cbccd59f523d9e4239" + integrity sha512-qNiuwC4ZDAUNcY47xgaSuS92cjf8JbSUoaKS77bmLG1rU7MlATVSiw/IlrjtIyyskXBZ8KkNfjK/P5na7rgXbQ== + dependencies: + undici-types "~6.20.0" + +"@types/node@^17.0.5": + version "17.0.45" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" + integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== + +"@types/sax@^1.2.1": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@types/sax/-/sax-1.2.7.tgz#ba5fe7df9aa9c89b6dff7688a19023dd2963091d" + integrity sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A== + dependencies: + "@types/node" "*" + +"@types/tar@^6.1.13": + version "6.1.13" + resolved "https://registry.yarnpkg.com/@types/tar/-/tar-6.1.13.tgz#9b5801c02175344101b4b91086ab2bbc8e93a9b6" + integrity sha512-IznnlmU5f4WcGTh2ltRu/Ijpmk8wiWXfF0VA4s+HPjHZgvFggk1YaIkbo5krX/zUCzWF8N/l4+W/LNxnvAJ8nw== + dependencies: + "@types/node" "*" + minipass "^4.0.0" + +"@types/unist@*", "@types/unist@^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" + integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== + +"@types/unist@^2", "@types/unist@^2.0.0": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.11.tgz#11af57b127e32487774841f7a4e54eab166d03c4" + integrity sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA== + +"@types/yauzl@^2.9.1": + version "2.10.3" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999" + integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q== + dependencies: + "@types/node" "*" + +"@ungap/structured-clone@^1.0.0": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.1.tgz#28fa185f67daaf7b7a1a8c1d445132c5d979f8bd" + integrity sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA== + +"@volar/kit@~2.4.7": + version "2.4.11" + resolved "https://registry.yarnpkg.com/@volar/kit/-/kit-2.4.11.tgz#12fa1825bdbaa54752e86d9eecb0d3b6d1c60f5e" + integrity sha512-ups5RKbMzMCr6RKafcCqDRnJhJDNWqo2vfekwOAj6psZ15v5TlcQFQAyokQJ3wZxVkzxrQM+TqTRDENfQEXpmA== + dependencies: + "@volar/language-service" "2.4.11" + "@volar/typescript" "2.4.11" + typesafe-path "^0.2.2" + vscode-languageserver-textdocument "^1.0.11" + vscode-uri "^3.0.8" + +"@volar/language-core@2.4.11", "@volar/language-core@~2.4.7": + version "2.4.11" + resolved "https://registry.yarnpkg.com/@volar/language-core/-/language-core-2.4.11.tgz#d95a9ec4f14fbdb41a6a64f9f321d11d23a5291c" + integrity sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg== + dependencies: + "@volar/source-map" "2.4.11" + +"@volar/language-server@~2.4.7": + version "2.4.11" + resolved "https://registry.yarnpkg.com/@volar/language-server/-/language-server-2.4.11.tgz#e0d87bd8d4eee0470e806e832ed26f27caf08d81" + integrity sha512-W9P8glH1M8LGREJ7yHRCANI5vOvTrRO15EMLdmh5WNF9sZYSEbQxiHKckZhvGIkbeR1WAlTl3ORTrJXUghjk7g== + dependencies: + "@volar/language-core" "2.4.11" + "@volar/language-service" "2.4.11" + "@volar/typescript" "2.4.11" + path-browserify "^1.0.1" + request-light "^0.7.0" + vscode-languageserver "^9.0.1" + vscode-languageserver-protocol "^3.17.5" + vscode-languageserver-textdocument "^1.0.11" + vscode-uri "^3.0.8" + +"@volar/language-service@2.4.11", "@volar/language-service@~2.4.7": + version "2.4.11" + resolved "https://registry.yarnpkg.com/@volar/language-service/-/language-service-2.4.11.tgz#44008ad68ff82c618fe4f6ad338af9164853e82b" + integrity sha512-KIb6g8gjUkS2LzAJ9bJCLIjfsJjeRtmXlu7b2pDFGD3fNqdbC53cCAKzgWDs64xtQVKYBU13DLWbtSNFtGuMLQ== + dependencies: + "@volar/language-core" "2.4.11" + vscode-languageserver-protocol "^3.17.5" + vscode-languageserver-textdocument "^1.0.11" + vscode-uri "^3.0.8" + +"@volar/source-map@2.4.11": + version "2.4.11" + resolved "https://registry.yarnpkg.com/@volar/source-map/-/source-map-2.4.11.tgz#5876d4531508129724c2755e295db1df98bd5895" + integrity sha512-ZQpmafIGvaZMn/8iuvCFGrW3smeqkq/IIh9F1SdSx9aUl0J4Iurzd6/FhmjNO5g2ejF3rT45dKskgXWiofqlZQ== + +"@volar/typescript@2.4.11": + version "2.4.11" + resolved "https://registry.yarnpkg.com/@volar/typescript/-/typescript-2.4.11.tgz#aafbfa413337654db211bf4d8fb6670c89f6fa57" + integrity sha512-2DT+Tdh88Spp5PyPbqhyoYavYCPDsqbHLFwcUI9K1NlY1YgUJvujGdrqUp0zWxnW7KWNTr3xSpMuv2WnaTKDAw== + dependencies: + "@volar/language-core" "2.4.11" + path-browserify "^1.0.1" + vscode-uri "^3.0.8" + +"@vscode/emmet-helper@^2.9.3": + version "2.11.0" + resolved "https://registry.yarnpkg.com/@vscode/emmet-helper/-/emmet-helper-2.11.0.tgz#7a53e4fdb17329cc2ed88036905c78d811d231d6" + integrity sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw== + dependencies: + emmet "^2.4.3" + jsonc-parser "^2.3.0" + vscode-languageserver-textdocument "^1.0.1" + vscode-languageserver-types "^3.15.1" + vscode-uri "^3.0.8" + +"@vscode/l10n@^0.0.18": + version "0.0.18" + resolved "https://registry.yarnpkg.com/@vscode/l10n/-/l10n-0.0.18.tgz#916d3a5e960dbab47c1c56f58a7cb5087b135c95" + integrity sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ== + +"@vue/reactivity@~3.1.1": + version "3.1.5" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.1.5.tgz#dbec4d9557f7c8f25c2635db1e23a78a729eb991" + integrity sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg== + dependencies: + "@vue/shared" "3.1.5" + +"@vue/shared@3.1.5": + version "3.1.5" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.1.5.tgz#74ee3aad995d0a3996a6bb9533d4d280514ede03" + integrity sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA== + +acorn-jsx@^5.0.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.0.0, acorn@^8.11.2, acorn@^8.14.0: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== + +ajv@^8.11.0: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + +alpinejs@^3.13.10: + version "3.14.8" + resolved "https://registry.yarnpkg.com/alpinejs/-/alpinejs-3.14.8.tgz#062de45daa219db14375b3cb35ba28e5b0627337" + integrity sha512-wT2fuP2DXpGk/jKaglwy7S/IJpm1FD+b7U6zUrhwErjoq5h27S4dxkJEXVvhbdwyPv9U+3OkUuNLkZT4h2Kfrg== + dependencies: + "@vue/reactivity" "~3.1.1" + +ansi-align@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== + dependencies: + string-width "^4.1.0" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0, ansi-styles@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^5.0.0, arg@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +aria-query@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59" + integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw== + +array-iterate@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/array-iterate/-/array-iterate-2.0.1.tgz#6efd43f8295b3fee06251d3d62ead4bd9805dd24" + integrity sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg== + +astring@^1.8.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/astring/-/astring-1.9.0.tgz#cc73e6062a7eb03e7d19c22d8b0b3451fd9bfeef" + integrity sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg== + +astro-expressive-code@^0.35.2: + version "0.35.6" + resolved "https://registry.yarnpkg.com/astro-expressive-code/-/astro-expressive-code-0.35.6.tgz#5029d44cba99c674e1225968de1382466b997f3d" + integrity sha512-1U4KrvFuodaCV3z4I1bIR16SdhQlPkolGsYTtiANxPZUVv/KitGSCTjzksrkPonn1XuwVqvnwmUUVzTLWngnBA== + dependencies: + rehype-expressive-code "^0.35.6" + +astro-icon@^1.1.0: + version "1.1.5" + resolved "https://registry.yarnpkg.com/astro-icon/-/astro-icon-1.1.5.tgz#5d24a55caed5886514a6edde5ea7e623dfc22f59" + integrity sha512-CJYS5nWOw9jz4RpGWmzNQY7D0y2ZZacH7atL2K9DeJXJVaz7/5WrxeyIxO8KASk1jCM96Q4LjRx/F3R+InjJrw== + dependencies: + "@iconify/tools" "^4.0.5" + "@iconify/types" "^2.0.0" + "@iconify/utils" "^2.1.30" + +astro@^4.7.1: + version "4.16.18" + resolved "https://registry.yarnpkg.com/astro/-/astro-4.16.18.tgz#c7db47d5554d865543d6917f42b5129819c6bc88" + integrity sha512-G7zfwJt9BDHEZwlaLNvjbInIw2hPryyD654314KV/XT34pJU6SfN1S+mWa8RAkALcZNJnJXCJmT3JXLQStD3Lw== + dependencies: + "@astrojs/compiler" "^2.10.3" + "@astrojs/internal-helpers" "0.4.1" + "@astrojs/markdown-remark" "5.3.0" + "@astrojs/telemetry" "3.1.0" + "@babel/core" "^7.26.0" + "@babel/plugin-transform-react-jsx" "^7.25.9" + "@babel/types" "^7.26.0" + "@oslojs/encoding" "^1.1.0" + "@rollup/pluginutils" "^5.1.3" + "@types/babel__core" "^7.20.5" + "@types/cookie" "^0.6.0" + acorn "^8.14.0" + aria-query "^5.3.2" + axobject-query "^4.1.0" + boxen "8.0.1" + ci-info "^4.1.0" + clsx "^2.1.1" + common-ancestor-path "^1.0.1" + cookie "^0.7.2" + cssesc "^3.0.0" + debug "^4.3.7" + deterministic-object-hash "^2.0.2" + devalue "^5.1.1" + diff "^5.2.0" + dlv "^1.1.3" + dset "^3.1.4" + es-module-lexer "^1.5.4" + esbuild "^0.21.5" + estree-walker "^3.0.3" + fast-glob "^3.3.2" + flattie "^1.1.1" + github-slugger "^2.0.0" + gray-matter "^4.0.3" + html-escaper "^3.0.3" + http-cache-semantics "^4.1.1" + js-yaml "^4.1.0" + kleur "^4.1.5" + magic-string "^0.30.14" + magicast "^0.3.5" + micromatch "^4.0.8" + mrmime "^2.0.0" + neotraverse "^0.6.18" + ora "^8.1.1" + p-limit "^6.1.0" + p-queue "^8.0.1" + preferred-pm "^4.0.0" + prompts "^2.4.2" + rehype "^13.0.2" + semver "^7.6.3" + shiki "^1.23.1" + tinyexec "^0.3.1" + tsconfck "^3.1.4" + unist-util-visit "^5.0.0" + vfile "^6.0.3" + vite "^5.4.11" + vitefu "^1.0.4" + which-pm "^3.0.0" + xxhash-wasm "^1.1.0" + yargs-parser "^21.1.1" + zod "^3.23.8" + zod-to-json-schema "^3.23.5" + zod-to-ts "^1.2.0" + optionalDependencies: + sharp "^0.33.3" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +autoprefixer@^10.4.20: + version "10.4.20" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.20.tgz#5caec14d43976ef42e32dcb4bd62878e96be5b3b" + integrity sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g== + dependencies: + browserslist "^4.23.3" + caniuse-lite "^1.0.30001646" + fraction.js "^4.3.7" + normalize-range "^0.1.2" + picocolors "^1.0.1" + postcss-value-parser "^4.2.0" + +axios@^1.7.9: + version "1.7.9" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.9.tgz#d7d071380c132a24accda1b2cfc1535b79ec650a" + integrity sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +axobject-query@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee" + integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ== + +bail@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d" + integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base-64@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/base-64/-/base-64-1.0.0.tgz#09d0f2084e32a3fd08c2475b973788eee6ae8f4a" + integrity sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg== + +bcp-47-match@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/bcp-47-match/-/bcp-47-match-2.0.3.tgz#603226f6e5d3914a581408be33b28a53144b09d0" + integrity sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ== + +bcp-47@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/bcp-47/-/bcp-47-2.1.0.tgz#7e80734c3338fe8320894981dccf4968c3092df6" + integrity sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w== + dependencies: + is-alphabetical "^2.0.0" + is-alphanumerical "^2.0.0" + is-decimal "^2.0.0" + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + +boxen@8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-8.0.1.tgz#7e9fcbb45e11a2d7e6daa8fdcebfc3242fc19fe3" + integrity sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw== + dependencies: + ansi-align "^3.0.1" + camelcase "^8.0.0" + chalk "^5.3.0" + cli-boxes "^3.0.0" + string-width "^7.2.0" + type-fest "^4.21.0" + widest-line "^5.0.0" + wrap-ansi "^9.0.0" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browserslist@^4.23.3, browserslist@^4.24.0: + version "4.24.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b" + integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A== + dependencies: + caniuse-lite "^1.0.30001688" + electron-to-chromium "^1.5.73" + node-releases "^2.0.19" + update-browserslist-db "^1.1.1" + +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + +camelcase-css@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + +camelcase@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-8.0.0.tgz#c0d36d418753fb6ad9c5e0437579745c1c14a534" + integrity sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA== + +caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001688: + version "1.0.30001692" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz#4585729d95e6b95be5b439da6ab55250cd125bf9" + integrity sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A== + +ccount@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" + integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== + +chalk@^5.3.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.4.1.tgz#1b48bf0963ec158dce2aacf69c093ae2dd2092d8" + integrity sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w== + +character-entities-html4@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b" + integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== + +character-entities-legacy@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" + integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== + +character-entities@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" + integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== + +character-reference-invalid@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9" + integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw== + +cheerio-select@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-2.1.0.tgz#4d8673286b8126ca2a8e42740d5e3c4884ae21b4" + integrity sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g== + dependencies: + boolbase "^1.0.0" + css-select "^5.1.0" + css-what "^6.1.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.0.1" + +cheerio@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0.tgz#1ede4895a82f26e8af71009f961a9b8cb60d6a81" + integrity sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww== + dependencies: + cheerio-select "^2.1.0" + dom-serializer "^2.0.0" + domhandler "^5.0.3" + domutils "^3.1.0" + encoding-sniffer "^0.2.0" + htmlparser2 "^9.1.0" + parse5 "^7.1.2" + parse5-htmlparser2-tree-adapter "^7.0.0" + parse5-parser-stream "^7.1.2" + undici "^6.19.5" + whatwg-mimetype "^4.0.0" + +chokidar@^3.5.3, chokidar@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chokidar@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" + integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== + dependencies: + readdirp "^4.0.1" + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +ci-info@^4.0.0, ci-info@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.1.0.tgz#92319d2fa29d2620180ea5afed31f589bc98cf83" + integrity sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A== + +cli-boxes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-3.0.0.tgz#71a10c716feeba005e4504f36329ef0b17cf3145" + integrity sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g== + +cli-cursor@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-5.0.0.tgz#24a4831ecf5a6b01ddeb32fb71a4b2088b0dce38" + integrity sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw== + dependencies: + restore-cursor "^5.0.0" + +cli-spinners@^2.9.2: + version "2.9.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" + integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +clsx@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" + integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== + +collapse-white-space@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-2.1.0.tgz#640257174f9f42c740b40f3b55ee752924feefca" + integrity sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw== + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a" + integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A== + dependencies: + color-convert "^2.0.1" + color-string "^1.9.0" + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +comma-separated-tokens@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" + integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== + +commander@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +common-ancestor-path@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz#4f7d2d1394d91b7abdf51871c62f71eadb0182a7" + integrity sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w== + +confbox@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.1.8.tgz#820d73d3b3c82d9bd910652c5d4d599ef8ff8b06" + integrity sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +cookie@^0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + +cross-spawn@^7.0.0: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +css-select@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" + integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== + dependencies: + boolbase "^1.0.0" + css-what "^6.1.0" + domhandler "^5.0.2" + domutils "^3.0.1" + nth-check "^2.0.1" + +css-selector-parser@^3.0.0: + version "3.0.5" + resolved "https://registry.yarnpkg.com/css-selector-parser/-/css-selector-parser-3.0.5.tgz#9b636ebccf7c4bcce5c1ac21ae27de9f01180ae9" + integrity sha512-3itoDFbKUNx1eKmVpYMFyqKX04Ww9osZ+dLgrk6GEv6KMVeXUhUnp4I5X+evw+u3ZxVU6RFXSSRxlTeMh8bA+g== + +css-tree@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20" + integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw== + dependencies: + mdn-data "2.0.30" + source-map-js "^1.0.1" + +css-tree@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032" + integrity sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA== + dependencies: + mdn-data "2.0.28" + source-map-js "^1.0.1" + +css-what@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" + integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +csso@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6" + integrity sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ== + dependencies: + css-tree "~2.2.0" + +cssom@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" + integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== + +debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4, debug@^4.3.7, debug@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + +decode-named-character-reference@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz#daabac9690874c394c81e4162a0304b35d824f0e" + integrity sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg== + dependencies: + character-entities "^2.0.0" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +dequal@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== + +detect-libc@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" + integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== + +deterministic-object-hash@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/deterministic-object-hash/-/deterministic-object-hash-2.0.2.tgz#b251ddc801443905f0e9fef08816a46bc9fe3807" + integrity sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ== + dependencies: + base-64 "^1.0.0" + +devalue@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/devalue/-/devalue-5.1.1.tgz#a71887ac0f354652851752654e4bd435a53891ae" + integrity sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw== + +devlop@^1.0.0, devlop@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018" + integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== + dependencies: + dequal "^2.0.0" + +didyoumean@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" + integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== + +diff@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" + integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== + +direction@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/direction/-/direction-2.0.1.tgz#71800dd3c4fa102406502905d3866e65bdebb985" + integrity sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA== + +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +domutils@^3.0.1, domutils@^3.1.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.2.2.tgz#edbfe2b668b0c1d97c24baf0f1062b132221bc78" + integrity sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + +dset@^3.1.3, dset@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.4.tgz#f8eaf5f023f068a036d08cd07dc9ffb7d0065248" + integrity sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA== + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +electron-to-chromium@^1.5.73: + version "1.5.80" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.80.tgz#ca7a8361d7305f0ec9e203ce4e633cbb8a8ef1b1" + integrity sha512-LTrKpW0AqIuHwmlVNV+cjFYTnXtM9K37OGhpe0ZI10ScPSxqVSryZHIY3WnCS5NSYbBODRTZyhRMS2h5FAEqAw== + +emmet@^2.4.3: + version "2.4.11" + resolved "https://registry.yarnpkg.com/emmet/-/emmet-2.4.11.tgz#b331f572df37a252360ebee7dc4462c8d2e32f5c" + integrity sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ== + dependencies: + "@emmetio/abbreviation" "^2.3.3" + "@emmetio/css-abbreviation" "^2.1.8" + +emoji-regex-xs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz#e8af22e5d9dbd7f7f22d280af3d19d2aab5b0724" + integrity sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg== + +emoji-regex@^10.3.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.4.0.tgz#03553afea80b3975749cfcb36f776ca268e413d4" + integrity sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +encoding-sniffer@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz#799569d66d443babe82af18c9f403498365ef1d5" + integrity sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg== + dependencies: + iconv-lite "^0.6.3" + whatwg-encoding "^3.1.1" + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +entities@^4.2.0, entities@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +es-module-lexer@^1.4.1, es-module-lexer@^1.5.4: + version "1.6.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.6.0.tgz#da49f587fd9e68ee2404fe4e256c0c7d3a81be21" + integrity sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ== + +esast-util-from-estree@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz#8d1cfb51ad534d2f159dc250e604f3478a79f1ad" + integrity sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ== + dependencies: + "@types/estree-jsx" "^1.0.0" + devlop "^1.0.0" + estree-util-visit "^2.0.0" + unist-util-position-from-estree "^2.0.0" + +esast-util-from-js@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz#5147bec34cc9da44accf52f87f239a40ac3e8225" + integrity sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw== + dependencies: + "@types/estree-jsx" "^1.0.0" + acorn "^8.0.0" + esast-util-from-estree "^2.0.0" + vfile-message "^4.0.0" + +esbuild@^0.21.3, esbuild@^0.21.5: + version "0.21.5" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" + integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== + optionalDependencies: + "@esbuild/aix-ppc64" "0.21.5" + "@esbuild/android-arm" "0.21.5" + "@esbuild/android-arm64" "0.21.5" + "@esbuild/android-x64" "0.21.5" + "@esbuild/darwin-arm64" "0.21.5" + "@esbuild/darwin-x64" "0.21.5" + "@esbuild/freebsd-arm64" "0.21.5" + "@esbuild/freebsd-x64" "0.21.5" + "@esbuild/linux-arm" "0.21.5" + "@esbuild/linux-arm64" "0.21.5" + "@esbuild/linux-ia32" "0.21.5" + "@esbuild/linux-loong64" "0.21.5" + "@esbuild/linux-mips64el" "0.21.5" + "@esbuild/linux-ppc64" "0.21.5" + "@esbuild/linux-riscv64" "0.21.5" + "@esbuild/linux-s390x" "0.21.5" + "@esbuild/linux-x64" "0.21.5" + "@esbuild/netbsd-x64" "0.21.5" + "@esbuild/openbsd-x64" "0.21.5" + "@esbuild/sunos-x64" "0.21.5" + "@esbuild/win32-arm64" "0.21.5" + "@esbuild/win32-ia32" "0.21.5" + "@esbuild/win32-x64" "0.21.5" + +escalade@^3.1.1, escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escape-string-regexp@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" + integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +estree-util-attach-comments@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz#344bde6a64c8a31d15231e5ee9e297566a691c2d" + integrity sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw== + dependencies: + "@types/estree" "^1.0.0" + +estree-util-build-jsx@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz#b6d0bced1dcc4f06f25cf0ceda2b2dcaf98168f1" + integrity sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ== + dependencies: + "@types/estree-jsx" "^1.0.0" + devlop "^1.0.0" + estree-util-is-identifier-name "^3.0.0" + estree-walker "^3.0.0" + +estree-util-is-identifier-name@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz#0b5ef4c4ff13508b34dcd01ecfa945f61fce5dbd" + integrity sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg== + +estree-util-scope@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/estree-util-scope/-/estree-util-scope-1.0.0.tgz#9cbdfc77f5cb51e3d9ed4ad9c4adbff22d43e585" + integrity sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ== + dependencies: + "@types/estree" "^1.0.0" + devlop "^1.0.0" + +estree-util-to-js@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz#10a6fb924814e6abb62becf0d2bc4dea51d04f17" + integrity sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg== + dependencies: + "@types/estree-jsx" "^1.0.0" + astring "^1.8.0" + source-map "^0.7.0" + +estree-util-visit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/estree-util-visit/-/estree-util-visit-2.0.0.tgz#13a9a9f40ff50ed0c022f831ddf4b58d05446feb" + integrity sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww== + dependencies: + "@types/estree-jsx" "^1.0.0" + "@types/unist" "^3.0.0" + +estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +estree-walker@^3.0.0, estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + +expressive-code@^0.35.6: + version "0.35.6" + resolved "https://registry.yarnpkg.com/expressive-code/-/expressive-code-0.35.6.tgz#ad8ee87bd5d5376c07a5f4475bbf98366b5b307a" + integrity sha512-+mx+TPTbMqgo0mL92Xh9QgjW0kSQIsEivMgEcOnaqKqL7qCw8Vkqc5Rg/di7ZYw4aMUSr74VTc+w8GQWu05j1g== + dependencies: + "@expressive-code/core" "^0.35.6" + "@expressive-code/plugin-frames" "^0.35.6" + "@expressive-code/plugin-shiki" "^0.35.6" + "@expressive-code/plugin-text-markers" "^0.35.6" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== + dependencies: + is-extendable "^0.1.0" + +extend@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extract-zip@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== + dependencies: + debug "^4.1.1" + get-stream "^5.1.0" + yauzl "^2.10.0" + optionalDependencies: + "@types/yauzl" "^2.9.1" + +fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.2.12, fast-glob@^3.3.1, fast-glob@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" + integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.8" + +fast-uri@^3.0.1: + version "3.0.5" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.5.tgz#19f5f9691d0dab9b85861a7bb5d98fca961da9cd" + integrity sha512-5JnBCWpFlMo0a3ciDy/JckMzzv1U9coZrIhedq+HXxxUfDTAiS0LA8OKVao4G9BxmCVck/jtA5r3KAtRWEyD8Q== + +fastq@^1.6.0: + version "1.18.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.18.0.tgz#d631d7e25faffea81887fe5ea8c9010e1b36fee0" + integrity sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw== + dependencies: + reusify "^1.0.4" + +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== + dependencies: + pend "~1.2.0" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +find-up-simple@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/find-up-simple/-/find-up-simple-1.0.0.tgz#21d035fde9fdbd56c8f4d2f63f32fd93a1cfc368" + integrity sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw== + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-yarn-workspace-root2@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/find-yarn-workspace-root2/-/find-yarn-workspace-root2-1.2.16.tgz#60287009dd2f324f59646bdb4b7610a6b301c2a9" + integrity sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA== + dependencies: + micromatch "^4.0.2" + pkg-dir "^4.2.0" + +flattie@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/flattie/-/flattie-1.1.1.tgz#88182235723113667d36217fec55359275d6fe3d" + integrity sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ== + +follow-redirects@^1.15.6: + version "1.15.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + +foreground-child@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" + integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + +form-data@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" + integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +fraction.js@^4.3.7: + version "4.3.7" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" + integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-east-asian-width@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz#21b4071ee58ed04ee0db653371b55b4299875389" + integrity sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ== + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +github-slugger@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-2.0.0.tgz#52cf2f9279a21eb6c59dd385b410f0c0adda8f1a" + integrity sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw== + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@^10.3.10: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^15.13.0: + version "15.14.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-15.14.0.tgz#b8fd3a8941ff3b4d38f3319d433b61bbb482e73f" + integrity sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig== + +graceful-fs@^4.1.5: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +gray-matter@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.3.tgz#e893c064825de73ea1f5f7d88c7a9f7274288798" + integrity sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q== + dependencies: + js-yaml "^3.13.1" + kind-of "^6.0.2" + section-matter "^1.0.0" + strip-bom-string "^1.0.0" + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +hast-util-embedded@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz#be4477780fbbe079cdba22982e357a0de4ba853e" + integrity sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA== + dependencies: + "@types/hast" "^3.0.0" + hast-util-is-element "^3.0.0" + +hast-util-format@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hast-util-format/-/hast-util-format-1.1.0.tgz#373e77382e07deb04f6676f1b4437e7d8549d985" + integrity sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA== + dependencies: + "@types/hast" "^3.0.0" + hast-util-embedded "^3.0.0" + hast-util-minify-whitespace "^1.0.0" + hast-util-phrasing "^3.0.0" + hast-util-whitespace "^3.0.0" + html-whitespace-sensitive-tag-names "^3.0.0" + unist-util-visit-parents "^6.0.0" + +hast-util-from-html@^2.0.0, hast-util-from-html@^2.0.1, hast-util-from-html@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz#485c74785358beb80c4ba6346299311ac4c49c82" + integrity sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw== + dependencies: + "@types/hast" "^3.0.0" + devlop "^1.1.0" + hast-util-from-parse5 "^8.0.0" + parse5 "^7.0.0" + vfile "^6.0.0" + vfile-message "^4.0.0" + +hast-util-from-parse5@^8.0.0: + version "8.0.2" + resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-8.0.2.tgz#29b42758ba96535fd6021f0f533c000886c0f00f" + integrity sha512-SfMzfdAi/zAoZ1KkFEyyeXBn7u/ShQrfd675ZEE9M3qj+PMFX05xubzRyF76CCSJu8au9jgVxDV1+okFvgZU4A== + dependencies: + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + devlop "^1.0.0" + hastscript "^9.0.0" + property-information "^6.0.0" + vfile "^6.0.0" + vfile-location "^5.0.0" + web-namespaces "^2.0.0" + +hast-util-has-property@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz#4e595e3cddb8ce530ea92f6fc4111a818d8e7f93" + integrity sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA== + dependencies: + "@types/hast" "^3.0.0" + +hast-util-is-body-ok-link@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/hast-util-is-body-ok-link/-/hast-util-is-body-ok-link-3.0.1.tgz#ef63cb2f14f04ecf775139cd92bda5026380d8b4" + integrity sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ== + dependencies: + "@types/hast" "^3.0.0" + +hast-util-is-element@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz#6e31a6532c217e5b533848c7e52c9d9369ca0932" + integrity sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g== + dependencies: + "@types/hast" "^3.0.0" + +hast-util-minify-whitespace@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hast-util-minify-whitespace/-/hast-util-minify-whitespace-1.0.1.tgz#7588fd1a53f48f1d30406b81959dffc3650daf55" + integrity sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw== + dependencies: + "@types/hast" "^3.0.0" + hast-util-embedded "^3.0.0" + hast-util-is-element "^3.0.0" + hast-util-whitespace "^3.0.0" + unist-util-is "^6.0.0" + +hast-util-parse-selector@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz#352879fa86e25616036037dd8931fb5f34cb4a27" + integrity sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A== + dependencies: + "@types/hast" "^3.0.0" + +hast-util-phrasing@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/hast-util-phrasing/-/hast-util-phrasing-3.0.1.tgz#fa284c0cd4a82a0dd6020de8300a7b1ebffa1690" + integrity sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ== + dependencies: + "@types/hast" "^3.0.0" + hast-util-embedded "^3.0.0" + hast-util-has-property "^3.0.0" + hast-util-is-body-ok-link "^3.0.0" + hast-util-is-element "^3.0.0" + +hast-util-raw@^9.0.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-9.1.0.tgz#79b66b26f6f68fb50dfb4716b2cdca90d92adf2e" + integrity sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw== + dependencies: + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + "@ungap/structured-clone" "^1.0.0" + hast-util-from-parse5 "^8.0.0" + hast-util-to-parse5 "^8.0.0" + html-void-elements "^3.0.0" + mdast-util-to-hast "^13.0.0" + parse5 "^7.0.0" + unist-util-position "^5.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.0" + web-namespaces "^2.0.0" + zwitch "^2.0.0" + +hast-util-select@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/hast-util-select/-/hast-util-select-6.0.3.tgz#d30471b26efc88ae8a126ec36cd8ee6420fe3839" + integrity sha512-OVRQlQ1XuuLP8aFVLYmC2atrfWHS5UD3shonxpnyrjcCkwtvmt/+N6kYJdcY4mkMJhxp4kj2EFIxQ9kvkkt/eQ== + dependencies: + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + bcp-47-match "^2.0.0" + comma-separated-tokens "^2.0.0" + css-selector-parser "^3.0.0" + devlop "^1.0.0" + direction "^2.0.0" + hast-util-has-property "^3.0.0" + hast-util-to-string "^3.0.0" + hast-util-whitespace "^3.0.0" + nth-check "^2.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + unist-util-visit "^5.0.0" + zwitch "^2.0.0" + +hast-util-to-estree@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/hast-util-to-estree/-/hast-util-to-estree-3.1.1.tgz#b7f0b247d9f62127bb5db34e3a86c93d17279071" + integrity sha512-IWtwwmPskfSmma9RpzCappDUitC8t5jhAynHhc1m2+5trOgsrp7txscUSavc5Ic8PATyAjfrCK1wgtxh2cICVQ== + dependencies: + "@types/estree" "^1.0.0" + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + comma-separated-tokens "^2.0.0" + devlop "^1.0.0" + estree-util-attach-comments "^3.0.0" + estree-util-is-identifier-name "^3.0.0" + hast-util-whitespace "^3.0.0" + mdast-util-mdx-expression "^2.0.0" + mdast-util-mdx-jsx "^3.0.0" + mdast-util-mdxjs-esm "^2.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + style-to-object "^1.0.0" + unist-util-position "^5.0.0" + zwitch "^2.0.0" + +hast-util-to-html@^9.0.0, hast-util-to-html@^9.0.1, hast-util-to-html@^9.0.4: + version "9.0.4" + resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-9.0.4.tgz#d689c118c875aab1def692c58603e34335a0f5c5" + integrity sha512-wxQzXtdbhiwGAUKrnQJXlOPmHnEehzphwkK7aluUPQ+lEc1xefC8pblMgpp2w5ldBTEfveRIrADcrhGIWrlTDA== + dependencies: + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + ccount "^2.0.0" + comma-separated-tokens "^2.0.0" + hast-util-whitespace "^3.0.0" + html-void-elements "^3.0.0" + mdast-util-to-hast "^13.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + stringify-entities "^4.0.0" + zwitch "^2.0.4" + +hast-util-to-jsx-runtime@^2.0.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.2.tgz#6d11b027473e69adeaa00ca4cfb5bb68e3d282fa" + integrity sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg== + dependencies: + "@types/estree" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + comma-separated-tokens "^2.0.0" + devlop "^1.0.0" + estree-util-is-identifier-name "^3.0.0" + hast-util-whitespace "^3.0.0" + mdast-util-mdx-expression "^2.0.0" + mdast-util-mdx-jsx "^3.0.0" + mdast-util-mdxjs-esm "^2.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + style-to-object "^1.0.0" + unist-util-position "^5.0.0" + vfile-message "^4.0.0" + +hast-util-to-parse5@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz#477cd42d278d4f036bc2ea58586130f6f39ee6ed" + integrity sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw== + dependencies: + "@types/hast" "^3.0.0" + comma-separated-tokens "^2.0.0" + devlop "^1.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + web-namespaces "^2.0.0" + zwitch "^2.0.0" + +hast-util-to-string@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz#a4f15e682849326dd211c97129c94b0c3e76527c" + integrity sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A== + dependencies: + "@types/hast" "^3.0.0" + +hast-util-to-text@^4.0.0, hast-util-to-text@^4.0.1, hast-util-to-text@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz#57b676931e71bf9cb852453678495b3080bfae3e" + integrity sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A== + dependencies: + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + hast-util-is-element "^3.0.0" + unist-util-find-after "^5.0.0" + +hast-util-whitespace@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz#7778ed9d3c92dd9e8c5c8f648a49c21fc51cb621" + integrity sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== + dependencies: + "@types/hast" "^3.0.0" + +hastscript@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-8.0.0.tgz#4ef795ec8dee867101b9f23cc830d4baf4fd781a" + integrity sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw== + dependencies: + "@types/hast" "^3.0.0" + comma-separated-tokens "^2.0.0" + hast-util-parse-selector "^4.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + +hastscript@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-9.0.0.tgz#2b76b9aa3cba8bf6d5280869f6f6f7165c230763" + integrity sha512-jzaLBGavEDKHrc5EfFImKN7nZKKBdSLIdGvCwDZ9TfzbF2ffXiov8CKE445L2Z1Ek2t/m4SKQ2j6Ipv7NyUolw== + dependencies: + "@types/hast" "^3.0.0" + comma-separated-tokens "^2.0.0" + hast-util-parse-selector "^4.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + +html-escaper@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-3.0.3.tgz#4d336674652beb1dcbc29ef6b6ba7f6be6fdfed6" + integrity sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ== + +html-void-elements@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-3.0.0.tgz#fc9dbd84af9e747249034d4d62602def6517f1d7" + integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg== + +html-whitespace-sensitive-tag-names@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/html-whitespace-sensitive-tag-names/-/html-whitespace-sensitive-tag-names-3.0.1.tgz#c35edd28205f3bf8c1fd03274608d60b923de5b2" + integrity sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA== + +htmlparser2@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-9.1.0.tgz#cdb498d8a75a51f739b61d3f718136c369bc8c23" + integrity sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.1.0" + entities "^4.5.0" + +http-cache-semantics@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + +iconv-lite@0.6.3, iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +immutable@^5.0.2: + version "5.0.3" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-5.0.3.tgz#aa037e2313ea7b5d400cd9298fa14e404c933db1" + integrity sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw== + +import-meta-resolve@^4.0.0, import-meta-resolve@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz#f9db8bead9fafa61adb811db77a2bf22c5399706" + integrity sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw== + +inline-style-parser@0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.2.4.tgz#f4af5fe72e612839fcd453d989a586566d695f22" + integrity sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q== + +is-alphabetical@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz#01072053ea7c1036df3c7d19a6daaec7f19e789b" + integrity sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ== + +is-alphanumerical@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz#7c03fbe96e3e931113e57f964b0a368cc2dfd875" + integrity sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw== + dependencies: + is-alphabetical "^2.0.0" + is-decimal "^2.0.0" + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-buffer@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +is-core-module@^2.16.0: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + +is-decimal@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.1.tgz#9469d2dc190d0214fd87d78b78caecc0cc14eef7" + integrity sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A== + +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + +is-extendable@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-hexadecimal@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz#86b5bf668fca307498d319dfc03289d781a90027" + integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg== + +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + +is-interactive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90" + integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-obj@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" + integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== + +is-unicode-supported@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz#d824984b616c292a2e198207d4a609983842f714" + integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== + +is-unicode-supported@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz#09f0ab0de6d3744d48d265ebb98f65d11f2a9b3a" + integrity sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ== + +is-wsl@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== + dependencies: + is-inside-container "^1.0.0" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +jiti@^1.21.6: + version "1.21.7" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.7.tgz#9dd81043424a3d28458b193d965f0d18a2300ba9" + integrity sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.0, js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsesc@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" + integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonc-parser@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342" + integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg== + +jsonc-parser@^3.0.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.3.1.tgz#f2a524b4f7fd11e3d791e559977ad60b98b798b4" + integrity sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +kleur@^4.1.4, kleur@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780" + integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== + +kolorist@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/kolorist/-/kolorist-1.8.0.tgz#edddbbbc7894bc13302cdf740af6374d4a04743c" + integrity sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ== + +lilconfig@^3.0.0, lilconfig@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.3.tgz#a1bcfd6257f9585bf5ae14ceeebb7b559025e4c4" + integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +linkedom@^0.16.11: + version "0.16.11" + resolved "https://registry.yarnpkg.com/linkedom/-/linkedom-0.16.11.tgz#2419649b178be5a627f6b8b2cfad521dfa726ae9" + integrity sha512-WgaTVbj7itjyXTsCvgerpneERXShcnNJF5VIV+/4SLtyRLN+HppPre/WDHRofAr2IpEuujSNgJbCBd5lMl6lRw== + dependencies: + css-select "^5.1.0" + cssom "^0.5.0" + html-escaper "^3.0.3" + htmlparser2 "^9.1.0" + uhyphen "^0.2.0" + +load-yaml-file@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/load-yaml-file/-/load-yaml-file-0.2.0.tgz#af854edaf2bea89346c07549122753c07372f64d" + integrity sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw== + dependencies: + graceful-fs "^4.1.5" + js-yaml "^3.13.0" + pify "^4.0.1" + strip-bom "^3.0.0" + +local-pkg@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.5.1.tgz#69658638d2a95287534d4c2fff757980100dbb6d" + integrity sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ== + dependencies: + mlly "^1.7.3" + pkg-types "^1.2.1" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lodash@4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-6.0.0.tgz#bb95e5f05322651cac30c0feb6404f9f2a8a9439" + integrity sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw== + dependencies: + chalk "^5.3.0" + is-unicode-supported "^1.3.0" + +longest-streak@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4" + integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== + +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +magic-string@^0.30.14: + version "0.30.17" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" + integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + +magicast@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/magicast/-/magicast-0.3.5.tgz#8301c3c7d66704a0771eb1bad74274f0ec036739" + integrity sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ== + dependencies: + "@babel/parser" "^7.25.4" + "@babel/types" "^7.25.4" + source-map-js "^1.2.0" + +markdown-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/markdown-extensions/-/markdown-extensions-2.0.0.tgz#34bebc83e9938cae16e0e017e4a9814a8330d3c4" + integrity sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q== + +markdown-table@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.4.tgz#fe44d6d410ff9d6f2ea1797a3f60aa4d2b631c2a" + integrity sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw== + +mdast-util-definitions@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz#c1bb706e5e76bb93f9a09dd7af174002ae69ac24" + integrity sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ== + dependencies: + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + unist-util-visit "^5.0.0" + +mdast-util-directive@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-directive/-/mdast-util-directive-3.0.0.tgz#3fb1764e705bbdf0afb0d3f889e4404c3e82561f" + integrity sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q== + dependencies: + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + devlop "^1.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + parse-entities "^4.0.0" + stringify-entities "^4.0.0" + unist-util-visit-parents "^6.0.0" + +mdast-util-find-and-replace@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz#70a3174c894e14df722abf43bc250cbae44b11df" + integrity sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg== + dependencies: + "@types/mdast" "^4.0.0" + escape-string-regexp "^5.0.0" + unist-util-is "^6.0.0" + unist-util-visit-parents "^6.0.0" + +mdast-util-from-markdown@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz#4850390ca7cf17413a9b9a0fbefcd1bc0eb4160a" + integrity sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA== + dependencies: + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + mdast-util-to-string "^4.0.0" + micromark "^4.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-decode-string "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + unist-util-stringify-position "^4.0.0" + +mdast-util-gfm-autolink-literal@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz#abd557630337bd30a6d5a4bd8252e1c2dc0875d5" + integrity sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ== + dependencies: + "@types/mdast" "^4.0.0" + ccount "^2.0.0" + devlop "^1.0.0" + mdast-util-find-and-replace "^3.0.0" + micromark-util-character "^2.0.0" + +mdast-util-gfm-footnote@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz#25a1753c7d16db8bfd53cd84fe50562bd1e6d6a9" + integrity sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ== + dependencies: + "@types/mdast" "^4.0.0" + devlop "^1.1.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + +mdast-util-gfm-strikethrough@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz#d44ef9e8ed283ac8c1165ab0d0dfd058c2764c16" + integrity sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-gfm-table@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz#7a435fb6223a72b0862b33afbd712b6dae878d38" + integrity sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg== + dependencies: + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + markdown-table "^3.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-gfm-task-list-item@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz#e68095d2f8a4303ef24094ab642e1047b991a936" + integrity sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ== + dependencies: + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-gfm@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz#3f2aecc879785c3cb6a81ff3a243dc11eca61095" + integrity sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw== + dependencies: + mdast-util-from-markdown "^2.0.0" + mdast-util-gfm-autolink-literal "^2.0.0" + mdast-util-gfm-footnote "^2.0.0" + mdast-util-gfm-strikethrough "^2.0.0" + mdast-util-gfm-table "^2.0.0" + mdast-util-gfm-task-list-item "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-mdx-expression@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz#43f0abac9adc756e2086f63822a38c8d3c3a5096" + integrity sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ== + dependencies: + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-mdx-jsx@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz#fd04c67a2a7499efb905a8a5c578dddc9fdada0d" + integrity sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q== + dependencies: + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + ccount "^2.0.0" + devlop "^1.1.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + parse-entities "^4.0.0" + stringify-entities "^4.0.0" + unist-util-stringify-position "^4.0.0" + vfile-message "^4.0.0" + +mdast-util-mdx@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz#792f9cf0361b46bee1fdf1ef36beac424a099c41" + integrity sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w== + dependencies: + mdast-util-from-markdown "^2.0.0" + mdast-util-mdx-expression "^2.0.0" + mdast-util-mdx-jsx "^3.0.0" + mdast-util-mdxjs-esm "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-mdxjs-esm@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz#019cfbe757ad62dd557db35a695e7314bcc9fa97" + integrity sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg== + dependencies: + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-phrasing@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz#7cc0a8dec30eaf04b7b1a9661a92adb3382aa6e3" + integrity sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w== + dependencies: + "@types/mdast" "^4.0.0" + unist-util-is "^6.0.0" + +mdast-util-to-hast@^13.0.0: + version "13.2.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz#5ca58e5b921cc0a3ded1bc02eed79a4fe4fe41f4" + integrity sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA== + dependencies: + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + "@ungap/structured-clone" "^1.0.0" + devlop "^1.0.0" + micromark-util-sanitize-uri "^2.0.0" + trim-lines "^3.0.0" + unist-util-position "^5.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.0" + +mdast-util-to-markdown@^2.0.0, mdast-util-to-markdown@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz#f910ffe60897f04bb4b7e7ee434486f76288361b" + integrity sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA== + dependencies: + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + longest-streak "^3.0.0" + mdast-util-phrasing "^4.0.0" + mdast-util-to-string "^4.0.0" + micromark-util-classify-character "^2.0.0" + micromark-util-decode-string "^2.0.0" + unist-util-visit "^5.0.0" + zwitch "^2.0.0" + +mdast-util-to-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz#7a5121475556a04e7eddeb67b264aae79d312814" + integrity sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg== + dependencies: + "@types/mdast" "^4.0.0" + +mdn-data@2.0.28: + version "2.0.28" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba" + integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g== + +mdn-data@2.0.30: + version "2.0.30" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" + integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== + +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromark-core-commonmark@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz#6a45bbb139e126b3f8b361a10711ccc7c6e15e93" + integrity sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w== + dependencies: + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + micromark-factory-destination "^2.0.0" + micromark-factory-label "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-factory-title "^2.0.0" + micromark-factory-whitespace "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-classify-character "^2.0.0" + micromark-util-html-tag-name "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-subtokenize "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-directive@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz#2eb61985d1995a7c1ff7621676a4f32af29409e8" + integrity sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA== + dependencies: + devlop "^1.0.0" + micromark-factory-space "^2.0.0" + micromark-factory-whitespace "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + parse-entities "^4.0.0" + +micromark-extension-gfm-autolink-literal@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz#6286aee9686c4462c1e3552a9d505feddceeb935" + integrity sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-footnote@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz#4dab56d4e398b9853f6fe4efac4fc9361f3e0750" + integrity sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw== + dependencies: + devlop "^1.0.0" + micromark-core-commonmark "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-strikethrough@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz#86106df8b3a692b5f6a92280d3879be6be46d923" + integrity sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw== + dependencies: + devlop "^1.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-classify-character "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-table@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.0.tgz#5cadedfbb29fca7abf752447967003dc3b6583c9" + integrity sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g== + dependencies: + devlop "^1.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-tagfilter@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz#f26d8a7807b5985fba13cf61465b58ca5ff7dc57" + integrity sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg== + dependencies: + micromark-util-types "^2.0.0" + +micromark-extension-gfm-task-list-item@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz#bcc34d805639829990ec175c3eea12bb5b781f2c" + integrity sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw== + dependencies: + devlop "^1.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz#3e13376ab95dd7a5cfd0e29560dfe999657b3c5b" + integrity sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w== + dependencies: + micromark-extension-gfm-autolink-literal "^2.0.0" + micromark-extension-gfm-footnote "^2.0.0" + micromark-extension-gfm-strikethrough "^2.0.0" + micromark-extension-gfm-table "^2.0.0" + micromark-extension-gfm-tagfilter "^2.0.0" + micromark-extension-gfm-task-list-item "^2.0.0" + micromark-util-combine-extensions "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-mdx-expression@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.0.tgz#1407b9ce69916cf5e03a196ad9586889df25302a" + integrity sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ== + dependencies: + "@types/estree" "^1.0.0" + devlop "^1.0.0" + micromark-factory-mdx-expression "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-events-to-acorn "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-mdx-jsx@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.1.tgz#5abb83da5ddc8e473a374453e6ea56fbd66b59ad" + integrity sha512-vNuFb9czP8QCtAQcEJn0UJQJZA8Dk6DXKBqx+bg/w0WGuSxDxNr7hErW89tHUY31dUW4NqEOWwmEUNhjTFmHkg== + dependencies: + "@types/acorn" "^4.0.0" + "@types/estree" "^1.0.0" + devlop "^1.0.0" + estree-util-is-identifier-name "^3.0.0" + micromark-factory-mdx-expression "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-events-to-acorn "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + vfile-message "^4.0.0" + +micromark-extension-mdx-md@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz#1d252881ea35d74698423ab44917e1f5b197b92d" + integrity sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ== + dependencies: + micromark-util-types "^2.0.0" + +micromark-extension-mdxjs-esm@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz#de21b2b045fd2059bd00d36746081de38390d54a" + integrity sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A== + dependencies: + "@types/estree" "^1.0.0" + devlop "^1.0.0" + micromark-core-commonmark "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-events-to-acorn "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + unist-util-position-from-estree "^2.0.0" + vfile-message "^4.0.0" + +micromark-extension-mdxjs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz#b5a2e0ed449288f3f6f6c544358159557549de18" + integrity sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ== + dependencies: + acorn "^8.0.0" + acorn-jsx "^5.0.0" + micromark-extension-mdx-expression "^3.0.0" + micromark-extension-mdx-jsx "^3.0.0" + micromark-extension-mdx-md "^2.0.0" + micromark-extension-mdxjs-esm "^3.0.0" + micromark-util-combine-extensions "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-destination@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz#8fef8e0f7081f0474fbdd92deb50c990a0264639" + integrity sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-label@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz#5267efa97f1e5254efc7f20b459a38cb21058ba1" + integrity sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg== + dependencies: + devlop "^1.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-mdx-expression@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.2.tgz#2afaa8ba6d5f63e0cead3e4dee643cad184ca260" + integrity sha512-5E5I2pFzJyg2CtemqAbcyCktpHXuJbABnsb32wX2U8IQKhhVFBqkcZR5LRm1WVoFqa4kTueZK4abep7wdo9nrw== + dependencies: + "@types/estree" "^1.0.0" + devlop "^1.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-events-to-acorn "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + unist-util-position-from-estree "^2.0.0" + vfile-message "^4.0.0" + +micromark-factory-space@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz#36d0212e962b2b3121f8525fc7a3c7c029f334fc" + integrity sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-title@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz#237e4aa5d58a95863f01032d9ee9b090f1de6e94" + integrity sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw== + dependencies: + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-whitespace@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz#06b26b2983c4d27bfcc657b33e25134d4868b0b1" + integrity sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ== + dependencies: + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-character@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz#2f987831a40d4c510ac261e89852c4e9703ccda6" + integrity sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q== + dependencies: + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-chunked@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz#47fbcd93471a3fccab86cff03847fc3552db1051" + integrity sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA== + dependencies: + micromark-util-symbol "^2.0.0" + +micromark-util-classify-character@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz#d399faf9c45ca14c8b4be98b1ea481bced87b629" + integrity sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-combine-extensions@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz#2a0f490ab08bff5cc2fd5eec6dd0ca04f89b30a9" + integrity sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg== + dependencies: + micromark-util-chunked "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-decode-numeric-character-reference@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz#fcf15b660979388e6f118cdb6bf7d79d73d26fe5" + integrity sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw== + dependencies: + micromark-util-symbol "^2.0.0" + +micromark-util-decode-string@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz#6cb99582e5d271e84efca8e61a807994d7161eb2" + integrity sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ== + dependencies: + decode-named-character-reference "^1.0.0" + micromark-util-character "^2.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-symbol "^2.0.0" + +micromark-util-encode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz#0d51d1c095551cfaac368326963cf55f15f540b8" + integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== + +micromark-util-events-to-acorn@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.2.tgz#4275834f5453c088bd29cd72dfbf80e3327cec07" + integrity sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA== + dependencies: + "@types/acorn" "^4.0.0" + "@types/estree" "^1.0.0" + "@types/unist" "^3.0.0" + devlop "^1.0.0" + estree-util-visit "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + vfile-message "^4.0.0" + +micromark-util-html-tag-name@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz#e40403096481986b41c106627f98f72d4d10b825" + integrity sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA== + +micromark-util-normalize-identifier@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz#c30d77b2e832acf6526f8bf1aa47bc9c9438c16d" + integrity sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q== + dependencies: + micromark-util-symbol "^2.0.0" + +micromark-util-resolve-all@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz#e1a2d62cdd237230a2ae11839027b19381e31e8b" + integrity sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg== + dependencies: + micromark-util-types "^2.0.0" + +micromark-util-sanitize-uri@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz#ab89789b818a58752b73d6b55238621b7faa8fd7" + integrity sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-encode "^2.0.0" + micromark-util-symbol "^2.0.0" + +micromark-util-subtokenize@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.3.tgz#70ffb99a454bd8c913c8b709c3dc97baefb65f96" + integrity sha512-VXJJuNxYWSoYL6AJ6OQECCFGhIU2GGHMw8tahogePBrjkG8aCCas3ibkp7RnVOSTClg2is05/R7maAhF1XyQMg== + dependencies: + devlop "^1.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-symbol@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz#e5da494e8eb2b071a0d08fb34f6cefec6c0a19b8" + integrity sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q== + +micromark-util-types@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.1.tgz#a3edfda3022c6c6b55bfb049ef5b75d70af50709" + integrity sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ== + +micromark@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/micromark/-/micromark-4.0.1.tgz#294c2f12364759e5f9e925a767ae3dfde72223ff" + integrity sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw== + dependencies: + "@types/debug" "^4.0.0" + debug "^4.0.0" + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + micromark-core-commonmark "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-combine-extensions "^2.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-encode "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-subtokenize "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromatch@^4.0.2, micromatch@^4.0.5, micromatch@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mimic-function@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" + integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== + +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minipass@^3.0.0: + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^4.0.0: + version "4.2.8" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.8.tgz#f0010f64393ecfc1d1ccb5f582bcaf45f48e1a3a" + integrity sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ== + +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mlly@^1.7.3: + version "1.7.4" + resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.7.4.tgz#3d7295ea2358ec7a271eaa5d000a0f84febe100f" + integrity sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw== + dependencies: + acorn "^8.14.0" + pathe "^2.0.1" + pkg-types "^1.3.0" + ufo "^1.5.4" + +mrmime@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.0.tgz#151082a6e06e59a9a39b46b3e14d5cfe92b3abb4" + integrity sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw== + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +muggle-string@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/muggle-string/-/muggle-string-0.4.1.tgz#3b366bd43b32f809dc20659534dd30e7c8a0d328" + integrity sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ== + +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +nanoid@^3.3.8: + version "3.3.8" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" + integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== + +neotraverse@^0.6.18: + version "0.6.18" + resolved "https://registry.yarnpkg.com/neotraverse/-/neotraverse-0.6.18.tgz#abcb33dda2e8e713cf6321b29405e822230cdb30" + integrity sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA== + +nlcst-to-string@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/nlcst-to-string/-/nlcst-to-string-3.1.1.tgz#83b90f2e1ee2081e14701317efc26d3bbadc806e" + integrity sha512-63mVyqaqt0cmn2VcI2aH6kxe1rLAmSROqHMA0i4qqg1tidkfExgpb0FGMikMCn86mw5dFtBtEANfmSSK7TjNHw== + dependencies: + "@types/nlcst" "^1.0.0" + +nlcst-to-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz#05511e8461ebfb415952eb0b7e9a1a7d40471bd4" + integrity sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA== + dependencies: + "@types/nlcst" "^2.0.0" + +node-addon-api@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" + integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== + +node-releases@^2.0.19: + version "2.0.19" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" + integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== + +nth-check@^2.0.0, nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + +object-assign@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== + +once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-7.0.0.tgz#9f16c92d8c9ef5120e3acd9dd9957cceecc1ab60" + integrity sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== + dependencies: + mimic-function "^5.0.0" + +oniguruma-to-es@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/oniguruma-to-es/-/oniguruma-to-es-1.0.0.tgz#6f7104cf0492e25d42b203d892b6d2d5f5f4d2e7" + integrity sha512-kihvp0O4lFwf5tZMkfanwQLIZ9ORe9OeOFgZonH0BQeThgwfJiaZFeOfvvJVnJIM9TiVmx0RDD35hUJDR0++rQ== + dependencies: + emoji-regex-xs "^1.0.0" + regex "^5.1.1" + regex-recursion "^5.1.1" + +ora@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-8.1.1.tgz#8efc8865e44c87e4b55468a47e80a03e678b0e54" + integrity sha512-YWielGi1XzG1UTvOaCFaNgEnuhZVMSHYkW/FQ7UX8O26PtlpdM84c0f7wLPlkvx2RfiQmnzd61d/MGxmpQeJPw== + dependencies: + chalk "^5.3.0" + cli-cursor "^5.0.0" + cli-spinners "^2.9.2" + is-interactive "^2.0.0" + is-unicode-supported "^2.0.0" + log-symbols "^6.0.0" + stdin-discarder "^0.2.2" + string-width "^7.2.0" + strip-ansi "^7.1.0" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^6.1.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-6.2.0.tgz#c254d22ba6aeef441a3564c5e6c2f2da59268a0f" + integrity sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA== + dependencies: + yocto-queue "^1.1.1" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-queue@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-8.0.1.tgz#718b7f83836922ef213ddec263ff4223ce70bef8" + integrity sha512-NXzu9aQJTAzbBqOt2hwsR63ea7yvxJc0PwN/zobNAudYfb1B7R08SzB4TsLeSbUCuG467NhnoT0oO6w1qRO+BA== + dependencies: + eventemitter3 "^5.0.1" + p-timeout "^6.1.2" + +p-timeout@^6.1.2: + version "6.1.4" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-6.1.4.tgz#418e1f4dd833fa96a2e3f532547dd2abdb08dbc2" + integrity sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg== + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + +package-manager-detector@^0.2.0: + version "0.2.8" + resolved "https://registry.yarnpkg.com/package-manager-detector/-/package-manager-detector-0.2.8.tgz#f5ace2dbd37666af54e5acec11bc37c8450f72d0" + integrity sha512-ts9KSdroZisdvKMWVAVCXiKqnqNfXz4+IbrBG8/BWx/TR5le+jfenvoBuIZ6UWM9nz47W7AbD9qYfAwfWMIwzA== + +pagefind@^1.0.3: + version "1.3.0" + resolved "https://registry.yarnpkg.com/pagefind/-/pagefind-1.3.0.tgz#467560447dcc7bbe590f1b888cc8bc733bb377fa" + integrity sha512-8KPLGT5g9s+olKMRTU9LFekLizkVIu9tes90O1/aigJ0T5LmyPqTzGJrETnSw3meSYg58YH7JTzhTTW/3z6VAw== + optionalDependencies: + "@pagefind/darwin-arm64" "1.3.0" + "@pagefind/darwin-x64" "1.3.0" + "@pagefind/linux-arm64" "1.3.0" + "@pagefind/linux-x64" "1.3.0" + "@pagefind/windows-x64" "1.3.0" + +parse-entities@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-4.0.2.tgz#61d46f5ed28e4ee62e9ddc43d6b010188443f159" + integrity sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw== + dependencies: + "@types/unist" "^2.0.0" + character-entities-legacy "^3.0.0" + character-reference-invalid "^2.0.0" + decode-named-character-reference "^1.0.0" + is-alphanumerical "^2.0.0" + is-decimal "^2.0.0" + is-hexadecimal "^2.0.0" + +parse-latin@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/parse-latin/-/parse-latin-5.0.1.tgz#f3b4fac54d06f6a0501cf8b8ecfafa4cbb4f2f47" + integrity sha512-b/K8ExXaWC9t34kKeDV8kGXBkXZ1HCSAZRYE7HR14eA1GlXX5L8iWhs8USJNhQU9q5ci413jCKF0gOyovvyRBg== + dependencies: + nlcst-to-string "^3.0.0" + unist-util-modify-children "^3.0.0" + unist-util-visit-children "^2.0.0" + +parse-latin@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/parse-latin/-/parse-latin-7.0.0.tgz#8dfacac26fa603f76417f36233fc45602a323e1d" + integrity sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ== + dependencies: + "@types/nlcst" "^2.0.0" + "@types/unist" "^3.0.0" + nlcst-to-string "^4.0.0" + unist-util-modify-children "^4.0.0" + unist-util-visit-children "^3.0.0" + vfile "^6.0.0" + +parse5-htmlparser2-tree-adapter@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz#b5a806548ed893a43e24ccb42fbb78069311e81b" + integrity sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g== + dependencies: + domhandler "^5.0.3" + parse5 "^7.0.0" + +parse5-parser-stream@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz#d7c20eadc37968d272e2c02660fff92dd27e60e1" + integrity sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow== + dependencies: + parse5 "^7.0.0" + +parse5@^7.0.0, parse5@^7.1.2: + version "7.2.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.2.1.tgz#8928f55915e6125f430cc44309765bf17556a33a" + integrity sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ== + dependencies: + entities "^4.5.0" + +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +pathe@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" + integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== + +pathe@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.1.tgz#ee1e6965c5ccfc98dc5a4b366a6ba6dd624a33d6" + integrity sha512-6jpjMpOth5S9ITVu5clZ7NOgHNsv5vRQdheL9ztp2vZmM6fRbLvyua1tiBIL4lk8SAe3ARzeXEly6siXCjDHDw== + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== + +picocolors@^1.0.0, picocolors@^1.0.1, picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +picomatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" + integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== + +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pirates@^4.0.1: + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +pkg-types@^1.2.1, pkg-types@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.3.0.tgz#53d915eb99485798c554ad8eb2dc2af7c03006eb" + integrity sha512-kS7yWjVFCkIw9hqdJBoMxDdzEngmkr5FXeWZZfQ6GoYacjVnsW6l2CcYW/0ThD0vF4LPJgVYnrg4d0uuhwYQbg== + dependencies: + confbox "^0.1.8" + mlly "^1.7.3" + pathe "^1.1.2" + +postcss-import@^15.1.0: + version "15.1.0" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-15.1.0.tgz#41c64ed8cc0e23735a9698b3249ffdbf704adc70" + integrity sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew== + dependencies: + postcss-value-parser "^4.0.0" + read-cache "^1.0.0" + resolve "^1.1.7" + +postcss-js@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.1.tgz#61598186f3703bab052f1c4f7d805f3991bee9d2" + integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw== + dependencies: + camelcase-css "^2.0.1" + +postcss-load-config@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.2.tgz#7159dcf626118d33e299f485d6afe4aff7c4a3e3" + integrity sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ== + dependencies: + lilconfig "^3.0.0" + yaml "^2.3.4" + +postcss-nested@^6.0.1, postcss-nested@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.2.0.tgz#4c2d22ab5f20b9cb61e2c5c5915950784d068131" + integrity sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ== + dependencies: + postcss-selector-parser "^6.1.1" + +postcss-selector-parser@^6.1.1, postcss-selector-parser@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de" + integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@^8.4.38, postcss@^8.4.43, postcss@^8.4.47, postcss@^8.4.49: + version "8.5.0" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.0.tgz#15244b9fd65f809b2819682456f0e7e1e30c145b" + integrity sha512-27VKOqrYfPncKA2NrFOVhP5MGAfHKLYn/Q0mz9cNQyRAKYi3VNHwYU2qKKqPCqgBmeeJ0uAFB56NumXZ5ZReXg== + dependencies: + nanoid "^3.3.8" + picocolors "^1.1.1" + source-map-js "^1.2.1" + +preferred-pm@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/preferred-pm/-/preferred-pm-4.0.0.tgz#6b256a44d39181fb3829b3abbd9ea2ead6db082b" + integrity sha512-gYBeFTZLu055D8Vv3cSPox/0iTPtkzxpLroSYYA7WXgRi31WCJ51Uyl8ZiPeUUjyvs2MBzK+S8v9JVUgHU/Sqw== + dependencies: + find-up-simple "^1.0.0" + find-yarn-workspace-root2 "1.2.16" + which-pm "^3.0.0" + +prettier-plugin-astro@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/prettier-plugin-astro/-/prettier-plugin-astro-0.13.0.tgz#0e1ff91caae17cad5b9493eb55bbd7114515bc5b" + integrity sha512-5HrJNnPmZqTUNoA97zn4gNQv9BgVhv+et03314WpQ9H9N8m2L9OSV798olwmG2YLXPl1iSstlJCR1zB3x5xG4g== + dependencies: + "@astrojs/compiler" "^1.5.5" + prettier "^3.0.0" + sass-formatter "^0.7.6" + +prettier@2.8.7: + version "2.8.7" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" + integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== + +prettier@^3.0.0, prettier@^3.2.5: + version "3.4.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.4.2.tgz#a5ce1fb522a588bf2b78ca44c6e6fe5aa5a2b13f" + integrity sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ== + +prismjs@^1.29.0: + version "1.29.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" + integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q== + +prompts@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +property-information@^6.0.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.5.0.tgz#6212fbb52ba757e92ef4fb9d657563b933b7ffec" + integrity sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig== + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +pump@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.2.tgz#836f3edd6bc2ee599256c924ffe0d88573ddcbf8" + integrity sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== + dependencies: + pify "^2.3.0" + +readdirp@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.1.tgz#bd115327129672dc47f87408f05df9bd9ca3ef55" + integrity sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw== + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +recma-build-jsx@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz#c02f29e047e103d2fab2054954e1761b8ea253c4" + integrity sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew== + dependencies: + "@types/estree" "^1.0.0" + estree-util-build-jsx "^3.0.0" + vfile "^6.0.0" + +recma-jsx@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/recma-jsx/-/recma-jsx-1.0.0.tgz#f7bef02e571a49d6ba3efdfda8e2efab48dbe3aa" + integrity sha512-5vwkv65qWwYxg+Atz95acp8DMu1JDSqdGkA2Of1j6rCreyFUE/gp15fC8MnGEuG1W68UKjM6x6+YTWIh7hZM/Q== + dependencies: + acorn-jsx "^5.0.0" + estree-util-to-js "^2.0.0" + recma-parse "^1.0.0" + recma-stringify "^1.0.0" + unified "^11.0.0" + +recma-parse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/recma-parse/-/recma-parse-1.0.0.tgz#c351e161bb0ab47d86b92a98a9d891f9b6814b52" + integrity sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ== + dependencies: + "@types/estree" "^1.0.0" + esast-util-from-js "^2.0.0" + unified "^11.0.0" + vfile "^6.0.0" + +recma-stringify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/recma-stringify/-/recma-stringify-1.0.0.tgz#54632030631e0c7546136ff9ef8fde8e7b44f130" + integrity sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g== + dependencies: + "@types/estree" "^1.0.0" + estree-util-to-js "^2.0.0" + unified "^11.0.0" + vfile "^6.0.0" + +regex-recursion@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/regex-recursion/-/regex-recursion-5.1.1.tgz#5a73772d18adbf00f57ad097bf54171b39d78f8b" + integrity sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w== + dependencies: + regex "^5.1.1" + regex-utilities "^2.3.0" + +regex-utilities@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/regex-utilities/-/regex-utilities-2.3.0.tgz#87163512a15dce2908cf079c8960d5158ff43280" + integrity sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng== + +regex@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/regex/-/regex-5.1.1.tgz#cf798903f24d6fe6e531050a36686e082b29bd03" + integrity sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw== + dependencies: + regex-utilities "^2.3.0" + +rehype-expressive-code@^0.35.6: + version "0.35.6" + resolved "https://registry.yarnpkg.com/rehype-expressive-code/-/rehype-expressive-code-0.35.6.tgz#aacae31a68664b4055b4c969a69992787ace5307" + integrity sha512-pPdE+pRcRw01kxMOwHQjuRxgwlblZt5+wAc3w2aPGgmcnn57wYjn07iKO7zaznDxYVxMYVvYlnL+R3vWFQS4Gw== + dependencies: + expressive-code "^0.35.6" + +rehype-format@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/rehype-format/-/rehype-format-5.0.1.tgz#e255e59bed0c062156aaf51c16fad5a521a1f5c8" + integrity sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ== + dependencies: + "@types/hast" "^3.0.0" + hast-util-format "^1.0.0" + +rehype-parse@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-9.0.1.tgz#9993bda129acc64c417a9d3654a7be38b2a94c20" + integrity sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag== + dependencies: + "@types/hast" "^3.0.0" + hast-util-from-html "^2.0.0" + unified "^11.0.0" + +rehype-raw@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/rehype-raw/-/rehype-raw-7.0.0.tgz#59d7348fd5dbef3807bbaa1d443efd2dd85ecee4" + integrity sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww== + dependencies: + "@types/hast" "^3.0.0" + hast-util-raw "^9.0.0" + vfile "^6.0.0" + +rehype-recma@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rehype-recma/-/rehype-recma-1.0.0.tgz#d68ef6344d05916bd96e25400c6261775411aa76" + integrity sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw== + dependencies: + "@types/estree" "^1.0.0" + "@types/hast" "^3.0.0" + hast-util-to-estree "^3.0.0" + +rehype-stringify@^10.0.0, rehype-stringify@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/rehype-stringify/-/rehype-stringify-10.0.1.tgz#2ec1ebc56c6aba07905d3b4470bdf0f684f30b75" + integrity sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA== + dependencies: + "@types/hast" "^3.0.0" + hast-util-to-html "^9.0.0" + unified "^11.0.0" + +rehype@^13.0.1, rehype@^13.0.2: + version "13.0.2" + resolved "https://registry.yarnpkg.com/rehype/-/rehype-13.0.2.tgz#ab0b3ac26573d7b265a0099feffad450e4cf1952" + integrity sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A== + dependencies: + "@types/hast" "^3.0.0" + rehype-parse "^9.0.0" + rehype-stringify "^10.0.0" + unified "^11.0.0" + +remark-directive@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/remark-directive/-/remark-directive-3.0.0.tgz#34452d951b37e6207d2e2a4f830dc33442923268" + integrity sha512-l1UyWJ6Eg1VPU7Hm/9tt0zKtReJQNOA4+iDMAxTyZNWnJnFlbS/7zhiel/rogTLQ2vMYwDzSJa4BiVNqGlqIMA== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-directive "^3.0.0" + micromark-extension-directive "^3.0.0" + unified "^11.0.0" + +remark-gfm@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/remark-gfm/-/remark-gfm-4.0.0.tgz#aea777f0744701aa288b67d28c43565c7e8c35de" + integrity sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-gfm "^3.0.0" + micromark-extension-gfm "^3.0.0" + remark-parse "^11.0.0" + remark-stringify "^11.0.0" + unified "^11.0.0" + +remark-mdx@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-3.1.0.tgz#f979be729ecb35318fa48e2135c1169607a78343" + integrity sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA== + dependencies: + mdast-util-mdx "^3.0.0" + micromark-extension-mdxjs "^3.0.0" + +remark-parse@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-11.0.0.tgz#aa60743fcb37ebf6b069204eb4da304e40db45a1" + integrity sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-from-markdown "^2.0.0" + micromark-util-types "^2.0.0" + unified "^11.0.0" + +remark-rehype@^11.0.0, remark-rehype@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-11.1.1.tgz#f864dd2947889a11997c0a2667cd6b38f685bca7" + integrity sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ== + dependencies: + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + mdast-util-to-hast "^13.0.0" + unified "^11.0.0" + vfile "^6.0.0" + +remark-smartypants@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/remark-smartypants/-/remark-smartypants-2.1.0.tgz#afd26d8ff40def346c6516e38b46994449fb2efe" + integrity sha512-qoF6Vz3BjU2tP6OfZqHOvCU0ACmu/6jhGaINSQRI9mM7wCxNQTKB3JUAN4SVoN2ybElEDTxBIABRep7e569iJw== + dependencies: + retext "^8.1.0" + retext-smartypants "^5.2.0" + unist-util-visit "^5.0.0" + +remark-smartypants@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/remark-smartypants/-/remark-smartypants-3.0.2.tgz#cbaf2b39624c78fcbd6efa224678c1d2e9bc1dfb" + integrity sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA== + dependencies: + retext "^9.0.0" + retext-smartypants "^6.0.0" + unified "^11.0.4" + unist-util-visit "^5.0.0" + +remark-stringify@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-11.0.0.tgz#4c5b01dd711c269df1aaae11743eb7e2e7636fd3" + integrity sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-to-markdown "^2.0.0" + unified "^11.0.0" + +request-light@^0.5.7: + version "0.5.8" + resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.5.8.tgz#8bf73a07242b9e7b601fac2fa5dc22a094abcc27" + integrity sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg== + +request-light@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.7.0.tgz#885628bb2f8040c26401ebf258ec51c4ae98ac2a" + integrity sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q== + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +resolve@^1.1.7, resolve@^1.22.8: + version "1.22.10" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" + integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== + dependencies: + is-core-module "^2.16.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +restore-cursor@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-5.1.0.tgz#0766d95699efacb14150993f55baf0953ea1ebe7" + integrity sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA== + dependencies: + onetime "^7.0.0" + signal-exit "^4.1.0" + +retext-latin@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/retext-latin/-/retext-latin-3.1.0.tgz#72b0176af2c69a373fd0d37eadd3924418bb3a89" + integrity sha512-5MrD1tuebzO8ppsja5eEu+ZbBeUNCjoEarn70tkXOS7Bdsdf6tNahsv2bY0Z8VooFF6cw7/6S+d3yI/TMlMVVQ== + dependencies: + "@types/nlcst" "^1.0.0" + parse-latin "^5.0.0" + unherit "^3.0.0" + unified "^10.0.0" + +retext-latin@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/retext-latin/-/retext-latin-4.0.0.tgz#d02498aa1fd39f1bf00e2ff59b1384c05d0c7ce3" + integrity sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA== + dependencies: + "@types/nlcst" "^2.0.0" + parse-latin "^7.0.0" + unified "^11.0.0" + +retext-smartypants@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/retext-smartypants/-/retext-smartypants-5.2.0.tgz#da9cb79cc60f36aa33a20a462dfc663bec0068b4" + integrity sha512-Do8oM+SsjrbzT2UNIKgheP0hgUQTDDQYyZaIY3kfq0pdFzoPk+ZClYJ+OERNXveog4xf1pZL4PfRxNoVL7a/jw== + dependencies: + "@types/nlcst" "^1.0.0" + nlcst-to-string "^3.0.0" + unified "^10.0.0" + unist-util-visit "^4.0.0" + +retext-smartypants@^6.0.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/retext-smartypants/-/retext-smartypants-6.2.0.tgz#4e852c2974cf2cfa253eeec427c97efc43b5d158" + integrity sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ== + dependencies: + "@types/nlcst" "^2.0.0" + nlcst-to-string "^4.0.0" + unist-util-visit "^5.0.0" + +retext-stringify@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/retext-stringify/-/retext-stringify-3.1.0.tgz#46ed45e077bfc4a8334977f6c2d6611e1d36263a" + integrity sha512-767TLOaoXFXyOnjx/EggXlb37ZD2u4P1n0GJqVdpipqACsQP+20W+BNpMYrlJkq7hxffnFk+jc6mAK9qrbuB8w== + dependencies: + "@types/nlcst" "^1.0.0" + nlcst-to-string "^3.0.0" + unified "^10.0.0" + +retext-stringify@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/retext-stringify/-/retext-stringify-4.0.0.tgz#501d5440bd4d121e351c7c509f8507de9611e159" + integrity sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA== + dependencies: + "@types/nlcst" "^2.0.0" + nlcst-to-string "^4.0.0" + unified "^11.0.0" + +retext@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/retext/-/retext-8.1.0.tgz#c43437fb84cd46285ad240a9279142e239bada8d" + integrity sha512-N9/Kq7YTn6ZpzfiGW45WfEGJqFf1IM1q8OsRa1CGzIebCJBNCANDRmOrholiDRGKo/We7ofKR4SEvcGAWEMD3Q== + dependencies: + "@types/nlcst" "^1.0.0" + retext-latin "^3.0.0" + retext-stringify "^3.0.0" + unified "^10.0.0" + +retext@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/retext/-/retext-9.0.0.tgz#ab5cd72836894167b0ca6ae70fdcfaa166267f7a" + integrity sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA== + dependencies: + "@types/nlcst" "^2.0.0" + retext-latin "^4.0.0" + retext-stringify "^4.0.0" + unified "^11.0.0" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rollup@^4.20.0: + version "4.30.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.30.1.tgz#d5c3d066055259366cdc3eb6f1d051c5d6afaf74" + integrity sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w== + dependencies: + "@types/estree" "1.0.6" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.30.1" + "@rollup/rollup-android-arm64" "4.30.1" + "@rollup/rollup-darwin-arm64" "4.30.1" + "@rollup/rollup-darwin-x64" "4.30.1" + "@rollup/rollup-freebsd-arm64" "4.30.1" + "@rollup/rollup-freebsd-x64" "4.30.1" + "@rollup/rollup-linux-arm-gnueabihf" "4.30.1" + "@rollup/rollup-linux-arm-musleabihf" "4.30.1" + "@rollup/rollup-linux-arm64-gnu" "4.30.1" + "@rollup/rollup-linux-arm64-musl" "4.30.1" + "@rollup/rollup-linux-loongarch64-gnu" "4.30.1" + "@rollup/rollup-linux-powerpc64le-gnu" "4.30.1" + "@rollup/rollup-linux-riscv64-gnu" "4.30.1" + "@rollup/rollup-linux-s390x-gnu" "4.30.1" + "@rollup/rollup-linux-x64-gnu" "4.30.1" + "@rollup/rollup-linux-x64-musl" "4.30.1" + "@rollup/rollup-win32-arm64-msvc" "4.30.1" + "@rollup/rollup-win32-ia32-msvc" "4.30.1" + "@rollup/rollup-win32-x64-msvc" "4.30.1" + fsevents "~2.3.2" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +s.color@0.0.15: + version "0.0.15" + resolved "https://registry.yarnpkg.com/s.color/-/s.color-0.0.15.tgz#6b32cd22d8dba95703a5122ddede2020a1560186" + integrity sha512-AUNrbEUHeKY8XsYr/DYpl+qk5+aM+DChopnWOPEzn8YKzOhv4l2zH6LzZms3tOZP3wwdOyc0RmTciyi46HLIuA== + +"safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sass-formatter@^0.7.6: + version "0.7.9" + resolved "https://registry.yarnpkg.com/sass-formatter/-/sass-formatter-0.7.9.tgz#cf77e02e98f81daabd91b185192144d29fc04ca5" + integrity sha512-CWZ8XiSim+fJVG0cFLStwDvft1VI7uvXdCNJYXhDvowiv+DsbD1nXLiQ4zrE5UBvj5DWZJ93cwN0NX5PMsr1Pw== + dependencies: + suf-log "^2.5.3" + +sass@^1.76.0: + version "1.83.1" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.83.1.tgz#dee1ab94b47a6f9993d3195d36f556bcbda64846" + integrity sha512-EVJbDaEs4Rr3F0glJzFSOvtg2/oy2V/YrGFPqPY24UqcLDWcI9ZY5sN+qyO3c/QCZwzgfirvhXvINiJCE/OLcA== + dependencies: + chokidar "^4.0.0" + immutable "^5.0.2" + source-map-js ">=0.6.2 <2.0.0" + optionalDependencies: + "@parcel/watcher" "^2.4.1" + +sax@^1.2.4: + version "1.4.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" + integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== + +section-matter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167" + integrity sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA== + dependencies: + extend-shallow "^2.0.1" + kind-of "^6.0.0" + +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.8, semver@^7.6.2, semver@^7.6.3: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + +sharp@^0.33.3: + version "0.33.5" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.33.5.tgz#13e0e4130cc309d6a9497596715240b2ec0c594e" + integrity sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw== + dependencies: + color "^4.2.3" + detect-libc "^2.0.3" + semver "^7.6.3" + optionalDependencies: + "@img/sharp-darwin-arm64" "0.33.5" + "@img/sharp-darwin-x64" "0.33.5" + "@img/sharp-libvips-darwin-arm64" "1.0.4" + "@img/sharp-libvips-darwin-x64" "1.0.4" + "@img/sharp-libvips-linux-arm" "1.0.5" + "@img/sharp-libvips-linux-arm64" "1.0.4" + "@img/sharp-libvips-linux-s390x" "1.0.4" + "@img/sharp-libvips-linux-x64" "1.0.4" + "@img/sharp-libvips-linuxmusl-arm64" "1.0.4" + "@img/sharp-libvips-linuxmusl-x64" "1.0.4" + "@img/sharp-linux-arm" "0.33.5" + "@img/sharp-linux-arm64" "0.33.5" + "@img/sharp-linux-s390x" "0.33.5" + "@img/sharp-linux-x64" "0.33.5" + "@img/sharp-linuxmusl-arm64" "0.33.5" + "@img/sharp-linuxmusl-x64" "0.33.5" + "@img/sharp-wasm32" "0.33.5" + "@img/sharp-win32-ia32" "0.33.5" + "@img/sharp-win32-x64" "0.33.5" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shiki@^1.1.2, shiki@^1.1.7, shiki@^1.22.0, shiki@^1.23.1: + version "1.26.2" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-1.26.2.tgz#b4b0434dca20e22150fbcf89b7cd59297df3d74f" + integrity sha512-iP7u2NA9A6JwRRCkIUREEX2cMhlYV5EBmbbSlfSRvPThwca8HBRbVkWuNWW+kw9+i6BSUZqqG6YeUs5dC2SjZw== + dependencies: + "@shikijs/core" "1.26.2" + "@shikijs/engine-javascript" "1.26.2" + "@shikijs/engine-oniguruma" "1.26.2" + "@shikijs/langs" "1.26.2" + "@shikijs/themes" "1.26.2" + "@shikijs/types" "1.26.2" + "@shikijs/vscode-textmate" "^10.0.1" + "@types/hast" "^3.0.4" + +signal-exit@^4.0.1, signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== + dependencies: + is-arrayish "^0.3.1" + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +sitemap@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/sitemap/-/sitemap-8.0.0.tgz#eb6ea48f95787cd680b83683c555d6f6b5a903fd" + integrity sha512-+AbdxhM9kJsHtruUF39bwS/B0Fytw6Fr1o4ZAIAEqA6cke2xcoO2GleBw9Zw7nRzILVEgz7zBM5GiTJjie1G9A== + dependencies: + "@types/node" "^17.0.5" + "@types/sax" "^1.2.1" + arg "^5.0.0" + sax "^1.2.4" + +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.2.0, source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +source-map@^0.7.0, source-map@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + +space-separated-tokens@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" + integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stdin-discarder@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/stdin-discarder/-/stdin-discarder-0.2.2.tgz#390037f44c4ae1a1ae535c5fe38dc3aba8d997be" + integrity sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ== + +stream-replace-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/stream-replace-string/-/stream-replace-string-2.0.0.tgz#e49fd584bd1c633613e010bc73b9db49cb5024ad" + integrity sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w== + +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string-width@^7.0.0, string-width@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.2.0.tgz#b5bb8e2165ce275d4d43476dd2700ad9091db6dc" + integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== + dependencies: + emoji-regex "^10.3.0" + get-east-asian-width "^1.0.0" + strip-ansi "^7.1.0" + +stringify-entities@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-4.0.4.tgz#b3b79ef5f277cc4ac73caeb0236c5ba939b3a4f3" + integrity sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg== + dependencies: + character-entities-html4 "^2.0.0" + character-entities-legacy "^3.0.0" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1, strip-ansi@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-bom-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" + integrity sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g== + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +style-to-object@^1.0.0: + version "1.0.8" + resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-1.0.8.tgz#67a29bca47eaa587db18118d68f9d95955e81292" + integrity sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g== + dependencies: + inline-style-parser "0.2.4" + +sucrase@^3.35.0: + version "3.35.0" + resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263" + integrity sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA== + dependencies: + "@jridgewell/gen-mapping" "^0.3.2" + commander "^4.0.0" + glob "^10.3.10" + lines-and-columns "^1.1.6" + mz "^2.7.0" + pirates "^4.0.1" + ts-interface-checker "^0.1.9" + +suf-log@^2.5.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/suf-log/-/suf-log-2.5.3.tgz#0919a7fceea532a99b578c97814c4e335b2d64d1" + integrity sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow== + dependencies: + s.color "0.0.15" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +svgo@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.3.2.tgz#ad58002652dffbb5986fc9716afe52d869ecbda8" + integrity sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^5.1.0" + css-tree "^2.3.1" + css-what "^6.1.0" + csso "^5.0.5" + picocolors "^1.0.0" + +tailwindcss@^3.4.3: + version "3.4.17" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.17.tgz#ae8406c0f96696a631c790768ff319d46d5e5a63" + integrity sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og== + dependencies: + "@alloc/quick-lru" "^5.2.0" + arg "^5.0.2" + chokidar "^3.6.0" + didyoumean "^1.2.2" + dlv "^1.1.3" + fast-glob "^3.3.2" + glob-parent "^6.0.2" + is-glob "^4.0.3" + jiti "^1.21.6" + lilconfig "^3.1.3" + micromatch "^4.0.8" + normalize-path "^3.0.0" + object-hash "^3.0.0" + picocolors "^1.1.1" + postcss "^8.4.47" + postcss-import "^15.1.0" + postcss-js "^4.0.1" + postcss-load-config "^4.0.2" + postcss-nested "^6.2.0" + postcss-selector-parser "^6.1.2" + resolve "^1.22.8" + sucrase "^3.35.0" + +tar@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" + integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + +tinyexec@^0.3.0, tinyexec@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" + integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +trim-lines@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" + integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== + +trough@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/trough/-/trough-2.2.0.tgz#94a60bd6bd375c152c1df911a4b11d5b0256f50f" + integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw== + +ts-interface-checker@^0.1.9: + version "0.1.13" + resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" + integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== + +tsconfck@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/tsconfck/-/tsconfck-3.1.4.tgz#de01a15334962e2feb526824339b51be26712229" + integrity sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ== + +tslib@^2.4.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +type-fest@^4.21.0: + version "4.32.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.32.0.tgz#55bacdd6f2cf1392b7e9cde894e9b1d726807e97" + integrity sha512-rfgpoi08xagF3JSdtJlCwMq9DGNDE0IMh3Mkpc1wUypg9vPi786AiqeBBKcqvIkq42azsBM85N490fyZjeUftw== + +typesafe-path@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/typesafe-path/-/typesafe-path-0.2.2.tgz#91a436681b2f514badb114061b6a5e5c2b8943b1" + integrity sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA== + +typescript-auto-import-cache@^0.3.3: + version "0.3.5" + resolved "https://registry.yarnpkg.com/typescript-auto-import-cache/-/typescript-auto-import-cache-0.3.5.tgz#402f98995037734ef3fc208180331adfd5e495fc" + integrity sha512-fAIveQKsoYj55CozUiBoj4b/7WpN0i4o74wiGY5JVUEoD0XiqDk1tJqTEjgzL2/AizKQrXxyRosSebyDzBZKjw== + dependencies: + semver "^7.3.8" + +typescript@^5.4.5: + version "5.7.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.3.tgz#919b44a7dbb8583a9b856d162be24a54bf80073e" + integrity sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw== + +ufo@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.5.4.tgz#16d6949674ca0c9e0fbbae1fa20a71d7b1ded754" + integrity sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ== + +uhyphen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/uhyphen/-/uhyphen-0.2.0.tgz#8fdf0623314486e020a3c00ee5cc7a12fe722b81" + integrity sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA== + +undici-types@~6.20.0: + version "6.20.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" + integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== + +undici@^6.19.5: + version "6.21.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-6.21.0.tgz#4b3d3afaef984e07b48e7620c34ed8a285ed4cd4" + integrity sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw== + +unherit@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/unherit/-/unherit-3.0.1.tgz#65b98bb7cb58cee755d7ec699a49e9e8ff172e23" + integrity sha512-akOOQ/Yln8a2sgcLj4U0Jmx0R5jpIg2IUyRrWOzmEbjBtGzBdHtSeFKgoEcoH4KYIG/Pb8GQ/BwtYm0GCq1Sqg== + +unified@^10.0.0: + version "10.1.2" + resolved "https://registry.yarnpkg.com/unified/-/unified-10.1.2.tgz#b1d64e55dafe1f0b98bb6c719881103ecf6c86df" + integrity sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q== + dependencies: + "@types/unist" "^2.0.0" + bail "^2.0.0" + extend "^3.0.0" + is-buffer "^2.0.0" + is-plain-obj "^4.0.0" + trough "^2.0.0" + vfile "^5.0.0" + +unified@^11.0.0, unified@^11.0.4, unified@^11.0.5: + version "11.0.5" + resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.5.tgz#f66677610a5c0a9ee90cab2b8d4d66037026d9e1" + integrity sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA== + dependencies: + "@types/unist" "^3.0.0" + bail "^2.0.0" + devlop "^1.0.0" + extend "^3.0.0" + is-plain-obj "^4.0.0" + trough "^2.0.0" + vfile "^6.0.0" + +unist-util-find-after@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz#3fccc1b086b56f34c8b798e1ff90b5c54468e896" + integrity sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + +unist-util-is@^5.0.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-5.2.1.tgz#b74960e145c18dcb6226bc57933597f5486deae9" + integrity sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw== + dependencies: + "@types/unist" "^2.0.0" + +unist-util-is@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.0.tgz#b775956486aff107a9ded971d996c173374be424" + integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-modify-children@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/unist-util-modify-children/-/unist-util-modify-children-3.1.1.tgz#c4018b86441aa3b54b3edff1151d0aa062384c82" + integrity sha512-yXi4Lm+TG5VG+qvokP6tpnk+r1EPwyYL04JWDxLvgvPV40jANh7nm3udk65OOWquvbMDe+PL9+LmkxDpTv/7BA== + dependencies: + "@types/unist" "^2.0.0" + array-iterate "^2.0.0" + +unist-util-modify-children@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz#981d6308e887b005d1f491811d3cbcc254b315e9" + integrity sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw== + dependencies: + "@types/unist" "^3.0.0" + array-iterate "^2.0.0" + +unist-util-position-from-estree@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz#d94da4df596529d1faa3de506202f0c9a23f2200" + integrity sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-position@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-5.0.0.tgz#678f20ab5ca1207a97d7ea8a388373c9cf896be4" + integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-remove-position@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz#fea68a25658409c9460408bc6b4991b965b52163" + integrity sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q== + dependencies: + "@types/unist" "^3.0.0" + unist-util-visit "^5.0.0" + +unist-util-stringify-position@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz#03ad3348210c2d930772d64b489580c13a7db39d" + integrity sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg== + dependencies: + "@types/unist" "^2.0.0" + +unist-util-stringify-position@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2" + integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-visit-children@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/unist-util-visit-children/-/unist-util-visit-children-2.0.2.tgz#0f00a5caff567074568da2d89c54b5ee4a8c5440" + integrity sha512-+LWpMFqyUwLGpsQxpumsQ9o9DG2VGLFrpz+rpVXYIEdPy57GSy5HioC0g3bg/8WP9oCLlapQtklOzQ8uLS496Q== + dependencies: + "@types/unist" "^2.0.0" + +unist-util-visit-children@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz#4bced199b71d7f3c397543ea6cc39e7a7f37dc7e" + integrity sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-visit-parents@^5.1.1: + version "5.1.3" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz#b4520811b0ca34285633785045df7a8d6776cfeb" + integrity sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^5.0.0" + +unist-util-visit-parents@^6.0.0, unist-util-visit-parents@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz#4d5f85755c3b8f0dc69e21eca5d6d82d22162815" + integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + +unist-util-visit@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-4.1.2.tgz#125a42d1eb876283715a3cb5cceaa531828c72e2" + integrity sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^5.0.0" + unist-util-visit-parents "^5.1.1" + +unist-util-visit@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz#a7de1f31f72ffd3519ea71814cccf5fd6a9217d6" + integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + unist-util-visit-parents "^6.0.0" + +update-browserslist-db@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz#97e9c96ab0ae7bcac08e9ae5151d26e6bc6b5580" + integrity sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + +util-deprecate@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +vfile-location@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-5.0.3.tgz#cb9eacd20f2b6426d19451e0eafa3d0a846225c3" + integrity sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg== + dependencies: + "@types/unist" "^3.0.0" + vfile "^6.0.0" + +vfile-message@^3.0.0: + version "3.1.4" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-3.1.4.tgz#15a50816ae7d7c2d1fa87090a7f9f96612b59dea" + integrity sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw== + dependencies: + "@types/unist" "^2.0.0" + unist-util-stringify-position "^3.0.0" + +vfile-message@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.2.tgz#c883c9f677c72c166362fd635f21fc165a7d1181" + integrity sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-stringify-position "^4.0.0" + +vfile@^5.0.0: + version "5.3.7" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-5.3.7.tgz#de0677e6683e3380fafc46544cfe603118826ab7" + integrity sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g== + dependencies: + "@types/unist" "^2.0.0" + is-buffer "^2.0.0" + unist-util-stringify-position "^3.0.0" + vfile-message "^3.0.0" + +vfile@^6.0.0, vfile@^6.0.1, vfile@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.3.tgz#3652ab1c496531852bf55a6bac57af981ebc38ab" + integrity sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q== + dependencies: + "@types/unist" "^3.0.0" + vfile-message "^4.0.0" + +vite@^5.4.11: + version "5.4.11" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.11.tgz#3b415cd4aed781a356c1de5a9ebafb837715f6e5" + integrity sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q== + dependencies: + esbuild "^0.21.3" + postcss "^8.4.43" + rollup "^4.20.0" + optionalDependencies: + fsevents "~2.3.3" + +vitefu@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/vitefu/-/vitefu-1.0.5.tgz#eab501e07da167bbb68e957685823e6b425e7ce2" + integrity sha512-h4Vflt9gxODPFNGPwp4zAMZRpZR7eslzwH2c5hn5kNZ5rhnKyRJ50U+yGCdc2IRaBs8O4haIgLNGrV5CrpMsCA== + +volar-service-css@0.0.62: + version "0.0.62" + resolved "https://registry.yarnpkg.com/volar-service-css/-/volar-service-css-0.0.62.tgz#4866091bd217b548470f24706f53feba7a57345b" + integrity sha512-JwNyKsH3F8PuzZYuqPf+2e+4CTU8YoyUHEHVnoXNlrLe7wy9U3biomZ56llN69Ris7TTy/+DEX41yVxQpM4qvg== + dependencies: + vscode-css-languageservice "^6.3.0" + vscode-languageserver-textdocument "^1.0.11" + vscode-uri "^3.0.8" + +volar-service-emmet@0.0.62: + version "0.0.62" + resolved "https://registry.yarnpkg.com/volar-service-emmet/-/volar-service-emmet-0.0.62.tgz#451c60f73cb2c84c5ce2e4b70901de09c38920af" + integrity sha512-U4dxWDBWz7Pi4plpbXf4J4Z/ss6kBO3TYrACxWNsE29abu75QzVS0paxDDhI6bhqpbDFXlpsDhZ9aXVFpnfGRQ== + dependencies: + "@emmetio/css-parser" "^0.4.0" + "@emmetio/html-matcher" "^1.3.0" + "@vscode/emmet-helper" "^2.9.3" + vscode-uri "^3.0.8" + +volar-service-html@0.0.62: + version "0.0.62" + resolved "https://registry.yarnpkg.com/volar-service-html/-/volar-service-html-0.0.62.tgz#791c2b05f5e97bc4c35fac4dbae1cb57cc66570a" + integrity sha512-Zw01aJsZRh4GTGUjveyfEzEqpULQUdQH79KNEiKVYHZyuGtdBRYCHlrus1sueSNMxwwkuF5WnOHfvBzafs8yyQ== + dependencies: + vscode-html-languageservice "^5.3.0" + vscode-languageserver-textdocument "^1.0.11" + vscode-uri "^3.0.8" + +volar-service-prettier@0.0.62: + version "0.0.62" + resolved "https://registry.yarnpkg.com/volar-service-prettier/-/volar-service-prettier-0.0.62.tgz#aae89a26b27ad048f4452482888533ed7123f5c4" + integrity sha512-h2yk1RqRTE+vkYZaI9KYuwpDfOQRrTEMvoHol0yW4GFKc75wWQRrb5n/5abDrzMPrkQbSip8JH2AXbvrRtYh4w== + dependencies: + vscode-uri "^3.0.8" + +volar-service-typescript-twoslash-queries@0.0.62: + version "0.0.62" + resolved "https://registry.yarnpkg.com/volar-service-typescript-twoslash-queries/-/volar-service-typescript-twoslash-queries-0.0.62.tgz#9bf63fcf89688fae12f492168d3b447be3bdf385" + integrity sha512-KxFt4zydyJYYI0kFAcWPTh4u0Ha36TASPZkAnNY784GtgajerUqM80nX/W1d0wVhmcOFfAxkVsf/Ed+tiYU7ng== + dependencies: + vscode-uri "^3.0.8" + +volar-service-typescript@0.0.62: + version "0.0.62" + resolved "https://registry.yarnpkg.com/volar-service-typescript/-/volar-service-typescript-0.0.62.tgz#d99c42e2e08742f27b9bb186180dac93ce730ee6" + integrity sha512-p7MPi71q7KOsH0eAbZwPBiKPp9B2+qrdHAd6VY5oTo9BUXatsOAdakTm9Yf0DUj6uWBAaOT01BSeVOPwucMV1g== + dependencies: + path-browserify "^1.0.1" + semver "^7.6.2" + typescript-auto-import-cache "^0.3.3" + vscode-languageserver-textdocument "^1.0.11" + vscode-nls "^5.2.0" + vscode-uri "^3.0.8" + +volar-service-yaml@0.0.62: + version "0.0.62" + resolved "https://registry.yarnpkg.com/volar-service-yaml/-/volar-service-yaml-0.0.62.tgz#143aaab83cae8c7c82f68502100d300ec687b59e" + integrity sha512-k7gvv7sk3wa+nGll3MaSKyjwQsJjIGCHFjVkl3wjaSP2nouKyn9aokGmqjrl39mi88Oy49giog2GkZH526wjig== + dependencies: + vscode-uri "^3.0.8" + yaml-language-server "~1.15.0" + +vscode-css-languageservice@^6.3.0: + version "6.3.2" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-6.3.2.tgz#dd54161776f1663fa514a1b5df0d3990bda604bb" + integrity sha512-GEpPxrUTAeXWdZWHev1OJU9lz2Q2/PPBxQ2TIRmLGvQiH3WZbqaNoute0n0ewxlgtjzTW3AKZT+NHySk5Rf4Eg== + dependencies: + "@vscode/l10n" "^0.0.18" + vscode-languageserver-textdocument "^1.0.12" + vscode-languageserver-types "3.17.5" + vscode-uri "^3.0.8" + +vscode-html-languageservice@^5.2.0, vscode-html-languageservice@^5.3.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-5.3.1.tgz#93cac1cebb42165b52a15220f02c47d1320fc43a" + integrity sha512-ysUh4hFeW/WOWz/TO9gm08xigiSsV/FOAZ+DolgJfeLftna54YdmZ4A+lIn46RbdO3/Qv5QHTn1ZGqmrXQhZyA== + dependencies: + "@vscode/l10n" "^0.0.18" + vscode-languageserver-textdocument "^1.0.12" + vscode-languageserver-types "^3.17.5" + vscode-uri "^3.0.8" + +vscode-json-languageservice@4.1.8: + version "4.1.8" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-4.1.8.tgz#397a39238d496e3e08a544a8b93df2cd13347d0c" + integrity sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg== + dependencies: + jsonc-parser "^3.0.0" + vscode-languageserver-textdocument "^1.0.1" + vscode-languageserver-types "^3.16.0" + vscode-nls "^5.0.0" + vscode-uri "^3.0.2" + +vscode-jsonrpc@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz#108bdb09b4400705176b957ceca9e0880e9b6d4e" + integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg== + +vscode-jsonrpc@8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz#f43dfa35fb51e763d17cd94dcca0c9458f35abf9" + integrity sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA== + +vscode-languageserver-protocol@3.16.0: + version "3.16.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz#34135b61a9091db972188a07d337406a3cdbe821" + integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A== + dependencies: + vscode-jsonrpc "6.0.0" + vscode-languageserver-types "3.16.0" + +vscode-languageserver-protocol@3.17.5, vscode-languageserver-protocol@^3.17.5: + version "3.17.5" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz#864a8b8f390835572f4e13bd9f8313d0e3ac4bea" + integrity sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg== + dependencies: + vscode-jsonrpc "8.2.0" + vscode-languageserver-types "3.17.5" + +vscode-languageserver-textdocument@^1.0.1, vscode-languageserver-textdocument@^1.0.11, vscode-languageserver-textdocument@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz#457ee04271ab38998a093c68c2342f53f6e4a631" + integrity sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA== + +vscode-languageserver-types@3.16.0: + version "3.16.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247" + integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA== + +vscode-languageserver-types@3.17.5, vscode-languageserver-types@^3.15.1, vscode-languageserver-types@^3.16.0, vscode-languageserver-types@^3.17.5: + version "3.17.5" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz#3273676f0cf2eab40b3f44d085acbb7f08a39d8a" + integrity sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg== + +vscode-languageserver@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz#49b068c87cfcca93a356969d20f5d9bdd501c6b0" + integrity sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw== + dependencies: + vscode-languageserver-protocol "3.16.0" + +vscode-languageserver@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz#500aef82097eb94df90d008678b0b6b5f474015b" + integrity sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g== + dependencies: + vscode-languageserver-protocol "3.17.5" + +vscode-nls@^5.0.0, vscode-nls@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" + integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== + +vscode-uri@^3.0.2, vscode-uri@^3.0.8: + version "3.0.8" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.8.tgz#1770938d3e72588659a172d0fd4642780083ff9f" + integrity sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw== + +web-namespaces@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-2.0.1.tgz#1010ff7c650eccb2592cebeeaf9a1b253fd40692" + integrity sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ== + +whatwg-encoding@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" + integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== + dependencies: + iconv-lite "0.6.3" + +whatwg-mimetype@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" + integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== + +which-pm-runs@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.1.0.tgz#35ccf7b1a0fce87bd8b92a478c9d045785d3bf35" + integrity sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA== + +which-pm@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/which-pm/-/which-pm-3.0.0.tgz#78f2088b345a63cec9f838b390332fb1e680221f" + integrity sha512-ysVYmw6+ZBhx3+ZkcPwRuJi38ZOTLJJ33PSHaitLxSKUMsh0LkKd0nC69zZCwt5D+AYUcMK2hhw4yWny20vSGg== + dependencies: + load-yaml-file "^0.2.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +widest-line@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-5.0.0.tgz#b74826a1e480783345f0cd9061b49753c9da70d0" + integrity sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA== + dependencies: + string-width "^7.0.0" + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrap-ansi@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-9.0.0.tgz#1a3dc8b70d85eeb8398ddfb1e4a02cd186e58b3e" + integrity sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q== + dependencies: + ansi-styles "^6.2.1" + string-width "^7.0.0" + strip-ansi "^7.1.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +xxhash-wasm@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz#ffe7f0b98220a4afac171e3fb9b6d1f8771f015e" + integrity sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml-language-server@~1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/yaml-language-server/-/yaml-language-server-1.15.0.tgz#3bd36f1f7fd74e63b591e5148df992c7327be05a" + integrity sha512-N47AqBDCMQmh6mBLmI6oqxryHRzi33aPFPsJhYy3VTUGCdLHYjGh4FZzpUjRlphaADBBkDmnkM/++KNIOHi5Rw== + dependencies: + ajv "^8.11.0" + lodash "4.17.21" + request-light "^0.5.7" + vscode-json-languageservice "4.1.8" + vscode-languageserver "^7.0.0" + vscode-languageserver-textdocument "^1.0.1" + vscode-languageserver-types "^3.16.0" + vscode-nls "^5.0.0" + vscode-uri "^3.0.2" + yaml "2.2.2" + optionalDependencies: + prettier "2.8.7" + +yaml@2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.2.tgz#ec551ef37326e6d42872dad1970300f8eb83a073" + integrity sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA== + +yaml@^2.3.4, yaml@^2.5.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.7.0.tgz#aef9bb617a64c937a9a748803786ad8d3ffe1e98" + integrity sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + +yocto-queue@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.1.1.tgz#fef65ce3ac9f8a32ceac5a634f74e17e5b232110" + integrity sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g== + +zod-to-json-schema@^3.23.5: + version "3.24.1" + resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz#f08c6725091aadabffa820ba8d50c7ab527f227a" + integrity sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w== + +zod-to-ts@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/zod-to-ts/-/zod-to-ts-1.2.0.tgz#873a2fd8242d7b649237be97e0c64d7954ae0c51" + integrity sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA== + +zod@^3.23.8: + version "3.24.1" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.1.tgz#27445c912738c8ad1e9de1bea0359fa44d9d35ee" + integrity sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A== + +zwitch@^2.0.0, zwitch@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7" + integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A== From 79b47ef30afbc784d466e7ddf76ec79944211061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Wed, 15 Jan 2025 01:53:54 -0600 Subject: [PATCH 096/141] refactor(website): Update and improve documentation. --- website/src/components/SiteTitle.astro | 6 +- .../docs/core_concepts/state_management.mdx | 47 ++++-- .../es/core_concepts/state_management.mdx | 132 +++++++++++++++ .../src/content/docs/es/getting_started.mdx | 153 ++++++++++++++++++ website/src/content/docs/es/index.mdx | 84 ++++++++++ website/src/content/docs/es/overview.mdx | 54 +++++++ .../content/docs/es/shareds/state_methods.mdx | 23 +++ website/src/content/docs/getting_started.mdx | 22 +-- website/src/content/docs/index.mdx | 6 +- website/src/content/docs/overview.mdx | 58 ++++--- .../content/docs/shareds/state_methods.mdx | 2 +- 11 files changed, 523 insertions(+), 64 deletions(-) create mode 100644 website/src/content/docs/es/core_concepts/state_management.mdx create mode 100644 website/src/content/docs/es/getting_started.mdx create mode 100644 website/src/content/docs/es/index.mdx create mode 100644 website/src/content/docs/es/overview.mdx create mode 100644 website/src/content/docs/es/shareds/state_methods.mdx diff --git a/website/src/components/SiteTitle.astro b/website/src/components/SiteTitle.astro index 6b4834f5..ec11a509 100644 --- a/website/src/components/SiteTitle.astro +++ b/website/src/components/SiteTitle.astro @@ -3,10 +3,10 @@ import { logos } from "virtual:starlight/user-images"; import config from "virtual:starlight/user-config"; import type { Props } from "@astrojs/starlight/props"; -const href = Astro.props.locale ?? "/"; +const { siteTitle, siteTitleHref } = Astro.props; --- - + { config.logo && logos.dark && ( <> @@ -36,7 +36,7 @@ const href = Astro.props.locale ?? "/"; "sr-only": config.logo?.replacesTitle, }} > - {config.title.en} + {siteTitle} diff --git a/website/src/content/docs/core_concepts/state_management.mdx b/website/src/content/docs/core_concepts/state_management.mdx index ff04b9c4..2163de28 100644 --- a/website/src/content/docs/core_concepts/state_management.mdx +++ b/website/src/content/docs/core_concepts/state_management.mdx @@ -7,13 +7,19 @@ sidebar: import { HE, HM, HT } from '@/components/Highlight'; import StateMethods from '@/content/docs/shareds/state_methods.mdx'; -State management is a critical aspect of any application. -It allows you to manage the state of your application, and facilitates seamless tracking and handling of changes to it. +State management is a critical aspect of any application, as it is responsible for controlling and maintaining data consistency over time. +It facilitates updating and tracking changes in the state, ensuring a smooth user experience and stable application operation. + +At Reactter, we understand the importance of state management, which is why we have designed a state management system that is efficient, reactive, and easy to use. + +To continue, we will show you the mechanisms that Reactter offers for state management and how they work. ## API Reactter provides a variety of mechanisms for state management, including classes, hooks, and methods: +- Mixins + - [`RtStateBase`](/reactter/classes/RtStateBase) - Classes - [`Signal`](/reactter/classes/signal) - Hooks @@ -39,10 +45,10 @@ To dive into the concept, let's start by exploring what constitutes a state in R ### State -All state in Reactter are classes that inherit `RtState`, +All states in Reactter are classes that inherit `RtState`, which encapsulates the data and behavior of a particular state, and provides a way to notify observers when the state changes. -Reactter offers two fundamental approaches for creating states: [`Signal`](/reactter/classes/signal) and [`Hooks`](/reactter/core_concepts/hooks). +Reactter offers three fundamental approaches for creating states: [`RtStateBase`](/reactter/classes/RtStateBase), [`Signal`](/reactter/classes/signal) and [`Hooks`](/reactter/core_concepts/hooks). ### State methods @@ -52,7 +58,7 @@ Reactter offers two fundamental approaches for creating states: [`Signal`](/ ### Example -Let's see an example of how a `Signal` state is used and what happens under the hood. +Let's see an countdown example using `Signal` and understand what happens under the hood. ```dart title="main.dart" /(Signal)(?!\u0060)/ /count(?!down)(?!\u0060)/ "count.value" "Rt.on" "Lifecycle.didUpdate" import 'dart:async'; @@ -62,7 +68,7 @@ import 'package:reactter/reactter.dart'; final count = Signal(10); void main() async { - // Listen to the `didUpdate` event of the `count` state + // Liste n to the `didUpdate` event of the `count` state // and print the `value` of `count` each time it changes Rt.on( count, @@ -70,8 +76,7 @@ void main() async { (_, __) => print('Count: $count') ); - // Create a timer that decrements the `value` of `count` - // by 1 every second until it reaches 0 + // Create a timer that invoked the `countdown` function every second await Timer.periodic(Duration(seconds: 1), countdown); } @@ -85,21 +90,32 @@ void countdown(Timer timer) { } } ``` +Ahora veamos que contiene la clase `Signal` y cómo se actualiza el estado `count` en el ejemplo anterior. -During the process, as the `value` of `count` changes and triggers the `Lifecycle.didUpdate` event, -internally within the `Signal` class, the `update` method is invoked to notify its listeners(in line 11 of the code below), as follows: +Now let's see what the `Signal` class contains and how the `count` state is updated in the example above. -```dart title="signal.dart" "RtState" "_value" "update" -class Signal extends RtState[...] { +```dart title="signal.dart" "RtContextMixin" "Rt.createState" "RtStateBase" "_value" "update" +class Signal with RtContextMixin, RtStateBase> { + // State value T _value; - Signal(this._value); + // Private constructor, only a `Signal` instance can be created through the factory. + Signal._(this._value); + + factory Signal(T value) { + // Register a new state in the Reactter context + return Rt.createState( + () => Signal._(value), + ); + } T get value => _value; set value(T val) { if (_value == val) return; + // Notify listeners that the state has changed, + // triggering the `Lifecycle.willUpdate` and `Lifecycle.didUpdate` events in order. update((_) => _value = val); } @@ -107,6 +123,11 @@ class Signal extends RtState[...] { } ``` +During the process, as the `value` of `count` changes, the `Lifecycle.didUpdate` event is triggered, which is fired by the `update` method(`signal.dart`, line 22). +This event is listened to by the `Rt.on` method(`main.dart`, line 10), which prints the `value` of `count`. + +This occurs thanks to the reactivity of Reactter, which is responsible for notifying listeners by emitting events related to the **_lifecycle_** of the state. + :::tip Learn about [Lifecycle](/reactter/core_concepts/lifecycle). ::: \ No newline at end of file diff --git a/website/src/content/docs/es/core_concepts/state_management.mdx b/website/src/content/docs/es/core_concepts/state_management.mdx new file mode 100644 index 00000000..040da058 --- /dev/null +++ b/website/src/content/docs/es/core_concepts/state_management.mdx @@ -0,0 +1,132 @@ +--- +title: Gestor de estados +description: Reactter proporciona una forma simple y eficiente de gestionar el estado de tu aplicación. +sidebar: + order: 1 +--- +import { HE, HM, HT } from '@/components/Highlight'; +import StateMethods from '@/content/docs/es/shareds/state_methods.mdx'; + +La gestión de estados es un aspecto crítico de cualquier aplicación, ya que es la encargada de controlar y mantener la coherencia de los datos a lo largo del tiempo. +Facilita la actualización y el seguimiento de los cambios en el estado, asegurando una experiencia de usuario fluida y un funcionamiento estable de la aplicación. + +En Reactter sabemos la importancia de la gestión de estados, por lo que hemos diseñado un sistema de gestión de estados que es eficiente, reactivo y fácil de usar. + +A continuación, te mostramos los mecanimos que Reactter ofrece para la gestión de estados y aprenderás cómo funcionan. + +## API + +Reactter proporciona una gran variedad de mecanismos para la gestión de estados, incluyendo clases, hooks y métodos: + +- Mixins + - [`RtStateBase`](/reactter/es/classes/RtStateBase) +- Classes + - [`Signal`](/reactter/es/classes/signal) +- Hooks + - [`UseState`](/reactter/es/hooks/use_state) + - [`UseAsyncState`](/reactter/es/hooks/use_async_state) + - [`UseReducer`](/reactter/es/hooks/use_reducer) + - [`UseCompute`](/reactter/es/hooks/use_compute) +- Methods + - [`Rt.lazyState`](/reactter/es/methods/state_management_methods/#rtlazy_state) + - [`Rt.batch`](/reactter/es/methods/state_management_methods/#rtbatch) + - [`Rt.untracked`](/reactter/es/methods/state_management_methods/#rtuntracked) + +:::tip + Aprende sobre [Hooks](/reactter/es/core_concepts/hooks). +::: + +## Cómo funciona + +El sistema de gestión de estados de Reactter se basa en el concepto de _reactividad_. +Contrario a la noción predominante de que implementar programación reactiva en Dart puede ser desafiante, +Reactter simplifica en gran medida este proceso. +Para adentrarnos en el concepto, comencemos explorando qué constituye un estado en Reactter. + +### Estado + +Todos los estados en Reactter son clases que heredan de `RtState`, +la cual encapsula los datos y el comportamiento de un estado particular, y proporciona una forma de notificar a los observadores cuando el estado cambia. + +Reactter ofrece tres enfoques fundamentales para crear estados: [`RtStateBase`](/reactter/es/classes/RtStateBase), [`Signal`](/reactter/es/classes/signal) y [`Hooks`](/reactter/es/core_concepts/hooks). + +### Metodos del estado + +La clase `RtState` proporciona algunos métodos para gestionar los estados, que son: + + + +### Ejemplo + +Veamos un ejemplo de un cuenta-atras utilizando `Signal` y desentrañaremos lo qué sucede bajo el capó. + +```dart title="main.dart" /(Signal)(?!\u0060)/ /count(?!down)(?!\u0060)/ "count.value" "Rt.on" "Lifecycle.didUpdate" +import 'dart:async'; +import 'package:reactter/reactter.dart'; + +// Crea un estado reactivo llamado `count` utilizando la clase `Signal` +final count = Signal(10); + +void main() async { + // Escucha el evento `didUpdate` del estado `count` + // e imprime `value` de `count` con cada actualización + Rt.on( + count, + Lifecycle.didUpdate, + (_, __) => print('Count: $count'), + ); + + // Crea un temporizador que invoca la función `countdown` cada segundo + await Timer.periodic(Duration(seconds: 1), countdown); +} + +// Decrementa `value` de `count` en 1 cada ciclo del temporizador +// y cancela el `timer` cuando `value` de `count` llegue a 0 +void countdown(Timer timer) { + count.value -= 1; + + if (count.value == 0) { + timer.cancel(); + } +} +``` + +Ahora veamos que contiene la clase `Signal` y cómo se actualiza el estado `count` en el ejemplo anterior. + +```dart title="signal.dart" "RtContextMixin" "Rt.createState" "RtStateBase" "_value" "update" +class Signal with RtContextMixin, RtStateBase> { + // Valor del estado + T _value; + + // Constructor privada, solo se puede crear una instancia de `Signal` a través del factory. + Signal._(this._value); + + factory Signal(T value) { + // Se registra un nuevo estado en el contexto de Reactter + return Rt.createState( + () => Signal._(value), + ); + } + + T get value => _value; + + set value(T val) { + if (_value == val) return; + + // Notifica a los oyentes que el estado ha cambiado, + // disparando los eventos `Lifecycle.willUpdate` y `Lifecycle.didUpdate` en orden. + update((_) => _value = val); + } + + [...] +} +``` + +Durante el proceso, a medida que el `value` de `count` cambia, se desencadena el evento `Lifecycle.didUpdate`, el cual es disparado por el método `update`(`signal.dart`, linea 22). +Este evento es escuchado por el método `Rt.on`(`main.dart`, linea 10), que imprime el `value` de `count`. + +Esto ocurre gracias a la reactividad de Reactter, que es encargada de notificar a los oyentes mediante la emisión de eventos relacionados con el **_ciclo de vida_** del estado. + +:::tip + Aprende sobre el [Ciclo de vida](/reactter/es/core_concepts/lifecycle). +::: \ No newline at end of file diff --git a/website/src/content/docs/es/getting_started.mdx b/website/src/content/docs/es/getting_started.mdx new file mode 100644 index 00000000..8aa8f923 --- /dev/null +++ b/website/src/content/docs/es/getting_started.mdx @@ -0,0 +1,153 @@ +--- +title: Empezando +description: Una guía paso a paso sobre la instalación y el uso de Reactter. +--- + +import { Code } from "@astrojs/starlight/components"; + +import reactterVersion from "@/data/reactter_version"; +import flutterReactterVersion from "@/data/flutter_reactter_version"; +import { Tabs, TabItem } from "@/components/Tabs"; + + +export const reactterPubspecCode = ` +dependencies: + reactter: ${reactterVersion ? `^${reactterVersion}` : "# add version here"} +`; + +Antes de nada, debes saber que Reactter se distribuye en dos paquetes: + +
    +
  • + + Reactter + + : El paquete central que contiene las funcionalidades básicas de Reactter. Se recomienda utilizar este paquete si está desarrollando una aplicación Dart. +
  • +
  • + + Flutter Reactter + + : Este paquete incluye el paquete principal y funciones adicionales de Flutter. Se recomienda utilizar este paquete si está desarrollando una aplicación Flutter. +
  • +
+ +
+ +## Instalación + +Para seguir esta guía, necesitarás un proyecto Dart/Flutter existente. +Si aún no tienes uno, puedes crear una app Dart aquí o una app Flutter aquí. + +Una vez configurado el proyecto, proceda a instalar el paquete utilizando uno de los siguientes métodos: + +- [Instalación automatizada](#instalación-automatizada) +- [Instalación manual](#instalación-manual) + +### Instalación automatizada + +Añada el paquete a su proyecto ejecutando el siguiente comando: + + + + ```shell showLineNumbers=false + dart pub add reactter + ``` + + + ```shell showLineNumbers=false + flutter pub add flutter_reactter + ``` + + + +### Instalación manual + +Añada el paquete a su proyecto añadiendo la siguiente línea a su archivo `pubspec.yaml`: + + + + + + + + + + +Y ejecute el siguiente comando para instalar el paquete: + + + + ```shell showLineNumbers=false +dart pub get + ``` + + + ```shell showLineNumbers=false +flutter pub get + ``` + + + +## Configuraciones adicionales + +Este paso es opcional pero recomendable. +Utilice el paquete [![Reactter Lint](https://img.shields.io/pub/v/reactter_lint?color=1d7fac&labelColor=29b6f6&label=reactter_lint&logo=dart)](https://pub.dev/packages/reactter_lint) para aplicar las mejores prácticas y convenciones en su proyecto:: + +```shell showLineNumbers=false +dart pub add reactter_lint +``` + +Además, si estás desarrollando con Visual Studio Code, es una buena idea utilizar [Reactter Snippets](https://marketplace.visualstudio.com/items?itemName=CarLeonDev.reacttersnippets) para mejorar la productividad. + +## Uso + +Para utilizar Reactter, debe importar el paquete en su archivo Dart donde lo necesite: + + + + ```dart +import 'package:reactter/reactter.dart'; + ``` + + + ```dart +import 'package:flutter_reactter/flutter_reactter.dart'; + ``` + + + +Listo! No tienes que preocuparte por la configuración, Reactter ya esta listo para usarse en su proyecto. + +
+ +### ¿Qué sigue? + +Si eres nuevo en Reactter, puedes empezar leyendo la sección de _**Conceptos básicos**_ . +Esto te ayudará a entender los conceptos básicos de Reactter y cómo utilizarlos en tu proyecto. + +En esta sección, los temas que aprenderá son: + +- [Gestor de estados](/reactter/es/core_concepts/state_management) +- [Inyección de dependencia](/reactter/es/core_concepts/dependency_injection) +- [Manejador de eventos](/reactter/es/core_concepts/event_handler) +- [Control del renderizado](/reactter/es/core_concepts/rendering_control) +- [Ciclo de vida](/reactter/es/core_concepts/lifecycle) +- [Hooks](/reactter/es/core_concepts/hooks) + diff --git a/website/src/content/docs/es/index.mdx b/website/src/content/docs/es/index.mdx new file mode 100644 index 00000000..28d7f991 --- /dev/null +++ b/website/src/content/docs/es/index.mdx @@ -0,0 +1,84 @@ +--- +title: Reactter +description: Un ligero, potente y reactivo Gestor de Estados, Inyector de Dependencia y Manejador de Eventos para Dart/Flutter. +template: splash +hero: + title:

Reactter

+ tagline: +

+ Un paquete + ligero, + potente y + reactivo +

+

+ Gestor de Estados, + Inyector de Dependencias y + Manejador de Eventos para + Dart/Flutter. +

+ image: + html: + actions: + - text: Empezar + link: /reactter/es/getting_started/ + icon: rocket + variant: primary + - text: Explore en pub.dev + link: https://pub.dev/packages/reactter + icon: external + attrs: + target: _blank + footer: TEST +--- + +import { CardGrid } from "@astrojs/starlight/components"; +import Card from "@/components/Card.astro"; + +
+ ## Pruébalo +

Experimente todo el potencial de Reactter probando en Zapp.

+ + +:::tip[¡El poder está en tus manos!] + [Empieza instalando](/reactter/es/getting_started) Reactter en tus proyectos y desbloquea todo su potencial. +::: \ No newline at end of file diff --git a/website/src/content/docs/es/shareds/state_methods.mdx b/website/src/content/docs/es/shareds/state_methods.mdx new file mode 100644 index 00000000..f4a06d61 --- /dev/null +++ b/website/src/content/docs/es/shareds/state_methods.mdx @@ -0,0 +1,23 @@ +--- +title: Metodos del estado +--- +import { HE, HM, HT } from '@/components/Highlight'; + +- `update`: Ejecuta una función de devolución de llamada y notifica a sus oyentes que el estado ha cambiado. +Cuando se invoca, emite dos eventos para señalar la transición de estado: +Primeramente se emite `Lifecycle.willUpdate`, indicando la próxima actualización, seguido de `Lifecycle.didUpdate` una vez que el proceso de actualización está completo. +- `notify`: Fuerza la notificacion a los oyentes del estado de su cambio. +A diferencia de `update`, emite solo el evento `Lifecycle.didUpdate`, ya que no implica ningún paso preparatorio antes de la notificación. +- `bind`: Establece una conexión entre el estado y una instancia específica. +Esta conexión permite que la instancia se actualice de forma reactiva en función de los cambios en el estado. +Al enlazar el estado, la instancia es notificada de los cambios en el estado y puede reflejar adecuadamente esos cambios en su comportamiento. +- `unbind`: Libera la conexión entre el estado y la instancia. +Al desvincular, la instancia ya no recibirá actualizaciones del estado. +Esto puede ser útil cuando una instancia ya no está utilizando activamente el estado o cuando necesita separarse del estado temporalmente o permanentemente. +- `dispose`: Es responsable de limpiar el estado y cualquier oyente o recurso asociado. +Disponer del estado garantiza que se libere correctamente y que ya no consuma memoria o recursos de procesamiento innecesariamente. + +:::note +Los métodos `bind`, `unbind` y `dispose` se utilizan normalmente para gestionar el [Ciclo de vida](/reactter/es/core_concepts/lifecycle) de un estado. +Sin embargo, Reactter maneja esto automáticamente, cuando el estado se declara dentro de una instancia que existe dentro del contexto de Reactter a través de la [inyección de dependencias](/reactter/core_concepts/es/dependency_injection/), por lo que no es necesario preocuparse por ello(Aprende más sobre [vinculación del estado a una dependencia](/reactter/es/extra_topics/binding_state_to_dependency/)). +::: \ No newline at end of file diff --git a/website/src/content/docs/getting_started.mdx b/website/src/content/docs/getting_started.mdx index 4cbdf33d..1d810ea0 100644 --- a/website/src/content/docs/getting_started.mdx +++ b/website/src/content/docs/getting_started.mdx @@ -41,7 +41,7 @@ Before anything, you need to be aware that Reactter is distributed on two packag ## Installation To follow this guide, you’ll need an existing Dart/Flutter project. -If you don't get one yet, head to here. +If you don't get one yet, you can create a Dart app here or a Flutter app here. Once you have your project set up, proceed to install the package using one of the following methods: @@ -95,7 +95,7 @@ dependencies: -After adding the package to your `pubspec.yaml` file, run the following command to install the package: +And run the following command to install the package: @@ -137,15 +137,7 @@ import 'package:flutter_reactter/flutter_reactter.dart'; -That's it! You are now ready to use Reactter in your Dart/Flutter project. - -{/* -:::note -Reactter classes are generally prefixed with `Reactter` to avoid conflicts with other packages, so you can easily identify them. -This may seem verbose and redundant, but you can rename them to shorter names or other aliases if you prefer. -Check the [Renaming clases](/reactter/extra_topics/renaming_classes) section for more information. -::: -*/} +You're done! You don't have to worry about configuration, Reactter is ready to use in your project.
@@ -155,10 +147,10 @@ If you are new to Reactter, you can start by reading the _**Core Concepts**_ sec This will help you understand the basic concepts of Reactter and how to use them in your project. In this section, the topics you will learn about are: -- [State Management](/reactter/core_concepts/state_management) -- [Dependency Injection](/reactter/core_concepts/dependency_injection) -- [Event Handler](/reactter/core_concepts/event_handler) -- [Rendering Control](/reactter/core_concepts/rendering_control) +- [State management](/reactter/core_concepts/state_management) +- [Dependency injection](/reactter/core_concepts/dependency_injection) +- [Event handler](/reactter/core_concepts/event_handler) +- [Rendering control](/reactter/core_concepts/rendering_control) - [Lifecycle](/reactter/core_concepts/lifecycle) - [Hooks](/reactter/core_concepts/hooks) diff --git a/website/src/content/docs/index.mdx b/website/src/content/docs/index.mdx index 4c8ab979..312a1eb9 100644 --- a/website/src/content/docs/index.mdx +++ b/website/src/content/docs/index.mdx @@ -14,7 +14,7 @@ hero:

State Management, Dependency Injection and - Event Handler for + Event Handler package for Dart/Flutter.

image: @@ -61,7 +61,7 @@ import Card from "@/components/Card.astro"; relying on any external dependencies. - Share states and logic across your app using [Dependency + Share states and logic across your app via [Dependency Injection](/reactter/core_concepts/dependency_injection) and [Custom Hooks](/reactter/core_concepts/hooks/#custom-hook). @@ -83,4 +83,6 @@ import Card from "@/components/Card.astro"; Reduce code significantly and simplify your app. + + And much more to explore [here](/reactter/overview/#features). \ No newline at end of file diff --git a/website/src/content/docs/overview.mdx b/website/src/content/docs/overview.mdx index 9dd5448c..94b76ed4 100644 --- a/website/src/content/docs/overview.mdx +++ b/website/src/content/docs/overview.mdx @@ -2,52 +2,50 @@ title: Overview description: Know about Reactter, why it was created and its features. --- -Reactter started as an experiment to redesign what a state management solution should be. -The goal was to create a state management solution that was **reactive**, **_simple_**, **_fast_**, and **_powerful_**. The result was Reactter. -Reactter is a dart package that stands out as an **_lightweight_**, **_powerful_**, and _**reactive**_ **state management**, **dependency injection** and **event handler** solution. +State management is fundamental to applications, as it determines how information is stored, updated and shared within the application. +Flutter provides `StafulWidgets` to manage local state, which is usually sufficient for small applications, +However, as the application grows, state management becomes more complex and difficult to maintain. +This is why there are several packages to manage global state. -:::note -Reactter is inspired by the ReactJS library, taking some concepts and ideas from it and adapt to Dart and Flutter. -The name **Reactter** is a combination of the words (**_Reac_**)t and Flu(**_tter_**). -::: +Although state management is a key component of an application architecture, it is not the only factor to consider. +Dependency injection, event handling, and, in the case of Flutter applications, proper rendering control, +are equally crucial to ensure that the application is scalable, flexible and easy to maintain in the long term. +All these aspects must be considered together to create a robust and efficient architecture. -## Motivation +While numerous state management packages exist, many of them do not offer complete or robust enough solutions. +This challenge is further intensified in Dart-only applications, where the lack of adequate options for global state management makes management even more challenging. -State management is fundamental to apps, because it determines how information is stored, updated and shared within app. -While there are several state management solutions available, often these solutions don't provide a full integration with equally important concepts, such as dependency injection and event handler. +With this in mind, **Reactter** was created—a **_light_**, **_powerful_** and **_reactive_** [**state management**](/reactter/en/core_concepts/state_management), [**dependency injection**](/reactter/en/core_concepts/dependency_injection) and [**event_handler**](/reactter/en/core_concepts/event_handler) package for Dart/Flutter. -You might think, _"But we already have packages for it"_ or _"I can develop it myself"_ -While that's true, handling these aspects separately can limit the development experience and result in critical aspects being overlooked: +## Motivation -- **Scalability**: Without proper integration between these 3 concepts, the app may have difficulty scaling efficiently as it grows in complexity and size. -- **Flexibility**: An integrated architecture becomes easier to adapt and extend the app to meet new requirements or incorporate new features without major overhauls. -- **Maintainability**: Disjointed these 3 concepts lead to a fragmented codebase, which makes it harder to understand, debug, and modify. -- **Performance**: Poor integration between these 3 concepts, the app may become less responsive and inefficient in using system resources. -This can lead to slower execution times, increased memory usage, and a suboptimal user experience. +**Reactter** was born as an experiment in developing a new way to manage state in Flutter projects that was efficient, reactive, scalable, yet simple and easy to use. -Reactter addresses these challenges by offering a comprehensive framework that integrates state management, dependency injection, and event handling. This integration provides a smoother, more consistent development experience and ensures that applications are robust, efficient, and maintainable. +Inspired by ReactJS, Reactter(**Reac**t-Fu**tter**) adopts key concepts from this library to Dart and Flutter, offering a streamlined solution consistent with the needs of development modern. ## Features Reactter offers the following features: -- **Fine-grained reactivity** (Signals, Hooks). -- **Lightweight** and **fast** -- **No boilerplate** (no recurring code) -- **Easy to use** -- **Highly reusable** (Custom hooks, Dependency injection) -- **100% Dart**(Compatible with latest version of Dart) -- **Fully testable** (100% coverage) -- **No code generation** -- **0 configuration** -- **0 dependencies** -- **Rendering control** (using `flutter_reactter` package) -- **Not opinionated** (You can use it with any architecture or pattern. Do whatever you want!!) +- ⚡️ Engineered for **speed**. +- 🪶 Super **lightweight**. +- 👓 **Simple syntax**, easy to learn. +- ✂️ **Reduce boilerplate code** significantly. +- 👁️ Improve **code readability**. +- 🚀 **Granular reactivity** using [state](/reactter/core_concepts/state_management/#state) and [hooks](/reactter/core_concepts/hooks). +- 🧩 **Highly reusable** states and logic via [custom hooks](/reactter/core_concepts/hooks/#custom-hook) and [dependency injection](/reactter/core_concepts/dependency_injection/). +- 🎮 Total [**rendering control**](/reactter/core_concepts/rendering_control). +- ✅ **Highly testable** with 100% code coverage. +- 🐞 **Fully debuggable** using the [Reactter DevTools extension](/reactter/devtools_extension). +- 💧 **Not opinionated**. Use it with any architecture or pattern. +- 🪄 **Zero dependencies**, **zero configuration** and **no code generation**. +- 💙 **Compatible with Dart and Flutter**, supporting the latest Dart version. ## Try Reactter online Experience the full potential of Reactter by trying it online on Zapp. + :::tip[The power is in your hand!!] diff --git a/website/src/content/docs/shareds/state_methods.mdx b/website/src/content/docs/shareds/state_methods.mdx index 0fe8d64a..30ea6680 100644 --- a/website/src/content/docs/shareds/state_methods.mdx +++ b/website/src/content/docs/shareds/state_methods.mdx @@ -6,7 +6,7 @@ import { HE, HM, HT } from '@/components/Highlight'; - `update`: Executes a callback function and notify its listeners that the state has changed. When it is invoked, it emits two events to signal the state transition: `Lifecycle.willUpdate` is emitted first, indicating the impending update, followed by `Lifecycle.didUpdate` once the update process is complete. -- `refresh`: Forces the state to notify its listeners that it has changed. +- `notify`: Forces the state to notify its listeners that it has changed. Unlike `update`, it emits only the `Lifecycle.didUpdate` event, as it doesn't involve any preparatory steps before the notification. - `bind`: Establishes a connection between the state and a specific instance. This connection allows the instance to reactively update based on changes to the state. From 7eac05c4db5718eed06fe008333c45af39d63888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Wed, 15 Jan 2025 17:46:36 -0600 Subject: [PATCH 097/141] refactor(website): Update state management and binding state to dependency documentation --- .../content/docs/es/shareds/state_methods.mdx | 2 +- .../binding_state_to_dependency.mdx | 68 +++++++++++-------- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/website/src/content/docs/es/shareds/state_methods.mdx b/website/src/content/docs/es/shareds/state_methods.mdx index f4a06d61..76e4f255 100644 --- a/website/src/content/docs/es/shareds/state_methods.mdx +++ b/website/src/content/docs/es/shareds/state_methods.mdx @@ -19,5 +19,5 @@ Disponer del estado garantiza que se libere correctamente y que ya no consuma me :::note Los métodos `bind`, `unbind` y `dispose` se utilizan normalmente para gestionar el [Ciclo de vida](/reactter/es/core_concepts/lifecycle) de un estado. -Sin embargo, Reactter maneja esto automáticamente, cuando el estado se declara dentro de una instancia que existe dentro del contexto de Reactter a través de la [inyección de dependencias](/reactter/core_concepts/es/dependency_injection/), por lo que no es necesario preocuparse por ello(Aprende más sobre [vinculación del estado a una dependencia](/reactter/es/extra_topics/binding_state_to_dependency/)). +Sin embargo, Reactter maneja esto automáticamente, cuando el estado se declara dentro de una instancia que existe dentro del contexto de Reactter a través de la [inyección de dependencias](/reactter/es/core_concepts/dependency_injection/), por lo que no es necesario preocuparse por ello(Aprende más sobre [vinculación del estado a una dependencia](/reactter/es/extra_topics/binding_state_to_dependency/)). ::: \ No newline at end of file diff --git a/website/src/content/docs/extra_topics/binding_state_to_dependency.mdx b/website/src/content/docs/extra_topics/binding_state_to_dependency.mdx index 29d976fe..30143f21 100644 --- a/website/src/content/docs/extra_topics/binding_state_to_dependency.mdx +++ b/website/src/content/docs/extra_topics/binding_state_to_dependency.mdx @@ -39,19 +39,6 @@ class CountController { In the example above, the `uCount` state is declared as a property of the `CountController` class and the `UseEffect` hook is used within the constructor to react to changes in the `uCount` state, printing its value whenever it changes. This automatically binds the `uCount` state and `UseEffect` hook to the `CountController` instance, demonstrates how Reactter handles the binding and reactivity seamlessly. -```dart title="main.dart" "Rt.create" "Rt.delete" -import "./count_controller.dart"; - -void main() { - final controller = Rt.create(() => CountController(10)); - controller.uCount.value += 2; // Count: 12 - Rt.delete(); - controller.uCount.value += 3; // Error: "Can't update when it's been disposed" -} -``` - -In the example above, the `uCount` state is accessed after creating the `CountController` instance, demonstrating that the state is bound to the dependency and can be manipulated directly from the instance. When the `CountController` instance is deleted, the state is automatically disposed of, ensuring that resources are released efficiently. - :::caution If the state is declared lazily (e.g., using `late` keyword), it will not be automatically bound to the dependency. In such cases, you need to use the `Rt.lazyState` method to bind the state to the dependency(See [Lazy state binding](#lazy-state-binding)). ::: @@ -81,19 +68,6 @@ class CountController { In the example above, the `uCount` state is declared lazily using the `late` keyword. To bind the state to the `CountController` instance, the [`Rt.lazyState`](/reactter/methods/state_management_methods/#rtlazystate) method is used, passing the state creation function and the dependency instance as arguments. This ensures that when `uCount` is accessed, it will be automatically bound to the `CountController` instance, e.g.: -```dart title="main.dart" "Rt.create" "Rt.delete" -import "./count_controller.dart"; - -void main() { - final controller = Rt.create(() => CountController(10)); - controller.uCount.value += 2; // Count: 12 - Rt.delete(); - controller.uCount.value += 3; // Error: "Can't update when it's been disposed" -} -``` - -In the example above, the `uCount` state is accessed after creating the `CountController` instance, demonstrating that the state is bound to the dependency and can be manipulated directly from the instance. When the `CountController` instance is deleted, the state is automatically disposed of, ensuring that resources are released efficiently. - ### Manual binding While automatic binding simplifies state management, there may be scenarios where you need to manually bind the state to a dependency. Manual binding provides greater control over how and when the state is associated with the dependency. @@ -114,6 +88,17 @@ class CountController { In the example above, the `uCount` state is declared lazily using the `late` keyword. To manually bind the state to the `CountController` instance, the `bind` method is called within the constructor, passing the dependency instance as an argument. This ensures that the `uCount` state is associated with the `CountController` instance, e.g.: +:::note +[Manual binding](#manual-binding) and [lazy state binding](#lazy-state-binding) serve different purposes. **Manual binding** is useful when you need precise control over the state binding process, while **Lazy state binding** is suitable when you want the state to be initialized only when it is first accessed. Choose the appropriate approach based on your specific requirements. +::: + +### Automatic unbinding + +When a dependency is deleted, the associated state is automatically disposed of, ensuring that resources are released efficiently. +This automatic unbinding mechanism simplifies state management and reduces the risk of memory leaks or resource wastage. + +In the example below, the `uCount` state is automatically disposed of when the `CountController` instance is deleted, ensuring that resources are released efficiently: + ```dart title="main.dart" "Rt.create" "Rt.delete" import "./count_controller.dart"; @@ -125,9 +110,34 @@ void main() { } ``` -In the example above, the `uCount` state is accessed after creating the `CountController` instance, demonstrating that the state is bound to the dependency and can be manipulated directly from the instance. When the `CountController` instance is deleted, the state is automatically disposed of, ensuring that resources are released efficiently. +### Manual unbinding -:::note -[Manual binding](#manual-binding) and [lazy state binding](#lazy-state-binding) serve different purposes. **Manual binding** is useful when you need precise control over the state binding process, while **Lazy state binding** is suitable when you want the state to be initialized only when it is first accessed. Choose the appropriate approach based on your specific requirements. +In some cases, you may need to manually unbind a state from a dependency. +Manual unbinding provides greater control over when the state is released and can be useful in scenarios where you want to detach the state temporarily or permanently. + +To manually unbind a state from a dependency, you can use the `unbind` method of the state, e.g.: + +```dart title="count_controller.dart" "UseState" "unbind" +class CountController { + late final uCount = UseState(this.initialCount); + + final int initialCount; + + CountController([this.initialCount = 0]) { + count.bind(this); + } + + void dispose() { + count.unbind(this); + } +} +``` + +:::caution +While manual unbinding provides greater control, improper use can lead to memory management issues and increase the risk of errors. +It is recommended to use [automatic unbinding](#automatic-unbinding) whenever possible, as it simplifies the process and reduces the likelihood of introducing memory leaks or failing to release resources properly. + +Manual unbinding should be used cautiously and only when absolutely necessary. Additionally, developers must ensure that they keep track of the unbinding process to avoid leaving unused states in memory, which could negatively affect performance and resource utilization. ::: + From cfd242d0660037018c157a4ffe21f867e6d0871a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Thu, 16 Jan 2025 15:44:48 -0600 Subject: [PATCH 098/141] refactor(website): Update Spanish documentation. --- .../core_concepts/dependency_injection.mdx | 382 +++++++++--------- .../es/core_concepts/dependency_injection.mdx | 355 ++++++++++++++++ .../es/core_concepts/state_management.mdx | 8 +- website/src/content/docs/es/index.mdx | 4 +- website/src/examples/marks.ts | 3 + 5 files changed, 559 insertions(+), 193 deletions(-) create mode 100644 website/src/content/docs/es/core_concepts/dependency_injection.mdx diff --git a/website/src/content/docs/core_concepts/dependency_injection.mdx b/website/src/content/docs/core_concepts/dependency_injection.mdx index ba30f43a..379e0ff4 100644 --- a/website/src/content/docs/core_concepts/dependency_injection.mdx +++ b/website/src/content/docs/core_concepts/dependency_injection.mdx @@ -4,18 +4,22 @@ description: Learn how to manage dependencies in Reactter. sidebar: order: 2 --- -import { HE, HM, HN, HT } from '@/components/Highlight'; +import { HE, HM, HN, HS, HT } from '@/components/Highlight'; import CodeTabs from '@/components/CodeTabs.astro'; import { Tabs, TabItem } from "@/components/Tabs"; import { Code } from "@astrojs/starlight/components"; import { marks } from '@/examples/marks.ts' -With Reactter, managing objects becomes straightforward. You can create, delete, and access desired objects from a single centralized location, accessible from anywhere in your code, all thanks to Reactter's robust dependency injection system. +Dependency injection is a design pattern that simplifies the management of an object's dependencies. -Dependency injection offers several benefits. +With Reactter, managing objects becomes easy. You can create, delete, and access desired objects from a single centralized place, accessible from anywhere in your code, all thanks to Reactter's solid dependency injection system. -- **Inversion of Control**: It adheres to the principle of inversion of control, where the responsibility for object creation and management is delegated to Reactter. This results in improved code _modularity_, _reusability_, and _testability_. -- **Simplified Code**: By offloading the responsibility of creating dependencies from individual classes, dependency injection simplifies code, allowing classes to focus more on their core functionality. +Dependency injection offers several benefits. Some of the most notable are: + +- **Decoupling**: Dependency injection decouples classes from their dependencies, making it easier to modify and reuse code. +- **Inversion of control**: It adheres to the principle of inversion of control, where the responsibility of creating and managing objects is delegated to Reactter. +This results in better _modularity_, _reusability_, and _testability_ of the code. +- **Simplified code**: By delegating the responsibility of creating dependencies from individual classes, dependency injection simplifies the code, allowing classes to focus more on their core functionality. ## API @@ -35,74 +39,89 @@ Reactter provides the following dependencies injection mechanisms: - [`Rt.get`](/reactter/methods/dependency_injection_methods/#rtget) - [`Rt.find`](/reactter/methods/dependency_injection_methods/#rtfind) - [`Rt.exists`](/reactter/methods/dependency_injection_methods/#rtexists) + - [`Rt.isActive`](/reactter/methods/dependency_injection_methods/#rtis_active) - [`Rt.getDependencyMode`](/reactter/methods/dependency_injection_methods/#rtget_dependency_mode) - [`Rt.delete`](/reactter/methods/dependency_injection_methods/#rtdelete) - [`Rt.destroy`](/reactter/methods/dependency_injection_methods/#rtdestroy) - [`Rt.unregister`](/reactter/methods/dependency_injection_methods/#rtunregister) - - [`Rt.isActive`](/reactter/methods/dependency_injection_methods/#rtis_active) - :::tip[With Flutter] -If you are using Flutter, go to [Rendering Control](/reactter/core_concepts/rendering_control) to learn how it manage dependencies through Widgets and BuildContext extension methods. +If you are using Flutter, go to [rendering control](/reactter/core_concepts/rendering_control) to learn how it manage dependencies through `Widget` and `BuildContext` extension methods. ::: -## How it works +## How It Works -Reactter manages the dependencies through a centralized mechanism. -This core component serves as a central repository for registering, resolving, and providing dependencies across the app. -To comprehend this mechanism thoroughly, let's break down the process into five stages: +Reactter manages dependencies through a centralized mechanism that acts as a main repository responsible for registering, resolving, and providing dependencies throughout the application. +To understand how this system works in its entirety, we will break down the process into the following stages: -1. **Registration**: This stage involves registering the dependency into Reactter's context with the specified `id` and `mode` params. +1. **Registration**: This stage involves registering the dependency within the Reactter context using a specific type, a `builder` function, an `id`, and a dependency `mode`. - For this, you can use the following methods: - - [`Rt.register`](/reactter/methods/dependency_injection_methods/#rtregister) - - [`Rt.lazyBuilder`](/reactter/methods/dependency_injection_methods/#rtlazy_builder)(registers with `builder` mode) - - [`Rt.lazyFactory`](/reactter/methods/dependency_injection_methods/#rtlazy_factory)(registers with `factory` mode) - - [`Rt.lazySingleton`](/reactter/methods/dependency_injection_methods/#rtlazy_singleton)(registers with `singleton` mode) + To perform this registration, you can use the following methods: + - [`Rt.register`](/reactter/en/methods/dependency_injection_methods/#rtregister) + - [`Rt.lazyBuilder`](/reactter/en/methods/dependency_injection_methods/#rtlazy_builder) + - [`Rt.lazyFactory`](/reactter/en/methods/dependency_injection_methods/#rtlazy_factory) + - [`Rt.lazySingleton`](/reactter/en/methods/dependency_injection_methods/#rtlazy_singleton) - The `Lifecycle.registered` event is emitted. + During registration, the event `Lifecycle.registered` is triggered. -2. **Resolving**: When there is a request for getting a dependency, Reactter gets it according to `id` and the `mode` through the registry. -If the dependency with/without `id` is not yet created, Reactter initializes it based on registry(this condition doesn't apply to `find` method). +2. **Resolution**: When a dependency is requested, Reactter creates an instance of the dependency from the registered `builder` function, according to the provided type and `id`. For this, you can use the following methods: - - [`Rt.get`](/reactter/methods/dependency_injection_methods/#rtget) - - [`Rt.find`](/reactter/methods/dependency_injection_methods/#rtfind)(doesn't create the dependency) - - [`Rt.create`](/reactter/methods/dependency_injection_methods/#rtcreate)(registers if not found in registry) - - [`Rt.builder`](/reactter/methods/dependency_injection_methods/#rtbuilder)(registers with `builder` mode if not found in registry) - - [`Rt.factory`](/reactter/methods/dependency_injection_methods/#rtfactory)(registers with `factory` mode if not found in registry) - - [`Rt.singleton`](/reactter/methods/dependency_injection_methods/#rtsingleton)(registers with `singleton` mode if not found in registry) - - The following events are fired(only if the dependency instance is created): + - [`Rt.get`](/reactter/en/methods/dependency_injection_methods/#rtget) + - [`Rt.create`](/reactter/en/methods/dependency_injection_methods/#rtcreate) + - [`Rt.builder`](/reactter/en/methods/dependency_injection_methods/#rtbuilder) + - [`Rt.factory`](/reactter/en/methods/dependency_injection_methods/#rtfactory) + - [`Rt.singleton`](/reactter/en/methods/dependency_injection_methods/#rtsingleton) + + :::note + All of the above methods, except `Rt.get`, in addition to instantiating the dependency, may also register it if it hasn't been registered previously. + ::: + + If a new dependency instance is created, the following events will be emitted: - `Lifecycle.created` - - `Lifecycle.willMount`(_flutter_reactter_ only) - - `Lifecycle.didMount`(_flutter_reactter_ only) + - `Lifecycle.willMount` (only in _flutter_reactter_) + - `Lifecycle.didMount` (only in _flutter_reactter_) + +3. **Usage**: Once the dependency is resolved, its instance can be used anywhere within the application. -3. **Usage**: The dependency is then used across the app as needed. + To access the dependency or check its state, you can use the following methods: + - [`Rt.find`](/reactter/en/methods/dependency_injection_methods/#rtfind) + - [`Rt.get`](/reactter/en/methods/dependency_injection_methods/#rtget) + - [`Rt.exists`](/reactter/en/methods/dependency_injection_methods/#rtexists) + - [`Rt.isActive`](/reactter/en/methods/dependency_injection_methods/#rtis_active) + - [`Rt.getDependencyMode`](/reactter/en/methods/dependency_injection_methods/#rtget_dependency_mode) - Some of these events may occur: + If the dependency's state is updated, the following events will be emitted: - `Lifecycle.willUpdate` - `Lifecycle.didUpdate` -4. **Deleting**: When the dependency with/without `id` is no longer required, Reactter deletes it. +4. **Deletion**: In this stage, Reactter removes the dependency instance based on the provided type and `id`. - For this, you can use the following methods: - - [`Rt.delete`](/reactter/methods/dependency_injection_methods/#rtdelete) - - [`Rt.destroy`](/reactter/methods/dependency_injection_methods/#rtdestroy)(delete & unregister) + To do this, you can use the following methods: + - [`Rt.delete`](/reactter/en/methods/dependency_injection_methods/#rtdelete) + - [`Rt.destroy`](/reactter/en/methods/dependency_injection_methods/#rtdestroy) + + :::note + Depending on the dependency `mode`, the dependency instance may not be deleted, or the registration may also be removed. + ::: - The following events are fired: - - `Lifecycle.willUnmount`(_flutter_reactter_ only) - - `Lifecycle.didUnmount`(_flutter_reactter_ only) + During deletion, the following events will be emitted: + - `Lifecycle.willUnmount` (only in _flutter_reactter_) + - `Lifecycle.didUnmount` (only in _flutter_reactter_) - `Lifecycle.deleted` -5. **Unregistration**: When the dependency with/without `id` is no longer required and depending on `mode`, Reactter unregisters it. +5. **Unregistration**: In this stage, Reactter removes the dependency's registration based on the provided type and `id`. - For this, you can use the following methods: - - [`Rt.unregister`](/reactter/methods/dependency_injection_methods/#rtunregister) - - [`Rt.delete`](/reactter/methods/dependency_injection_methods/#rtdelete) - - [`Rt.destroy`](/reactter/methods/dependency_injection_methods/#rtdestroy) + To unregister the dependency, you can use the following methods: + - [`Rt.unregister`](/reactter/en/methods/dependency_injection_methods/#rtunregister) + - [`Rt.delete`](/reactter/en/methods/dependency_injection_methods/#rtdelete) + - [`Rt.destroy`](/reactter/en/methods/dependency_injection_methods/#rtdestroy) + + :::note + Depending on the dependency `mode`, the dependency registration may not be removed. + ::: - The `Lifecycle.unregistered` event is emitted. + When the dependency registration is removed, the event `Lifecycle.unregistered` will be emitted. :::note `id` and `mode` are optional parameters. @@ -122,156 +141,154 @@ The scope of the registered dependency is global. This indicates that using the ### Example -To understand it better, we will return to the countdown example seen from the [State Management](/reactter/core_concepts/state_management/#example) page, -but now using the dependency injection: +To better understand this, let's go back the countdown example from the [state manager page](/reactter/en/core_concepts/state_management/#example), but now using dependency injection: Countdown())!; - // Start the countdown - await countdown.run(); -} + import 'package:reactter/reactter.dart'; + import 'countdown.dart'; + + void main() async { + // Create an instance of the 'Countdown' class + final countdown = Rt.create(() => Countdown())!; + // Start the countdown + await countdown.run(); + } `} lang="dart" mark={marks} /> (() => Counter(10)); - - // Get the instance of the \`Counter\` class - Counter get counter => uCounter.instance; - - /// Start the countdown - Future run() { - // Listen to the \`didUpdate\` event of the \`counter\` instance - // and print the current \`value\` of \`count\` each time it changes - Rt.on( - counter, - Lifecycle.didUpdate, - (_, __) => print('Count: \${counter.count}'), - ); - - // Create a timer that decrements the \`count\` state by 1 - // every second until it reaches 0 - return Timer.periodic(Duration(seconds: 1), _countdown); - } + import 'package:reactter/reactter.dart'; + import 'counter.dart'; + + /// A class representing a countdown + class Countdown { + // Create an instance of the 'Counter' class using the 'UseDependency' hook + // with an initial value of 10 + final uCounter = UseDependency.create(() => Counter(10)); + + // Get the 'Counter' instance + Counter get counter => uCounter.instance; + + /// Start the countdown + Future run() { + // Listen for the 'didUpdate' event on the 'counter' instance + // and print the current value of 'count' + Rt.on( + counter, + Lifecycle.didUpdate, + (_, __) => print('Count: \${counter.count}'), + ); + + // Create a timer that calls the '_countdown' function + // every second + return Timer.periodic(Duration(seconds: 1), _countdown); + } - // Decrement the \`count\` state by 1 each time the timer ticks - // and delete the \`Counter\` instance when the count value reaches 0 - void _countdown(Timer timer) { - counter.decrement(); + // Decrement the 'count' state by 1 every timer cycle + // and delete the 'Counter' instance when the value reaches 0 + void _countdown(Timer timer) { + counter.decrement(); - if (counter.count == 0) { - timer.cancel(); - Rt.delete(); + if (counter.count == 0) { + timer.cancel(); + Rt.delete(); + } } } -} `} lang="dart" mark={marks} /> _count; + /// A class representing a counter that holds the 'count' state + class Counter { + final Signal _count; - int get count => _count.value; + int get count => _count.value; - const Counter(int initialValue) : _count = Signal(initialValue); + const Counter(int initialValue) : _count = Signal(initialValue); - void decrement() => _count.value -= 1; -} + void decrement() => _count.value -= 1; + } `} lang="dart" mark={marks} /> -In this example, we have create a countdown of `10` seconds, and when it reaches `0`, the `Counter` instance is deleted. -But we will make a small tweak to change the countdown behavior. +In this example, we've created a countdown from `10` seconds, and when it reaches `0`, the `Counter` instance is deleted. +But if we want to use the `Counter` instance elsewhere in the code, we can do it like this: ```dart title="main.dart" ins={3, 6-7} collapse={8-11} "Rt.register" "Rt.create" /Counter(?!\u0060)/ /Countdown(?!\u0060)/ /(countdown) =/ "countdown.run" -import 'package:reactter/reactter.dart'; -import 'countdown.dart'; -import 'counter.dart'; - -void main() async { - // Register the `Counter` class with an initial value of 20 - Rt.register(() => Counter(20)); - // Create an instance of the `Countdown` class - final countdown = Rt.create(() => Countdown())!; - // Start the countdown - await countdown.run(); -} + import 'package:reactter/reactter.dart'; + import 'countdown.dart'; + import 'counter.dart'; + + void main() async { + // Register the 'Counter' class with an initial value of 20 + Rt.register(() => Counter(20)); + // Create an instance of the 'Countdown' class + final countdown = Rt.create(() => Countdown())!; + // Start the countdown + await countdown.run(); + } ``` -Now, the countdown will start from `20` and when it reaches `0`, the `Counter` instance is deleted. -What happens is that the `Counter` instance is registered with an initial value of `20`, -and when the `Countdown` instance is created, it uses the `Counter` instance registered. +Now, the countdown will start from `20`, and when it reaches `0`, the instance of `Counter` will be deleted. +What happens is that the instance of `Counter` is registered with an initial value of `20`, +and when the instance of `Countdown` is created, it uses the registered instance of `Counter`. -Ok, but what if we want to use the `Counter` instance in another part of the code? Let's look: +Ok, But what if we want to use the instance of `Counter` elsewhere in the code? Let's see: ```dart title="main.dart" ins={12-15} collapse={6-11} "Rt.register" "Rt.create" "Rt.get" /counter(?!\\.)/ "counter?.count" /Counter(?!\u0060)(?! )/ /Countdown(?!\u0060)/ /(countdown) =/ "countdown.run" -import 'package:reactter/reactter.dart'; -import 'countdown.dart'; -import 'counter.dart'; - -void main() async { - // Register the `Counter` class with an initial value of 20 - Rt.register(() => Counter(20)); - // Create an instance of the `Countdown` class - final countdown = Rt.create(() => Countdown())!; - // Start the countdown - await countdown.run(); - // Get the instance of the `Counter` class - final counter = Rt.get(); - // Try to print the current count value - print('Count: ${counter?.count ?? 'Counter instance not found'}'); -} + import 'package:reactter/reactter.dart'; + import 'countdown.dart'; + import 'counter.dart'; + + void main() async { + // Register the `Counter` class with an initial value of 20 + Rt.register(() => Counter(20)); + // Create an instance of the `Countdown` class + final countdown = Rt.create(() => Countdown())!; + // Start the countdown + await countdown.run(); + // Get the instance of the `Counter` class + final counter = Rt.get(); + // Try to print the current `count` value + print('Count: ${counter?.count ?? 'Counter instance not found'}'); + } ``` -In this case, the countdown will work as before, but when trying to get the `Counter` instance to print its value, -the ouput will be _“Counter instance not found”_. -This occurs because `Counter` was registered as `DependencyMode.builder`(the default mode), -so when it was deleted at the end of the countdown its registration was also deleted. -If we want to get the `Counter` instance to print its value, we need to register using the `DependencyMode.singleton` mode, looking like this: +In this case, the countdown will work as before, but when trying to get the instance of `Counter` to print its value, +the output will be 'Counter instance not found'. +This happens because `Counter` was registered as `DependencyMode.builder` (the default mode), +so when it is deleted at the end of the countdown, its registration is also removed. + +If we want to obtain the instance of Counter to print its value, we need to register it using the DependencyMode.singleton mode, as shown below: ```dart title="main.dart" {7} collapse={8-15} "Rt.register" "Rt.create" "Rt.get" "DependencyMode.singleton" /counter(?!\\.)/ "counter?.count" /Counter(?!\u0060)(?! )/ /Countdown(?!\u0060)/ /(countdown) =/ "countdown.run" -import 'package:reactter/reactter.dart'; -import 'countdown.dart'; -import 'counter.dart'; - -void main() async { - // Register the `Counter` class as singleton mode with an initial value of 20 - Rt.register(() => Counter(20), mode: DependencyMode.singleton); - // Create an instance of the `Countdown` class - final countdown = Rt.create(() => Countdown())!; - // Start the countdown - await countdown.run(); - // Get the instance of the `Counter` class - final counter = Rt.get(); - // Try to print the current count value - print('Count: ${counter?.count ?? 'Counter instance not found'}'); + import 'package:reactter/reactter.dart'; + import 'countdown.dart'; + import 'counter.dart'; + + void main() async { + // Register the `Counter` class as singleton mode with an initial value of 20 + Rt.register(() => Counter(20), mode: DependencyMode.singleton); + // Create an instance of the `Countdown` class + final countdown = Rt.create(() => Countdown())!; + // Start the countdown + await countdown.run(); + // Get the instance of the `Counter` class + final counter = Rt.get(); + // Try to print the current `count` value + print('Count: ${counter?.count ?? 'Counter instance not found'}'); } ``` -Let's now delve into the **modes** of dependency registration. - ## Dependency Modes The **mode** with which a dependency is registered determines how it is managed by Reactter. There are three modes: @@ -282,63 +299,54 @@ The **mode** with which a dependency is registered determines how it is managed ### Builder -Builder is a ways to manage a dependency, -which registers a builder function and creates the instance, -unless it has already done so. +The Builder mode is a way to manage a dependency by registering a `builder` function and creating an instance only if it hasn't been created previously. -In builder mode, when the dependency tree no longer needs it, -it is completely deleted, -including unregistration (deleting the builder function). +In this mode, when the dependency tree no longer needs it, the instance is completely removed, including the registration and the `builder` function. -Reactter identifies the builder mode as +Reactter identifies the Builder mode as `DependencyMode.builder` -and it's using for default. +and uses it by default. :::note - **Builder** uses less RAM than [Factory](#factory) and [Singleton](#singleton), - but it consumes more CPU than the other modes. + Builder uses less RAM than [Factory](#factory) and [Singleton](#singleton), + but consumes more CPU than the other modes. ::: ### Factory -Factory is a ways to manage a dependency, -which registers a builder function only once -and creates the instance if not already done. +The Factory mode is a way to manage a dependency in which a `builder` function is registered, and a new instance is created every time it is requested. -In factory mode, when the dependency tree no longer needs it, -the instance is deleted and the builder function is kept in the register. +In this mode, when the dependency tree no longer uses it, the instance is removed, but the `builder` function remains registered. -Reactter identifies the factory mode as +Reactter identifies the Factory mode as `DependencyMode.factory` -and to active it,set it in the `mode` argument of [`Rt.register`](/reactter/methods/dependency_injection_methods/#rtregister) and [`Rt.create`](/reactter/methods/dependency_injection_methods/#rtcreate), -or use [`Rt.lazyFactory`](/reactter/methods/dependency_injection_methods/#rtlazy_factory), [`Rt.factory`](/reactter/methods/dependency_injection_methods/#rtfactory). +and to activate it, set the `mode` in the argument of [`Rt.register`](/reactter/methods/dependency_injection_methods/#rtregister) and [`Rt.create`](/reactter/methods/dependency_injection_methods/#rtcreate), +or use [`Rt.lazyFactory`](/reactter/methods/dependency_injection_methods/#rtlazy_factory), [`Rt.factory`](/reactter/methods/dependency_injection_methods/#rtfactory). :::note - **Factory** uses more RAM than [Builder](#builder) + Factory uses more RAM than [Builder](#builder), but not more than [Singleton](#singleton), - and consumes more CPU than [Singleton](#singleton) + and consumes more CPU than [Singleton](#singleton), but not more than [Builder](#builder). ::: ### Singleton -Singleton is a ways to manage a dependency, -which registers a builder function and creates the instance only once. +The Singleton mode is a way to manage a dependency by registering a `builder` function and ensuring that the instance is created only once. + +When using the singleton mode, the dependency instance and its states remain active, even if the dependency tree no longer uses it. +This also includes the creation function, unless it is explicitly removed. -The singleton mode preserves the instance and its states, -even if the dependency tree stops using it. +:::tip + Use [`Rt.destroy`](/reactter/methods/dependency_injection_methods/#rtdestroy) if you want to delete both the instance and the registration of a dependency in singleton mode. +::: -Reactter identifies the singleton mode as +Reactter identifies the Singleton mode as `DependencyMode.singleton` -and to active it, -set it in the `mode` argument of [`Rt.register`](/reactter/methods/dependency_injection_methods/#rtregister) and [`Rt.create`](/reactter/methods/dependency_injection_methods/#rtcreate), +and to activate it, set the `mode` in the argument of [`Rt.register`](/reactter/methods/dependency_injection_methods/#rtregister) and [`Rt.create`](/reactter/methods/dependency_injection_methods/#rtcreate), or use [`Rt.lazySingleton`](/reactter/methods/dependency_injection_methods/#rtlazy_singleton), [`Rt.singleton`](/reactter/methods/dependency_injection_methods/#rtsingleton). :::note - **Singleton** consumes less CPU than [Builder](#builder) and [Factory](#factory), + Singleton consumes less CPU than [Builder](#builder) and [Factory](#factory), but uses more RAM than the other modes. ::: - -:::note - Use [`Rt.destroy`](/reactter/methods/dependency_injection_methods/#rtdestroy) if you want to force destroy the instance and its register. -::: diff --git a/website/src/content/docs/es/core_concepts/dependency_injection.mdx b/website/src/content/docs/es/core_concepts/dependency_injection.mdx new file mode 100644 index 00000000..3ad508a6 --- /dev/null +++ b/website/src/content/docs/es/core_concepts/dependency_injection.mdx @@ -0,0 +1,355 @@ +--- +title: Inyección de dependencias +description: Aprende a gestionar las dependencias en Reactter. +sidebar: + order: 2 +--- +import { HE, HM, HN, HS, HT } from '@/components/Highlight'; +import CodeTabs from '@/components/CodeTabs.astro'; +import { Tabs, TabItem } from "@/components/Tabs"; +import { Code } from "@astrojs/starlight/components"; +import { marks } from '@/examples/marks.ts' + +La inyección de dependencias es un patrón de diseño que facilita la gestión de las dependencias de un objeto. + +Con Reactter, la gestión de objetos se vuelve sencilla. +Puedes crear, eliminar y acceder a los objetos deseados desde un único lugar centralizado, accesible desde cualquier parte de tu código, todo gracias al sólido sistema de inyección de dependencias de Reactter. + +La inyección de dependencias ofrece varios beneficios. Algunos de los más destacados son: + +- **Desacoplamiento**: La inyección de dependencias desacopla las clases de sus dependencias, lo que facilita la modificación y la reutilización del código. +- **Inversión de control**: Se adhiere al principio de inversión de control, donde la responsabilidad de la creación y gestión de objetos se delega a Reactter. +Esto resulta en una mejor _modularidad_, _reutilización_ y _testabilidad_ del código. +- **Código simplificado**: Al delegar la responsabilidad de crear dependencias desde las clases individuales, la inyección de dependencias simplifica el código, permitiendo que las clases se centren más en su funcionalidad principal. + +## API + +Reactter proporciona los siguientes mecanismos de inyección de dependencias: + +- Hooks + - [`UseDependency`](/reactter/es/hooks/UseDependency) +- Métodos + - [`Rt.register`](/reactter/es/methods/dependency_injection_methods/#rtregister) + - [`Rt.lazyBuilder`](/reactter/es/methods/dependency_injection_methods/#rtlazy_builder) + - [`Rt.lazyFactory`](/reactter/es/methods/dependency_injection_methods/#rtlazy_factory) + - [`Rt.lazySingleton`](/reactter/es/methods/dependency_injection_methods/#rtlazy_singleton) + - [`Rt.create`](/reactter/es/methods/dependency_injection_methods/#rtcreate) + - [`Rt.builder`](/reactter/es/methods/dependency_injection_methods/#rtbuilder) + - [`Rt.factory`](/reactter/es/methods/dependency_injection_methods/#rtfactory) + - [`Rt.singleton`](/reactter/es/methods/dependency_injection_methods/#rtsingleton) + - [`Rt.get`](/reactter/es/methods/dependency_injection_methods/#rtget) + - [`Rt.find`](/reactter/es/methods/dependency_injection_methods/#rtfind) + - [`Rt.exists`](/reactter/es/methods/dependency_injection_methods/#rtexists) + - [`Rt.isActive`](/reactter/es/methods/dependency_injection_methods/#rtis_active) + - [`Rt.getDependencyMode`](/reactter/es/methods/dependency_injection_methods/#rtget_dependency_mode) + - [`Rt.delete`](/reactter/es/methods/dependency_injection_methods/#rtdelete) + - [`Rt.destroy`](/reactter/es/methods/dependency_injection_methods/#rtdestroy) + - [`Rt.unregister`](/reactter/es/methods/dependency_injection_methods/#rtunregister) + +:::tip[Con Flutter] +Sí estás utilizando Flutter, consulta acerca del [control de renderizado](/reactter/es/core_concepts/rendering_control) para aprender a gestionar las dependencias a través de los `Widget` y los métodos extendibles del `BuildContext`. +::: + +## ¿Cómo funciona? + +Reactter gestiona las dependencias a través de un mecanismo centralizado que actua como un repositorio principal encargado de registrar, resolver y suministrar dependencias en toda la aplicación. +Para comprender cómo funciona este sistema en su totalidad, desglosaremos el proceso en las siguientes etapas: + +1. **Registro**: Esta etapa implica registrar la dependencia en el contexto de Reactter utilizando un tipo específico, una función de creación(`builder`), un identificador(`id`) y un modo de dependencia(`mode`). + + Para realizar este registro, puedes utilizar los siguientes métodos: + - [`Rt.register`](/reactter/es/methods/dependency_injection_methods/#rtregister) + - [`Rt.lazyBuilder`](/reactter/es/methods/dependency_injection_methods/#rtlazy_builder) + - [`Rt.lazyFactory`](/reactter/es/methods/dependency_injection_methods/#rtlazy_factory) + - [`Rt.lazySingleton`](/reactter/es/methods/dependency_injection_methods/#rtlazy_singleton) + + Durante el registro se emite el evento `Lifecycle.registered`. + +2. **Resolución**: Cuando se solicita una dependencia, Reactter crea una instancia de la dependencia a partir de la funcion de creación(`builder`) registrada, según el tipo e identificador(`id`) proporcionados. + + Para ello, puedes utilizar los siguientes métodos: + - [`Rt.get`](/reactter/es/methods/dependency_injection_methods/#rtget) + - [`Rt.create`](/reactter/es/methods/dependency_injection_methods/#rtcreate) + - [`Rt.builder`](/reactter/es/methods/dependency_injection_methods/#rtbuilder) + - [`Rt.factory`](/reactter/es/methods/dependency_injection_methods/#rtfactory) + - [`Rt.singleton`](/reactter/es/methods/dependency_injection_methods/#rtsingleton) + + :::note + Todos los anteriores métodos, excepto `Rt.get`, ademas de instanciar la dependencia, pueden registrarla si no ha sido registrada previamente. + ::: + + Si se crea una nueva instancia de la dependencia, se emitirán los siguientes eventos: + - `Lifecycle.created` + - `Lifecycle.willMount`(solo en _flutter_reactter_) + - `Lifecycle.didMount`(solo en _flutter_reactter_) + +3. **Uso**: Una vez resuelta la dependencia, su instancia puede ser utilizada en cualquier parte de la aplicación. + + Para poder acceder a la dependencia o comprobar su estado, puedes utilizar los siguientes métodos: + - [`Rt.find`](/reactter/es/methods/dependency_injection_methods/#rtfind) + - [`Rt.get`](/reactter/es/methods/dependency_injection_methods/#rtget) + - [`Rt.exists`](/reactter/es/methods/dependency_injection_methods/#rtexists) + - [`Rt.isActive`](/reactter/es/methods/dependency_injection_methods/#rtis_active) + - [`Rt.getDependencyMode`](/reactter/es/methods/dependency_injection_methods/#rtget_dependency_mode) + + Si algún estado de la dependencia se actualiza, se emitirán los siguientes eventos: + - `Lifecycle.willUpdate` + - `Lifecycle.didUpdate` + +4. **Eliminación**: En esta etapa, Reactter elimina la instancia de la dependencia según el tipo e identificador(`id`) proporcionados. + + Para ello, puedes utilizar los siguientes métodos: + - [`Rt.delete`](/reactter/es/methods/dependency_injection_methods/#rtdelete) + - [`Rt.destroy`](/reactter/es/methods/dependency_injection_methods/#rtdestroy) + + :::note + Según el modo de la dependencia(`mode`), es posible que no se elimine la instancia de la dependencia o, en su caso, que también se elimine su registro. + ::: + + Durante la eliminación se emiten los siguientes eventos: + - `Lifecycle.willUnmount`(solo en _flutter_reactter_) + - `Lifecycle.didUnmount`(solo en _flutter_reactter_) + - `Lifecycle.deleted` + +5. **Desregistro**: En esta etapa, Reactter elimina el registro de la dependencia del tipo y el identificador(`id`) proporcionados. + + Para desregistrar la dependencia, puedes utilizar los siguientes métodos: + - [`Rt.unregister`](/reactter/es/methods/dependency_injection_methods/#rtunregister) + - [`Rt.delete`](/reactter/es/methods/dependency_injection_methods/#rtdelete) + - [`Rt.destroy`](/reactter/es/methods/dependency_injection_methods/#rtdestroy) + + :::note + Según el modo de la dependencia(`mode`), es posible que no se elimine el registro de la dependencia. + ::: + + Cuando se elimina el registro de la dependencia, se emitirá el evento `Lifecycle.unregistered`. + +:::note +`id` y `mode` son parámetros opcionales. + +Si no se proporciona el `id`, Reactter resolverá la dependencia en función del tipo. + +Si no se proporciona el `mode`, Reactter utilizará el modo predeterminado, que es `DependencyMode.builder`. + +Una **dependencia** en Reactter se refiere a una **instancia** de un tipo específico. + +El ámbito de la dependencia registrada es global. Esto indica que al utilizar los [atajos para gestionar dependencias](/reactter/es/shortcuts/dependency) o el [`UseDependency`](/reactter/es/hooks/UseDependency), podrás acceder a ellas desde cualquier parte del proyecto. +::: + +:::tip + Aprende sobre el [Ciclo de vida](/reactter/es/core_concepts/lifecycle). +::: + +### Ejemplo + +Para entenderlo mejor, retomaremos el ejemplo de la cuenta regresiva visto desde la página del [gestor de estados](/reactter/es/core_concepts/state_management/#ejemplo), +pero ahora utilizando la inyección de dependencias: + + + + + Countdown())!; + // Inicia la cuenta regresiva + await countdown.run(); + } + `} lang="dart" mark={marks} /> + + + + (() => Counter(10)); + + // Obtiene la instancia de \`Counter\` + Counter get counter => uCounter.instance; + + /// Inicia la cuenta regresiva + Future run() { + // Escucha el evento \`didUpdate\` de la instancia \`counter\` + // e imprime el valor actual de \`count\` + Rt.on( + counter, + Lifecycle.didUpdate, + (_, __) => print('Count: \${counter.count}'), + ); + + // Crea un temporizador que invoca la función \`_countdown\` + // cada segundo + return Timer.periodic(Duration(seconds: 1), _countdown); + } + + // Decrementa el estado \`count\` en 1 cada ciclo del temporizador + // y elimina la instancia de \`Counter\` cuando el valor llega a 0 + void _countdown(Timer timer) { + counter.decrement(); + + if (counter.count == 0) { + timer.cancel(); + Rt.delete(); + } + } + } + `} lang="dart" mark={marks} /> + + + + _count; + + int get count => _count.value; + + const Counter(int initialValue) : _count = Signal(initialValue); + + void decrement() => _count.value -= 1; + } + `} lang="dart" mark={marks} /> + + + + +En este ejemplo, hemos creado una cuenta regresiva de `10` segundos, y cuando llega a `0`, la instancia de `Counter` es eliminada. +Pero si queremos utilizar la instancia de `Counter` en otra parte del código, podemos hacerlo de la siguiente manera: + +```dart title="main.dart" ins={3, 6-7} collapse={8-11} "Rt.register" "Rt.create" /Counter(?!\u0060)/ /Countdown(?!\u0060)/ /(countdown) =/ "countdown.run" + import 'package:reactter/reactter.dart'; + import 'countdown.dart'; + import 'counter.dart'; + + void main() async { + // Registra la clase `Counter` con un valor inicial de 20 + Rt.register(() => Counter(20)); + // Crea una instancia de la clase `Countdown` + final countdown = Rt.create(() => Countdown())!; + // Inicia la cuenta regresiva + await countdown.run(); + } +``` + +Ahora, la cuenta regresiva iniciará desde `20` y cuando llegue a `0`, la instancia de `Counter` será eliminada. +Lo que sucede es que la instancia de `Counter` es registrada con un valor inicial de `20`, +y cuando se crea la instancia de `Countdown`, utiliza la instancia de `Counter` registrada. + +Pero, ¿qué pasa si queremos utilizar la instancia de `Counter` en otra parte del código? Veamos: + +```dart title="main.dart" ins={12-15} collapse={6-11} "Rt.register" "Rt.create" "Rt.get" /counter(?!\\.)/ "counter?.count" /Counter(?!\u0060)(?! )/ /Countdown(?!\u0060)/ /(countdown) =/ "countdown.run" + import 'package:reactter/reactter.dart'; + import 'countdown.dart'; + import 'counter.dart'; + + void main() async { + // Registra la clase `Counter` con un valor inicial de 20 + Rt.register(() => Counter(20)); + // Crea una instancia de la clase `Countdown` + final countdown = Rt.create(() => Countdown())!; + // Inicia la cuenta regresiva + await countdown.run(); + // Obtiene la instancia de la clase `Counter` + final counter = Rt.get(); + // Intenta imprimir el valor actual de `count` + print('Count: ${counter?.count ?? 'Counter instance not found'}'); + } +``` + +En este caso, la cuenta regresiva funcionará como antes, pero al intentar obtener la instancia de `Counter` para imprimir su valor, +la salida será 'Counter instance not found'. +Esto ocurre porque `Counter` fue registrado como `DependencyMode.builder`(el modo predeterminado), +por lo que al ser eliminada al final de la cuenta regresiva, su registro también se elimina. + +Si queremos obtener la instancia de `Counter` para imprimir su valor, necesitamos registrarla utilizando el modo `DependencyMode.singleton`, quedando de la siguiente manera: + +```dart title="main.dart" {7} collapse={8-15} "Rt.register" "Rt.create" "Rt.get" "DependencyMode.singleton" /counter(?!\\.)/ "counter?.count" /Counter(?!\u0060)(?! )/ /Countdown(?!\u0060)/ /(countdown) =/ "countdown.run" + import 'package:reactter/reactter.dart'; + import 'countdown.dart'; + import 'counter.dart'; + + void main() async { + // Registra la clase `Counter` con un valor inicial de 20 + Rt.register(() => Counter(20), mode: DependencyMode.singleton); + // Crea una instancia de la clase `Countdown` + final countdown = Rt.create(() => Countdown())!; + // Inicia la cuenta regresiva + await countdown.run(); + // Obtiene la instancia de la clase `Counter` + final counter = Rt.get(); + // Intenta imprimir el valor actual de la cuenta + print('Count: ${counter?.count ?? 'Counter instance not found'}'); + } +``` + +## Modos de dependencia + +El `mode` con el que se registra una dependencia determina cómo es gestionada por Reactter. Existe tres modos de dependencia: + +- [Builder](#builder) +- [Factory](#factory) +- [Singleton](#singleton) + +### Builder + +El modo Builder es una forma de gestionar una dependencia que registra una función de creación(`builder`) y crea una instancia solo si no ha sido creada previamente. + +En este modo, cuando el árbol de dependencias deja de necesitarla, se elimina por completo, incluyendo el registro y la función de creación(`builder`). + +Reactter identifica el modo builder como +`DependencyMode.builder` +y lo utiliza por defecto. + +:::note + Builder utiliza menos RAM que [Factory](#factory) y [Singleton](#singleton), + pero consume más CPU que los otros modos. +::: + +### Factory + +El modo Factory es una forma de gestionar una dependencia en la que se registra una función de creación(`builder`) y se crea una nueva instancia cada vez que se solicita. + +En este modo, cuando el árbol de dependencias deja de utilizarla, la instancia se elimina, pero la función de creación(`builder`) permanece registrada. + +Reactter identifica el modo factory como +`DependencyMode.factory` +y para activarlo, establezca el `mode` en el argumento de [`Rt.register`](/reactter/es/methods/dependency_injection_methods/#rtregister) y [`Rt.create`](/reactter/es/methods/dependency_injection_methods/#rtcreate), +o utilice [`Rt.lazyFactory`](/reactter/es/methods/dependency_injection_methods/#rtlazy_factory), [`Rt.factory`](/reactter/es/methods/dependency_injection_methods/#rtfactory). + +:::note + Factory utiliza más RAM que [Builder](#builder), + pero no más que [Singleton](#singleton), + y consume más CPU que [Singleton](#singleton), + pero no más que [Builder](#builder). +::: + +### Singleton + +El modo Singleton es una forma de gestionar una dependencia que registra una función de creación(`builder`) y garantiza que la instancia se cree solo una vez. + +Cuando se utiliza el modo singleton, la instancia de la dependencia y sus estados se mantienen activos, incluso si el árbol de dependencias ya no la utiliza. +Esto incluye también la función de creación, a menos que se elimine explícitamente. + +:::tip + Utilice [`Rt.detroy`](/reactter/es/methods/dependency_injection_methods/#rtdestroy) si deseas eliminar tanto la instancia como el registro de una dependencia en modo singleton. +::: + +Reactter identifica el modo singleton como +`DependencyMode.singleton` +y para activarlo, establezca el `mode` en el argumento de [`Rt.register`](/reactter/es/methods/dependency_injection_methods/#rtregister) y [`Rt.create`](/reactter/es/methods/dependency_injection_methods/#rtcreate), +o utilice [`Rt.lazySingleton`](/reactter/es/methods/dependency_injection_methods/#rtlazy_singleton), [`Rt.singleton`](/reactter/es/methods/dependency_injection_methods/#rtsingleton). + +:::note + Singleton consume menos CPU que [Builder](#builder) y [Factory](#factory), + pero utiliza más RAM que los otros modos. +::: diff --git a/website/src/content/docs/es/core_concepts/state_management.mdx b/website/src/content/docs/es/core_concepts/state_management.mdx index 040da058..d62319dd 100644 --- a/website/src/content/docs/es/core_concepts/state_management.mdx +++ b/website/src/content/docs/es/core_concepts/state_management.mdx @@ -20,14 +20,14 @@ Reactter proporciona una gran variedad de mecanismos para la gestión de estados - Mixins - [`RtStateBase`](/reactter/es/classes/RtStateBase) -- Classes +- Clases - [`Signal`](/reactter/es/classes/signal) - Hooks - [`UseState`](/reactter/es/hooks/use_state) - [`UseAsyncState`](/reactter/es/hooks/use_async_state) - [`UseReducer`](/reactter/es/hooks/use_reducer) - [`UseCompute`](/reactter/es/hooks/use_compute) -- Methods +- Metodos - [`Rt.lazyState`](/reactter/es/methods/state_management_methods/#rtlazy_state) - [`Rt.batch`](/reactter/es/methods/state_management_methods/#rtbatch) - [`Rt.untracked`](/reactter/es/methods/state_management_methods/#rtuntracked) @@ -36,7 +36,7 @@ Reactter proporciona una gran variedad de mecanismos para la gestión de estados Aprende sobre [Hooks](/reactter/es/core_concepts/hooks). ::: -## Cómo funciona +## ¿Cómo funciona? El sistema de gestión de estados de Reactter se basa en el concepto de _reactividad_. Contrario a la noción predominante de que implementar programación reactiva en Dart puede ser desafiante, @@ -58,7 +58,7 @@ La clase Reactter @@ -13,7 +13,7 @@ hero:

Gestor de Estados, - Inyector de Dependencias y + Inyección de Dependencias y Manejador de Eventos para Dart/Flutter.

diff --git a/website/src/examples/marks.ts b/website/src/examples/marks.ts index 7af0b91d..f0c15074 100644 --- a/website/src/examples/marks.ts +++ b/website/src/examples/marks.ts @@ -1,4 +1,5 @@ export const marks = [ + "Rt.creteState", "Rt.lazyState", "Rt.batch", "Rt.untracked", @@ -24,7 +25,9 @@ export const marks = [ "Rt.off", "Rt.offAll", "RtHook", + "RtContextMixin", "RtState", + "RtStateBase", "RtDependency", "RtDependencyMode", "RtDependencyMode.builder", From 10f51acccda621e664e336a48a9926e50e733978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Mon, 20 Jan 2025 23:11:56 -0600 Subject: [PATCH 099/141] refactor(website): Add new documentation files for Reactter API and update documentation. --- website/astro.config.mjs | 99 +++++---- .../content/docs/{ => api}/classes/args.mdx | 2 +- .../docs/api/classes/auto_dispatch_effect.mdx | 6 + .../docs/api/classes/lifecycle_observer.mdx | 6 + .../content/docs/{ => api}/classes/memo.mdx | 4 +- .../docs/api/classes/rt_context_mixin.mdx | 6 + .../docs/api/classes/rt_dependency.mdx | 6 + .../api/classes/rt_dependency_observer.mdx | 6 + .../docs/api/classes/rt_state_base.mdx | 6 + .../docs/api/classes/rt_state_observer.mdx | 6 + .../content/docs/{ => api}/classes/signal.mdx | 2 +- .../extensions/_build_context_select.mdx} | 2 +- .../extensions/_build_context_use.mdx} | 2 +- .../extensions/_build_context_watch.mdx} | 6 +- .../extensions/_build_context_watch_id.mdx} | 4 +- .../extensions/build_context_extensions.mdx | 8 + .../docs/{ => api}/hooks/use_async_state.mdx | 4 +- .../docs/{ => api}/hooks/use_compute.mdx | 2 +- .../docs/{ => api}/hooks/use_dependency.mdx | 8 +- .../docs/{ => api}/hooks/use_effect.mdx | 0 .../docs/{ => api}/hooks/use_reducer.mdx | 2 +- .../docs/{ => api}/hooks/use_state.mdx | 0 .../content/docs/api/methods/core_methods.mdx | 6 + .../methods/dependency_injection_methods.mdx | 2 +- .../methods/event_handler_methods.mdx | 2 +- .../methods/state_management_methods.mdx | 2 +- .../{classes => api/widgets}/rt_component.mdx | 6 +- .../docs/{ => api}/widgets/rt_consumer.mdx | 4 +- .../{ => api}/widgets/rt_multi_provider.mdx | 0 .../docs/{ => api}/widgets/rt_provider.mdx | 0 .../docs/{ => api}/widgets/rt_scope.mdx | 2 +- .../docs/{ => api}/widgets/rt_selector.mdx | 4 +- .../{ => api}/widgets/rt_signal_watcher.mdx | 2 +- .../docs/{ => api}/widgets/rt_watcher.mdx | 2 +- .../core_concepts/dependency_injection.mdx | 174 +++++++-------- .../docs/core_concepts/event_handler.mdx | 49 ++--- .../src/content/docs/core_concepts/hooks.mdx | 16 +- .../content/docs/core_concepts/lifecycle.mdx | 6 +- .../docs/core_concepts/rendering_control.mdx | 49 ++--- .../docs/core_concepts/state_management.mdx | 30 ++- .../src/content/docs/devtools_extension.mdx | 4 + .../es/core_concepts/dependency_injection.mdx | 208 +++++++++--------- .../docs/es/core_concepts/event_handler.mdx | 130 +++++++++++ .../es/core_concepts/rendering_control.mdx | 114 ++++++++++ .../es/core_concepts/state_management.mdx | 26 +-- .../src/content/docs/es/getting_started.mdx | 14 +- website/src/content/docs/es/index.mdx | 2 +- .../binding_state_to_dependency.mdx | 6 +- .../docs/shareds/tip_lifecycle_example.mdx | 2 +- 49 files changed, 680 insertions(+), 369 deletions(-) rename website/src/content/docs/{ => api}/classes/args.mdx (96%) create mode 100644 website/src/content/docs/api/classes/auto_dispatch_effect.mdx create mode 100644 website/src/content/docs/api/classes/lifecycle_observer.mdx rename website/src/content/docs/{ => api}/classes/memo.mdx (94%) create mode 100644 website/src/content/docs/api/classes/rt_context_mixin.mdx create mode 100644 website/src/content/docs/api/classes/rt_dependency.mdx create mode 100644 website/src/content/docs/api/classes/rt_dependency_observer.mdx create mode 100644 website/src/content/docs/api/classes/rt_state_base.mdx create mode 100644 website/src/content/docs/api/classes/rt_state_observer.mdx rename website/src/content/docs/{ => api}/classes/signal.mdx (94%) rename website/src/content/docs/{extensions/build_context_select.mdx => api/extensions/_build_context_select.mdx} (97%) rename website/src/content/docs/{extensions/build_context_use.mdx => api/extensions/_build_context_use.mdx} (94%) rename website/src/content/docs/{extensions/build_context_watch.mdx => api/extensions/_build_context_watch.mdx} (89%) rename website/src/content/docs/{extensions/build_context_watch_id.mdx => api/extensions/_build_context_watch_id.mdx} (90%) create mode 100644 website/src/content/docs/api/extensions/build_context_extensions.mdx rename website/src/content/docs/{ => api}/hooks/use_async_state.mdx (97%) rename website/src/content/docs/{ => api}/hooks/use_compute.mdx (98%) rename website/src/content/docs/{ => api}/hooks/use_dependency.mdx (97%) rename website/src/content/docs/{ => api}/hooks/use_effect.mdx (100%) rename website/src/content/docs/{ => api}/hooks/use_reducer.mdx (98%) rename website/src/content/docs/{ => api}/hooks/use_state.mdx (100%) create mode 100644 website/src/content/docs/api/methods/core_methods.mdx rename website/src/content/docs/{ => api}/methods/dependency_injection_methods.mdx (99%) rename website/src/content/docs/{ => api}/methods/event_handler_methods.mdx (99%) rename website/src/content/docs/{ => api}/methods/state_management_methods.mdx (99%) rename website/src/content/docs/{classes => api/widgets}/rt_component.mdx (95%) rename website/src/content/docs/{ => api}/widgets/rt_consumer.mdx (97%) rename website/src/content/docs/{ => api}/widgets/rt_multi_provider.mdx (100%) rename website/src/content/docs/{ => api}/widgets/rt_provider.mdx (100%) rename website/src/content/docs/{ => api}/widgets/rt_scope.mdx (79%) rename website/src/content/docs/{ => api}/widgets/rt_selector.mdx (97%) rename website/src/content/docs/{ => api}/widgets/rt_signal_watcher.mdx (86%) rename website/src/content/docs/{ => api}/widgets/rt_watcher.mdx (99%) create mode 100644 website/src/content/docs/devtools_extension.mdx create mode 100644 website/src/content/docs/es/core_concepts/event_handler.mdx create mode 100644 website/src/content/docs/es/core_concepts/rendering_control.mdx diff --git a/website/astro.config.mjs b/website/astro.config.mjs index 7fd6b587..cfdbde47 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -73,6 +73,13 @@ export default defineConfig({ es: "Empezando", }, }, + { + label: "DevTools extension 🚧", + link: "/devtools_extension", + translations: { + es: "Extensión de DevTools 🚧", + }, + }, ], }, { @@ -85,51 +92,61 @@ export default defineConfig({ }, }, { - label: "Hooks", - translations: { - es: "Hooks", - }, - autogenerate: { - directory: "hooks", - }, - }, - { - label: "Classes", - translations: { - es: "Clases", - }, - autogenerate: { - directory: "classes", - }, - }, - { - label: "Widgets", - badge: "Flutter", - translations: { - es: "Widgets", - }, - autogenerate: { - directory: "widgets", - }, - }, - { - label: "Methods", - translations: { - es: "Métodos", - }, - autogenerate: { - directory: "methods", - }, - }, - { - label: "Extensions", - badge: "Flutter", + label: "API", translations: { - es: "Extensiones", + es: "API", }, autogenerate: { - directory: "extensions", + directory: "api", }, + items: [ + { + label: "Hooks", + translations: { + es: "Hooks", + }, + autogenerate: { + directory: "api/hooks", + }, + }, + { + label: "Widgets", + badge: "Flutter", + translations: { + es: "Widgets", + }, + autogenerate: { + directory: "api/widgets", + }, + }, + { + label: "Classes", + translations: { + es: "Clases", + }, + autogenerate: { + directory: "api/classes", + }, + }, + { + label: "Methods", + translations: { + es: "Métodos", + }, + autogenerate: { + directory: "api/methods", + }, + }, + { + label: "Extensions", + translations: { + es: "Extensiones", + }, + autogenerate: { + directory: "api/extensions", + }, + }, + ], }, { label: "Extra topics", diff --git a/website/src/content/docs/classes/args.mdx b/website/src/content/docs/api/classes/args.mdx similarity index 96% rename from website/src/content/docs/classes/args.mdx rename to website/src/content/docs/api/classes/args.mdx index 522970bd..aa770425 100644 --- a/website/src/content/docs/classes/args.mdx +++ b/website/src/content/docs/api/classes/args.mdx @@ -18,7 +18,7 @@ It is usefult to use in the following cases: - **Function Argument Bundling**: When you need to pass multiple arguments to a function via a single argument. - **Argument Extraction**: When you required to get the arguments of a function as a list. - **Adaptation for Single-Argument Expectation**: In situations where a class or function anticipates a function with a single argument, but you need to provide it with a function that accepts multiple arguments, the [`ary`](#ary-function) method is very useful. -This adaptation is particularly valuable in contexts like employing the [`Memo`](/reactter/classes/memo) class or the [`UseAsyncState.withArg`](/reactter/hooks/use_async_state) hook, where compatibility hinges on transforming a multi-argument function into a single-argument one. +This adaptation is particularly valuable in contexts like employing the [`Memo`](/reactter/api/classes/memo) class or the [`UseAsyncState.withArg`](/reactter/api/hooks/use_async_state) hook, where compatibility hinges on transforming a multi-argument function into a single-argument one. ## Classes diff --git a/website/src/content/docs/api/classes/auto_dispatch_effect.mdx b/website/src/content/docs/api/classes/auto_dispatch_effect.mdx new file mode 100644 index 00000000..262adc34 --- /dev/null +++ b/website/src/content/docs/api/classes/auto_dispatch_effect.mdx @@ -0,0 +1,6 @@ +--- +title: AutoDispatchEffect 🚧 +description: A mixin to execute the effect on initialization. +sidebar: + order: 4 +--- \ No newline at end of file diff --git a/website/src/content/docs/api/classes/lifecycle_observer.mdx b/website/src/content/docs/api/classes/lifecycle_observer.mdx new file mode 100644 index 00000000..d5b753d8 --- /dev/null +++ b/website/src/content/docs/api/classes/lifecycle_observer.mdx @@ -0,0 +1,6 @@ +--- +title: LifecycleObserver 🚧 +description: The base class for all lifecycle observers in Reactter. +sidebar: + order: 5 +--- \ No newline at end of file diff --git a/website/src/content/docs/classes/memo.mdx b/website/src/content/docs/api/classes/memo.mdx similarity index 94% rename from website/src/content/docs/classes/memo.mdx rename to website/src/content/docs/api/classes/memo.mdx index a1ca812e..f28b0250 100644 --- a/website/src/content/docs/classes/memo.mdx +++ b/website/src/content/docs/api/classes/memo.mdx @@ -90,10 +90,10 @@ When the same number is passed to the `factorialMemo` function again, t :::note The `computeValue` of `Memo` accepts one argument only. If you want to add more arguments, you can supply it using the
`Record`
(if your proyect support) -or [`Args`](/reactter/classes/args)(A generic arguments provided by Reactter). +or [`Args`](/reactter/api/classes/args)(A generic arguments provided by Reactter). ::: :::note Use `Memo.inline` in case there is a typing conflict, -e.g. with the [`UseAsyncState`](/reactter/hooks/use_async_state) and [`UseCompute`](/reactter/hooks/use_compute) hooks which a `Function` type is required. +e.g. with the [`UseAsyncState`](/reactter/api/hooks/use_async_state) and [`UseCompute`](/reactter/api/hooks/use_compute) hooks which a `Function` type is required. ::: \ No newline at end of file diff --git a/website/src/content/docs/api/classes/rt_context_mixin.mdx b/website/src/content/docs/api/classes/rt_context_mixin.mdx new file mode 100644 index 00000000..196c802f --- /dev/null +++ b/website/src/content/docs/api/classes/rt_context_mixin.mdx @@ -0,0 +1,6 @@ +--- +title: RtContextMixin 🚧 +description: The base class for all context mixins in Reactter. +sidebar: + order: 10 +--- \ No newline at end of file diff --git a/website/src/content/docs/api/classes/rt_dependency.mdx b/website/src/content/docs/api/classes/rt_dependency.mdx new file mode 100644 index 00000000..f9eab78b --- /dev/null +++ b/website/src/content/docs/api/classes/rt_dependency.mdx @@ -0,0 +1,6 @@ +--- +title: RtDependency 🚧 +description: Represents dependency managed by Reactter's dependency injection. +sidebar: + order: 8 +--- \ No newline at end of file diff --git a/website/src/content/docs/api/classes/rt_dependency_observer.mdx b/website/src/content/docs/api/classes/rt_dependency_observer.mdx new file mode 100644 index 00000000..1fee73d1 --- /dev/null +++ b/website/src/content/docs/api/classes/rt_dependency_observer.mdx @@ -0,0 +1,6 @@ +--- +title: RtDependencyObserver 🚧 +description: The base class for all dependency observers in Reactter. +sidebar: + order: 9 +--- \ No newline at end of file diff --git a/website/src/content/docs/api/classes/rt_state_base.mdx b/website/src/content/docs/api/classes/rt_state_base.mdx new file mode 100644 index 00000000..b876d05d --- /dev/null +++ b/website/src/content/docs/api/classes/rt_state_base.mdx @@ -0,0 +1,6 @@ +--- +title: RtStateBase 🚧 +description: The base class for all states in Reactter. +sidebar: + order: 6 +--- \ No newline at end of file diff --git a/website/src/content/docs/api/classes/rt_state_observer.mdx b/website/src/content/docs/api/classes/rt_state_observer.mdx new file mode 100644 index 00000000..8875db5e --- /dev/null +++ b/website/src/content/docs/api/classes/rt_state_observer.mdx @@ -0,0 +1,6 @@ +--- +title: RtStateObserver 🚧 +description: The base class for all state observers in Reactter. +sidebar: + order: 7 +--- \ No newline at end of file diff --git a/website/src/content/docs/classes/signal.mdx b/website/src/content/docs/api/classes/signal.mdx similarity index 94% rename from website/src/content/docs/classes/signal.mdx rename to website/src/content/docs/api/classes/signal.mdx index 3b7f6c8a..03fa3b39 100644 --- a/website/src/content/docs/classes/signal.mdx +++ b/website/src/content/docs/api/classes/signal.mdx @@ -111,5 +111,5 @@ userSignal.refresh(); }}/> :::note[With Flutter] -[`RtSignalWatcher`](/reactter/widgets/rt_signal_watcher) is a way to keep the widgets automatically updates, accessing the `value` of `Signal` reactively. +[`RtSignalWatcher`](/reactter/api/widgets/rt_signal_watcher) is a way to keep the widgets automatically updates, accessing the `value` of `Signal` reactively. ::: \ No newline at end of file diff --git a/website/src/content/docs/extensions/build_context_select.mdx b/website/src/content/docs/api/extensions/_build_context_select.mdx similarity index 97% rename from website/src/content/docs/extensions/build_context_select.mdx rename to website/src/content/docs/api/extensions/_build_context_select.mdx index 761d070f..7c5f8551 100644 --- a/website/src/content/docs/extensions/build_context_select.mdx +++ b/website/src/content/docs/api/extensions/_build_context_select.mdx @@ -26,7 +26,7 @@ Consequently, each time the selected states change, the value is recalculated an :::tip `BuildContext.select` is useful when you need to render using a computed value based on multiple states. -This aproach is more efficient than using [`BuildContext.watch`](/reactter/extensions/build_context_watch) because it avoids unnecessary rebuilds. +This aproach is more efficient than using [`BuildContext.watch`](/reactter/api/extensions/build_context_watch) because it avoids unnecessary rebuilds. ::: diff --git a/website/src/content/docs/extensions/build_context_use.mdx b/website/src/content/docs/api/extensions/_build_context_use.mdx similarity index 94% rename from website/src/content/docs/extensions/build_context_use.mdx rename to website/src/content/docs/api/extensions/_build_context_use.mdx index 79afe310..2658b340 100644 --- a/website/src/content/docs/extensions/build_context_use.mdx +++ b/website/src/content/docs/api/extensions/_build_context_use.mdx @@ -17,7 +17,7 @@ import counterCode from '@/examples/build_context/lib/counter.dart?raw'; import counterViewCode from '@/examples/build_context/lib/counter_view.dart?raw'; import mainCode from '@/examples/build_context/lib/main.dart?raw'; -`BuildContext.use` is a method that gets an instance of `T` dependency from the nearest ancestor provider([`RtProvider`](/reactter/widgets/rt_provider) or [`RtComponent`](/reactter/widgets/rt_component)) of type `T`. +`BuildContext.use` is a method that gets an instance of `T` dependency from the nearest ancestor provider([`RtProvider`](/reactter/api/widgets/rt_provider) or [`RtComponent`](/reactter/api/widgets/rt_component)) of type `T`. ## Syntax & description diff --git a/website/src/content/docs/extensions/build_context_watch.mdx b/website/src/content/docs/api/extensions/_build_context_watch.mdx similarity index 89% rename from website/src/content/docs/extensions/build_context_watch.mdx rename to website/src/content/docs/api/extensions/_build_context_watch.mdx index 33ab745b..9c8f206c 100644 --- a/website/src/content/docs/extensions/build_context_watch.mdx +++ b/website/src/content/docs/api/extensions/_build_context_watch.mdx @@ -19,12 +19,12 @@ import counterCode from '@/examples/build_context/lib/counter.dart?raw'; import counterViewCode from '@/examples/build_context/lib/counter_view.dart?raw'; import mainCode from '@/examples/build_context/lib/main.dart?raw'; -`BuildContext.watch` is a method similar to [`BuildContext.use`](/reactter/extensions/build_context_use), but with an added functionality. +`BuildContext.watch` is a method similar to [`BuildContext.use`](/reactter/api/extensions/build_context_use), but with an added functionality. It not only retrieves the `T` dependency but also registers the current widget as a listener for any changes to the states set or otherwise any changes in the `T` dependency. Consequently, whenever changes, the widget using `BuildContext.watch` is automatically notified and rebuilt. :::tip -You can optionally use `BuildContext.watch` with an `id`, but when you need to listen changes in a dependency's states using an `id`, it's better to use [`BuildContext.watchId`](/reactter/extensions/build_context_watch_id) for improved readability and clarity. +You can optionally use `BuildContext.watch` with an `id`, but when you need to listen changes in a dependency's states using an `id`, it's better to use [`BuildContext.watchId`](/reactter/api/extensions/build_context_watch_id) for improved readability and clarity. ```dart showLineNumbers=false context.watch(null, 'myId'); @@ -34,7 +34,7 @@ context.watchId('myId'); ::: :::caution -To render using a computed value based on multiple states, use [`BuildContext.select`](/reactter/extensions/build_context_select) instead of `BuildContext.watch` or pre-compute the value using [`UseCompute`](/reactter/hooks/use_compute) to prevent unnecessary rebuilds. +To render using a computed value based on multiple states, use [`BuildContext.select`](/reactter/api/extensions/build_context_select) instead of `BuildContext.watch` or pre-compute the value using [`UseCompute`](/reactter/api/hooks/use_compute) to prevent unnecessary rebuilds. ::: diff --git a/website/src/content/docs/extensions/build_context_watch_id.mdx b/website/src/content/docs/api/extensions/_build_context_watch_id.mdx similarity index 90% rename from website/src/content/docs/extensions/build_context_watch_id.mdx rename to website/src/content/docs/api/extensions/_build_context_watch_id.mdx index 94881280..a9fb11fe 100644 --- a/website/src/content/docs/extensions/build_context_watch_id.mdx +++ b/website/src/content/docs/api/extensions/_build_context_watch_id.mdx @@ -17,12 +17,12 @@ import counterCode from '@/examples/build_context/lib/counter.dart?raw'; import counterViewCode from '@/examples/build_context/lib/counter_view.dart?raw'; import mainCode from '@/examples/build_context/lib/main.dart?raw'; -`BuildContext.watchId` is a method similar to [`BuildContext.watch`](/reactter/extensions/build_context_watch), but use `id` as first argument to locate the dependency. +`BuildContext.watchId` is a method similar to [`BuildContext.watch`](/reactter/api/extensions/build_context_watch), but use `id` as first argument to locate the dependency. :::tip This approach is useful when you have multiple dependencies of the same type and you want to access a specific one. -While you can use [`BuildContext.watch`](/reactter/extensions/build_context_watch) with `id`, `BuildContext.watchId` is more readable and clear when an `id` is required. +While you can use [`BuildContext.watch`](/reactter/api/extensions/build_context_watch) with `id`, `BuildContext.watchId` is more readable and clear when an `id` is required. ```dart showLineNumbers=false context.watch(null, 'myId'); diff --git a/website/src/content/docs/api/extensions/build_context_extensions.mdx b/website/src/content/docs/api/extensions/build_context_extensions.mdx new file mode 100644 index 00000000..a13f5b19 --- /dev/null +++ b/website/src/content/docs/api/extensions/build_context_extensions.mdx @@ -0,0 +1,8 @@ +--- +title: BuildContext Extensions 🚧 +description: The extensions for the BuildContext class in Reactter. +sidebar: + order: 1 + badge: + text: Flutter +--- \ No newline at end of file diff --git a/website/src/content/docs/hooks/use_async_state.mdx b/website/src/content/docs/api/hooks/use_async_state.mdx similarity index 97% rename from website/src/content/docs/hooks/use_async_state.mdx rename to website/src/content/docs/api/hooks/use_async_state.mdx index 16e85878..ab48ca28 100644 --- a/website/src/content/docs/hooks/use_async_state.mdx +++ b/website/src/content/docs/api/hooks/use_async_state.mdx @@ -154,7 +154,7 @@ print("${uAsyncStateWithArg.value}"); // Resolved value with arg: 10 }}/> If you want to add more arguments, you can supply it using the `Record`(if your proyect support) -or [`Args`](/reactter/classes/args)(A generic arguments provided by Reactter), e.g.: +or [`Args`](/reactter/api/classes/args)(A generic arguments provided by Reactter), e.g.: ```dart showLineNumbers=false "UseAsyncState.withArg" "asyncFunctionWithArgs" ".resolve" ".value" final uAsyncStateWithArgs = UseAsyncState.withArg>( @@ -174,7 +174,7 @@ print("${uAsyncStateWithArgs.value}"); // Resolved value with args: arg1, arg2, ### Using with Memo -`UseAsyncState` does not cache the resolving `value`, meaning that it will resolve the `value` every time `resolve` is called, potentially impacting performance, especially if the `asyncFunction` is expensive. In this case, you should consider using [`Memo`](/reactter/classes/memo) to cache the resolving `value`, e.g.: +`UseAsyncState` does not cache the resolving `value`, meaning that it will resolve the `value` every time `resolve` is called, potentially impacting performance, especially if the `asyncFunction` is expensive. In this case, you should consider using [`Memo`](/reactter/api/classes/memo) to cache the resolving `value`, e.g.: ```dart "UseAsyncState" "Memo.inline" final translateState = UseAsyncState.withArg>( diff --git a/website/src/content/docs/hooks/use_compute.mdx b/website/src/content/docs/api/hooks/use_compute.mdx similarity index 98% rename from website/src/content/docs/hooks/use_compute.mdx rename to website/src/content/docs/api/hooks/use_compute.mdx index 09e9a10a..95bcc88e 100644 --- a/website/src/content/docs/hooks/use_compute.mdx +++ b/website/src/content/docs/api/hooks/use_compute.mdx @@ -147,7 +147,7 @@ But when the calculated `value` is same as the previous `value`, it will not not ### Using with Memo -`UseCompute` does not cache the computed `value`, meaning that it will computing the `value` every time the `dependencies` is changed, potentially impacting performance, especially if the `computeValue` is expensive. In this case, you should consider using [`Memo`](/reactter/classes/memo) to cache the computed `value`, e.g.: +`UseCompute` does not cache the computed `value`, meaning that it will computing the `value` every time the `dependencies` is changed, potentially impacting performance, especially if the `computeValue` is expensive. In this case, you should consider using [`Memo`](/reactter/api/classes/memo) to cache the computed `value`, e.g.: ```dart showLineNumbers=false "UseCompute" "Memo" final addAB = Memo( diff --git a/website/src/content/docs/hooks/use_dependency.mdx b/website/src/content/docs/api/hooks/use_dependency.mdx similarity index 97% rename from website/src/content/docs/hooks/use_dependency.mdx rename to website/src/content/docs/api/hooks/use_dependency.mdx index e10bd5cf..c00f5f91 100644 --- a/website/src/content/docs/hooks/use_dependency.mdx +++ b/website/src/content/docs/api/hooks/use_dependency.mdx @@ -131,9 +131,9 @@ class MyController { :::note The default constructor only will find the dependency by `T` type. Note that the `instance` property will be `null` if the dependency is not created already. -To register and create the dependency instance, using the `UseDependency.create` hook instead. Learn more about it in [Creating a dependency](/reactter/hooks/use_dependency#creating-a-dependency). +To register and create the dependency instance, using the `UseDependency.create` hook instead. Learn more about it in [Creating a dependency](/reactter/api/hooks/use_dependency#creating-a-dependency). -If only you need to retrieve the dependency instance, using the `UseDependency.get` hook instead. Learn more about it in [Getting a dependency](/reactter/hooks/use_dependency#getting-a-dependency). +If only you need to retrieve the dependency instance, using the `UseDependency.get` hook instead. Learn more about it in [Getting a dependency](/reactter/api/hooks/use_dependency#getting-a-dependency). ::: :::tip @@ -269,7 +269,7 @@ class MyController { :::note `UseDependency.get` hook will get the dependency instance inmediately. If the dependency is not created yet, it will create it, but if the dependency is not registered, the `instance` property will be `null`. -To ensure that the dependency is created, you can using the `UseDependency.create` hook instead. Learn more about it in [Creating a dependency](/reactter/hooks/use_dependency#creating-a-dependency). +To ensure that the dependency is created, you can using the `UseDependency.create` hook instead. Learn more about it in [Creating a dependency](/reactter/api/hooks/use_dependency#creating-a-dependency). ::: :::tip @@ -341,7 +341,7 @@ class MyController { } ``` -Also, you can create the dependency instance inmediately using the `UseDependency.create` hook instead. Learn more about it in [Creating a dependency](/reactter/hooks/use_dependency#creating-a-dependency). +Also, you can create the dependency instance inmediately using the `UseDependency.create` hook instead. Learn more about it in [Creating a dependency](/reactter/api/hooks/use_dependency#creating-a-dependency). ::: :::tip diff --git a/website/src/content/docs/hooks/use_effect.mdx b/website/src/content/docs/api/hooks/use_effect.mdx similarity index 100% rename from website/src/content/docs/hooks/use_effect.mdx rename to website/src/content/docs/api/hooks/use_effect.mdx diff --git a/website/src/content/docs/hooks/use_reducer.mdx b/website/src/content/docs/api/hooks/use_reducer.mdx similarity index 98% rename from website/src/content/docs/hooks/use_reducer.mdx rename to website/src/content/docs/api/hooks/use_reducer.mdx index 7fcd6bf0..f400885e 100644 --- a/website/src/content/docs/hooks/use_reducer.mdx +++ b/website/src/content/docs/api/hooks/use_reducer.mdx @@ -15,7 +15,7 @@ import * as NoteStateValueNotifies from '@/content/docs/shareds/note_state_value import * as ListeningChangesState from '@/content/docs/shareds/listening_changes_state.mdx'; `UseReducer` is a [hook](/reactter/core_concepts/hooks) that manages state using reducer function. -An alternative to [`UseState`](/reactter/hooks/use_state). +An alternative to [`UseState`](/reactter/api/hooks/use_state). :::tip `UseReducer` is usually preferable over `UseState` when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. diff --git a/website/src/content/docs/hooks/use_state.mdx b/website/src/content/docs/api/hooks/use_state.mdx similarity index 100% rename from website/src/content/docs/hooks/use_state.mdx rename to website/src/content/docs/api/hooks/use_state.mdx diff --git a/website/src/content/docs/api/methods/core_methods.mdx b/website/src/content/docs/api/methods/core_methods.mdx new file mode 100644 index 00000000..c7e8c74a --- /dev/null +++ b/website/src/content/docs/api/methods/core_methods.mdx @@ -0,0 +1,6 @@ +--- +title: Core Methods 🚧 +description: The core methods of Reactter. +sidebar: + order: 1 +--- \ No newline at end of file diff --git a/website/src/content/docs/methods/dependency_injection_methods.mdx b/website/src/content/docs/api/methods/dependency_injection_methods.mdx similarity index 99% rename from website/src/content/docs/methods/dependency_injection_methods.mdx rename to website/src/content/docs/api/methods/dependency_injection_methods.mdx index 7e36c172..00b4b9c8 100644 --- a/website/src/content/docs/methods/dependency_injection_methods.mdx +++ b/website/src/content/docs/api/methods/dependency_injection_methods.mdx @@ -2,7 +2,7 @@ title: Dependency Injection Methods description: Documentation of Dependency Injection Methods in Reactter. sidebar: - order: 2 + order: 3 --- import { HE, HK, HM, HN, HS, HT } from '@/components/Highlight'; diff --git a/website/src/content/docs/methods/event_handler_methods.mdx b/website/src/content/docs/api/methods/event_handler_methods.mdx similarity index 99% rename from website/src/content/docs/methods/event_handler_methods.mdx rename to website/src/content/docs/api/methods/event_handler_methods.mdx index 4e5413b3..eded9879 100644 --- a/website/src/content/docs/methods/event_handler_methods.mdx +++ b/website/src/content/docs/api/methods/event_handler_methods.mdx @@ -2,7 +2,7 @@ title: Event Handler Methods description: Documentation of Event Handler Methods in Reactter. sidebar: - order: 3 + order: 4 --- import { HE, HK, HM, HN, HS, HT } from '@/components/Highlight'; diff --git a/website/src/content/docs/methods/state_management_methods.mdx b/website/src/content/docs/api/methods/state_management_methods.mdx similarity index 99% rename from website/src/content/docs/methods/state_management_methods.mdx rename to website/src/content/docs/api/methods/state_management_methods.mdx index 024d689a..a78fd05c 100644 --- a/website/src/content/docs/methods/state_management_methods.mdx +++ b/website/src/content/docs/api/methods/state_management_methods.mdx @@ -2,7 +2,7 @@ title: State Management Methods description: Documentation of State Management Methods in Reactter. sidebar: - order: 1 + order: 2 --- import { HE, HK, HM, HN, HS, HT } from '@/components/Highlight'; diff --git a/website/src/content/docs/classes/rt_component.mdx b/website/src/content/docs/api/widgets/rt_component.mdx similarity index 95% rename from website/src/content/docs/classes/rt_component.mdx rename to website/src/content/docs/api/widgets/rt_component.mdx index c437cb6c..ac1a7fb6 100644 --- a/website/src/content/docs/classes/rt_component.mdx +++ b/website/src/content/docs/api/widgets/rt_component.mdx @@ -2,9 +2,7 @@ title: RtComponent description: Learn how to use the RtComponent in Reactter. sidebar: - order: 4 - badge: - text: Flutter + order: 7 --- import { HM, HT, HN } from '@/components/Highlight'; import CodeTabs from '@/components/CodeTabs.astro'; @@ -20,7 +18,7 @@ import counterViewCode from '@/examples/rt_component/lib/counter_view.dart?raw'; import mainCode from '@/examples/rt_component/lib/main.dart?raw'; The `RtComponent` is a `StatelessWidget` hat simplifies the creation of reactive widgets by utilizing the dependency injection system. -It provides features similar to [`RtProvider`](/reactter/widgets/rt_provider) and [`RtConsumer`](/reactter/widgets/rt_consumer) that are used to inject and consume dependencies. +It provides features similar to [`RtProvider`](/reactter/api/widgets/rt_provider) and [`RtConsumer`](/reactter/api/widgets/rt_consumer) that are used to inject and consume dependencies. :::tip The `RtComponent` widget is a more efficient way to create reactive widgets than using `RtProvider` and `RtConsumer` widgets separately. diff --git a/website/src/content/docs/widgets/rt_consumer.mdx b/website/src/content/docs/api/widgets/rt_consumer.mdx similarity index 97% rename from website/src/content/docs/widgets/rt_consumer.mdx rename to website/src/content/docs/api/widgets/rt_consumer.mdx index 72fd4b0f..aea954b6 100644 --- a/website/src/content/docs/widgets/rt_consumer.mdx +++ b/website/src/content/docs/api/widgets/rt_consumer.mdx @@ -41,10 +41,10 @@ import counterChildCode from '@/examples/rt_consumer_child/lib/counter.dart?raw' import counterViewChildCode from '@/examples/rt_consumer_child/lib/counter_view.dart?raw'; import mainChildCode from '@/examples/rt_consumer_child/lib/main.dart?raw'; -The `RtConsumer` widget obtains the dependency provided by the closest [`RtProvider`](/reactter/widgets/rt_provider) widget and rebuilds itself whenever there are changes in the dependency or any defined states. +The `RtConsumer` widget obtains the dependency provided by the closest [`RtProvider`](/reactter/api/widgets/rt_provider) widget and rebuilds itself whenever there are changes in the dependency or any defined states. :::note -`RtConsumer` is similar to the [`BuildContext.use`](/reactter/extensions/builder_context_use) and [`BuildContext.watch`](/reactter/extensions/builder_context_watch) functions, but it is designed to be used within the widget tree. +`RtConsumer` is similar to the [`BuildContext.use`](/reactter/api/extensions/builder_context_use) and [`BuildContext.watch`](/reactter/api/extensions/builder_context_watch) functions, but it is designed to be used within the widget tree. ::: ## Syntax diff --git a/website/src/content/docs/widgets/rt_multi_provider.mdx b/website/src/content/docs/api/widgets/rt_multi_provider.mdx similarity index 100% rename from website/src/content/docs/widgets/rt_multi_provider.mdx rename to website/src/content/docs/api/widgets/rt_multi_provider.mdx diff --git a/website/src/content/docs/widgets/rt_provider.mdx b/website/src/content/docs/api/widgets/rt_provider.mdx similarity index 100% rename from website/src/content/docs/widgets/rt_provider.mdx rename to website/src/content/docs/api/widgets/rt_provider.mdx diff --git a/website/src/content/docs/widgets/rt_scope.mdx b/website/src/content/docs/api/widgets/rt_scope.mdx similarity index 79% rename from website/src/content/docs/widgets/rt_scope.mdx rename to website/src/content/docs/api/widgets/rt_scope.mdx index 5cb0179c..a8df65fe 100644 --- a/website/src/content/docs/widgets/rt_scope.mdx +++ b/website/src/content/docs/api/widgets/rt_scope.mdx @@ -14,7 +14,7 @@ import { marks } from '@/examples/marks.ts' import counterViewCode from '@/examples/rt_scope/lib/counter_view.dart?raw'; import mainCode from '@/examples/rt_scope/lib/main.dart?raw'; -The `RtScope` widget serves as an entry point for Reactter, allowing you to use any consumer(such as [`RtConsumer`](/reactter/widgets/rt_consumer), [`RtSelector`](/reactter/widgets/rt_selector), [`RtComponent`](/reactter/widgets/rt_component), [`BuildContext.watch`](/reactter/extensions/builder_context_watch), [`BuildContext.select`](/reactter/extensions/builder_context_select)) to observe state changes within the subtree of the widget it wraps, without the need to explicitly define dependencies. +The `RtScope` widget serves as an entry point for Reactter, allowing you to use any consumer(such as [`RtConsumer`](/reactter/api/widgets/rt_consumer), [`RtSelector`](/reactter/api/widgets/rt_selector), [`RtComponent`](/reactter/api/widgets/rt_component), [`BuildContext.watch`](/reactter/api/extensions/builder_context_watch), [`BuildContext.select`](/reactter/api/extensions/builder_context_select)) to observe state changes within the subtree of the widget it wraps, without the need to explicitly define dependencies. :::caution The `RtScope` widget is optional, but if you use any consumers without defining dependencies and their ancestor widgets are not wrapped by it, a `RtScopeNotFoundException` error will be thrown. Therefore, make sure this widget is correctly placed in the widget tree hierarchy. diff --git a/website/src/content/docs/widgets/rt_selector.mdx b/website/src/content/docs/api/widgets/rt_selector.mdx similarity index 97% rename from website/src/content/docs/widgets/rt_selector.mdx rename to website/src/content/docs/api/widgets/rt_selector.mdx index 02176da8..c29c8b54 100644 --- a/website/src/content/docs/widgets/rt_selector.mdx +++ b/website/src/content/docs/api/widgets/rt_selector.mdx @@ -30,8 +30,8 @@ import counterDivisibleIdCode from '@/examples/rt_selector_id/lib/counter_divisi import counterViewIdCode from '@/examples/rt_selector_id/lib/counter_view.dart?raw'; import mainIdCode from '@/examples/rt_selector_id/lib/main.dart?raw'; -The `RtSelector` widget is a similarly to [`RtConsumer`](/reactter/widgets/rt_consumer). -It obtains the dependency provided by the closest [`RtProvider`](/reactter/widgets/rt_provider) widget and allows you to select specific states to compute a value. +The `RtSelector` widget is a similarly to [`RtConsumer`](/reactter/api/widgets/rt_consumer). +It obtains the dependency provided by the closest [`RtProvider`](/reactter/api/widgets/rt_provider) widget and allows you to select specific states to compute a value. This value will trigger a rebuild of the widget tree whenever it changes. ## Syntax diff --git a/website/src/content/docs/widgets/rt_signal_watcher.mdx b/website/src/content/docs/api/widgets/rt_signal_watcher.mdx similarity index 86% rename from website/src/content/docs/widgets/rt_signal_watcher.mdx rename to website/src/content/docs/api/widgets/rt_signal_watcher.mdx index 4e499d68..5c9527b4 100644 --- a/website/src/content/docs/widgets/rt_signal_watcher.mdx +++ b/website/src/content/docs/api/widgets/rt_signal_watcher.mdx @@ -23,7 +23,7 @@ It acts as a listener for all `Signal` instances within its subtree, tr It is not recommended to use `RtSignalWatcher` with a widget tree where it may contain a considerable number of arbitrary unchecked signals. Because `RtSignalWatcher` will listen any signals in the widget tree, regardless of its level, this could result in needless rebuilds that negatively affect performance. -For more granular control and performance optimization, consider using [`RtWatcher`](/reactter/widgets/rt_watcher), [`RtConsumer`](/reactter/widgets/rt_consumer), [`RtSelector`](/reactter/widgets/rt_selector), [`BuildContext.watch`](/reactter/extensions/builder_context_watch), [`BuildContext.select`](/reactter/extensions/builder_context_select) instead to listen for specific states and rebuild parts of the widget tree that require it. +For more granular control and performance optimization, consider using [`RtWatcher`](/reactter/api/widgets/rt_watcher), [`RtConsumer`](/reactter/api/widgets/rt_consumer), [`RtSelector`](/reactter/api/widgets/rt_selector), [`BuildContext.watch`](/reactter/api/extensions/builder_context_watch), [`BuildContext.select`](/reactter/api/extensions/builder_context_select) instead to listen for specific states and rebuild parts of the widget tree that require it. ::: ## Syntax diff --git a/website/src/content/docs/widgets/rt_watcher.mdx b/website/src/content/docs/api/widgets/rt_watcher.mdx similarity index 99% rename from website/src/content/docs/widgets/rt_watcher.mdx rename to website/src/content/docs/api/widgets/rt_watcher.mdx index dd7e87ae..fa5b375c 100644 --- a/website/src/content/docs/widgets/rt_watcher.mdx +++ b/website/src/content/docs/api/widgets/rt_watcher.mdx @@ -2,7 +2,7 @@ title: RtWatcher description: Learn how to use the RtWatcher in Reactter. sidebar: - order: 7 + order: 6 --- import { HM, HT, HN } from "@/components/Highlight"; diff --git a/website/src/content/docs/core_concepts/dependency_injection.mdx b/website/src/content/docs/core_concepts/dependency_injection.mdx index 379e0ff4..4e1989d8 100644 --- a/website/src/content/docs/core_concepts/dependency_injection.mdx +++ b/website/src/content/docs/core_concepts/dependency_injection.mdx @@ -1,6 +1,6 @@ --- -title: Dependency Injection -description: Learn how to manage dependencies in Reactter. +title: Dependency injection +description: Learn about the dependency injection system in Reactter. sidebar: order: 2 --- @@ -26,24 +26,24 @@ This results in better _modularity_, _reusability_, and _testability_ of the cod Reactter provides the following dependencies injection mechanisms: - Hooks - - [`UseDependency`](/reactter/hooks/UseDependency) + - [`UseDependency`](/reactter/api/hooks/UseDependency) - Methods - - [`Rt.register`](/reactter/methods/dependency_injection_methods/#rtregister) - - [`Rt.lazyBuilder`](/reactter/methods/dependency_injection_methods/#rtlazy_builder) - - [`Rt.lazyFactory`](/reactter/methods/dependency_injection_methods/#rtlazy_factory) - - [`Rt.lazySingleton`](/reactter/methods/dependency_injection_methods/#rtlazy_singleton) - - [`Rt.create`](/reactter/methods/dependency_injection_methods/#rtcreate) - - [`Rt.builder`](/reactter/methods/dependency_injection_methods/#rtbuilder) - - [`Rt.factory`](/reactter/methods/dependency_injection_methods/#rtfactory) - - [`Rt.singleton`](/reactter/methods/dependency_injection_methods/#rtsingleton) - - [`Rt.get`](/reactter/methods/dependency_injection_methods/#rtget) - - [`Rt.find`](/reactter/methods/dependency_injection_methods/#rtfind) - - [`Rt.exists`](/reactter/methods/dependency_injection_methods/#rtexists) - - [`Rt.isActive`](/reactter/methods/dependency_injection_methods/#rtis_active) - - [`Rt.getDependencyMode`](/reactter/methods/dependency_injection_methods/#rtget_dependency_mode) - - [`Rt.delete`](/reactter/methods/dependency_injection_methods/#rtdelete) - - [`Rt.destroy`](/reactter/methods/dependency_injection_methods/#rtdestroy) - - [`Rt.unregister`](/reactter/methods/dependency_injection_methods/#rtunregister) + - [`Rt.register`](/reactter/api/methods/dependency_injection_methods/#rtregister) + - [`Rt.lazyBuilder`](/reactter/api/methods/dependency_injection_methods/#rtlazy_builder) + - [`Rt.lazyFactory`](/reactter/api/methods/dependency_injection_methods/#rtlazy_factory) + - [`Rt.lazySingleton`](/reactter/api/methods/dependency_injection_methods/#rtlazy_singleton) + - [`Rt.create`](/reactter/api/methods/dependency_injection_methods/#rtcreate) + - [`Rt.builder`](/reactter/api/methods/dependency_injection_methods/#rtbuilder) + - [`Rt.factory`](/reactter/api/methods/dependency_injection_methods/#rtfactory) + - [`Rt.singleton`](/reactter/api/methods/dependency_injection_methods/#rtsingleton) + - [`Rt.get`](/reactter/api/methods/dependency_injection_methods/#rtget) + - [`Rt.find`](/reactter/api/methods/dependency_injection_methods/#rtfind) + - [`Rt.exists`](/reactter/api/methods/dependency_injection_methods/#rtexists) + - [`Rt.isActive`](/reactter/api/methods/dependency_injection_methods/#rtis_active) + - [`Rt.getDependencyMode`](/reactter/api/methods/dependency_injection_methods/#rtget_dependency_mode) + - [`Rt.delete`](/reactter/api/methods/dependency_injection_methods/#rtdelete) + - [`Rt.destroy`](/reactter/api/methods/dependency_injection_methods/#rtdestroy) + - [`Rt.unregister`](/reactter/api/methods/dependency_injection_methods/#rtunregister) :::tip[With Flutter] If you are using Flutter, go to [rendering control](/reactter/core_concepts/rendering_control) to learn how it manage dependencies through `Widget` and `BuildContext` extension methods. @@ -57,21 +57,21 @@ To understand how this system works in its entirety, we will break down the proc 1. **Registration**: This stage involves registering the dependency within the Reactter context using a specific type, a `builder` function, an `id`, and a dependency `mode`. To perform this registration, you can use the following methods: - - [`Rt.register`](/reactter/en/methods/dependency_injection_methods/#rtregister) - - [`Rt.lazyBuilder`](/reactter/en/methods/dependency_injection_methods/#rtlazy_builder) - - [`Rt.lazyFactory`](/reactter/en/methods/dependency_injection_methods/#rtlazy_factory) - - [`Rt.lazySingleton`](/reactter/en/methods/dependency_injection_methods/#rtlazy_singleton) + - [`Rt.register`](/reactter/en/api/methods/dependency_injection_methods/#rtregister) + - [`Rt.lazyBuilder`](/reactter/en/api/methods/dependency_injection_methods/#rtlazy_builder) + - [`Rt.lazyFactory`](/reactter/en/api/methods/dependency_injection_methods/#rtlazy_factory) + - [`Rt.lazySingleton`](/reactter/en/api/methods/dependency_injection_methods/#rtlazy_singleton) During registration, the event `Lifecycle.registered` is triggered. 2. **Resolution**: When a dependency is requested, Reactter creates an instance of the dependency from the registered `builder` function, according to the provided type and `id`. For this, you can use the following methods: - - [`Rt.get`](/reactter/en/methods/dependency_injection_methods/#rtget) - - [`Rt.create`](/reactter/en/methods/dependency_injection_methods/#rtcreate) - - [`Rt.builder`](/reactter/en/methods/dependency_injection_methods/#rtbuilder) - - [`Rt.factory`](/reactter/en/methods/dependency_injection_methods/#rtfactory) - - [`Rt.singleton`](/reactter/en/methods/dependency_injection_methods/#rtsingleton) + - [`Rt.get`](/reactter/en/api/methods/dependency_injection_methods/#rtget) + - [`Rt.create`](/reactter/en/api/methods/dependency_injection_methods/#rtcreate) + - [`Rt.builder`](/reactter/en/api/methods/dependency_injection_methods/#rtbuilder) + - [`Rt.factory`](/reactter/en/api/methods/dependency_injection_methods/#rtfactory) + - [`Rt.singleton`](/reactter/en/api/methods/dependency_injection_methods/#rtsingleton) :::note All of the above methods, except `Rt.get`, in addition to instantiating the dependency, may also register it if it hasn't been registered previously. @@ -85,11 +85,11 @@ To understand how this system works in its entirety, we will break down the proc 3. **Usage**: Once the dependency is resolved, its instance can be used anywhere within the application. To access the dependency or check its state, you can use the following methods: - - [`Rt.find`](/reactter/en/methods/dependency_injection_methods/#rtfind) - - [`Rt.get`](/reactter/en/methods/dependency_injection_methods/#rtget) - - [`Rt.exists`](/reactter/en/methods/dependency_injection_methods/#rtexists) - - [`Rt.isActive`](/reactter/en/methods/dependency_injection_methods/#rtis_active) - - [`Rt.getDependencyMode`](/reactter/en/methods/dependency_injection_methods/#rtget_dependency_mode) + - [`Rt.find`](/reactter/en/api/methods/dependency_injection_methods/#rtfind) + - [`Rt.get`](/reactter/en/api/methods/dependency_injection_methods/#rtget) + - [`Rt.exists`](/reactter/en/api/methods/dependency_injection_methods/#rtexists) + - [`Rt.isActive`](/reactter/en/api/methods/dependency_injection_methods/#rtis_active) + - [`Rt.getDependencyMode`](/reactter/en/api/methods/dependency_injection_methods/#rtget_dependency_mode) If the dependency's state is updated, the following events will be emitted: - `Lifecycle.willUpdate` @@ -98,8 +98,8 @@ To understand how this system works in its entirety, we will break down the proc 4. **Deletion**: In this stage, Reactter removes the dependency instance based on the provided type and `id`. To do this, you can use the following methods: - - [`Rt.delete`](/reactter/en/methods/dependency_injection_methods/#rtdelete) - - [`Rt.destroy`](/reactter/en/methods/dependency_injection_methods/#rtdestroy) + - [`Rt.delete`](/reactter/en/api/methods/dependency_injection_methods/#rtdelete) + - [`Rt.destroy`](/reactter/en/api/methods/dependency_injection_methods/#rtdestroy) :::note Depending on the dependency `mode`, the dependency instance may not be deleted, or the registration may also be removed. @@ -113,9 +113,9 @@ To understand how this system works in its entirety, we will break down the proc 5. **Unregistration**: In this stage, Reactter removes the dependency's registration based on the provided type and `id`. To unregister the dependency, you can use the following methods: - - [`Rt.unregister`](/reactter/en/methods/dependency_injection_methods/#rtunregister) - - [`Rt.delete`](/reactter/en/methods/dependency_injection_methods/#rtdelete) - - [`Rt.destroy`](/reactter/en/methods/dependency_injection_methods/#rtdestroy) + - [`Rt.unregister`](/reactter/en/api/methods/dependency_injection_methods/#rtunregister) + - [`Rt.delete`](/reactter/en/api/methods/dependency_injection_methods/#rtdelete) + - [`Rt.destroy`](/reactter/en/api/methods/dependency_injection_methods/#rtdestroy) :::note Depending on the dependency `mode`, the dependency registration may not be removed. @@ -132,7 +132,7 @@ If `mode` is not provided, Reactter will use the default mode, which is `Dep A **dependency** in Reactter is referred to as an **instance** of specific type. -The scope of the registered dependency is global. This indicates that using the [shortcuts to manage dependency](/reactter/shortcuts/dependency) or [`UseDependency`](/reactter/hooks/UseDependency) will allow you to access them from anywhere in the project. +The scope of the registered dependency is global. This indicates that using the [shortcuts to manage dependency](/reactter/shortcuts/dependency) or [`UseDependency`](/reactter/api/hooks/UseDependency) will allow you to access them from anywhere in the project. ::: :::tip @@ -225,18 +225,18 @@ In this example, we've created a countdown from `10` seconds, and when But if we want to use the `Counter` instance elsewhere in the code, we can do it like this: ```dart title="main.dart" ins={3, 6-7} collapse={8-11} "Rt.register" "Rt.create" /Counter(?!\u0060)/ /Countdown(?!\u0060)/ /(countdown) =/ "countdown.run" - import 'package:reactter/reactter.dart'; - import 'countdown.dart'; - import 'counter.dart'; - - void main() async { - // Register the 'Counter' class with an initial value of 20 - Rt.register(() => Counter(20)); - // Create an instance of the 'Countdown' class - final countdown = Rt.create(() => Countdown())!; - // Start the countdown - await countdown.run(); - } +import 'package:reactter/reactter.dart'; +import 'countdown.dart'; +import 'counter.dart'; + +void main() async { + // Register the 'Counter' class with an initial value of 20 + Rt.register(() => Counter(20)); + // Create an instance of the 'Countdown' class + final countdown = Rt.create(() => Countdown())!; + // Start the countdown + await countdown.run(); +} ``` Now, the countdown will start from `20`, and when it reaches `0`, the instance of `Counter` will be deleted. @@ -246,22 +246,22 @@ and when the instance of `Countdown` is created, it uses the registered Ok, But what if we want to use the instance of `Counter` elsewhere in the code? Let's see: ```dart title="main.dart" ins={12-15} collapse={6-11} "Rt.register" "Rt.create" "Rt.get" /counter(?!\\.)/ "counter?.count" /Counter(?!\u0060)(?! )/ /Countdown(?!\u0060)/ /(countdown) =/ "countdown.run" - import 'package:reactter/reactter.dart'; - import 'countdown.dart'; - import 'counter.dart'; - - void main() async { - // Register the `Counter` class with an initial value of 20 - Rt.register(() => Counter(20)); - // Create an instance of the `Countdown` class - final countdown = Rt.create(() => Countdown())!; - // Start the countdown - await countdown.run(); - // Get the instance of the `Counter` class - final counter = Rt.get(); - // Try to print the current `count` value - print('Count: ${counter?.count ?? 'Counter instance not found'}'); - } +import 'package:reactter/reactter.dart'; +import 'countdown.dart'; +import 'counter.dart'; + +void main() async { + // Register the `Counter` class with an initial value of 20 + Rt.register(() => Counter(20)); + // Create an instance of the `Countdown` class + final countdown = Rt.create(() => Countdown())!; + // Start the countdown + await countdown.run(); + // Get the instance of the `Counter` class + final counter = Rt.get(); + // Try to print the current `count` value + print('Count: ${counter?.count ?? 'Counter instance not found'}'); +} ``` In this case, the countdown will work as before, but when trying to get the instance of `Counter` to print its value, the output will be 'Counter instance not found'. @@ -271,21 +271,21 @@ so when it is deleted at the end of the countdown, its registration is also remo If we want to obtain the instance of Counter to print its value, we need to register it using the DependencyMode.singleton mode, as shown below: ```dart title="main.dart" {7} collapse={8-15} "Rt.register" "Rt.create" "Rt.get" "DependencyMode.singleton" /counter(?!\\.)/ "counter?.count" /Counter(?!\u0060)(?! )/ /Countdown(?!\u0060)/ /(countdown) =/ "countdown.run" - import 'package:reactter/reactter.dart'; - import 'countdown.dart'; - import 'counter.dart'; - - void main() async { - // Register the `Counter` class as singleton mode with an initial value of 20 - Rt.register(() => Counter(20), mode: DependencyMode.singleton); - // Create an instance of the `Countdown` class - final countdown = Rt.create(() => Countdown())!; - // Start the countdown - await countdown.run(); - // Get the instance of the `Counter` class - final counter = Rt.get(); - // Try to print the current `count` value - print('Count: ${counter?.count ?? 'Counter instance not found'}'); +import 'package:reactter/reactter.dart'; +import 'countdown.dart'; +import 'counter.dart'; + +void main() async { + // Register the `Counter` class as singleton mode with an initial value of 20 + Rt.register(() => Counter(20), mode: DependencyMode.singleton); + // Create an instance of the `Countdown` class + final countdown = Rt.create(() => Countdown())!; + // Start the countdown + await countdown.run(); + // Get the instance of the `Counter` class + final counter = Rt.get(); + // Try to print the current `count` value + print('Count: ${counter?.count ?? 'Counter instance not found'}'); } ``` @@ -320,8 +320,8 @@ In this mode, when the dependency tree no longer uses it, the instance is remove Reactter identifies the Factory mode as `DependencyMode.factory` -and to activate it, set the `mode` in the argument of [`Rt.register`](/reactter/methods/dependency_injection_methods/#rtregister) and [`Rt.create`](/reactter/methods/dependency_injection_methods/#rtcreate), -or use [`Rt.lazyFactory`](/reactter/methods/dependency_injection_methods/#rtlazy_factory), [`Rt.factory`](/reactter/methods/dependency_injection_methods/#rtfactory). +and to activate it, set the `mode` in the argument of [`Rt.register`](/reactter/api/methods/dependency_injection_methods/#rtregister) and [`Rt.create`](/reactter/api/methods/dependency_injection_methods/#rtcreate), +or use [`Rt.lazyFactory`](/reactter/api/methods/dependency_injection_methods/#rtlazy_factory), [`Rt.factory`](/reactter/api/methods/dependency_injection_methods/#rtfactory). :::note Factory uses more RAM than [Builder](#builder), @@ -338,13 +338,13 @@ When using the singleton mode, the dependency instance and its states remain act This also includes the creation function, unless it is explicitly removed. :::tip - Use [`Rt.destroy`](/reactter/methods/dependency_injection_methods/#rtdestroy) if you want to delete both the instance and the registration of a dependency in singleton mode. + Use [`Rt.destroy`](/reactter/api/methods/dependency_injection_methods/#rtdestroy) if you want to delete both the instance and the registration of a dependency in singleton mode. ::: Reactter identifies the Singleton mode as `DependencyMode.singleton` -and to activate it, set the `mode` in the argument of [`Rt.register`](/reactter/methods/dependency_injection_methods/#rtregister) and [`Rt.create`](/reactter/methods/dependency_injection_methods/#rtcreate), -or use [`Rt.lazySingleton`](/reactter/methods/dependency_injection_methods/#rtlazy_singleton), [`Rt.singleton`](/reactter/methods/dependency_injection_methods/#rtsingleton). +and to activate it, set the `mode` in the argument of [`Rt.register`](/reactter/api/methods/dependency_injection_methods/#rtregister) and [`Rt.create`](/reactter/api/methods/dependency_injection_methods/#rtcreate), +or use [`Rt.lazySingleton`](/reactter/api/methods/dependency_injection_methods/#rtlazy_singleton), [`Rt.singleton`](/reactter/api/methods/dependency_injection_methods/#rtsingleton). :::note Singleton consumes less CPU than [Builder](#builder) and [Factory](#factory), diff --git a/website/src/content/docs/core_concepts/event_handler.mdx b/website/src/content/docs/core_concepts/event_handler.mdx index 7dab9d74..6765c782 100644 --- a/website/src/content/docs/core_concepts/event_handler.mdx +++ b/website/src/content/docs/core_concepts/event_handler.mdx @@ -1,10 +1,10 @@ --- -title: "Event Handler" -description: "The base class for all event handlers in Reactter." +title: Event handler +description: Learn about the event handler system in Reactter. sidebar: order: 3 --- -import { HE, HN, HM, HT } from '@/components/Highlight'; +import { HE, HK, HN, HM, HT } from '@/components/Highlight'; In Reactter, event handler plays a pivotal role in facilitating seamless communication and coordination between various components within the application. @@ -16,23 +16,26 @@ fostering a cohesive ecosystem where different parts of the application can inte Reactter offers the following event handler mechanisms: - Hooks - - [`UseEffect`](/reactter/hooks/UseEffect) + - [`UseEffect`](/reactter/api/hooks/UseEffect) - Methods - - [`Rt.on`](/reactter/methods/event_handler_methods/#rton) - - [`Rt.one`](/reactter/methods/event_handler_methods/#rtone) - - [`Rt.emit`](/reactter/methods/event_handler_methods/#rtemit) - - [`Rt.off`](/reactter/methods/event_handler_methods/#rtoff) - - [`Rt.offAll`](/reactter/methods/event_handler_methods/#rtoffall) + - [`Rt.on`](/reactter/api/methods/event_handler_methods/#rton) + - [`Rt.one`](/reactter/api/methods/event_handler_methods/#rtone) + - [`Rt.emit`](/reactter/api/methods/event_handler_methods/#rtemit) + - [`Rt.off`](/reactter/api/methods/event_handler_methods/#rtoff) + - [`Rt.offAll`](/reactter/api/methods/event_handler_methods/#rtoffall) ## How it works -Event handler in Reactter is based on a few fundamental concepts: +The event handler in Reactter is based on some fundamental concepts: -- **Event**: Is a enum that represents a specific action or occurrence in the application. -- **Instance**: Is an object that emits and listens to events. -- **Listener**: Is a function that is executed when an event is emitted. +- **Event**: It is an `enum` that represents a specific action or occurrence associated with a particular instance. +It defines the type of interaction or change that can happen. +- **Instance**: It is an `Object` used to identify the entity that triggers the event and to emit the corresponding events. +It acts as the emitter that connects the event to the action. +- **Action**: It is a `Function` that executes in response to an emitted event. +It contains the logic needed to manage the event and define the desired behavior. -Understanding these concepts is crucial for effectively managing event-driven interactions in Reactter apps. +Understanding these concepts is crucial for efficiently managing event-driven interactions in Reactter applications. ### Example @@ -55,7 +58,7 @@ void main() async { ); // Create a timer that decrements the `value` of `count` - // by 1 every second until it reaches 0 + // by 1 each time second until it reaches 0 await Timer.periodic(Duration(seconds: 1), countdown); } @@ -70,17 +73,11 @@ void countdown(Timer timer) { } ``` -In this example, the line 10 to 14, we see that the `Rt.on` method is used to subscribe to the `Lifecycle.didUpdate` **event** of the `count` **instance**. -Whenever the `count` state changes, the **listener** function is invoked, printing the current `value` of the `count` state. +In this example, we see that the `Rt.on` method is used to subscribe to the **event** `Lifecycle.didUpdate` of the `count` **instance** from line 10 to 14. +Whenever the count state changes, the listener function is invoked, printing the current value of the count state. -Here, we can't see the emitter, because it's encapsulated within the `Signal` class, and it's called when the `value` of the `count` state changes. -This mechanism is made possible by the underlying state management system. - -:::tip - Learn about [Lifecycle](/reactter/core_concepts/lifecycles). -::: - -Now, we do a small tweak to add an emitter: +Here, we cannot see the emitting statement(`emit`) because it is encapsulated inside the `Signal` class and is called when the `value` of the `count` state changes. +To see how an event is emitted, let's make a small adjustment to add an emitter: ```dart title="main.dart" ins={4,18-24,38-39} /count(?!down)(?!\u0060)/ "count.value" "Rt.on" "Lifecycle.didUpdate" "Rt.emit" /(CustomEvent) / /(CustomEvent(\.countdownFinished))(?!\u0060)/ import 'dart:async'; @@ -109,7 +106,7 @@ void main() async { ); // Create a timer that decrements the `value` of `count` - // by 1 every second until it reaches 0 + // by 1 each time second until it reaches 0 await Timer.periodic(Duration(seconds: 1), countdown); } diff --git a/website/src/content/docs/core_concepts/hooks.mdx b/website/src/content/docs/core_concepts/hooks.mdx index 7820c3ed..8e74c5f1 100644 --- a/website/src/content/docs/core_concepts/hooks.mdx +++ b/website/src/content/docs/core_concepts/hooks.mdx @@ -24,12 +24,12 @@ Hooks are classes with the ability to use states and manage side effects. They a Reactter provider some hooks to manage the state and side effects of the application, which are: -- [`UseState`](/reactter/hooks/use_state) -- [`UseAsyncState`](/reactter/hooks/use_async_state) -- [`UseReducer`](/reactter/hooks/use_reducer) -- [`UseCompute`](/reactter/hooks/use_compute) -- [`UseEffect`](/reactter/hooks/use_effect) -- [`UseDependency`](/reactter/hooks/use_instance) +- [`UseState`](/reactter/api/hooks/use_state) +- [`UseAsyncState`](/reactter/api/hooks/use_async_state) +- [`UseReducer`](/reactter/api/hooks/use_reducer) +- [`UseCompute`](/reactter/api/hooks/use_compute) +- [`UseEffect`](/reactter/api/hooks/use_effect) +- [`UseDependency`](/reactter/api/hooks/use_instance) ## How it works @@ -75,7 +75,7 @@ It's important to note that the states and hooks defined above this line are not To avoid this, it's recommended to place this line as the first line in the class body. ::: -As shown in the example above, we can utilize other hooks such as [`UseEffect`](/reactter/hooks/use_effect) to monitor changes in the text input's controller and ensure it is disposed when the hook is destroyed. +As shown in the example above, we can utilize other hooks such as [`UseEffect`](/reactter/api/hooks/use_effect) to monitor changes in the text input's controller and ensure it is disposed when the hook is destroyed. The `update` method is used to set the internal `_value` to the current text in the controller, which keeps the state synchronized with the input. This methods is a part of the `RtHook` class that allows you to update the state of the hook. @@ -110,5 +110,5 @@ You can then call that _**Custom Hook**_ from anywhere in the code and get acces In the example above, the form captures first and last name inputs, combines them into a full name, and displays the result. `MyController` uses `UseTextInput` hook for capturing the first and last name. -The `fullName` state is defined using [`UseCompute`](/reactter/hooks/use_compute) to compute the full name based on the values of `firstNameInput` and `lastNameInput`. This ensures the full name is automatically updated whenever either input changes. +The `fullName` state is defined using [`UseCompute`](/reactter/api/hooks/use_compute) to compute the full name based on the values of `firstNameInput` and `lastNameInput`. This ensures the full name is automatically updated whenever either input changes. diff --git a/website/src/content/docs/core_concepts/lifecycle.mdx b/website/src/content/docs/core_concepts/lifecycle.mdx index 91595f54..d88d67aa 100644 --- a/website/src/content/docs/core_concepts/lifecycle.mdx +++ b/website/src/content/docs/core_concepts/lifecycle.mdx @@ -47,7 +47,7 @@ Let's explore the lifecycle events: ## Using Event Handler -You can listen to the lifecycle events of a dependency using `Rt.on` or `Rt.one` method of the event handler. e.g: +You can listen to the lifecycle events of an instance (like a dependency or state) using `Rt.on` or `Rt.one` method of the event handler. e.g: @@ -76,7 +76,7 @@ You can listen to the lifecycle events of a dependency using `Rt.on` or :::note - The `RtDependency` is a generic class that takes the type of the dependency and optionally pass an `id`. + The `RtDependency` is a generic class that takes the type of the dependency and optionally an `id`. This class is used to identify the dependency in the event handler. It ideal to use the `RtDependency` class when the dependency is not initialize yet. ::: @@ -113,7 +113,7 @@ and use its methods to observe the lifecycle events. e.g: ## Using UseEffect -The [`UseEffect`](/reactter/hooks/use_effect) hook can be used to listen to the lifecycle events of a dependency. e.g: +The [`UseEffect`](/reactter/api/hooks/use_effect) hook can be used to listen to the lifecycle events of a dependency or state. e.g: diff --git a/website/src/content/docs/core_concepts/rendering_control.mdx b/website/src/content/docs/core_concepts/rendering_control.mdx index 9820dd62..f4d472e6 100644 --- a/website/src/content/docs/core_concepts/rendering_control.mdx +++ b/website/src/content/docs/core_concepts/rendering_control.mdx @@ -1,6 +1,6 @@ --- -title: Rendering Control -description: Learn how to control the rendering of components in Reactter. +title: Rendering control +description: Learn how to control the rendering in Reactter. sidebar: order: 4 badge: @@ -18,28 +18,27 @@ import counterCode from '@/examples/counter/lib/counter.dart?raw'; import counterViewCode from '@/examples/counter/lib/counter_view.dart?raw'; import counterMainCode from '@/examples/counter/lib/main.dart?raw'; -In Flutter, efficient rendering control is essential for crafting high-performance, responsive, and scalable applications. -Reactter provides a way to easily control the rendering of components in the widget tree behavior effortlessly, using the _**flutter_reactter**_ package. +In Flutter, efficient rendering control is crucial for building high-performance, responsive, and scalable applications. +The **_flutter_reactter_** package offers a straightforward way to manage widget tree rendering behavior, enabling to optimize performance and fine-tune their app's responsiveness with ease. ## API -This package provides a collection of classes, widgets and some methods: +This package provides the following rendering control mechanisms: -- Classes - - [`RtComponent`](/reactter/widgets/rt_component) - Widgets - - [`RtScope`](/reactter/widgets/rt_scope) - - [`RtProvider`](/reactter/widgets/rt_provider) - - [`RtProviders`](/reactter/widgets/rt_providers) - - [`RtConsumer`](/reactter/widgets/rt_consumer) - - [`RtSelector`](/reactter/widgets/rt_selector) - - [`RtWatcher`](/reactter/widgets/rt_watcher) - - [`RtSignalWatcher`](/reactter/widgets/rt_signal_watcher) + - [`RtScope`](/reactter/api/widgets/rt_scope) + - [`RtProvider`](/reactter/api/widgets/rt_provider) + - [`RtProviders`](/reactter/api/widgets/rt_providers) + - [`RtConsumer`](/reactter/api/widgets/rt_consumer) + - [`RtSelector`](/reactter/api/widgets/rt_selector) + - [`RtWatcher`](/reactter/api/widgets/rt_watcher) + - [`RtComponent`](/reactter/api/widgets/rt_component) + - [`RtSignalWatcher`](/reactter/api/widgets/rt_signal_watcher) - Extensions - - [`BuilderContext.use`](/reactter/extensions/builder_context_use) - - [`BuilderContext.watch`](/reactter/extensions/builder_context_watch) - - [`BuilderContext.watchId`](/reactter/extensions/builder_context_watch_id) - - [`BuilderContext.select`](/reactter/extensions/builder_context_select) + - [`BuilderContext.use`](/reactter/api/extensions/builder_context_use) + - [`BuilderContext.watch`](/reactter/api/extensions/builder_context_watch) + - [`BuilderContext.watchId`](/reactter/api/extensions/builder_context_watch_id) + - [`BuilderContext.select`](/reactter/api/extensions/builder_context_select) ## How it works @@ -77,16 +76,15 @@ Let's create a simple counter app using Reactter to demonstrate how to control t +Now, when you run the app, you'll see a counter application with two buttons to increment and decrement the `count` value. -Now, when you run the app, you will see a counter app with two buttons to increment and decrement the `count` value. +In this scenario, only the `Text` widget will rebuild when the `count` value changes, while the `CounterView` widget will not rebuild entirely. +This happens because the `RtConsumer` widget observes the `count` property and triggers a rebuild of the widget tree only when the `count` value changes. -In this scenario, only the `Text` widget will be rebuilt when the `count` value changes, not the entire `CounterView` widget. -This is because the `RtConsumer` widget observes the `count` property and triggers the rebuilding of the widget tree when the `count` value changes. - -In example, we used the `RtConsumer` widget to observe the `count` property of the `counterController` instance, +In previous example, we used the `RtConsumer` widget to observe the `count` property of the `counterController` instance, but we can do the same functionality using the `watch` method of the `BuildContext` class. -Here's how we can refactor the code to use the `watch` method along with a `Builder` widget to achieve the same outcome: +Next, we'll refactor the code to use the `watch` method along with the `Builder` widget to achieve the same result: ```dart title="counter_view.dart" startLineNumber={23} del={1-5} ins={6-13} "RtConsumer" "context.watch" // Observe the `count` property of the `counterController` @@ -107,9 +105,8 @@ Here's how we can refactor the code to use the `watch` method along wit ), ``` -Although the `watch` method can be directly employed within the builder method of the `RtProvider` widget, +Although the `watch` method can be directly employed within the `builder` method of the `RtProvider` widget, it's advisable to utilize it alongside a `Builder` widget to prevent unnecessary rebuilds of the widget tree. This practice leverages the `BuildContext` scope, offering a more granular approach to rendering control within the widget tree. For more advanced use cases, you can employ other Reactter's widgets and `BuildContext` methods to further refine the rendering control of the widget tree. -By embracing these strategies, you can optimize the performance and efficiency of your Flutter applications while ensuring a seamless user experience. diff --git a/website/src/content/docs/core_concepts/state_management.mdx b/website/src/content/docs/core_concepts/state_management.mdx index 01b638d4..bf5fd849 100644 --- a/website/src/content/docs/core_concepts/state_management.mdx +++ b/website/src/content/docs/core_concepts/state_management.mdx @@ -1,6 +1,6 @@ --- -title: State Management -description: Reactter provides a simple and efficient way to manage the state of your application. +title: State management +description: Learn about the state management system in Reactter and how it works. sidebar: order: 1 --- @@ -16,23 +16,21 @@ To continue, we will show you the mechanisms that Reactter offers for state mana ## API -## API - Reactter provides a variety of mechanisms for state management, including classes, hooks, and methods: - Mixins - - [`RtStateBase`](/reactter/classes/RtStateBase) + - [`RtStateBase`](/reactter/api/classes/RtStateBase) - Classes - - [`Signal`](/reactter/classes/signal) + - [`Signal`](/reactter/api/classes/signal) - Hooks - - [`UseState`](/reactter/hooks/use_state) - - [`UseAsyncState`](/reactter/hooks/use_async_state) - - [`UseReducer`](/reactter/hooks/use_reducer) - - [`UseCompute`](/reactter/hooks/use_compute) + - [`UseState`](/reactter/api/hooks/use_state) + - [`UseAsyncState`](/reactter/api/hooks/use_async_state) + - [`UseReducer`](/reactter/api/hooks/use_reducer) + - [`UseCompute`](/reactter/api/hooks/use_compute) - Methods - - [`Rt.lazyState`](/reactter/methods/state_management_methods/#rtlazy_state) - - [`Rt.batch`](/reactter/methods/state_management_methods/#rtbatch) - - [`Rt.untracked`](/reactter/methods/state_management_methods/#rtuntracked) + - [`Rt.lazyState`](/reactter/api/methods/state_management_methods/#rtlazy_state) + - [`Rt.batch`](/reactter/api/methods/state_management_methods/#rtbatch) + - [`Rt.untracked`](/reactter/api/methods/state_management_methods/#rtuntracked) :::tip Learn about [Hooks](/reactter/core_concepts/hooks). @@ -50,7 +48,7 @@ To dive into the concept, let's start by exploring what constitutes a state in R All states in Reactter are classes that inherit `RtState`, which encapsulates the data and behavior of a particular state, and provides a way to notify observers when the state changes. -Reactter offers three fundamental approaches for creating states: [`RtStateBase`](/reactter/classes/RtStateBase), [`Signal`](/reactter/classes/signal) and [`Hooks`](/reactter/core_concepts/hooks). +Reactter offers three fundamental approaches for creating states: [`RtStateBase`](/reactter/api/classes/RtStateBase), [`Signal`](/reactter/api/classes/signal) and [`Hooks`](/reactter/core_concepts/hooks). ### State methods @@ -125,8 +123,8 @@ class Signal with RtContextMixin, RtStateBase> { } ``` -During the process, as the `value` of `count` changes, the `Lifecycle.didUpdate` event is triggered, which is fired by the `update` method(`signal.dart`, line 22). -This event is listened to by the `Rt.on` method(`main.dart`, line 10), which prints the `value` of `count`. +During the process, as the `value` of `count` changes, the `Lifecycle.didUpdate` event is triggered, which is fired by the `update` method (`signal.dart`, line 22). +This event is listened to by the `Rt.on` method (`main.dart`, line 10), which prints the `value` of `count`. This occurs thanks to the reactivity of Reactter, which is responsible for notifying listeners by emitting events related to the **_lifecycle_** of the state. diff --git a/website/src/content/docs/devtools_extension.mdx b/website/src/content/docs/devtools_extension.mdx new file mode 100644 index 00000000..9e034ce4 --- /dev/null +++ b/website/src/content/docs/devtools_extension.mdx @@ -0,0 +1,4 @@ +--- +title: DevTools extension 🚧 +description: Reactter DevTools Extension +--- \ No newline at end of file diff --git a/website/src/content/docs/es/core_concepts/dependency_injection.mdx b/website/src/content/docs/es/core_concepts/dependency_injection.mdx index 3ad508a6..1aad247a 100644 --- a/website/src/content/docs/es/core_concepts/dependency_injection.mdx +++ b/website/src/content/docs/es/core_concepts/dependency_injection.mdx @@ -1,6 +1,6 @@ --- title: Inyección de dependencias -description: Aprende a gestionar las dependencias en Reactter. +description: Aprende sobre la inyección de dependencias en Reactter. sidebar: order: 2 --- @@ -27,27 +27,27 @@ Esto resulta en una mejor _modularidad_, _reutilización_ y _testabilidad_ del c Reactter proporciona los siguientes mecanismos de inyección de dependencias: - Hooks - - [`UseDependency`](/reactter/es/hooks/UseDependency) + - [`UseDependency`](/reactter/es/api/hooks/UseDependency) - Métodos - - [`Rt.register`](/reactter/es/methods/dependency_injection_methods/#rtregister) - - [`Rt.lazyBuilder`](/reactter/es/methods/dependency_injection_methods/#rtlazy_builder) - - [`Rt.lazyFactory`](/reactter/es/methods/dependency_injection_methods/#rtlazy_factory) - - [`Rt.lazySingleton`](/reactter/es/methods/dependency_injection_methods/#rtlazy_singleton) - - [`Rt.create`](/reactter/es/methods/dependency_injection_methods/#rtcreate) - - [`Rt.builder`](/reactter/es/methods/dependency_injection_methods/#rtbuilder) - - [`Rt.factory`](/reactter/es/methods/dependency_injection_methods/#rtfactory) - - [`Rt.singleton`](/reactter/es/methods/dependency_injection_methods/#rtsingleton) - - [`Rt.get`](/reactter/es/methods/dependency_injection_methods/#rtget) - - [`Rt.find`](/reactter/es/methods/dependency_injection_methods/#rtfind) - - [`Rt.exists`](/reactter/es/methods/dependency_injection_methods/#rtexists) - - [`Rt.isActive`](/reactter/es/methods/dependency_injection_methods/#rtis_active) - - [`Rt.getDependencyMode`](/reactter/es/methods/dependency_injection_methods/#rtget_dependency_mode) - - [`Rt.delete`](/reactter/es/methods/dependency_injection_methods/#rtdelete) - - [`Rt.destroy`](/reactter/es/methods/dependency_injection_methods/#rtdestroy) - - [`Rt.unregister`](/reactter/es/methods/dependency_injection_methods/#rtunregister) + - [`Rt.register`](/reactter/es/api/methods/dependency_injection_methods/#rtregister) + - [`Rt.lazyBuilder`](/reactter/es/api/methods/dependency_injection_methods/#rtlazy_builder) + - [`Rt.lazyFactory`](/reactter/es/api/methods/dependency_injection_methods/#rtlazy_factory) + - [`Rt.lazySingleton`](/reactter/es/api/methods/dependency_injection_methods/#rtlazy_singleton) + - [`Rt.create`](/reactter/es/api/methods/dependency_injection_methods/#rtcreate) + - [`Rt.builder`](/reactter/es/api/methods/dependency_injection_methods/#rtbuilder) + - [`Rt.factory`](/reactter/es/api/methods/dependency_injection_methods/#rtfactory) + - [`Rt.singleton`](/reactter/es/api/methods/dependency_injection_methods/#rtsingleton) + - [`Rt.get`](/reactter/es/api/methods/dependency_injection_methods/#rtget) + - [`Rt.find`](/reactter/es/api/methods/dependency_injection_methods/#rtfind) + - [`Rt.exists`](/reactter/es/api/methods/dependency_injection_methods/#rtexists) + - [`Rt.isActive`](/reactter/es/api/methods/dependency_injection_methods/#rtis_active) + - [`Rt.getDependencyMode`](/reactter/es/api/methods/dependency_injection_methods/#rtget_dependency_mode) + - [`Rt.delete`](/reactter/es/api/methods/dependency_injection_methods/#rtdelete) + - [`Rt.destroy`](/reactter/es/api/methods/dependency_injection_methods/#rtdestroy) + - [`Rt.unregister`](/reactter/es/api/methods/dependency_injection_methods/#rtunregister) :::tip[Con Flutter] -Sí estás utilizando Flutter, consulta acerca del [control de renderizado](/reactter/es/core_concepts/rendering_control) para aprender a gestionar las dependencias a través de los `Widget` y los métodos extendibles del `BuildContext`. +Sí estás utilizando Flutter, consulta acerca del [control del renderizado](/reactter/es/core_concepts/rendering_control) para aprender a gestionar las dependencias a través de los `Widget` y los métodos extendibles del `BuildContext`. ::: ## ¿Cómo funciona? @@ -55,24 +55,24 @@ Sí estás utilizando Flutter, consulta acerca del [control de renderizado](/rea Reactter gestiona las dependencias a través de un mecanismo centralizado que actua como un repositorio principal encargado de registrar, resolver y suministrar dependencias en toda la aplicación. Para comprender cómo funciona este sistema en su totalidad, desglosaremos el proceso en las siguientes etapas: -1. **Registro**: Esta etapa implica registrar la dependencia en el contexto de Reactter utilizando un tipo específico, una función de creación(`builder`), un identificador(`id`) y un modo de dependencia(`mode`). +1. **Registro**: Esta etapa implica registrar la dependencia en el contexto de Reactter utilizando un tipo específico, una función de creación (`builder`), un identificador (`id`) y un modo de dependencia (`mode`). Para realizar este registro, puedes utilizar los siguientes métodos: - - [`Rt.register`](/reactter/es/methods/dependency_injection_methods/#rtregister) - - [`Rt.lazyBuilder`](/reactter/es/methods/dependency_injection_methods/#rtlazy_builder) - - [`Rt.lazyFactory`](/reactter/es/methods/dependency_injection_methods/#rtlazy_factory) - - [`Rt.lazySingleton`](/reactter/es/methods/dependency_injection_methods/#rtlazy_singleton) + - [`Rt.register`](/reactter/es/api/methods/dependency_injection_methods/#rtregister) + - [`Rt.lazyBuilder`](/reactter/es/api/methods/dependency_injection_methods/#rtlazy_builder) + - [`Rt.lazyFactory`](/reactter/es/api/methods/dependency_injection_methods/#rtlazy_factory) + - [`Rt.lazySingleton`](/reactter/es/api/methods/dependency_injection_methods/#rtlazy_singleton) Durante el registro se emite el evento `Lifecycle.registered`. -2. **Resolución**: Cuando se solicita una dependencia, Reactter crea una instancia de la dependencia a partir de la funcion de creación(`builder`) registrada, según el tipo e identificador(`id`) proporcionados. +2. **Resolución**: Cuando se solicita una dependencia, Reactter crea una instancia de la dependencia a partir de la funcion de creación (`builder`) registrada, según el tipo e identificador (`id`) proporcionados. Para ello, puedes utilizar los siguientes métodos: - - [`Rt.get`](/reactter/es/methods/dependency_injection_methods/#rtget) - - [`Rt.create`](/reactter/es/methods/dependency_injection_methods/#rtcreate) - - [`Rt.builder`](/reactter/es/methods/dependency_injection_methods/#rtbuilder) - - [`Rt.factory`](/reactter/es/methods/dependency_injection_methods/#rtfactory) - - [`Rt.singleton`](/reactter/es/methods/dependency_injection_methods/#rtsingleton) + - [`Rt.get`](/reactter/es/api/methods/dependency_injection_methods/#rtget) + - [`Rt.create`](/reactter/es/api/methods/dependency_injection_methods/#rtcreate) + - [`Rt.builder`](/reactter/es/api/methods/dependency_injection_methods/#rtbuilder) + - [`Rt.factory`](/reactter/es/api/methods/dependency_injection_methods/#rtfactory) + - [`Rt.singleton`](/reactter/es/api/methods/dependency_injection_methods/#rtsingleton) :::note Todos los anteriores métodos, excepto `Rt.get`, ademas de instanciar la dependencia, pueden registrarla si no ha sido registrada previamente. @@ -80,46 +80,46 @@ Para comprender cómo funciona este sistema en su totalidad, desglosaremos el pr Si se crea una nueva instancia de la dependencia, se emitirán los siguientes eventos: - `Lifecycle.created` - - `Lifecycle.willMount`(solo en _flutter_reactter_) - - `Lifecycle.didMount`(solo en _flutter_reactter_) + - `Lifecycle.willMount` (solo en _flutter_reactter_) + - `Lifecycle.didMount` (solo en _flutter_reactter_) 3. **Uso**: Una vez resuelta la dependencia, su instancia puede ser utilizada en cualquier parte de la aplicación. Para poder acceder a la dependencia o comprobar su estado, puedes utilizar los siguientes métodos: - - [`Rt.find`](/reactter/es/methods/dependency_injection_methods/#rtfind) - - [`Rt.get`](/reactter/es/methods/dependency_injection_methods/#rtget) - - [`Rt.exists`](/reactter/es/methods/dependency_injection_methods/#rtexists) - - [`Rt.isActive`](/reactter/es/methods/dependency_injection_methods/#rtis_active) - - [`Rt.getDependencyMode`](/reactter/es/methods/dependency_injection_methods/#rtget_dependency_mode) + - [`Rt.find`](/reactter/es/api/methods/dependency_injection_methods/#rtfind) + - [`Rt.get`](/reactter/es/api/methods/dependency_injection_methods/#rtget) + - [`Rt.exists`](/reactter/es/api/methods/dependency_injection_methods/#rtexists) + - [`Rt.isActive`](/reactter/es/api/methods/dependency_injection_methods/#rtis_active) + - [`Rt.getDependencyMode`](/reactter/es/api/methods/dependency_injection_methods/#rtget_dependency_mode) Si algún estado de la dependencia se actualiza, se emitirán los siguientes eventos: - `Lifecycle.willUpdate` - `Lifecycle.didUpdate` -4. **Eliminación**: En esta etapa, Reactter elimina la instancia de la dependencia según el tipo e identificador(`id`) proporcionados. +4. **Eliminación**: En esta etapa, Reactter elimina la instancia de la dependencia según el tipo e identificador (`id`) proporcionados. Para ello, puedes utilizar los siguientes métodos: - - [`Rt.delete`](/reactter/es/methods/dependency_injection_methods/#rtdelete) - - [`Rt.destroy`](/reactter/es/methods/dependency_injection_methods/#rtdestroy) + - [`Rt.delete`](/reactter/es/api/methods/dependency_injection_methods/#rtdelete) + - [`Rt.destroy`](/reactter/es/api/methods/dependency_injection_methods/#rtdestroy) :::note - Según el modo de la dependencia(`mode`), es posible que no se elimine la instancia de la dependencia o, en su caso, que también se elimine su registro. + Según el modo de la dependencia (`mode`), es posible que no se elimine la instancia de la dependencia o, en su caso, que también se elimine su registro. ::: Durante la eliminación se emiten los siguientes eventos: - - `Lifecycle.willUnmount`(solo en _flutter_reactter_) - - `Lifecycle.didUnmount`(solo en _flutter_reactter_) + - `Lifecycle.willUnmount` (solo en _flutter_reactter_) + - `Lifecycle.didUnmount` (solo en _flutter_reactter_) - `Lifecycle.deleted` -5. **Desregistro**: En esta etapa, Reactter elimina el registro de la dependencia del tipo y el identificador(`id`) proporcionados. +5. **Desregistro**: En esta etapa, Reactter elimina el registro de la dependencia del tipo y el identificador (`id`) proporcionados. Para desregistrar la dependencia, puedes utilizar los siguientes métodos: - - [`Rt.unregister`](/reactter/es/methods/dependency_injection_methods/#rtunregister) - - [`Rt.delete`](/reactter/es/methods/dependency_injection_methods/#rtdelete) - - [`Rt.destroy`](/reactter/es/methods/dependency_injection_methods/#rtdestroy) + - [`Rt.unregister`](/reactter/es/api/methods/dependency_injection_methods/#rtunregister) + - [`Rt.delete`](/reactter/es/api/methods/dependency_injection_methods/#rtdelete) + - [`Rt.destroy`](/reactter/es/api/methods/dependency_injection_methods/#rtdestroy) :::note - Según el modo de la dependencia(`mode`), es posible que no se elimine el registro de la dependencia. + Según el modo de la dependencia (`mode`), es posible que no se elimine el registro de la dependencia. ::: Cuando se elimina el registro de la dependencia, se emitirá el evento `Lifecycle.unregistered`. @@ -133,7 +133,7 @@ Si no se proporciona el `mode`, Reactter utilizará el modo predeterminado, que Una **dependencia** en Reactter se refiere a una **instancia** de un tipo específico. -El ámbito de la dependencia registrada es global. Esto indica que al utilizar los [atajos para gestionar dependencias](/reactter/es/shortcuts/dependency) o el [`UseDependency`](/reactter/es/hooks/UseDependency), podrás acceder a ellas desde cualquier parte del proyecto. +El ámbito de la dependencia registrada es global. Esto indica que al utilizar los [atajos para gestionar dependencias](/reactter/es/shortcuts/dependency) o el [`UseDependency`](/reactter/es/api/hooks/UseDependency), podrás acceder a ellas desde cualquier parte del proyecto. ::: :::tip @@ -227,18 +227,18 @@ En este ejemplo, hemos creado una cuenta regresiva de `10` segundos, y Pero si queremos utilizar la instancia de `Counter` en otra parte del código, podemos hacerlo de la siguiente manera: ```dart title="main.dart" ins={3, 6-7} collapse={8-11} "Rt.register" "Rt.create" /Counter(?!\u0060)/ /Countdown(?!\u0060)/ /(countdown) =/ "countdown.run" - import 'package:reactter/reactter.dart'; - import 'countdown.dart'; - import 'counter.dart'; - - void main() async { - // Registra la clase `Counter` con un valor inicial de 20 - Rt.register(() => Counter(20)); - // Crea una instancia de la clase `Countdown` - final countdown = Rt.create(() => Countdown())!; - // Inicia la cuenta regresiva - await countdown.run(); - } +import 'package:reactter/reactter.dart'; +import 'countdown.dart'; +import 'counter.dart'; + +void main() async { + // Registra la clase `Counter` con un valor inicial de 20 + Rt.register(() => Counter(20)); + // Crea una instancia de la clase `Countdown` + final countdown = Rt.create(() => Countdown())!; + // Inicia la cuenta regresiva + await countdown.run(); +} ``` Ahora, la cuenta regresiva iniciará desde `20` y cuando llegue a `0`, la instancia de `Counter` será eliminada. @@ -248,48 +248,48 @@ y cuando se crea la instancia de `Countdown`, utiliza la instancia de < Pero, ¿qué pasa si queremos utilizar la instancia de `Counter` en otra parte del código? Veamos: ```dart title="main.dart" ins={12-15} collapse={6-11} "Rt.register" "Rt.create" "Rt.get" /counter(?!\\.)/ "counter?.count" /Counter(?!\u0060)(?! )/ /Countdown(?!\u0060)/ /(countdown) =/ "countdown.run" - import 'package:reactter/reactter.dart'; - import 'countdown.dart'; - import 'counter.dart'; - - void main() async { - // Registra la clase `Counter` con un valor inicial de 20 - Rt.register(() => Counter(20)); - // Crea una instancia de la clase `Countdown` - final countdown = Rt.create(() => Countdown())!; - // Inicia la cuenta regresiva - await countdown.run(); - // Obtiene la instancia de la clase `Counter` - final counter = Rt.get(); - // Intenta imprimir el valor actual de `count` - print('Count: ${counter?.count ?? 'Counter instance not found'}'); - } +import 'package:reactter/reactter.dart'; +import 'countdown.dart'; +import 'counter.dart'; + +void main() async { + // Registra la clase `Counter` con un valor inicial de 20 + Rt.register(() => Counter(20)); + // Crea una instancia de la clase `Countdown` + final countdown = Rt.create(() => Countdown())!; + // Inicia la cuenta regresiva + await countdown.run(); + // Obtiene la instancia de la clase `Counter` + final counter = Rt.get(); + // Intenta imprimir el valor actual de `count` + print('Count: ${counter?.count ?? 'Counter instance not found'}'); +} ``` En este caso, la cuenta regresiva funcionará como antes, pero al intentar obtener la instancia de `Counter` para imprimir su valor, la salida será 'Counter instance not found'. -Esto ocurre porque `Counter` fue registrado como `DependencyMode.builder`(el modo predeterminado), +Esto ocurre porque `Counter` fue registrado como `DependencyMode.builder` (modo predeterminado), por lo que al ser eliminada al final de la cuenta regresiva, su registro también se elimina. Si queremos obtener la instancia de `Counter` para imprimir su valor, necesitamos registrarla utilizando el modo `DependencyMode.singleton`, quedando de la siguiente manera: ```dart title="main.dart" {7} collapse={8-15} "Rt.register" "Rt.create" "Rt.get" "DependencyMode.singleton" /counter(?!\\.)/ "counter?.count" /Counter(?!\u0060)(?! )/ /Countdown(?!\u0060)/ /(countdown) =/ "countdown.run" - import 'package:reactter/reactter.dart'; - import 'countdown.dart'; - import 'counter.dart'; - - void main() async { - // Registra la clase `Counter` con un valor inicial de 20 - Rt.register(() => Counter(20), mode: DependencyMode.singleton); - // Crea una instancia de la clase `Countdown` - final countdown = Rt.create(() => Countdown())!; - // Inicia la cuenta regresiva - await countdown.run(); - // Obtiene la instancia de la clase `Counter` - final counter = Rt.get(); - // Intenta imprimir el valor actual de la cuenta - print('Count: ${counter?.count ?? 'Counter instance not found'}'); - } +import 'package:reactter/reactter.dart'; +import 'countdown.dart'; +import 'counter.dart'; + +void main() async { + // Registra la clase `Counter` con un valor inicial de 20 + Rt.register(() => Counter(20), mode: DependencyMode.singleton); + // Crea una instancia de la clase `Countdown` + final countdown = Rt.create(() => Countdown())!; + // Inicia la cuenta regresiva + await countdown.run(); + // Obtiene la instancia de la clase `Counter` + final counter = Rt.get(); + // Intenta imprimir el valor actual de la cuenta + print('Count: ${counter?.count ?? 'Counter instance not found'}'); +} ``` ## Modos de dependencia @@ -302,9 +302,9 @@ El `mode` con el que se registra una dependencia determina cómo es gestionada p ### Builder -El modo Builder es una forma de gestionar una dependencia que registra una función de creación(`builder`) y crea una instancia solo si no ha sido creada previamente. +El modo Builder es una forma de gestionar una dependencia que registra una función de creación (`builder`) y crea una instancia solo si no ha sido creada previamente. -En este modo, cuando el árbol de dependencias deja de necesitarla, se elimina por completo, incluyendo el registro y la función de creación(`builder`). +En este modo, cuando el árbol de dependencias deja de necesitarla, se elimina por completo, incluyendo el registro y la función de creación (`builder`). Reactter identifica el modo builder como `DependencyMode.builder` @@ -317,14 +317,14 @@ y lo utiliza por defecto. ### Factory -El modo Factory es una forma de gestionar una dependencia en la que se registra una función de creación(`builder`) y se crea una nueva instancia cada vez que se solicita. +El modo Factory es una forma de gestionar una dependencia en la que se registra una función de creación (`builder`) y se crea una nueva instancia cada vez que se solicita. -En este modo, cuando el árbol de dependencias deja de utilizarla, la instancia se elimina, pero la función de creación(`builder`) permanece registrada. +En este modo, cuando el árbol de dependencias deja de utilizarla, la instancia se elimina, pero la función de creación (`builder`) permanece registrada. Reactter identifica el modo factory como `DependencyMode.factory` -y para activarlo, establezca el `mode` en el argumento de [`Rt.register`](/reactter/es/methods/dependency_injection_methods/#rtregister) y [`Rt.create`](/reactter/es/methods/dependency_injection_methods/#rtcreate), -o utilice [`Rt.lazyFactory`](/reactter/es/methods/dependency_injection_methods/#rtlazy_factory), [`Rt.factory`](/reactter/es/methods/dependency_injection_methods/#rtfactory). +y para activarlo, establezca el `mode` en el argumento de [`Rt.register`](/reactter/es/api/methods/dependency_injection_methods/#rtregister) y [`Rt.create`](/reactter/es/api/methods/dependency_injection_methods/#rtcreate), +o utilice [`Rt.lazyFactory`](/reactter/es/api/methods/dependency_injection_methods/#rtlazy_factory), [`Rt.factory`](/reactter/es/api/methods/dependency_injection_methods/#rtfactory). :::note Factory utiliza más RAM que [Builder](#builder), @@ -335,19 +335,19 @@ o utilice [`Rt.lazyFactory`](/reactter/es/methods/dependency_injection_metho ### Singleton -El modo Singleton es una forma de gestionar una dependencia que registra una función de creación(`builder`) y garantiza que la instancia se cree solo una vez. +El modo Singleton es una forma de gestionar una dependencia que registra una función de creación (`builder`) y garantiza que la instancia se cree solo una vez. Cuando se utiliza el modo singleton, la instancia de la dependencia y sus estados se mantienen activos, incluso si el árbol de dependencias ya no la utiliza. Esto incluye también la función de creación, a menos que se elimine explícitamente. :::tip - Utilice [`Rt.detroy`](/reactter/es/methods/dependency_injection_methods/#rtdestroy) si deseas eliminar tanto la instancia como el registro de una dependencia en modo singleton. + Utilice [`Rt.detroy`](/reactter/es/api/methods/dependency_injection_methods/#rtdestroy) si deseas eliminar tanto la instancia como el registro de una dependencia en modo singleton. ::: Reactter identifica el modo singleton como `DependencyMode.singleton` -y para activarlo, establezca el `mode` en el argumento de [`Rt.register`](/reactter/es/methods/dependency_injection_methods/#rtregister) y [`Rt.create`](/reactter/es/methods/dependency_injection_methods/#rtcreate), -o utilice [`Rt.lazySingleton`](/reactter/es/methods/dependency_injection_methods/#rtlazy_singleton), [`Rt.singleton`](/reactter/es/methods/dependency_injection_methods/#rtsingleton). +y para activarlo, establezca el `mode` en el argumento de [`Rt.register`](/reactter/es/api/methods/dependency_injection_methods/#rtregister) y [`Rt.create`](/reactter/es/api/methods/dependency_injection_methods/#rtcreate), +o utilice [`Rt.lazySingleton`](/reactter/es/api/methods/dependency_injection_methods/#rtlazy_singleton), [`Rt.singleton`](/reactter/es/api/methods/dependency_injection_methods/#rtsingleton). :::note Singleton consume menos CPU que [Builder](#builder) y [Factory](#factory), diff --git a/website/src/content/docs/es/core_concepts/event_handler.mdx b/website/src/content/docs/es/core_concepts/event_handler.mdx new file mode 100644 index 00000000..a5700c54 --- /dev/null +++ b/website/src/content/docs/es/core_concepts/event_handler.mdx @@ -0,0 +1,130 @@ +--- +title: Manejador de eventos +description: Aprende sobre el sistema de manejo de eventos en Reactter. +sidebar: + order: 3 +--- +import { HE, HK, HN, HM, HT } from '@/components/Highlight'; + +En Reactter, el manejador de eventos juega un papel fundamental en facilitar la comunicación +y coordinación sin problemas entre varios componentes dentro de la aplicación. +Diseñado para garantizar una gestión de estados eficiente e inyección de dependencias, +fomentando un ecosistema cohesivo donde diferentes partes de la aplicación pueden interactuar armoniosamente. + +## API + +Reactter ofrece los siguientes mecanismos de manejador de eventos: + +- Hooks + - [`UseEffect`](/reactter/es/api/hooks/UseEffect) +- Metodos + - [`Rt.on`](/reactter/es/api/methods/event_handler_methods/#rton) + - [`Rt.one`](/reactter/es/api/methods/event_handler_methods/#rtone) + - [`Rt.emit`](/reactter/es/api/methods/event_handler_methods/#rtemit) + - [`Rt.off`](/reactter/es/api/methods/event_handler_methods/#rtoff) + - [`Rt.offAll`](/reactter/es/api/methods/event_handler_methods/#rtoffall) + +## ¿Cómo funciona? + +El manejador de eventos en Reactter se basa en algunos conceptos fundamentales: + +- **Evento**: Es un enumerador (`enum`) que representa una acción o suceso específico asociado a una instancia en particular. +Define el tipo de interacción o cambio que puede ocurrir. +- **Instancia**: Es un objeto (`Object`) utilizado para identificar la entidad que origina el evento y para emitir los eventos correspondientes. +Actúa como el emisor que conecta el evento con la acción. +- **Acción**: Es una función (`Function`) que se ejecuta en respuesta a un evento emitido. +Contiene la lógica necesaria para gestionar el evento y definir el comportamiento deseado. + +Entender estos conceptos es crucial para gestionar eficazmente las interacciones basadas en eventos en las aplicaciones de Reactter. + +### Ejemplo + +Para ilustrar esto, tomemos un ejemplo de cuenta regresiva visto en la página de [Gestión de estados](/reactter/es/core_concepts/state_management/#ejemplo): + +```dart title="main.dart" {10-14} /count(?!down)(?!\u0060)/ "count.value" "Rt.on" "Lifecycle.didUpdate" +import 'dart:async'; +import 'package:reactter/reactter.dart'; + +// Crea un estado reactivo llamado `count` utilizando la clase `Signal` +final count = Signal(10); + +void main() async { + // Escucha el evento `didUpdate` del estado `count` + // e imprime `value` de `count` con cada actualización + Rt.on( + count, + Lifecycle.didUpdate, + (_, __) => print('Count: $count') + ); + + // Crea un temporizador que decrementa el `value` de `count` + // en 1 cada segundo hasta que llegue a 0 + await Timer.periodic(Duration(seconds: 1), countdown); +} + +// Decrementa `value` de `count` en 1 cada ciclo del temporizador +// y cancela el `timer` cuando `value` de `count` llegue a 0 +void countdown(Timer timer) { + count.value -= 1; + + if (count.value == 0) { + timer.cancel(); + } +} +``` + +En este ejemplo, desde la línea 10 a la 14, vemos que el método `Rt.on` se utiliza para suscribirse al **evento** `Lifecycle.didUpdate` de la **instancia** `count`. +Cada que cambie el estado de `count`, se invoca la función (**acción**), que imprime el valor (`value`) actual del estado de `count`. + +Aquí, no podemos ver la sentencia que emite (`emit`) porque se encuentra encapsulada dentro de la clase `Signal`, y se llama cuando el `value` del estado `count` cambia. +Para ver cómo se emite un evento, hagamos un pequeño ajuste para agregar un emisor: + +```dart title="main.dart" ins={4,18-24,38-39} /count(?!down)(?!\u0060)/ "count.value" "Rt.on" "Lifecycle.didUpdate" "Rt.emit" /(CustomEvent) / /(CustomEvent(\.countdownFinished))(?!\u0060)/ +import 'dart:async'; +import 'package:reactter/reactter.dart'; + +enum CustomEvent { countdownFinished } + +// Crea un estado reactivo llamado `count` utilizando la clase `Signal` +final count = Signal(10); + +void main() async { + // Escucha el evento `didUpdate` del estado `count` + // e imprime `value` de `count` con cada actualización + Rt.on( + count, + Lifecycle.didUpdate, + (_, __) => print('Count: $count') + ); + + // Escucha el evento `countdownFinished` del estado `count` + // e imprime un mensaje cuando la cuenta regresiva finaliza + Rt.on( + count, + CustomEvent.countdownFinished, + (_, __) => print('Countdown finished!') + ); + + // Crea un temporizador que decrementa el `value` de `count` + // en 1 cada segundo hasta que llegue a 0 + await Timer.periodic(Duration(seconds: 1), countdown); +} + + +// Decrementa `value` de `count` en 1 cada ciclo del temporizador +// y cancela el `timer` cuando `value` de `count` llegue a 0 +void countdown(Timer timer) { + count.value -= 1; + + if (count.value == 0) { + timer.cancel(); + // Emite el evento `countdownFinished` cuando la cuenta regresiva finaliza + Rt.emit(count, CustomEvent.countdownFinished); + } +} +``` +Hemos añadido un nuevo **evento** llamado `CustomEvent.countdownFinished` y una nueva function(**accion**) que imprime un mensaje cuando la cuenta regresiva finaliza. +Cuando la cuenta regresiva llega a `0`, la **instancia** `count` emite el **evento** `CustomEvent.countdownFinished`, y la función(**accion**) se invoca, imprimiendo el mensaje. + +Este ejemplo demuestra cómo el sistema de manejador de eventos en Reactter facilita la comunicación sin problemas entre diferentes partes de la aplicación, +facilitando una coordinación e interacción eficientes. diff --git a/website/src/content/docs/es/core_concepts/rendering_control.mdx b/website/src/content/docs/es/core_concepts/rendering_control.mdx new file mode 100644 index 00000000..bb0e8c90 --- /dev/null +++ b/website/src/content/docs/es/core_concepts/rendering_control.mdx @@ -0,0 +1,114 @@ +--- +title: Control del renderizado +description: Aprende cómo controlar el renderizado en Reactter. +sidebar: + order: 4 + badge: + text: Flutter +--- +import { HE, HM, HT } from '@/components/Highlight'; +import CodeTabs from '@/components/CodeTabs.astro'; +import { Tabs, TabItem } from "@/components/Tabs"; +import ZappButton from "@/components/ZappButton.astro"; +import { Code } from "@astrojs/starlight/components"; +import { marks } from '@/examples/marks.ts' + +import counterControllerCode from '@/examples/counter/lib/counter_controller.dart?raw'; +import counterCode from '@/examples/counter/lib/counter.dart?raw'; +import counterViewCode from '@/examples/counter/lib/counter_view.dart?raw'; +import counterMainCode from '@/examples/counter/lib/main.dart?raw'; + +En Flutter, el control eficiente del renderizado es crucial para crear aplicaciones de alto rendimiento, receptivas y escalables. +El paquete **_flutter_reactter_** ofrece una manera sencilla de gestionar el comportamiento del renderizado en el árbol de widgets, permitiendo optimizar el rendimiento y ajustar la capacidad de respuesta de la aplicación con facilidad. + +## API + +Este paquete proporciona los siguientes mecanismos de control del renderizado: + +- Widgets + - [`RtScope`](/reactter/api/widgets/rt_scope) + - [`RtProvider`](/reactter/api/widgets/rt_provider) + - [`RtProviders`](/reactter/api/widgets/rt_providers) + - [`RtConsumer`](/reactter/api/widgets/rt_consumer) + - [`RtSelector`](/reactter/api/widgets/rt_selector) + - [`RtWatcher`](/reactter/api/widgets/rt_watcher) + - [`RtComponent`](/reactter/api/widgets/rt_component) + - [`RtSignalWatcher`](/reactter/api/widgets/rt_signal_watcher) +- Extensiones + - [`BuilderContext.use`](/reactter/api/extensions/builder_context_use) + - [`BuilderContext.watch`](/reactter/api/extensions/builder_context_watch) + - [`BuilderContext.watchId`](/reactter/api/extensions/builder_context_watch_id) + - [`BuilderContext.select`](/reactter/api/extensions/builder_context_select) + +## How it works + +El control del renderizado en Reactter se basa en dos conceptos fundamentales de Flutter: + +- **InheritedWidget**: Este mecanismo poderoso comparte eficientemente datos en todo el árbol de widgets. +Reactter extiende esta capacidad con el widget `RtProvider`, que almacena dependencias utilizando el sistema de inyección de dependencias. +Esto permite que los widgets descendientes accedan a estas dependencias según sea necesario. + +- **Extensiones de BuildContext**: Estos métodos facilitan el acceso a dependencias y el control del renderizado dentro del árbol de widgets. +Los widgets de Reactter como `RtConsumer`, `RtSelector` y `RtComponent` utilizan estos métodos para observar dependencias o estados. +Cuando la dependencia o cualquier estado observado cambia, estos widgets activan rápidamente la reconstrucción del árbol de widgets para reflejar el estado actualizado. + +### Ejemplo + +Veamos cómo crear una aplicación de contador simple usando Reactter para demostrar cómo controlar el renderizado del árbol de widgets en Flutter. + + + + + + + + + + + + + + + + + + + + + + + +Ahora, cuando ejecutes la aplicación, verás una aplicación de contador con dos botones para incrementar y decrementar el valor de `count`. + +En este caso, solo el widget `Text` se reconstruirá cuando el valor de `count` cambie, mientras que el widget `CounterView` no se reconstruirá por completo. +Esto se debe a que el widget `RtConsumer` observa la propiedad `count` y activa la reconstrucción del árbol de widgets únicamente cuando el valor de `count` cambia. + +En el ejemplo anterior, utilizamos el widget `RtConsumer` para observar la propiedad `count` de la instancia `counterController`, +pero podemos lograr la misma funcionalidad utilizando el método `watch` de la clase `BuildContext`. + +A continuación, mostramos cómo modificar el código para emplear el método `watch` junto al widget `Builder` y lograr el mismo resultado: + +```dart title="counter_view.dart" startLineNumber={23} del={1-5} ins={6-13} "RtConsumer" "context.watch" + // Observe the `count` property of the `counterController` + // and rebuild the widget tree when the `count` value changes + RtConsumer( + listenStates: (counterController) => [counterController.count], + builder: (context, counterController, child) { + Builder( + builder: (context) { + // Observe the `count` property of the `counterController` + // and rebuild the widget tree when the `count` value changes + final counterController = context.watch( + (counterController) => [counterController.count], + ); + + return Text("${counterController.count}"); + }, + ), +``` + +Mientras que el método `watch` puede emplearse directamente dentro del método `builder` del widget `RtProvider`, +es aconsejable utilizarlo junto a un widget `Builder` para evitar reconstrucciones innecesarias del árbol de widgets. +Esta práctica aprovecha el alcance de `BuildContext`, ofreciendo un enfoque más granular para controlar el renderizado dentro del árbol de widgets. + +Para casos de uso más avanzados, puedes emplear otros widgets de Reactter y métodos de `BuildContext` para poder refinar aún más el control del renderizado del árbol de widgets. diff --git a/website/src/content/docs/es/core_concepts/state_management.mdx b/website/src/content/docs/es/core_concepts/state_management.mdx index d62319dd..c58f70ff 100644 --- a/website/src/content/docs/es/core_concepts/state_management.mdx +++ b/website/src/content/docs/es/core_concepts/state_management.mdx @@ -1,6 +1,6 @@ --- title: Gestor de estados -description: Reactter proporciona una forma simple y eficiente de gestionar el estado de tu aplicación. +description: Aprende sobre el sistema de gestión de estados en Reactter y cómo funciona. sidebar: order: 1 --- @@ -19,18 +19,18 @@ A continuación, te mostramos los mecanimos que Reactter ofrece para la gestión Reactter proporciona una gran variedad de mecanismos para la gestión de estados, incluyendo clases, hooks y métodos: - Mixins - - [`RtStateBase`](/reactter/es/classes/RtStateBase) + - [`RtStateBase`](/reactter/es/api/classes/RtStateBase) - Clases - - [`Signal`](/reactter/es/classes/signal) + - [`Signal`](/reactter/es/api/classes/signal) - Hooks - - [`UseState`](/reactter/es/hooks/use_state) - - [`UseAsyncState`](/reactter/es/hooks/use_async_state) - - [`UseReducer`](/reactter/es/hooks/use_reducer) - - [`UseCompute`](/reactter/es/hooks/use_compute) + - [`UseState`](/reactter/es/api/hooks/use_state) + - [`UseAsyncState`](/reactter/es/api/hooks/use_async_state) + - [`UseReducer`](/reactter/es/api/hooks/use_reducer) + - [`UseCompute`](/reactter/es/api/hooks/use_compute) - Metodos - - [`Rt.lazyState`](/reactter/es/methods/state_management_methods/#rtlazy_state) - - [`Rt.batch`](/reactter/es/methods/state_management_methods/#rtbatch) - - [`Rt.untracked`](/reactter/es/methods/state_management_methods/#rtuntracked) + - [`Rt.lazyState`](/reactter/es/api/methods/state_management_methods/#rtlazy_state) + - [`Rt.batch`](/reactter/es/api/methods/state_management_methods/#rtbatch) + - [`Rt.untracked`](/reactter/es/api/methods/state_management_methods/#rtuntracked) :::tip Aprende sobre [Hooks](/reactter/es/core_concepts/hooks). @@ -48,7 +48,7 @@ Para adentrarnos en el concepto, comencemos explorando qué constituye un estado Todos los estados en Reactter son clases que heredan de `RtState`, la cual encapsula los datos y el comportamiento de un estado particular, y proporciona una forma de notificar a los observadores cuando el estado cambia. -Reactter ofrece tres enfoques fundamentales para crear estados: [`RtStateBase`](/reactter/es/classes/RtStateBase), [`Signal`](/reactter/es/classes/signal) y [`Hooks`](/reactter/es/core_concepts/hooks). +Reactter ofrece tres enfoques fundamentales para crear estados: [`RtStateBase`](/reactter/es/api/classes/RtStateBase), [`Signal`](/reactter/es/api/classes/signal) y [`Hooks`](/reactter/es/core_concepts/hooks). ### Metodos del estado @@ -122,8 +122,8 @@ class Signal with RtContextMixin, RtStateBase> { } ``` -Durante el proceso, a medida que el `value` de `count` cambia, se desencadena el evento `Lifecycle.didUpdate`, el cual es disparado por el método `update`(`signal.dart`, linea 22). -Este evento es escuchado por el método `Rt.on`(`main.dart`, linea 10), que imprime el `value` de `count`. +Durante el proceso, a medida que el `value` de `count` cambia, se desencadena el evento `Lifecycle.didUpdate`, el cual es disparado por el método `update` (`signal.dart`, linea 22). +Este evento es escuchado por el método `Rt.on` (`main.dart`, linea 10), que imprime el `value` de `count`. Esto ocurre gracias a la reactividad de Reactter, que es encargada de notificar a los oyentes mediante la emisión de eventos relacionados con el **_ciclo de vida_** del estado. diff --git a/website/src/content/docs/es/getting_started.mdx b/website/src/content/docs/es/getting_started.mdx index 8aa8f923..a06bb3d2 100644 --- a/website/src/content/docs/es/getting_started.mdx +++ b/website/src/content/docs/es/getting_started.mdx @@ -63,7 +63,7 @@ Añada el paquete a su proyecto ejecutando el siguiente comando: ### Instalación manual -Añada el paquete a su proyecto añadiendo la siguiente línea a su archivo `pubspec.yaml`: +Agrega el paquete a su proyecto añadiendo la siguiente línea a su archivo `pubspec.yaml`: @@ -72,8 +72,8 @@ Añada el paquete a su proyecto añadiendo la siguiente línea a su archivo `pub lang="yaml" showLineNumbers={false} code={` -dependencies: - reactter: ${reactterVersion ? `^${reactterVersion}` : "# add version here"} + dependencies: + reactter: ${reactterVersion ? `^${reactterVersion}` : "# add version here"} `} /> @@ -83,8 +83,8 @@ dependencies: lang="yaml" showLineNumbers={false} code={` -dependencies: - flutter_reactter: ${flutterReactterVersion ? `^${flutterReactterVersion}` : "# add version here"} + dependencies: + flutter_reactter: ${flutterReactterVersion ? `^${flutterReactterVersion}` : "# add version here"} `} /> @@ -118,7 +118,7 @@ Además, si estás desarrollando con Visual Studio Code, es una buena idea utili ## Uso -Para utilizar Reactter, debe importar el paquete en su archivo Dart donde lo necesite: +Para utilizar Reactter, debe importar el paquete en su archivo Dart donde lo requieras: @@ -139,7 +139,7 @@ Listo! No tienes que preocuparte por la configuración, Reactter ya esta listo p ### ¿Qué sigue? -Si eres nuevo en Reactter, puedes empezar leyendo la sección de _**Conceptos básicos**_ . +Si eres nuevo en Reactter, puedes empezar leyendo la sección de _**Conceptos básicos**_. Esto te ayudará a entender los conceptos básicos de Reactter y cómo utilizarlos en tu proyecto. En esta sección, los temas que aprenderá son: diff --git a/website/src/content/docs/es/index.mdx b/website/src/content/docs/es/index.mdx index ee45d4e3..0787e8e5 100644 --- a/website/src/content/docs/es/index.mdx +++ b/website/src/content/docs/es/index.mdx @@ -53,7 +53,7 @@ import Card from "@/components/Card.astro"; Reacciona a los cambios al más mínimo nivel de granularidad, proporcionando un control preciso y capacidad de respuesta en tu aplicación. - Saber más sobre [Control de Renderizado](/reactter/es/core_concepts/rendering_control). + Saber más sobre [control del renderizado](/reactter/es/core_concepts/rendering_control). Reactter es superligero, lo que garantiza un rendimiento eficiente sin dependencias externas. diff --git a/website/src/content/docs/extra_topics/binding_state_to_dependency.mdx b/website/src/content/docs/extra_topics/binding_state_to_dependency.mdx index 30143f21..807d8ea0 100644 --- a/website/src/content/docs/extra_topics/binding_state_to_dependency.mdx +++ b/website/src/content/docs/extra_topics/binding_state_to_dependency.mdx @@ -11,7 +11,7 @@ This guies assumes you've already read the [Dependency Injection](/reactter/core It's recommended read that first if you're new in Reactter. ::: -A state([`RtState`](/reactter/core_concepts/state_management/#state) like [`Signal`](/reactter/classes/signal) or any [`Hooks`](/reactter/core_concepts/hooks)) can be bound to the dependency, allowing the state to be manipulated directly from the dependency and to notify its listeners about any changes. +A state([`RtState`](/reactter/core_concepts/state_management/#state) like [`Signal`](/reactter/api/classes/signal) or any [`Hooks`](/reactter/core_concepts/hooks)) can be bound to the dependency, allowing the state to be manipulated directly from the dependency and to notify its listeners about any changes. Additionally, it ensures that the state is automatically disposed of when the dependency is no longer needed. By integrating state directly within dependencies, you benefit from _**cleaner**_ and more _**maintainable**_ code. The automatic handling by Reactter means _**less boilerplate**_ and _**fewer errors**_ related to manual state management, leading to a more efficient development process. This approach _**simplifies**_ the synchronization between state and its associated dependency, enhancing the _**overall responsiveness**_ and _**reliability**_ of your application. @@ -45,7 +45,7 @@ If the state is declared lazily (e.g., using `late` keyword), it will n ### Lazy state binding -When a state is declared lazily, it is not automatically bound to the dependency. In such cases, you can use the [`Rt.lazyState`](/reactter/methods/state_management_methods/#rtlazystate) method to bind the state to the dependency, e.g.: +When a state is declared lazily, it is not automatically bound to the dependency. In such cases, you can use the [`Rt.lazyState`](/reactter/api/methods/state_management_methods/#rtlazystate) method to bind the state to the dependency, e.g.: ```dart title="count_controller.dart" "UseState" "Rt.lazyState" import "package:reactter/reactter.dart"; @@ -66,7 +66,7 @@ class CountController { } ``` -In the example above, the `uCount` state is declared lazily using the `late` keyword. To bind the state to the `CountController` instance, the [`Rt.lazyState`](/reactter/methods/state_management_methods/#rtlazystate) method is used, passing the state creation function and the dependency instance as arguments. This ensures that when `uCount` is accessed, it will be automatically bound to the `CountController` instance, e.g.: +In the example above, the `uCount` state is declared lazily using the `late` keyword. To bind the state to the `CountController` instance, the [`Rt.lazyState`](/reactter/api/methods/state_management_methods/#rtlazystate) method is used, passing the state creation function and the dependency instance as arguments. This ensures that when `uCount` is accessed, it will be automatically bound to the `CountController` instance, e.g.: ### Manual binding diff --git a/website/src/content/docs/shareds/tip_lifecycle_example.mdx b/website/src/content/docs/shareds/tip_lifecycle_example.mdx index 96c25955..725592df 100644 --- a/website/src/content/docs/shareds/tip_lifecycle_example.mdx +++ b/website/src/content/docs/shareds/tip_lifecycle_example.mdx @@ -3,6 +3,6 @@ title: Tip about the lifecycle example --- :::tip - This example is taken from [Rendering Control](http://localhost:4321/reactter/core_concepts/rendering_control/#example) page. + This example is taken from [rendering control](http://localhost:4321/reactter/core_concepts/rendering_control/#example) page. It's recommended to read that page first before proceeding. ::: \ No newline at end of file From dd0ae565db391a8b04fea81db252ab60ecae2321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Mon, 20 Jan 2025 23:37:35 -0600 Subject: [PATCH 100/141] refactor(website): Enhance lifecycle documentation and add Spanish translation --- .../content/docs/core_concepts/lifecycle.mdx | 12 +- .../docs/es/core_concepts/lifecycle.mdx | 141 ++++++++++++++++++ .../docs/es/shareds/tip_lifecycle_example.mdx | 8 + 3 files changed, 155 insertions(+), 6 deletions(-) create mode 100644 website/src/content/docs/es/core_concepts/lifecycle.mdx create mode 100644 website/src/content/docs/es/shareds/tip_lifecycle_example.mdx diff --git a/website/src/content/docs/core_concepts/lifecycle.mdx b/website/src/content/docs/core_concepts/lifecycle.mdx index d88d67aa..4f6867e0 100644 --- a/website/src/content/docs/core_concepts/lifecycle.mdx +++ b/website/src/content/docs/core_concepts/lifecycle.mdx @@ -1,9 +1,10 @@ --- title: Lifecycle -description: The lifecycle of a component in React. +description: Learn about the lifecycle in Reactter. sidebar: order: 5 --- + import { HE, HM, HT, HS } from '@/components/Highlight'; import CodeTabs from '@/components/CodeTabs.astro'; import { Tabs, TabItem } from "@/components/Tabs"; @@ -47,7 +48,7 @@ Let's explore the lifecycle events: ## Using Event Handler -You can listen to the lifecycle events of an instance (like a dependency or state) using `Rt.on` or `Rt.one` method of the event handler. e.g: +You can listen to the lifecycle events of an instance (like a dependency or state) using [`Rt.on`](/reactter/api/methods/event_handler_methods/#rton) or [`Rt.one`](/reactter/api/methods/event_handler_methods/#rtone) method of the event handler. e.g: @@ -76,14 +77,13 @@ You can listen to the lifecycle events of an instance (like a dependency or stat :::note - The `RtDependency` is a generic class that takes the type of the dependency and optionally an `id`. - This class is used to identify the dependency in the event handler. - It ideal to use the `RtDependency` class when the dependency is not initialize yet. + The [`RtDependency`](/reactter/api/classes/rt_dependency) is a generic class used to identify the dependency in the event handler. + It ideal to use the [`RtDependency`](/reactter/api/classes/rt_dependency) class when the dependency is not initialize yet. ::: ## Using LifecycleObserver -Extend your instances with `LifecycleObserver` +Extend your instances with [`LifecycleObserver`](/reactter/api/classes/lifecycle_observer) and use its methods to observe the lifecycle events. e.g: diff --git a/website/src/content/docs/es/core_concepts/lifecycle.mdx b/website/src/content/docs/es/core_concepts/lifecycle.mdx new file mode 100644 index 00000000..b31521e4 --- /dev/null +++ b/website/src/content/docs/es/core_concepts/lifecycle.mdx @@ -0,0 +1,141 @@ +--- +title: Cliclo de vida +description: Aprende sobre el ciclo de vida en Reactter. +sidebar: + order: 5 +--- + +import { HE, HM, HT, HS } from '@/components/Highlight'; +import CodeTabs from '@/components/CodeTabs.astro'; +import { Tabs, TabItem } from "@/components/Tabs"; +import ZappButton from "@/components/ZappButton.astro"; +import { Code } from "@astrojs/starlight/components"; +import { marks } from '@/examples/marks.ts' + +import TipLifecycleExample from '@/content/docs/es/shareds/tip_lifecycle_example.mdx'; + +import counterControllerEventHandlerCode from '@/examples/lifecycle_event_handler/lib/counter_controller.dart?raw'; +import counterEventHandlerCode from '@/examples/lifecycle_event_handler/lib/counter.dart?raw'; +import counterViewEventHandlerCode from '@/examples/lifecycle_event_handler/lib/counter_view.dart?raw'; +import counterMainEventHandlerCode from '@/examples/lifecycle_event_handler/lib/main.dart?raw'; + +import counterControllerLifecycleObserverCode from '@/examples/lifecycle_observer/lib/counter_controller.dart?raw'; +import counterLifecycleObserverCode from '@/examples/lifecycle_observer/lib/counter.dart?raw'; +import counterViewLifecycleObserverCode from '@/examples/lifecycle_observer/lib/counter_view.dart?raw'; +import counterMainLifecycleObserverCode from '@/examples/lifecycle_observer/lib/main.dart?raw'; + +import counterControllerLifecycleUseEffectCode from '@/examples/lifecycle_use_effect/lib/counter_controller.dart?raw'; +import counterLifecycleUseEffectCode from '@/examples/lifecycle_use_effect/lib/counter.dart?raw'; +import counterViewLifecycleUseEffectCode from '@/examples/lifecycle_use_effect/lib/counter_view.dart?raw'; +import counterMainLifecycleUseEffectCode from '@/examples/lifecycle_use_effect/lib/main.dart?raw'; + + +En Reactter, tanto los estados como la dependencia (gestionada por la [inyección de dependencia](/reactter/es/core_concepts/dependency_injection)) contienen diferentes etapas, +también conocidas como ciclo de vida (`Lifecycle`). +El ciclo de vida implica eventos emitidos a través del [manejador de eventos](/reactter/es/core_concepts/event_handler). + +Estos son los eventos del ciclo de vida: + +- `Lifecycle.registered`: se activa cuando la dependencia ha sido registrada. +- `Lifecycle.created`: se activa cuando la instancia de la dependencia ha sido creada. +- `Lifecycle.willMount` (exclusivo del paquete _**flutter_reactter**_): se activa cuando la dependencia va a ser montada en el árbol de widgets. +- `Lifecycle.didMount` (exclusivo del paquete _**flutter_reactter**_): se activa después de que la dependencia se haya montado con éxito en el árbol de widgets. +- `Lifecycle.willUpdate`: se activa en cualquier momento en que el estado o la dependencia estén a punto de ser actualizados. El parámetro de evento es un `RtState`. +- `Lifecycle.didUpdate`: se activa en cualquier momento en que el estado o la dependencia ha sido actualizado. El parámetro de evento es un `RtState`. +- `Lifecycle.willUnmount` (exclusivo del paquete _**flutter_reactter**_): se activa cuando la dependencia está a punto de ser desmontada del árbol de widgets. +- `Lifecycle.didUnmount` (exclusivo del paquete _**flutter_reactter**_): se activa cuando la dependencia ha sido desmontada con éxito del árbol de widgets. +- `Lifecycle.deleted`: se activa cuando la instancia de la dependencia ha sido eliminada. +- `Lifecycle.unregistered`: se activa cuando la dependencia ya no está registrada. + +## Usando el manejador de eventos + +Puedes escuchar los eventos del ciclo de vida de una instancia (como una dependencia o un estado) usando el método [`Rt.on`](/reactter/es/api/methods/event_handler_methods/#rton) o [`Rt.one`](/reactter/es/api/methods/event_handler_methods/#rtone) del manejador de eventos. Por ejemplo: + + + + + + + + + counter_view.dart + + + + + counter_controller.dart + + + + + + + + + + + + + +:::note + El [`RtDependency`](/reactter/es/api/classes/rt_dependency) es una clase genérica que se utiliza para identificar la dependencia en el manejador de eventos. + Es ideal usar la clase [`RtDependency`](/reactter/es/api/classes/rt_dependency) cuando la dependencia aún no se ha inicializado. +::: + +## Usando LifecycleObserver + +Extiende tus instancias con [`LifecycleObserver`](/reactter/es/api/classes/lifecycle_observer) para escuchar los eventos del ciclo de vida de una dependencia o estado. Por ejemplo: + + + + + + + + + counter_controller.dart + + + + + + + + + + + + + + + + + +## Usando UseEffect + +El hook [`UseEffect`](/reactter/es/api/hooks/use_effect) se puede usar para escuchar los eventos del ciclo de vida de una dependencia o estado. Por ejemplo: + + + + + + + + + counter_controller.dart + + + + + + + + + + + + + + + + diff --git a/website/src/content/docs/es/shareds/tip_lifecycle_example.mdx b/website/src/content/docs/es/shareds/tip_lifecycle_example.mdx new file mode 100644 index 00000000..c3f73bac --- /dev/null +++ b/website/src/content/docs/es/shareds/tip_lifecycle_example.mdx @@ -0,0 +1,8 @@ +--- +title: Consejo sobre el ejemplo del ciclo de vida +--- + +:::tip + Este ejemplo se toma de la página de [control de renderizado](http://localhost:4321/reactter/es/core_concepts/rendering_control/#example). + Se recomienda leer esa página primero antes de continuar. +::: \ No newline at end of file From 1f9a212edb8256397cfc28eac0aaba53cc862803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Tue, 21 Jan 2025 16:41:28 -0600 Subject: [PATCH 101/141] refactor(website): Add `RtHook` documentation and update state management examples. --- website/astro.config.mjs | 20 ++-- .../docs/api/classes/rt_context_mixin.mdx | 2 +- .../docs/api/classes/rt_dependency.mdx | 2 +- .../api/classes/rt_dependency_observer.mdx | 2 +- .../src/content/docs/api/classes/rt_hooks.mdx | 6 + .../docs/api/classes/rt_state_base.mdx | 2 +- .../docs/api/classes/rt_state_observer.mdx | 2 +- .../src/content/docs/core_concepts/hooks.mdx | 30 ++--- .../content/docs/es/core_concepts/hooks.mdx | 112 ++++++++++++++++++ .../docs/es/shareds/state_methods_lite.mdx | 16 +++ .../docs/shareds/state_methods_lite.mdx | 5 +- .../custom_hook/lib/use_text_input.dart | 5 +- website/src/examples/custom_hook/pubspec.yaml | 2 +- 13 files changed, 170 insertions(+), 36 deletions(-) create mode 100644 website/src/content/docs/api/classes/rt_hooks.mdx create mode 100644 website/src/content/docs/es/core_concepts/hooks.mdx create mode 100644 website/src/content/docs/es/shareds/state_methods_lite.mdx diff --git a/website/astro.config.mjs b/website/astro.config.mjs index cfdbde47..3f9b21ef 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -109,16 +109,6 @@ export default defineConfig({ directory: "api/hooks", }, }, - { - label: "Widgets", - badge: "Flutter", - translations: { - es: "Widgets", - }, - autogenerate: { - directory: "api/widgets", - }, - }, { label: "Classes", translations: { @@ -137,6 +127,16 @@ export default defineConfig({ directory: "api/methods", }, }, + { + label: "Widgets", + badge: "Flutter", + translations: { + es: "Widgets", + }, + autogenerate: { + directory: "api/widgets", + }, + }, { label: "Extensions", translations: { diff --git a/website/src/content/docs/api/classes/rt_context_mixin.mdx b/website/src/content/docs/api/classes/rt_context_mixin.mdx index 196c802f..af577f38 100644 --- a/website/src/content/docs/api/classes/rt_context_mixin.mdx +++ b/website/src/content/docs/api/classes/rt_context_mixin.mdx @@ -2,5 +2,5 @@ title: RtContextMixin 🚧 description: The base class for all context mixins in Reactter. sidebar: - order: 10 + order: 11 --- \ No newline at end of file diff --git a/website/src/content/docs/api/classes/rt_dependency.mdx b/website/src/content/docs/api/classes/rt_dependency.mdx index f9eab78b..01661bf2 100644 --- a/website/src/content/docs/api/classes/rt_dependency.mdx +++ b/website/src/content/docs/api/classes/rt_dependency.mdx @@ -2,5 +2,5 @@ title: RtDependency 🚧 description: Represents dependency managed by Reactter's dependency injection. sidebar: - order: 8 + order: 9 --- \ No newline at end of file diff --git a/website/src/content/docs/api/classes/rt_dependency_observer.mdx b/website/src/content/docs/api/classes/rt_dependency_observer.mdx index 1fee73d1..99de85fb 100644 --- a/website/src/content/docs/api/classes/rt_dependency_observer.mdx +++ b/website/src/content/docs/api/classes/rt_dependency_observer.mdx @@ -2,5 +2,5 @@ title: RtDependencyObserver 🚧 description: The base class for all dependency observers in Reactter. sidebar: - order: 9 + order: 10 --- \ No newline at end of file diff --git a/website/src/content/docs/api/classes/rt_hooks.mdx b/website/src/content/docs/api/classes/rt_hooks.mdx new file mode 100644 index 00000000..88cfa31c --- /dev/null +++ b/website/src/content/docs/api/classes/rt_hooks.mdx @@ -0,0 +1,6 @@ +--- +title: RtHook 🚧 +description: Aprende aceca de los Hooks en Reactter. +sidebar: + order: 6 +--- \ No newline at end of file diff --git a/website/src/content/docs/api/classes/rt_state_base.mdx b/website/src/content/docs/api/classes/rt_state_base.mdx index b876d05d..719cc7cb 100644 --- a/website/src/content/docs/api/classes/rt_state_base.mdx +++ b/website/src/content/docs/api/classes/rt_state_base.mdx @@ -2,5 +2,5 @@ title: RtStateBase 🚧 description: The base class for all states in Reactter. sidebar: - order: 6 + order: 7 --- \ No newline at end of file diff --git a/website/src/content/docs/api/classes/rt_state_observer.mdx b/website/src/content/docs/api/classes/rt_state_observer.mdx index 8875db5e..1232dfde 100644 --- a/website/src/content/docs/api/classes/rt_state_observer.mdx +++ b/website/src/content/docs/api/classes/rt_state_observer.mdx @@ -2,5 +2,5 @@ title: RtStateObserver 🚧 description: The base class for all state observers in Reactter. sidebar: - order: 7 + order: 8 --- \ No newline at end of file diff --git a/website/src/content/docs/core_concepts/hooks.mdx b/website/src/content/docs/core_concepts/hooks.mdx index 8e74c5f1..51a8df3d 100644 --- a/website/src/content/docs/core_concepts/hooks.mdx +++ b/website/src/content/docs/core_concepts/hooks.mdx @@ -4,6 +4,7 @@ description: Learn how to create a custom hook in Reactter. sidebar: order: 6 --- + import { HE, HM, HN, HT } from '@/components/Highlight'; import CodeTabs from '@/components/CodeTabs.astro'; import { Tabs, TabItem } from "@/components/Tabs"; @@ -22,7 +23,7 @@ Hooks are classes with the ability to use states and manage side effects. They a ## API -Reactter provider some hooks to manage the state and side effects of the application, which are: +Reactter provides the following hooks: - [`UseState`](/reactter/api/hooks/use_state) - [`UseAsyncState`](/reactter/api/hooks/use_async_state) @@ -33,18 +34,18 @@ Reactter provider some hooks to manage the state and side effects of the applica ## How it works -Hooks in Reactter are classes that extend `RtHook` and follow a special naming convention with the `Use` prefix. The `RtHook` class is responsible for binding the hook to other hooks and states, and managing the lifecycle of the hook. +Hooks in Reactter are classes that extend [`RtHook`](/reactter/api/classes/rt_hook) and follow a special naming convention with the `Use` prefix. The `RtHook` class is responsible for binding the hook to other hooks and states, and managing the lifecycle of the hook. -Hooks in Reactter are essentially stateful entities because `RtHook` inherits from `RtState` this inheritance allows hooks to manage both state and lifecycle methods efficiently. +Hooks in Reactter are essentially stateful entities because [`RtHook`](/reactter/api/classes/rt_hook) inherits from `RtState` this inheritance allows hooks to manage both state and lifecycle methods efficiently. To manage these aspects, Hooks provide the following: ## Custom hook -Reactter provides a way to create _**Custom Hooks**_ to encapsulate logic that can be reused across the application. +Reactter provides a way to create _**custom hooks**_ to encapsulate logic that can be reused across the application. -There are several advantages to using _**Custom Hooks**_: +There are several advantages to using _**custom hooks**_: - **Reusability**: to use the same hook again and again, without the need to write it twice. - **Clean Code**: extracting part of code into a hook will provide a cleaner codebase. @@ -52,20 +53,14 @@ There are several advantages to using _**Custom Hooks**_: ### Creating a custom hook -To create a _**Custom hook**_, you need to create a class that extends `RtHook` and follow the naming convention with the `Use` prefix. - -:::note -`RtHook` is a class that inherits from `RtState`. -It provides a set of methods to manage the state and lifecycle of the hook. -Learn more about [State methods](/reactter/core_concepts/state_management/#state-methods). -::: +To create a _**custom hook**_, you need to create a class that extends [`RtHook`](/reactter/api/classes/rt_hook) and follow the naming convention with the `Use` prefix. -Here's an example of a _**Custom Hook**_ that manages the state of a text input: +Here's an example of a _**custom hook**_ that manages the state of a text input: :::caution[Attention!!] -To create a _**Custom Hook**_(`RtHook`), you need to register it by adding the following line: +To create a _**hook**_, you need to register it by adding the following line: ```dart showLineNumbers=false final $ = RtHook.$register; @@ -108,7 +103,8 @@ You can then call that _**Custom Hook**_ from anywhere in the code and get acces -In the example above, the form captures first and last name inputs, combines them into a full name, and displays the result. -`MyController` uses `UseTextInput` hook for capturing the first and last name. -The `fullName` state is defined using [`UseCompute`](/reactter/api/hooks/use_compute) to compute the full name based on the values of `firstNameInput` and `lastNameInput`. This ensures the full name is automatically updated whenever either input changes. +In the previous example, the form captures the input from the name and surname fields, combines them into a full name, and displays the result. +The `MyController` component uses the `UseTextInput` hook (_**custom hook**_ created previously) to manage the name and lastname inputs. +The `fullName` state is defined using [`UseCompute`](/reactter/api/hooks/use_compute), which calculates the full name based on the values of `firstNameInput` and `lastNameInput`. +This ensures that the full name updates automatically whenever either input changes. \ No newline at end of file diff --git a/website/src/content/docs/es/core_concepts/hooks.mdx b/website/src/content/docs/es/core_concepts/hooks.mdx new file mode 100644 index 00000000..5232d2e1 --- /dev/null +++ b/website/src/content/docs/es/core_concepts/hooks.mdx @@ -0,0 +1,112 @@ +--- +title: Hooks +description: Aprende aceca de los Hooks en Reactter. +sidebar: + order: 6 +--- + +import { HE, HM, HN, HT } from '@/components/Highlight'; +import CodeTabs from '@/components/CodeTabs.astro'; +import { Tabs, TabItem } from "@/components/Tabs"; +import ZappButton from "@/components/ZappButton.astro"; +import { Code } from "@astrojs/starlight/components"; +import { marks } from '@/examples/marks.ts' + +import StateMethodsLite from '@/content/docs/es/shareds/state_methods_lite.mdx'; + +import MainCode from '@/examples/custom_hook/lib/main.dart?raw'; +import MyCustomFormCode from '@/examples/custom_hook/lib/my_custom_form.dart?raw'; +import MyControllerCode from '@/examples/custom_hook/lib/my_controller.dart?raw'; +import UseTextInputCode from '@/examples/custom_hook/lib/use_text_input.dart?raw'; + +Los hooks son clases con la capacidad de usar estados y gestionar efectos secundarios. +Son un concepto fundamental en Reactter y se utilizan para encapsular la lógica que se puede reutilizar en cualquier parte de la aplicación. + +## API + +Reactter provee los siguientes hooks: + +- [`UseState`](/reactter/es/api/hooks/use_state) +- [`UseAsyncState`](/reactter/es/api/hooks/use_async_state) +- [`UseReducer`](/reactter/es/api/hooks/use_reducer) +- [`UseCompute`](/reactter/es/api/hooks/use_compute) +- [`UseEffect`](/reactter/es/api/hooks/use_effect) +- [`UseDependency`](/reactter/es/api/hooks/use_instance) + +## ¿Cómo funciona? + +Los hooks en Reactter son clases que extienden de [`RtHook`](/reactter/es/api/classes/rt_hook) es responsable de vincular el hook a otros hooks y estados, y de gestionar el ciclo de vida del hook. + +Los hooks en Reactter son esencialmente entidades con estado porque [`RtHook`](/reactter/es/api/classes/rt_hook) hereda de `RtState` esta herencia permite a los hooks gestionar tanto el estado como los métodos del ciclo de vida de manera eficiente. + +Para gestionar estos aspectos, los Hooks proporcionan lo siguiente: + + + +## Hook personalizado + +Reactter proporciona una forma de crear _**hooks personalizados**_ para encapsular la lógica que se puede reutilizar en cualquier parte de la aplicación. + +Hay varias ventajas en usar _**hoks personalizados**_: + +- **Reusabilidad**: para usar el mismo hook una y otra vez, sin necesidad de escribirlo dos veces. +- **Código limpio**: extraer parte del código en un hook proporcionará una base de código más limpia. +- **Mantenibilidad**: más fácil de mantener. si necesita cambiar la lógica del hook, solo necesita cambiarla una vez. + +### Crear un hook personalizado + +Para crear un _**hook personalizado**_, debes crear una clase que extienda de [`RtHook`](/reactter/es/api/classes/rt_hook) y seguir la convención de nomenclatura con el prefijo `Use`. + +Aquí tienes un ejemplo de un _**hook personalizado**_ que gestiona el estado de un campo de texto: + + + +:::caution[¡Atención!] +Para crear un _**hook**_, debes registrarlo agregando la siguiente línea: + +```dart showLineNumbers=false +final $ = RtHook.$register; +``` + +Es importante tener en cuenta que los estados y hooks definidos por encima de esta línea no estarán vinculados al hook. +Para evitar esto, se recomienda colocar esta línea como la primera línea en el cuerpo de la clase. +::: + +Como se muestra en el ejemplo anterior, podemos utilizar otros hooks como [`UseEffect`](/reactter/es/api/hooks/use_effect) para monitorear los cambios en el controlador del campo de texto y asegurarnos de que se elimine cuando el hook se destruya. + +El método `update` se utiliza para establecer el `_value` interno en el texto actual del controlador, lo que mantiene el estado sincronizado con el campo de texto. +Este método es parte de la clase `RtHook` que te permite actualizar el estado del hook. + +### Usar un hook personalizado + +Los _**hook personalizados**_ pueden ser llamados desde cualquier parte del código y acceder a su lógica compartida, por ejemplo: + + + + + + + my_controller.dart + + + + + my_custom_form.dart + + + + + + + + + + + + + +En el ejemplo anterior, el formulario recopila los datos ingresados en los campos de nombre y apellido, los combina en un nombre completo y muestra el resultado. +`MyController` utiliza el hook `UseTextInput` (hook personalizado creado previamente) para capturar los valores de nombre y apellido. + +El estado `fullName` se define mediante [`UseCompute`](/reactter/es/api/hooks/use_compute), lo que permite calcular el nombre completo en función de los valores de `firstNameInput` y `lastNameInput`. +Esto garantiza que el nombre completo se actualice automáticamente cada vez que se modifiquen las entradas \ No newline at end of file diff --git a/website/src/content/docs/es/shareds/state_methods_lite.mdx b/website/src/content/docs/es/shareds/state_methods_lite.mdx new file mode 100644 index 00000000..0516696d --- /dev/null +++ b/website/src/content/docs/es/shareds/state_methods_lite.mdx @@ -0,0 +1,16 @@ +--- +title: State Methods +--- + +import { HM, HT } from '@/components/Highlight'; + +- Metodos heredados de `RtState`(Aprende más [aquí](/reactter/es/core_concepts/state_management/#state-methods)): + - `update`: Un método para notificar cambios después de ejecutar un conjunto de instrucciones. + - `notify`: Un método para forzar a notificar cambios. + - *`bind`: Un método para vincular una instancia a él. + - *`unbind`: Un método para desvincular una instancia de él. + - *`dispose`: Un método para eliminar todos los oyentes y liberar recursos. + + :::note + \* Estos métodos son innecesarios cuando se declara dentro de una dependencia(clase registrada a través de la [inyección de dependencia](/reactter/core_concepts/dependency_injection)). + ::: diff --git a/website/src/content/docs/shareds/state_methods_lite.mdx b/website/src/content/docs/shareds/state_methods_lite.mdx index 3da103ef..16597a4c 100644 --- a/website/src/content/docs/shareds/state_methods_lite.mdx +++ b/website/src/content/docs/shareds/state_methods_lite.mdx @@ -1,15 +1,16 @@ --- title: State Methods --- + import { HM, HT } from '@/components/Highlight'; - Methods inherited from `RtState`(Learn more [here](/reactter/core_concepts/state_management/#state-methods)): - `update`: A method to notify changes after run a set of instructions. - - `refresh`: A method to force to notify changes. + - `notify`: A method to force to notify changes. - *`bind`: A method to bind an instance to it. - *`unbind`: A method to unbind an instance to it. - *`dispose`: A method to remove all listeners and free resources. :::note \* These methods are unnecessary when is declared within a dependency(class registered via the [dependecy injection](/reactter/core_concepts/dependency_injection)). - ::: \ No newline at end of file + ::: diff --git a/website/src/examples/custom_hook/lib/use_text_input.dart b/website/src/examples/custom_hook/lib/use_text_input.dart index 120f090f..228c9b78 100644 --- a/website/src/examples/custom_hook/lib/use_text_input.dart +++ b/website/src/examples/custom_hook/lib/use_text_input.dart @@ -10,7 +10,8 @@ class UseTextInput extends RtHook { String _value = ''; String? get value => _value; - UseTextInput() { + @override + initHook() { UseEffect(() { controller.addListener(() { update(() => _value = controller.text); @@ -18,5 +19,7 @@ class UseTextInput extends RtHook { return controller.dispose; }, []); + + super.initHook(); } } diff --git a/website/src/examples/custom_hook/pubspec.yaml b/website/src/examples/custom_hook/pubspec.yaml index 0fc26139..09a2e0bb 100644 --- a/website/src/examples/custom_hook/pubspec.yaml +++ b/website/src/examples/custom_hook/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.19 flutter: uses-material-design: true From 8f041eeb4471c824c5953da83fb93094d5194f77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Tue, 21 Jan 2025 17:20:14 -0600 Subject: [PATCH 102/141] refactor(website): Join `BuildContext` extensions documentation. --- .../api/extensions/_build_context_select.mdx | 97 ------ .../api/extensions/_build_context_use.mdx | 63 ---- .../api/extensions/_build_context_watch.mdx | 88 ------ .../extensions/_build_context_watch_id.mdx | 78 ----- .../extensions/build_context_extensions.mdx | 285 +++++++++++++++++- 5 files changed, 283 insertions(+), 328 deletions(-) delete mode 100644 website/src/content/docs/api/extensions/_build_context_select.mdx delete mode 100644 website/src/content/docs/api/extensions/_build_context_use.mdx delete mode 100644 website/src/content/docs/api/extensions/_build_context_watch.mdx delete mode 100644 website/src/content/docs/api/extensions/_build_context_watch_id.mdx diff --git a/website/src/content/docs/api/extensions/_build_context_select.mdx b/website/src/content/docs/api/extensions/_build_context_select.mdx deleted file mode 100644 index 7c5f8551..00000000 --- a/website/src/content/docs/api/extensions/_build_context_select.mdx +++ /dev/null @@ -1,97 +0,0 @@ ---- -title: "BuildContext.select" -description: "Learn how to select a value in a BuildContext in Reactter." -sidebar: - order: 4 ---- - -import { HM, HT, HN } from '@/components/Highlight'; -import MDXRedry from '@/components/MDXRedry.astro'; -import CodeTabs from '@/components/CodeTabs.astro'; -import { Tabs, TabItem } from "@/components/Tabs"; -import ZappButton from "@/components/ZappButton.astro"; -import { Code } from "@astrojs/starlight/components"; -import { marks } from '@/examples/marks.ts' - -import * as WatchVsSelectComparation from '@/content/docs/shareds/watch_vs_select_comparation.mdx'; -import counterControllerCode from '@/examples/build_context_select/lib/counter_controller.dart?raw'; -import counterCode from '@/examples/build_context_select/lib/counter.dart?raw'; -import counterDivisibleCode from '@/examples/build_context_select/lib/counter_divisible.dart?raw'; -import counterViewCode from '@/examples/build_context_select/lib/counter_view.dart?raw'; -import mainCode from '@/examples/build_context_select/lib/main.dart?raw'; - -`BuildContext.select` is a method that computes a value whenever the selected states change and registers the current widget as a listener for these changes. -Consequently, each time the selected states change, the value is recalculated and if it is different from the previous one, the widget is automatically notified and rebuilt. - -:::tip - -`BuildContext.select` is useful when you need to render using a computed value based on multiple states. -This aproach is more efficient than using [`BuildContext.watch`](/reactter/api/extensions/build_context_watch) because it avoids unnecessary rebuilds. - - -::: - -## Syntax & description - -```dart showLineNumbers=false -V context.select( - V computeValue(T instance, RtState select(RtState state)), - [String? id], -); -``` - -- `context`: The `BuildContext` object, which provides information about the current widget in the tree. -- `T`: The type of the dependency you want to select. It is used to locate the dependency from the nearest ancestor provider. -- `V`: The type of the value you want to compute. It is the return type of the `computeValue` function. -- `computeValue`: A function that computes the value based on the selected states. It takes two arguments: - - `instance`: The instance of the dependency of type `T`. - - `select`: A function that allows you to wrap the state(`RtState`) to be listened for changes and returns it. -- `id`: An optional identifier for the `T` dependency. If omitted, the dependency will be located by its type (`T`). - -:::tip -It's a good idea to name the select parameter to something more simple and shorter like `$` to make it easier to read and understand the `selector` function when you have multiple states to select. - -```dart showLineNumbers=false {2} -RtSelector( - selector: (inst, $) => $(inst.firstName).value + ' ' + $(inst.lastName).value, - builder: (context, inst, fullName, child) { - return Text("Full name: $fullName"); - }, -) -``` -::: - -## Usage - -This following example demonstrates how to use `BuildContext.select`: - - - - - - - counter_divisible.dart - - - - - counter_view.dart - - - - - - - - - - - - - - - - - -In this example, checks if the `count` state from `CounterController` is divisible by a specified number (`byNum`). -The `BuildContext.select` method is used to perform this check and rebuild the widget tree whenever the divisibility status changes. \ No newline at end of file diff --git a/website/src/content/docs/api/extensions/_build_context_use.mdx b/website/src/content/docs/api/extensions/_build_context_use.mdx deleted file mode 100644 index 2658b340..00000000 --- a/website/src/content/docs/api/extensions/_build_context_use.mdx +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: BuildContext.use -description: Learn how to use a value in a BuildContext in Reactter. -sidebar: - order: 1 ---- - -import { HM, HT, HN } from '@/components/Highlight'; -import CodeTabs from '@/components/CodeTabs.astro'; -import { Tabs, TabItem } from "@/components/Tabs"; -import ZappButton from "@/components/ZappButton.astro"; -import { Code } from "@astrojs/starlight/components"; -import { marks } from '@/examples/marks.ts' - -import counterControllerCode from '@/examples/build_context/lib/counter_controller.dart?raw'; -import counterCode from '@/examples/build_context/lib/counter.dart?raw'; -import counterViewCode from '@/examples/build_context/lib/counter_view.dart?raw'; -import mainCode from '@/examples/build_context/lib/main.dart?raw'; - -`BuildContext.use` is a method that gets an instance of `T` dependency from the nearest ancestor provider([`RtProvider`](/reactter/api/widgets/rt_provider) or [`RtComponent`](/reactter/api/widgets/rt_component)) of type `T`. - -## Syntax & description - -```dart showLineNumbers=false -T context.use([String? id]); -``` - -- `context`: The `BuildContext` object, which provides information about the current widget in the tree. -- `T`: The type of the dependency you want to access. -These are some points to consider: - - If the type is nullable, the method will return `null` if the dependency is not found. - - If the type is not nullable, the method will throw an exception if the dependency is not found. -- `id`: An optional identifier for the `T` dependency. If omitted, the dependency will be located by its type(`T`). - -## Usage - -This following example demonstrates how to use `BuildContext.use`: - - - - - - - counter.dart - - - - - counter_view.dart - - - - - - - - - - - - - -In this example, the `Counter` widget uses `BuildContext.use` to get the instance of `CounterController` from the nearest ancestor provider(located in `main.dart`). \ No newline at end of file diff --git a/website/src/content/docs/api/extensions/_build_context_watch.mdx b/website/src/content/docs/api/extensions/_build_context_watch.mdx deleted file mode 100644 index 9c8f206c..00000000 --- a/website/src/content/docs/api/extensions/_build_context_watch.mdx +++ /dev/null @@ -1,88 +0,0 @@ ---- -title: BuildContext.watch -description: Learn how to watch a value in a BuildContext in Reactter. -sidebar: - order: 2 ---- - -import { HM, HT, HN } from '@/components/Highlight'; -import MDXRedry from '@/components/MDXRedry.astro'; -import CodeTabs from '@/components/CodeTabs.astro'; -import { Tabs, TabItem } from "@/components/Tabs"; -import ZappButton from "@/components/ZappButton.astro"; -import { Code } from "@astrojs/starlight/components"; -import { marks } from '@/examples/marks.ts' - -import * as WatchVsSelectComparation from '@/content/docs/shareds/watch_vs_select_comparation.mdx'; -import counterControllerCode from '@/examples/build_context/lib/counter_controller.dart?raw'; -import counterCode from '@/examples/build_context/lib/counter.dart?raw'; -import counterViewCode from '@/examples/build_context/lib/counter_view.dart?raw'; -import mainCode from '@/examples/build_context/lib/main.dart?raw'; - -`BuildContext.watch` is a method similar to [`BuildContext.use`](/reactter/api/extensions/build_context_use), but with an added functionality. -It not only retrieves the `T` dependency but also registers the current widget as a listener for any changes to the states set or otherwise any changes in the `T` dependency. -Consequently, whenever changes, the widget using `BuildContext.watch` is automatically notified and rebuilt. - -:::tip -You can optionally use `BuildContext.watch` with an `id`, but when you need to listen changes in a dependency's states using an `id`, it's better to use [`BuildContext.watchId`](/reactter/api/extensions/build_context_watch_id) for improved readability and clarity. - -```dart showLineNumbers=false -context.watch(null, 'myId'); -// is equivalent to -context.watchId('myId'); -``` -::: - -:::caution -To render using a computed value based on multiple states, use [`BuildContext.select`](/reactter/api/extensions/build_context_select) instead of `BuildContext.watch` or pre-compute the value using [`UseCompute`](/reactter/api/hooks/use_compute) to prevent unnecessary rebuilds. - - -::: - - -## Syntax & description - -```dart showLineNumbers=false -T context.watch([ - List listenStates?(T instance), - String? id, -]); -``` - -- `context`: The `BuildContext` object, which provides information about the current widget in the tree. -- `T`: The type of the dependency you want to access. -These are some points to consider: - - If the type is nullable, the method will return `null` if the dependency is not found. - - If the type is not nullable, the method will throw an exception if the dependency is not found. -- `listenStates`: An optional function that returns a list of state(`RtState`) to listen for changes. If omitted, the method will listen for any changes in the `T` dependency. -- `id`: An optional identifier for the `T` dependency. If omitted, the dependency will be located by its type(`T`). - -## Usage - -This following example demonstrates how to use `BuildContext.watch`: - - - - - - - counter.dart - - - - - counter_view.dart - - - - - - - - - - - - - -In this example, we use `BuildContext.watch` to get the instance of `CounterController` from the nearest ancestor provider(located in `main.dart`) and listen for changes in the `count` state. diff --git a/website/src/content/docs/api/extensions/_build_context_watch_id.mdx b/website/src/content/docs/api/extensions/_build_context_watch_id.mdx deleted file mode 100644 index a9fb11fe..00000000 --- a/website/src/content/docs/api/extensions/_build_context_watch_id.mdx +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: BuildContext.watchId -description: Learn how to watch using id in a BuildContext in Reactter. -sidebar: - order: 3 ---- - -import { HM, HT, HN } from '@/components/Highlight'; -import CodeTabs from '@/components/CodeTabs.astro'; -import { Tabs, TabItem } from "@/components/Tabs"; -import ZappButton from "@/components/ZappButton.astro"; -import { Code } from "@astrojs/starlight/components"; -import { marks } from '@/examples/marks.ts' - -import counterControllerCode from '@/examples/build_context/lib/counter_controller.dart?raw'; -import counterCode from '@/examples/build_context/lib/counter.dart?raw'; -import counterViewCode from '@/examples/build_context/lib/counter_view.dart?raw'; -import mainCode from '@/examples/build_context/lib/main.dart?raw'; - -`BuildContext.watchId` is a method similar to [`BuildContext.watch`](/reactter/api/extensions/build_context_watch), but use `id` as first argument to locate the dependency. - -:::tip -This approach is useful when you have multiple dependencies of the same type and you want to access a specific one. - -While you can use [`BuildContext.watch`](/reactter/api/extensions/build_context_watch) with `id`, `BuildContext.watchId` is more readable and clear when an `id` is required. - -```dart showLineNumbers=false -context.watch(null, 'myId'); -// is equivalent to -context.watchId('myId'); -``` -::: - -## Syntax & description - -```dart showLineNumbers=false -T context.watchId( - String id, - [List listenStates?(T instance)], -); -``` - -- `context`: The `BuildContext` object, which provides information about the current widget in the tree. -- `T`: The type of the dependency you want to access. These are some points to consider: - - If the type is nullable, the method will return `null` if the dependency is not found. - - If the type is not nullable, the method will throw an exception if the dependency is not found. -- `id`: An identifier for the `T` dependency. The dependency will be located by its type(`T`) and the `id`. -- `listenStates`: An optional function that returns a list of state(`RtState`) to listen for changes. If omitted, the method will listen for any changes in the `T` dependency. - -## Usage - -This following example demonstrates how to use `BuildContext.watchId`: - - - - - - - counter.dart - - - - - counter_view.dart - - - - - - - - - - - - - -In this example, the `Counter` widget uses `BuildContext.watchId` to get the instance of `CounterController` by its `id` from the nearest ancestor provider(located in `main.dart`) and listen for changes in the `count` state. diff --git a/website/src/content/docs/api/extensions/build_context_extensions.mdx b/website/src/content/docs/api/extensions/build_context_extensions.mdx index a13f5b19..76a5d9b2 100644 --- a/website/src/content/docs/api/extensions/build_context_extensions.mdx +++ b/website/src/content/docs/api/extensions/build_context_extensions.mdx @@ -1,8 +1,289 @@ --- -title: BuildContext Extensions 🚧 +title: BuildContext Extensions description: The extensions for the BuildContext class in Reactter. sidebar: order: 1 badge: text: Flutter ---- \ No newline at end of file +--- + +import { HM, HT, HN } from '@/components/Highlight'; +import MDXRedry from '@/components/MDXRedry.astro'; +import CodeTabs from '@/components/CodeTabs.astro'; +import { Tabs, TabItem } from "@/components/Tabs"; +import ZappButton from "@/components/ZappButton.astro"; +import { Code } from "@astrojs/starlight/components"; +import { marks } from '@/examples/marks.ts' + +import counterControllerCode from '@/examples/build_context/lib/counter_controller.dart?raw'; +import counterCode from '@/examples/build_context/lib/counter.dart?raw'; +import counterViewCode from '@/examples/build_context/lib/counter_view.dart?raw'; +import counterDivisibleCode from '@/examples/build_context_select/lib/counter_divisible.dart?raw'; +import mainCode from '@/examples/build_context/lib/main.dart?raw'; +import * as WatchVsSelectComparation from '@/content/docs/shareds/watch_vs_select_comparation.mdx'; + +The `BuildContext` class in Flutter provides information about the current widget in the tree. +Reactter extends the `BuildContext` class with additional methods to make it easier to work with dependencies. + +Reactter provides the following extensions for the `BuildContext` class: + +- [`BuildContext.use`](#buildcontextuse) +- [`BuildContext.watch`](#buildcontextwatch) +- [`BuildContext.watchId`](#buildcontextwatchid) +- [`BuildContext.select`](#buildcontextselect) + +## `BuildContext.use` + +`BuildContext.use` is a method that gets an instance of `T` dependency from the nearest ancestor provider ([`RtProvider`](/reactter/api/widgets/rt_provider) or [`RtComponent`](/reactter/api/widgets/rt_component)) of type `T`. + +### Syntax + +```dart showLineNumbers=false +T context.use([String? id]); +``` + +- `context`: The `BuildContext` object, which provides information about the current widget in the tree. +- `T`: The type of the dependency you want to access. +These are some points to consider: + - If the type is nullable, the method will return `null` if the dependency is not found. + - If the type is not nullable, the method will throw an exception if the dependency is not found. +- `id`: An optional identifier for the `T` dependency. If omitted, the dependency will be located by its type (`T`). + +### Usage + +This following example demonstrates how to use `BuildContext.use`: + + + + + + + counter.dart + + + + + counter_view.dart + + + + + + + + + + + + + +In this example, the `Counter` widget uses `BuildContext.use` to get the instance of `CounterController` from the nearest ancestor provider (located in `main.dart`). + +## `BuildContext.watch` + +`BuildContext.watch` is a method similar to [`BuildContext.use`](/reactter/api/extensions/build_context_use), but with an added functionality. +It not only retrieves the `T` dependency but also registers the current widget as a listener for any changes to the states set or otherwise any changes in the `T` dependency. +Consequently, whenever changes, the widget using `BuildContext.watch` is automatically notified and rebuilt. + +:::tip +You can optionally use `BuildContext.watch` with an `id`, but when you need to listen changes in a dependency's states using an `id`, it's better to use [`BuildContext.watchId`](/reactter/api/extensions/build_context_watch_id) for improved readability and clarity. + +```dart showLineNumbers=false +context.watch(null, 'myId'); +// is equivalent to +context.watchId('myId'); +``` +::: + +:::caution +To render using a computed value based on multiple states, use [`BuildContext.select`](/reactter/api/extensions/build_context_select) instead of `BuildContext.watch` or pre-compute the value using [`UseCompute`](/reactter/api/hooks/use_compute) to prevent unnecessary rebuilds. + + +::: + +### Syntax + +```dart showLineNumbers=false +T context.watch([ + List listenStates?(T instance), + String? id, +]); +``` + +- `context`: The `BuildContext` object, which provides information about the current widget in the tree. +- `T`: The type of the dependency you want to access. +These are some points to consider: + - If the type is nullable, the method will return `null` if the dependency is not found. + - If the type is not nullable, the method will throw an exception if the dependency is not found. +- `listenStates`: An optional function that returns a list of state (`RtState`) to listen for changes. If omitted, the method will listen for any changes in the `T` dependency. +- `id`: An optional identifier for the `T` dependency. If omitted, the dependency will be located by its type (`T`). + +### Usage + +This following example demonstrates how to use `BuildContext.watch`: + + + + + + + counter.dart + + + + + counter_view.dart + + + + + + + + + + + + + +In this example, we use `BuildContext.watch` to get the instance of `CounterController` from the nearest ancestor provider (located in `main.dart`) and listen for changes in the `count` state. + +## `BuildContext.watchId` + + +`BuildContext.watchId` is a method similar to [`BuildContext.watch`](/reactter/api/extensions/build_context_watch), but use `id` as first argument to locate the dependency. + +:::tip +This approach is useful when you have multiple dependencies of the same type and you want to access a specific one. + +While you can use [`BuildContext.watch`](/reactter/api/extensions/build_context_watch) with `id`, `BuildContext.watchId` is more readable and clear when an `id` is required. + +```dart showLineNumbers=false +context.watch(null, 'myId'); +// is equivalent to +context.watchId('myId'); +``` +::: + +### Syntax + +```dart showLineNumbers=false +T context.watchId( + String id, + [List listenStates?(T instance)], +); +``` + +- `context`: The `BuildContext` object, which provides information about the current widget in the tree. +- `T`: The type of the dependency you want to access. These are some points to consider: + - If the type is nullable, the method will return `null` if the dependency is not found. + - If the type is not nullable, the method will throw an exception if the dependency is not found. +- `id`: An identifier for the `T` dependency. The dependency will be located by its type (`T`) and the `id`. +- `listenStates`: An optional function that returns a list of state (`RtState`) to listen for changes. If omitted, the method will listen for any changes in the `T` dependency. + +### Usage + +This following example demonstrates how to use `BuildContext.watchId`: + + + + + + + counter.dart + + + + + counter_view.dart + + + + + + + + + + + + + +In this example, the `Counter` widget uses `BuildContext.watchId` to get the instance of `CounterController` by its `id` from the nearest ancestor provider (located in `main.dart`) and listen for changes in the `count` state. + +## `BuildContext.select` + +`BuildContext.select` is a method that computes a value whenever the selected states change and registers the current widget as a listener for these changes. +Consequently, each time the selected states change, the value is recalculated and if it is different from the previous one, the widget is automatically notified and rebuilt. + +:::tip +`BuildContext.select` is useful when you need to render using a computed value based on multiple states. +This aproach is more efficient than using [`BuildContext.watch`](/reactter/api/extensions/build_context_watch) because it avoids unnecessary rebuilds. + + +::: + +### Syntax + +```dart showLineNumbers=false +V context.select( + V computeValue(T instance, RtState select(RtState state)), + [String? id], +); +``` + +- `context`: The `BuildContext` object, which provides information about the current widget in the tree. +- `T`: The type of the dependency you want to select. It is used to locate the dependency from the nearest ancestor provider. +- `V`: The type of the value you want to compute. It is the return type of the `computeValue` function. +- `computeValue`: A function that computes the value based on the selected states. It takes two arguments: + - `instance`: The instance of the dependency of type `T`. + - `select`: A function that allows you to wrap the state (`RtState`) to be listened for changes and returns it. +- `id`: An optional identifier for the `T` dependency. If omitted, the dependency will be located by its type (`T`). + +:::tip +It's a good idea to name the select parameter to something more simple and shorter like `$` to make it easier to read and understand the `selector` function when you have multiple states to select. + +```dart showLineNumbers=false {2} +RtSelector( + selector: (inst, $) => $(inst.firstName).value + ' ' + $(inst.lastName).value, + builder: (context, inst, fullName, child) { + return Text("Full name: $fullName"); + }, +) +``` +::: + +### Usage + +This following example demonstrates how to use `BuildContext.select`: + + + + + + + counter_divisible.dart + + + + + counter_view.dart + + + + + + + + + + + + + + + + + +In this example, checks if the `count` state from `CounterController` is divisible by a specified number (`byNum`). +The `BuildContext.select` method is used to perform this check and rebuild the widget tree whenever the divisibility status changes. \ No newline at end of file From 037cbb2ddaf5acdad234474bf0ee5b69c84134b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Fri, 31 Jan 2025 23:22:32 -0600 Subject: [PATCH 103/141] refactor(website): Update documentation and improve DevTools integration --- CHANGELOG.md | 2 +- .../flutter_reactter/example/lib/main.dart | 1 + .../extensions/build_context_extension.dart | 2 +- .../lib/src/widgets/rt_selector.dart | 4 +- .../lib/src/framework/rt_state_base.dart | 4 +- .../lib/src/hooks/use_async_state.dart | 8 +- .../reactter/lib/src/hooks/use_effect.dart | 1 + website/astro.config.mjs | 4 +- .../public/devtools_extension/devtools.png | Bin 0 -> 413167 bytes .../devtools_extension/enable_button.png | Bin 0 -> 80815 bytes .../devtools_extension/extension_tab.png | Bin 0 -> 6695 bytes .../devtools_extension/reactter_extension.png | Bin 0 -> 119584 bytes website/src/components/Highlight/index.ts | 2 +- .../extensions/build_context_extensions.mdx | 2 +- .../docs/api/hooks/use_async_state.mdx | 112 ++++++++++++------ .../content/docs/api/hooks/use_compute.mdx | 4 +- .../content/docs/api/hooks/use_dependency.mdx | 47 ++++++-- .../src/content/docs/api/hooks/use_effect.mdx | 7 +- .../content/docs/api/hooks/use_reducer.mdx | 4 +- .../src/content/docs/api/hooks/use_state.mdx | 9 +- .../content/docs/api/methods/core_methods.mdx | 6 - .../docs/api/methods/debugging_methods.mdx | 16 +++ .../methods/dependency_injection_methods.mdx | 24 ++-- .../api/methods/event_handler_methods.mdx | 2 +- .../api/methods/state_management_methods.mdx | 2 +- .../content/docs/api/widgets/rt_component.mdx | 6 +- .../content/docs/api/widgets/rt_consumer.mdx | 12 +- .../docs/api/widgets/rt_multi_provider.mdx | 2 +- .../content/docs/api/widgets/rt_provider.mdx | 2 +- .../content/docs/api/widgets/rt_selector.mdx | 4 +- .../docs/api/widgets/rt_signal_watcher.mdx | 2 +- .../src/content/docs/devtools_extension.mdx | 48 +++++++- .../docs/shareds/listening_changes_state.mdx | 4 +- .../docs/shareds/state_methods_lite.mdx | 14 +-- 34 files changed, 238 insertions(+), 119 deletions(-) create mode 100644 website/public/devtools_extension/devtools.png create mode 100644 website/public/devtools_extension/enable_button.png create mode 100644 website/public/devtools_extension/extension_tab.png create mode 100644 website/public/devtools_extension/reactter_extension.png delete mode 100644 website/src/content/docs/api/methods/core_methods.mdx create mode 100644 website/src/content/docs/api/methods/debugging_methods.mdx diff --git a/CHANGELOG.md b/CHANGELOG.md index d57fed15..d312f552 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,7 +29,7 @@ - Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtStateBase-class.html) instead. - Replace [`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html) to `UseAsyncStateStatus.idle`. - Rename [`DispatchEffect`](https://pub.dev/documentation/reactter/7.3.0/reactter/DispatchEffect-class.html) to [`AutoDispatchEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/DispatchEffect-class.html). -- Move `asyncFunction` to the first argument of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/UseAsyncState/withArg.html). +- Move `asyncFunction` to the first parameter of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/UseAsyncState/withArg.html). - Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. - Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/isActive.html) instead. - Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/getDependencyMode.html) instead. diff --git a/packages/flutter_reactter/example/lib/main.dart b/packages/flutter_reactter/example/lib/main.dart index e2d51515..b3b7ea50 100644 --- a/packages/flutter_reactter/example/lib/main.dart +++ b/packages/flutter_reactter/example/lib/main.dart @@ -97,5 +97,6 @@ class MyApp extends StatelessWidget { } void main() { + Rt.initializeDevTools(); runApp(const MyApp()); } diff --git a/packages/flutter_reactter/lib/src/extensions/build_context_extension.dart b/packages/flutter_reactter/lib/src/extensions/build_context_extension.dart index d0438318..cfad86f0 100644 --- a/packages/flutter_reactter/lib/src/extensions/build_context_extension.dart +++ b/packages/flutter_reactter/lib/src/extensions/build_context_extension.dart @@ -8,7 +8,7 @@ extension ReactterBuildContextExtension on BuildContext { /// This evaluation only occurs if one of the selected [RtState]s gets updated, /// or by the dependency if the [selector] does not have any selected [RtState]s. /// - /// The [selector] callback has a two arguments, the first one is + /// The [selector] callback has two parameters, the first one is /// the dependency of [T] type which is obtained from the closest ancestor /// of [RtProvider] and the second one is a [Select] function which /// allows to wrapper any [RtState]s to listen. diff --git a/packages/flutter_reactter/lib/src/widgets/rt_selector.dart b/packages/flutter_reactter/lib/src/widgets/rt_selector.dart index b59085f9..b1c64277 100644 --- a/packages/flutter_reactter/lib/src/widgets/rt_selector.dart +++ b/packages/flutter_reactter/lib/src/widgets/rt_selector.dart @@ -10,7 +10,7 @@ part of '../widgets.dart'; /// This evaluation only occurs if one of the selected [RtState]s gets updated, /// or by the dependency if the [selector] does not have any selected [RtState]s. /// -/// The [selector] property has a two arguments, the first one is the instance +/// The [selector] property has two parameters, the first one is the instance /// of [T] dependency which is obtained from the closest ancestor [RtProvider]. /// and the second one is a [Select] function which allows to wrapper any /// [RtState]s to listen, and returns the value in each build. e.g: @@ -136,7 +136,7 @@ class RtSelector extends StatelessWidget { /// This evaluation only occurs if one of the selected [RtState]s gets updated, /// or by the dependency if the [selector] does not have any selected [RtState]s. /// - /// The [selector] callback has a two arguments, the first one is + /// The [selector] callback has two parameters, the first one is /// the instance of [T] dependency which is obtained from the closest ancestor /// of [RtProvider] and the second one is a [Select] function which /// allows to wrapper any [RtState]s to listen. diff --git a/packages/reactter/lib/src/framework/rt_state_base.dart b/packages/reactter/lib/src/framework/rt_state_base.dart index 6350747b..d79d56ad 100644 --- a/packages/reactter/lib/src/framework/rt_state_base.dart +++ b/packages/reactter/lib/src/framework/rt_state_base.dart @@ -103,12 +103,12 @@ abstract class RtStateBase> implements RtState { /// {@macro reactter.istate.update} /// - /// The [fnUpdate] must be a function without arguments(Function()). + /// The [fnUpdate] must be a function without parameters(Function()). void update(covariant Function? fnUpdate) { assert(!_isDisposed, "Can't update when it's been disposed"); assert( fnUpdate is Function(), - "The fnUpdate must be a function without arguments", + "The fnUpdate must be a function without parameters", ); if (_isUpdating || !_hasListeners) { diff --git a/packages/reactter/lib/src/hooks/use_async_state.dart b/packages/reactter/lib/src/hooks/use_async_state.dart index 4745cc2a..723ee8a0 100644 --- a/packages/reactter/lib/src/hooks/use_async_state.dart +++ b/packages/reactter/lib/src/hooks/use_async_state.dart @@ -127,7 +127,7 @@ abstract class UseAsyncStateBase extends RtHook { /// Returns a new value of [R] depending on the state of the hook: /// - /// `standby`: When the state has the initial value. + /// `idle`: When the state has the initial value. /// `loading`: When the request for the state is retrieving the value. /// `done`: When the request is done. /// `error`: If any errors happens in the request. @@ -136,7 +136,7 @@ abstract class UseAsyncStateBase extends RtHook { /// /// ```dart /// final valueComputed = appController.asyncState.when( - /// standby: (value) => "⚓️ Standby: ${value}", + /// idle: (value) => "⚓️ Idle: ${value}", /// loading: (value) => "⏳ Loading...", /// done: (value) => "✅ Resolved: ${value}", /// error: (error) => "❌ Error: ${error}", @@ -210,7 +210,7 @@ abstract class UseAsyncStateBase extends RtHook { /// /// ```dart /// final valueComputed = appController.asyncState.when( -/// standby: (value) => "⚓️ Standby: $value", +/// idle: (value) => "⚓️ Standby: $value", /// loading: (value) => "⏳ Loading...", /// done: (value) => "✅ Resolved: $value", /// error: (error) => "❌ Error: $error", @@ -292,7 +292,7 @@ class UseAsyncState extends UseAsyncStateBase { /// /// ```dart /// final valueComputed = appController.asyncState.when( -/// standby: (value) => "⚓️ Standby: $value", +/// idle: (value) => "⚓️ Standby: $value", /// loading: (value) => "⏳ Loading...", /// done: (value) => "✅ Resolved: $value", /// error: (error) => "❌ Error: $error", diff --git a/packages/reactter/lib/src/hooks/use_effect.dart b/packages/reactter/lib/src/hooks/use_effect.dart index e83f17af..06168aaa 100644 --- a/packages/reactter/lib/src/hooks/use_effect.dart +++ b/packages/reactter/lib/src/hooks/use_effect.dart @@ -314,4 +314,5 @@ class UseEffect extends RtHook { } } +/// A mixin to execute the effect on initialization. abstract class AutoDispatchEffect {} diff --git a/website/astro.config.mjs b/website/astro.config.mjs index 3f9b21ef..5ef217cf 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -74,10 +74,10 @@ export default defineConfig({ }, }, { - label: "DevTools extension 🚧", + label: "DevTools extension", link: "/devtools_extension", translations: { - es: "Extensión de DevTools 🚧", + es: "Extensión de DevTools", }, }, ], diff --git a/website/public/devtools_extension/devtools.png b/website/public/devtools_extension/devtools.png new file mode 100644 index 0000000000000000000000000000000000000000..2130726f6b8bf6ebc37b443d1c12cad7181a041b GIT binary patch literal 413167 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 z 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

38A^>wCf`=$hgIBH`2 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

_`PcMv=4*XU7hn5 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 literal 0 HcmV?d00001 diff --git a/website/public/devtools_extension/enable_button.png b/website/public/devtools_extension/enable_button.png new file mode 100644 index 0000000000000000000000000000000000000000..7e908a92bb9efa0c32ead824e614580274842ec9 GIT binary patch literal 80815 zcmeFZXIPV4w>BzWl%{|P(i8#dN|hRlNCyQ4=_*n~lim_QRBZI#k=~>99uSodp@tri zPJjp`ltAD-yz5=A_v~-8&-eRW`ws#kna><^lzZIwn7)6Yt44i+<-)mh=cqN*@9CdA zM}|Fjj^xvMa^N=?=S0JRe~8@m)s)Yb^|7v=JI8rW_56q zOp5J1f1Wew9PxkqlbG5+uNeozdJH}u)3us&eg&*`v_9^hsMt*Nx$V^%D{}?~(F|k<6GA9xQyGNz=w&itrCaU` z9o(Jc713g68}B?{q~GsI*1KOsKAUdvzEA|2Ra!8BgMOjrzx$CfuS@LQQ2#r-q9o$j46CvFyQQ$9uiH8nCwL1;(s zd+t)=nGdu@clV(XPE0PdKk;CzLwKF!tjC$XcOmuN9h?~O=v->To#|rj@5PtK&ivf- z-&JR4k4fBSTbgUv1(MTVJo(F+fdfbu|4MU)O9FmFdHst^zlDa5eByQc-pC-RzP@2+ zjELpiR5}W!v-YgW$wjuiKEWMI%OSHF+5@~RUG9LJPkOaBgNowJ9H+>cO-7T?9xw0j z#lF|Zz89nrGt0gQ?jSnbslF)Q*N*l#Bf)=434G;F7pivF6Ls7Uo|jKQ-nw`iRgDMR z8u2B@qEsJA&WsFB*DTU-R+$?O-W!)Y-S%s5&ef7(nH`pE%|yJ;%8ZI@GHHimI9k}UTPEh?(8?*Ol;XoP!TVDb(7C=r9d8y?G97ttf#H8mlwKM&ZQkV{7 zh<|x;g_oOKD?PxMJ~d&1>#Q`zNe2p+Z}94#Yb$&?{%ndE4`iH}g>~ck=v!WA7mJoo z&eOcVK3O9moPRkX2pTV_5iJ#XTzm6f+L@Sy(^ZsmeWuPJoK-q?)8K=aB1kRVELz?+ z+=KTm+yC&ca!S^zKt&qXv4qW`(|5Vd97S`3_3P=|XwGzBy1V12J|@`UjnUq-&y~&W ze^I;IOul}W6D!W%WO~=WP`gUAA~w~hk=}h*{;YEh_PF@nYAnrz0=u)&DWcIyRTHyW z+xbV#@xQD&|Tey~>%7!)Za>;Jq2krABi&V_r{;2WmgJQTkLf zZQt87&;5KVKk?!2dff1ENVA@lIT0aM+Fc7MaS)DF>a#2O-N&TI@~#hdF(~w*#}FgJ z_9y_g>3AmSbNxV>y$JA7xBf0o&AQXxiXfx(fauiJ5v0=i`LowDc*rS% z1LC8i8(Da|QfG{1baYGrTWr|>;IfQ==V-i)C$OtVU%ePX(vJ@SyEVYon;6jQeBPUHscq_vJ+sT6C%RUg?$;$|8b3r>-c3C%X~dW z6w|f;;SXo^f`XbsQq})~PdROZ#V)2_=i}om+ZZh~ZH4J4o&C&!1d^{G08YR74Q@MuX? zf+a-SAOlA&HFe8e#_NL?i&3ec!B4$Zx&%nBH*XSmCfxOyxHDOz7&er2*du3LzV2%| zSw8SESa(BKvrs?Rj1|*#()@{4{_>6gu>1Ly7>d6iDmccR(@YVCo(eqNkTU-AG`Nv4 zBbyZcHt2Y(Nf1~7bG0~JW_eM0fafE)O0}cjLOUi0i>Xu)$W|1Xb_*InkBS#~KC*8g)iYt^s@CgDpS!UkwVsCRg9SuKe zr4Yr8`zHN55=BSKQqZtQvdMbgv&J3`=58+)NoAQx4Ql2)TqLLqABF!D?TWWfTh#yD z*m;^2G#&d0SZ++!g<}7|>?|d(N0;iu|Wc^3&-kqBK1*gF2e= zf~&74zdQ@UZX$Va+Wepp=;*DPy0M(@9&Wz@>z--y*AWSl0FQR-!uQJv_o~s9B>?-h(&##;Z2Wubn>hMMGRFpujc&@^T4A{#ZZbML`i9uoRfg8in)EEp{Vn~bjrKDiaRzAx7MQj3d5s(QeaF9S?bK$tfB)Ef;h$3Ykdh7& z@!@^kpJ3||qC15On~kE-sk=r`4M=|Urby}0GYp^eNU3%js$8QTMLga7GS_B($T8Wg zmtf{rNX2}&gY?42_K1iY1R;@{3mmUI$z49J%K0yVqL@q*#f&Gv%u8 z(wN4bZx$V(<9!t#y>vm2z}@%Deyh`Zl|&H2(+8iG$p+@$d=mnl994W`kvD-JZ)i7 z5>9EjK@dKEu}ceo3$(S_-<&09CMTsZiQ+1^-1SZ zb#?t?(Ca{&jgkIi&KO0`_@L%==XNi)g}jt--46SNs8`Sr&7y{50^Sdr%6&sRq( z@|RhOEtYdz2Y|hG=U-EBC$L5k?$IM)zn_v7628+vBl*$y+8*=Du+*fxRfQtEPTMBU zhZGCD<@N@0s!^2t5laC@zH-~Jr#fmL`j^8Y)7>$+~ldO(XzCrmOIvo%q zKnt&S`V%I*VxxfWiWG_rkhJ^#)cB;+Z>PqN7~jhx6$Rw?ZtLu}hfY969f!MQXe`_3 zr!CmXDc1cPk6tyPr!3ekEu{+y~3D8NBWjxn!*=9hz6kU;J=KA?F21}@kEXYda^)g5a^w5b}7V7wvz{eMp`?U2{aG-x! z8+(B7bIO$j0aM*%DR+t`T*&vx&nhHs!>Rteb%TWlyhbj>@=M=TKc1G^rQcf!iyRwW^S>OY+^KtR9X8vR3Rh&Cw#Ra^j8tB>D9FUyG7-kvV@{QbQ%W&xK; zsqCqY$O~g>*cE%eaLf0kZLbIQ^AoGkn~O{|M`figrd3W($B@SZ&j<7MIE=t6*4+u+ z>sCtNAFO;O#rk_}2y<)3o?qv$&8;LTg+{C*YhcAARbuMwFv7hHE8s8Csj(BtZv?gp zfQ_QhvFc3iTye$fGjhHpfkUI1ZlkhyG8sPwGGP<}l@r%1sn)b+_&tfx9`O(N1C(;)JNgH1H&WeiXGJce!Xi!-DSb9_hwUv-rgGO3P5ogB%p zGrs4}+ z@!vm~vf6Gq;r$Hi-5upf@X3j@2bWVRhrwm?H`ZOn`cPTaEA0XxatbVfi2PR(byh6> zvz!N`sA`Rm@j^X*_&X&o846|TyfO?U0C?xV1|PeHY2I@lyPoaA0W3Hz;~j@CYv_xk zNS1KftV0?0ns#l}pf+Vfp#*%AG^eu82XN?*@@r|HKaE9SejE5)zP>%6#?gtm)b`7( zZ?&)TKe&PxKt?aQBmO+1H-5whLq!q`*6qilup* zo;{xp-ejQATK2L;yh&v+0*N1OcZ=}U!hii>dR`|U>^XmjuWYBKW?FwjyC)?!HkL;o zJz5&0q(b4JC-yIQ%SHt?_thX8M+SnLh1n*m6H;87$9Gb(B%f&Xa!XGvA@iQbHO@}S zxEo|x3fikTRX;FLIxZj8GBj2=JTB_8iWqSvjMKOOuvcKKU_40WDd&2Opj|+{5*htM9oYSZjuK1)$K=P6}hx@l%DL6WQKjb1# z31?Bz7p|Rs)dKe%9113(=vp_fFpJ4sd$Q!7oxedj9M zq-SJ`bA2byG$v0+CnESSBk|16U5Hzl3*|V~)ZX9_iaR--1~1xv_H)%s8@=;ZwA^Ex zs%T;F2N&=hnpP4Kd=}e%$gmQG-W2h;NoRri9~k~aNuTEf`pll-zq1z5X9Cbh z)_diq)QnFyx+g*Iq_GF$m3}Z;$v2eKVxNByeK6ZbYrWqw7wi-gI(l%3fm;t`sSakg zo)E{%OzG|{^)5!R1>Tpl5hNniRV=9kjREoD@T>!XNaZ31n!w(C%;Gc0ToLSS*Ay%c z-%Dn~+K1miyut)MYhu}Q?A^soUZpyu&h41JkVE0hY5nFO?c*fQ=#=` zM@hRV|GqFlj-8L=m}f>H-3QojF$z426)3AU>ytqXB3Y^W+&#lvVSy10e~?D7`O!{4 zNWVr&Ue6K+=}nc;GAuE>go~6Ns6^CI?D_XSwAD5INAdYT+i&1xFD?jhTD6D2q*l$I zPet$JCQrtk#ease@h+!BbQO7~A~~Y=g>MA}Q*lW4Fs=``;Ny%en8(B?=Da*Yo)F_> zcy*&sj+Q~e^GCarE`uf3?)^6@_HH5+eK+#Ms{_K3P@s~^(kG?H3f4?}rNq|mn$+!1 zfVqMGq0HSi+?##XZw0eOwSwM9`#}Kij(!_a{ln*vCD;(&=@BzVH7JYwADBdhe|%un z+9CQ$KJJ;KPDDtb_V23t@sYv{wrAV+Hf#Z79+-c}`EPi%cU|ReM=}l^p{0nzNUOz& z82{5ybCv^2Y{&hQKQ8jZFBsTNlRq{ZTvqqfQ}-Ht>Xy9!-i4!5gTwGsNRO21SCF*? zK|XB=CJVt33Tij%{vDvn*Ju|`>#6=1bbxM{RGAA^oE*)2Q>M2(+PmZP`zhap zOvTwo4{gHe#>BnFe1RWgnV2{ukLWSS@p7`kY@r_08vA(`uls>msulP_p0@f+Jp>Ev z=qk`fA4+cAgjwCn5s?gVUV7~+>}QcS;qBVX_j|@4)7fz+yT<`Y#8u$YXicE5O)P&{ z?6zK-8n-Xo`o2Z4jXBo<@W*-%@&~UEY#ZNBy>FLROOrPKN3iBBIn6QW|0Bl$Z2`P_ z0S6RViMsXjZZq3}-?K`qC^+143Tr)!$#rRdJW*}GaiN~$uB+k|5vau#5l;%7fKiEH zoB&8L@c1B~8E=gWhS=Xj%Rkm*zhyaNU=jisGO2v$P|G&01Vr5SUR(kmH>DB>zwAH% z_bhz)6R}Y@+SHG}&%dCi4YNl>0}f?jy3DUevFu48TjOOD8%?Yi3x~ zLoHI$oBTSZP>^{|)8Dw6a_f(kjzj!wsjRS|X7W!BUaP;>AY15oq3urIj2z}y%V_`? zGhn6GF!R7BQm zosN*PX%t%Va&r%;H&L7NezmVs>InzSX(C*2Y4=#IjwW5;ak}QRY7j;sO3}I@yjMry z8z?wx-|i#fl$Mj|Yd!|LlR))(8yz$I5A9H(K53b`mwIeWj4WdJKqv2L3U`3rYn7$e z<=lG@UUX1{k?1U_fk!(Kb1K=AQ~)>B&O+d%?=9c{!L%5Mw764D9)T-6um|{ra(vo| z=+^3cx6ve9a<4)TXTQBwu9W5(hJDlH#sllMSfHqmKnmc{o_%aZ|BIg`%r*p{g?6CN ztYT8l{lIAcNJg(Exe6Ti3_vgmK+D)&x15ePu?0Go@_@w)J2mP=5c7N|bhR6Jb+8Pw zf{?{;*`SUIKX(D}>rE)LA&}EViomDrj@kdRd~)LtnSZ*DD4^?5lYIV%>f#)_ND82M zjW6UCmVO7;K!aAlyf)G0(-L&-wmWXCRv}xVTM*QIEa^6OzV7$8i$m+^18yxj(5=@p zb^tQTN??*G?Pv?z+LVhYKU526;YNduF`CM9yYzejMs*yhd^7b@ZM>k@v*g60$-kQx zM4-b-wh|Evq=x7Gt_a7H^$W#bpq0Dnxi`iNC~fu_cKO=(t_C|LbG6%|}mtv+0++CF9m(Uq0*Cy3Q4ZXLBfD0i6V7 z8ohUB_-p_FH@!1}wJ}-rab+e={Og^Jf)5^;)HrkhC=*<|&_nK$!$F~NfHh#2aEM)w zl&yzO%XlufQn0%^6_r+}NP@4ptwUhnf3P5s5C#FSIB}Q3M;93R8Fm9s@Mw`FIj5o$ z)0$EDChX=Z+G4M@Z~N)yKVDvT-L&y?j?j3%c3Se%yh;vh$_x;$;kC>z zKe9>|J!d9I8a?=r2#{m~5XqJyA6OixtH244)73hoFjHOui-7BSI|FxmF!dFw%(8MW zGmSj^MQ#4;Gfk$@{W%I>FwOxvHe(?R+FN(GKJ5(W9zSPuR@+1KX1WX+%nz{W|C#{n zz(UOG+>$`x+mAr50biem^rT6N^tvipEdTNb0hj@oXk_s+H&(>bLbw5|-|1s51hE`-RP}KK2XgzurUyVKZFX^xu5Bu^U3G5IL89zxLQ`x{AB^h}5{k zhWZFHxw|pgs49l^ONR45E_wPE)RiQbpOcr@RW{wQ*ef*?d2lg9$DQ(M=@hs*z1plL z(D1oWI=QuyF(tBdj#t=%l6L`;xqGy9d-D*}iH5=Bc?iHmZoFOWijM(iX1WgETuhlS z$ft?Hq5_ZOUEs$*15zieA*RXm-^DUBX57OITb^0}L*hi9j#aV#6PW+?yZ;@l`WMhu z{J$8h;>^@z|I@nvO{n~l890dmRf4xg^~U@EYT`d#)@jd0`_E7R^}EP(z**7H-iW&Q z=lK47`roqZf66ca$BT68rz3Ebu2Jg$bj^Rm_&V*9T%2Z}ay`)>2RN!OX1hk((*`!dA%JFmW;_2-cqfQz*IT;EPJrU-wc#(_zov0%5UC2e>Vxo^8L^(!*83< zccqnL5fgYEn%;fN8x()e@`puCVIp?xU^~MrX)_> z-T-xL+~eq#wu$##>h--3jQ(DC&q(6cikHtYj*k1XI@=^rt<^4eT~cIJ!IR8H63l1a zJtI6>?UJx0luX81K;NFu3nVpX9$)w6IAbx}`&ElD_%VO!M0L2sbc}UwJ6KMbk=;)n zieKej9j@BxbTdg+V?mAfVHw;NjucM5aBJQB=)exbyJ|FNK69Fqw# z{_8YJ5@h2EW?<92zEc8e#^nNUGD+7>N*vdVz^Y!STU+E7vqax3UX}pRa93P|*m@}9 z-@HaQz$0HP-*m_jD>iK6o9ER95U1)fO_ioiA8PfkP`^%uH(|jC1DauPcMl}RHhKDD zWYX(40}sQibQA6r{<_7?9cky8{mGT`@h%|5W+)IrIO6+)j+-He3%}>uuJ3IFz3yO@ zzY&MprMj#RMp4(6a}`cV=jj(aQYnxP-fXG0nW~0Z!H3Gjd809UFR*jc7R2Y}{w*MD ztQG)5*fi}-1pf#rc1RSrF|Ag{?-l3xZ=Is*Tj8Uxa*r+SC|A@TKf2kY`E@2ch9{?k z**5h$D)6MqV`b{jv%Z+uZ?BwZM1Tg6GE)`Y18csEU9oXXLM9ziTxvO134PmyJydQT z=TM$@3~_!M&*2^>ffqd-1&Huvo~fSOU*~^zu6Y~MBO+X_Ootc&{h+6=!W^8@al|I< z!Vyu<&yI_6jo%sz^y`xqYl2-y>@`O>dgt5`sv;m8FBImdM-%+_y0GH|IIo8{iSeXZm1#j<{2R$^=jX(DX702* z?eB4mFtc$tH}lTu?a{T7^5YmBbBubpn;Drh8CDNlMZ<*EuJp5T(=YEym*}SYu{HJU zhiG7H08(P)$_w)b56UB_Z#fLU^I=GHv|V=tf1E%!z})8S#^@yn1(xh#UiPXdxh~JW zLhyD0y)15N@H!kBM$?~r2WZJ8bPk_xroFFMkfIx}?F9hgf!8dp(7!EO-| zYCA3=A}-GJ)x;e6?woS4VjI4;yVy&Jy*`;r1ZYiJ5j1o~=%(POb40CJ>j;1^)*4+X zDaW)lV>ebvozsllFZQlvE^jrHa)pLPUKDA3pm4lz#eCZ*6Ycu9Mu-*cjT1-+_zjGq z1Pq@?%2Y2-M}UrRZOBwh!C#)dg*(g<)CzgFb~(r34#U(keyO} zYTnOz?SxqLbKmf01|Er7Wszee;60u0q{R5GhWJXv-m_KS%#-i)dL3 zy^mc;!%GgYiP?NwT`Qhm3EV&SuS0J($Q$hbn;kQQKMwfvDptTq_mp+*j2B8-pJ*~| zo@q3;=}+PUlE%dnu%(=b*~uK&{f-zt1wK{b25qtX=GjwRZ(uU?^{>M?G*3L0v{c;B7w*AiaQYQeP zTmE8oP+W*c>#xy^gyG>wzKw=L_QPL-V{bjw*OlE67aJbf-I%mK*k0WJY3h=v;r{ix zQK!{NarYZ0GrPVm#AlUqt9BaW8fcSthO~U10E`xcE`!fdd1n5k5bp(2n^uBglmr@C zziTZ3I#nvn8@#$ak>(-?JIe_Xpo4qlG8Q+q%mcF@l{|R}2m{2OKnM78e=)&w0bn$; zoPF&rFZ1Z6-{dY6x^BcE#DB28Bq4b^BGU*kyXAl=5|9hS_SSTfEMgJ>atPHp3y=oz zrDT{%RoBSQuRb?#jS3!}Pc99+8;c1-@Y?YF1OKjt&Lxxn6e-IqBIbhLQ_#>#yP2sH zqbfX?eVOX?geNfI;1WNi2bxap<_J8x4kYOM*!tDH$e3Ra7aQw6*VKVVGhyf=!|lT( zOyG$?26X#9pvC^!@k4p{Lu|H9dyyI%8bDbJOhM-y^kQ;q9XzM0r4q%*D$$T~k00fV zEP2|=H^}rr<_ZL?!K&1kz~oBmYjTf->lUKFSRM4GNwk|K%&HC@+x52hWTdse1q3UN zjftx4=H-46Cct-nKrMGA&L~+f_i*HJqgM*09h0v-t-Wi3*m}FSIVmDl)V-R2((gy` z(@2sTukuYY?N&z{miC}L#zLn3y&IskBg)eU-&lJk&KsS3Qo{k=Z%xGjJh9XEyO~4( z-SsG7Ql`6db!c%96sT8`0xshhe#{RNWU>tS%I96#gABlR=4W&b7a9yv%|G@2y+E%= z9hxVsv+Z&< zSHl$7%$Nvi#fxk$)E3#@DKxIgqkOe0>9>gr*a_=Nls33e#o@fU%HHJPnPBPc;lu$R zztX+KJ?vSr*MpA$dBs(D5tt3~Q{2YDD->3iMX{+5zhu7XO@6$)ISo$N_{by@uO}4{ zzZA(^Ub1!JIB!{cRZbcbyoQ2h)Pb0UO>SPiBIKIZkiOT$ET&zzGl7O_buGlUURCGrS2i`ZlUEgaQo!P&ADjRV2!<@Uu@_ zUABZqEng9ejsP8gUu5CW!i7tBjN0OIt;b}V1|qITsn zM{fmp#qiFzxcOFimGihs*OXU^Z@7e~c1s>Qj74G4tT;jeukKvG%42}EAKkWN`yovK zQ(oxJ1;*%qX}_O1QZ{WJ1sB!*)-azx9my4Eb$ZuKd3>yNLH6x@>GS>ZYNuwDkr$X^ zN|*4{6Y+z-Oooi1b8u*-aLSx)NV958&4!W{B;4M$S}6p-l-`pf1!I--K@6`l$?v5W zV%8w_j;s1f@-EK9t%%w7Q5j2pSRBu?zsQ7$P&04q_fKdFtXlWos6sBz z9B#&)FbLiXB;XRn?PtW7mJ2fc7as*;)~2%)k^)!yK_{{Ib*|;tQEbQwoHE-oo@l?N z_Uj}RDfOX9QR^-S--9ZnLEH>uw^(c5b*?qo=L;ovD0(p!DnWVN!joSbNx9ApTFFgi zFi0TM8+oks@&il6=xv;7n3#08$aKGT0z*`5NVsI;%1m~sBOpt6B+>$mXfg36qnnu= zQMWcOK#B47Os2ln%|sK`4qP=ee#M1HxDBM<2gU|{O_ct`qwiQU6((w7JJ;Va5uo_G z{=SPhivUhoA0Kbp)16u^IGCAY7>h-jJY1R1*L(Wyq3LG?r)_-B%^f18nt^j~$!$Ca zg1oy8OU+8BvE08uUtsJ}z~w#)Ve_M+ma%`czG!3-RXCJSKFCmZ8$I>4g3@F)t9_;> z5S|(jf@_43Cj!HCD;I>viJwGdw@OgEt@-(-OC}bcj3(GF|0V$>W0PcYdk2eM&uJhD zC!JJJI!;t-)Z^CKx7NT?pPM20#QC*tz|M(;Rf;zY3ol~gHTd|-*V0CUA>+!3YV067 z&p9LTxQ>eLP7jbS-53jZ)g`P74#z6IVU)>5A!WrP3w=+`3SLiq$Ys~Ed{ylud!f%M{PW>3tycEWaGIOl)vP85zdqV4#LMZ| zeHboyIFwWDJniT98`1iOh)|YR-vNvSy6l#6P>SEpLh9^fkTXbNoc4cRT%E4*p4}(A z$Pnvn-p~ceGA{;e{P&z=y!@Ns_%(smnc~oo58FNb7oya9AbFE^&7(U*8{+T+RQSoU z5sSD3-JOgvbmpF3>nk$z)#^jbt#21a!F_@iBL4kgnT!(C_Nu@QzRx06$x@AP$-fBD zu1um9R!?O}p3-ESre%r1iOzxzd6@G_Ufz93Kl(R`PKu-i8hkum2ToY%i|dZ1cfbvH zi*#2q58bFIU6v?S2O~5v9E8mLZd-sYg&gituM0G_Hrv6JYQLLGH8J5*aWeM5O?TR* zms72_KgG|>`|rkrypEX1MpPC#Voj|5^;YF~dc^uW%9-LDx)vr~MCg5V#}3tp@{l_tfIoGHi<(JQ z##~+ON|SwHf%YwK%?Kn|Tilm`0CK>S?&>EQBA?4rP(e^kdedTZF^pym4T%O>D!7N? zl1i+%ibje>b&TnSGda-igWYb}cMq4iHkV*uKWel|b~2fysFf>`A2 zvDfq)_M-j5u7Ke8;sr0VAtKt{<&w+0jV%YtRdx`grKY{|c^rd|ByYF$Ktdb=SfwD) zXb^Ou`=jQhq?7hI?C{ujS+_Gj6}SSlaOZINC$qdi?m_@n815+BxBA6fKa1}a4-gkv zuufx{!M+Me7X&B5N}uFw3{P(2c8O<-q^RS{1NYKOVaIDfR@{97;U_PAi8Z_(BPKa2 z^AUyjzr_XWFS6U2CSx+cgb_?GOx)H4eb@tpz={>ek;dHj-FD3*-d7=kC_tw) zHoB%1P`R585Okw^Wlu6F{dztXz~B14{f8lP% zI9a#j8)f8x;*eN>AMK}0MN-Z`td#t1=^k?2){Ee98Eo{LmN9rgTDD=E_A1X78{DRsWuS9p_z~Qr#$kN0a(yK8MaWNTt|eX5ufrcgss{rx zV+-YYVpOh{t(b99^6tvGRx{z)uK%O@YQx;yYq=_uUOajZ$4R%Jy`nT(Ly%KnlZce< znt8Tcp>FD`P=h$~4z1o;TuO0~41QhBHYHTynCss5mU^(klA{7z@Xg#m#ZJJjNnv8p z&~z95waQwrsoqluI>dc96O|2&pFGTZ-HEKQ>6eTdS=BGqd|YUPyJS@=&#^9_b$AT{ zhg0f0un&y(SUMeRVrR;BUO4fY?$YcKUjZvxayYxd3E@4d5<`uNb_(_r1z+p(k^}v* z>-K&v3~)Tl>Ueg(v>x@E*>7(ylE_O}mY1{-SSaC@YIF{(c{+jNyq~yhUGjS~ zV16U~paOD$_={swdGO3_SQEZTJLOJW>c=G%m|r?sbtmP}_cQ||gGxrPFZCsX73kDV26tE^ za@XBPjjE5Ub>7l2v#fm0>wgu#F_9w2XeRXmxJZIT($>cC=+Th!@XI|>a**3i z>`k|*d4rZ=d2{JPpDn(NmZvkV)>E96;j);A@>B3FmG(o#Ooro z8q~alR~1_bA0-+sGY9((*wZRI361A@3hb0+Evl-hJ^k75`?h}hTN7-p{r1K{ewcaX zKopd&q6yDl_CTY^2~1y4WlK`D1wVQ>%c!^m=?svyqJd#AJkapM?i0)~dAhXR`tkVXRl1 z^TlXSHodO{=VJj^i*9dVj=LyzMMm;>mQYW<#|F#!SqFi#E2XCOUVyp}a@u}GUl};x zD`l@Dzl+2j=%pd|5>U?8aC#MSwuza4NlPHA;o7$8VP~ zdP3%44=ZdqXKh0gTZ5quFEapiu~rO|Y!mQYnHs3-cfXLv)fP%ER_a&vjmcjB8Q1|t zC+(fbIQG@*XJX6bS`8}xYkU1WHO&md_S!nj#uN;4$qnj(=(0-)PwQzy6nUasD5z{( zL7o%hzN=%uwLf3*mi<{O7PKl?y|KMXwPNY}BW@6e8Mrco-|8GURX)jUdLn*)qp`g* z?gua>>g-3o65;^d3^7N3)@tu_YonI>&Wf;vLz}2rMLViQAiksL>Z(_KfVrI)*xEZw zds|jNlSa*2r^mll)o*o`3;+jMx9p^NpQMgW#!FkQq$e>dTj$~zT7{$Ah(Y0DofkV3 zdA%vQrV-Beh85GxX;zo>rN)@LCCs1B#4Dvp$%4fmB2qnvK z4w$hD4TtoN?EB#!adGtN%2F;~j49?I7d6S*TZ zHSxiUzEKvV2TQm{_?@Rxz50u4rQM|Zz_(hXQcz<+9IgV`Z*cn~dN6RkXfQtmfnj|0SmA}P5t44lJ5Av!_ zpY@B%@T&r_GWcyEmMO1#e892%9z5tRI|tkO}}@B3D-cmdVIXH?>- z(OcSdr3Qnzj&Pqz#>*q0Y%@!o+{)FA(-d0|r!mbAW2x%1%%2&UQJsqi?mv8O z^gi(Ad2x>*5gUx!`%ceH(p}&)*vUXraVLTM1k{u36q%2?Pt78J`(;fLZ6a*?@`S zp}v-cbvQR7%~LrYs*~Y<^|CEdNn9oe6yNHpK3T=Pe_f8-y4;>ROQn>GUG!$}-6;iO z|70CUI@3S|_-TWUTC!qC_?)!Mv7GVxolO{|b1PZo`rS)WJQL+2MSVf`wOIS0m%Ho6 zOF!DcRMzs%JIPd6Z*$T;NZiNXGpGf-L?u;3K!2ADslS3n7|oTnp47Q7C0faHu=ly| zYBr?x^nnGXrbpSeK+Yq6zbNTbtAZD1=PX|BJTDQ#s#G>1CC^Da2LluD6Bxe_Wv3-ec?yNcVt`?3uH0==bLcX1T-kE@x6iNlyxNKu{)hMQimRT@6}CO194V=#4>u@8*wRaz z+anERs1)HdYW|aV|G^1K89~SM66^^8TklVkw&a=G^I#OX%Fw=@N@ZR z7@-=ZL$Ws_w>h#Z%NXT03lsDTc95P{)GM|g^8Nj|YG|KK6m={On`94F(u0M; z8=`iziaU>+I@WnaI*V=ld-!^oWHRzuNuKrSjOI4^?@f7 z^Y~<7MyZXQ_D(23v$$kZc$6$N3y00k7&$>?hf^P#>>=k$JXSi4ETH35C(C;P9fbw# zQc-qk)x@vK`%OdOkbBwJDS<}s_jVCU;cPOACF`tS?W4YpETq1RhySn0dk&dw2g>?~ zVAZ0G9|GwfqCZ;J-BHrtUOD|PptccsT);Vg5_r`xEO?y#nGCeKd1$oMNCeRq9t0KF z+sBpy3P@-M>bm#(=Tvhv)}@cKI<=G1NU6xYBL0!z{8|I}Q?8QkNPwYll8fmTH~O%Fodi zo+~?eqEl#+tC5` z6v-Ad=#0#=?pFNx(N#&CZB5m57M;~93R$JG5Uf~2-_ztObJ`H;KefN|8}5Qa>+ z6??<7MIKB<+W?ZDJgt1-rri826~4x9LV2?`yCeyn3CYi9+-NAJV?yHtWGZkq+mZY`mD*Lmc_}s0Ew)-3%W5bPUiiF*1Ok9(Xv~@ zypkfEM&j|_6Y$i7$x-00?y6^&3e>JXWGI(DtWQ=F^wFtq55NlKOIEI%NP$dDjpIk$ z!R581BT_8RSiuLuIb?7@Su-fp`!0_N1h@WceD!0==<#FaT1lz zXwm-!wD>M#{@(WcMdL*&)B*6RGw1qz6C9bZ6wTci?5}b4o$Pn-soumT(|1~gsC#}_ zM`{M{T|*$r&rhH=nI_#k0Pw73RlE1tf77B#d|kl_V|ER@cj!1kc*CLs5D{VO$U$_I zz!V`R-92p4Vy+2o=d1->LoB42v@kKD9R|kprfrUDU)uZm`Y#@00d)}f7ew|~kjK2| z=czlFF&c6ndbU- z(ZICYV-qn@Suh07ZQ`%2ItUJ-;8xpRx6!9F2kt8+G#?n+BX?KE+M=$&-kXKgynpny zGLFXf;(@D@q3!3x7ilur{UyhkJ~A1WQBkSpAYp-U=uZuKY{;h#nzhsx?f%wcR}oJ_ z7;y71Up)P^SREpiR2BxcM8dw?Ly;QVo@19M9lDvndu(zJHeE)07CZ)W1u)$yUb^MP z*&fd3V0l*(TKCXzcaw2M`iE?$3kg#c4Z9AVw7w$B_~&@MRlC3#(;ACUnyokz1-@ao zpy#Auv=0;1Ce{X05pr0bNMc1jmOSnvEcTGR+BaKdxS-M7P>q3HWMVFZ1@{%wu zVCJ#F%%tpl-5}Yzd~&Vuqmz3UKgt>hu^(Q}T->dH(-jK98f>i7S2;`N@qJ+0yBX_i zu~%5{wo&z?H`2vxV74b&f}<0-twS740P502R4O{NIb>pkKOM;N4 zaK&<;q&75zsU6Ku8z=RPz>1vri8GH{6R1xUf~Cn{2QEO+a)|cybpB$^5Z5=}SUyU# z$MI8$YRQWX)=`{nrcQb7zq_Pg{z9V;-6uQL(>7dZWm~uP_Sym=^F*q3E3;@?2I=qtXc<_6LC& zHmsFtZleAan*31SjI2}or@Q!k)2jW?`8SHDjOeieRamXEF#O1k{uVN+MAl=4F0pZ_ z_%`?vEO^Qv1$ow_xF3(1V0VI? zWH#F5YF|0?J|D>Jk9RWd^=Y-3rfIt1Z3~UaeFJ@(x~r3tW7@y@@S%;zMZ#N8bT?!V z<>FU+SKw21ei&i)d3$<_w0mBO&(@oQKJFPJNJ_q8^M2N;DqST0+Jq1A{{ww{e?3;% zQ_BMIY}~H8b*{4&hG>#R6XVq6U1=1l3s8jVvI#A>$xmtd2%SgHLkzv`ml{NXq3(9~ z(UaBJ!zOpqZ@+w=JQwJQRubCD>rK6scz$}bA3bjf+mz4s+4@yBBiZM^1;B}H$B#<~ zm-%4_z_$1DV}=s|0dHqZ1=O-4HX+UP(&iRe^hPF(C-}3nq)R+7$ec1`z=H75O<&*h zec8CV^}v_w|6%Vv|C-#ku3;;RG!+HuDheV^snQWPBGRNwN9hCz5;_D#1QZ0MNbglz zklurWO0NMTU8E!s2qhE~+PmV}``q`l_c_n+{R5s)eh|XSwbnJ)oMVnL=Jfx*0T?}P z>6(CQNX%=AHS&h`HE__~Gs}}9R9c39HnXd&QzSsrGz9#srVv6%Fwpc=cNB+SfAV1D|c_ zai2*Eimby44gb8U(k;%Hv>UUjpL+0|!%re%0B03+s?pP5@AN(Wdi4sF^t4bb_JNl7 zIKDaS8(WlH*(r`2L+;o?z@ECLb-i~UCP~%$5x+hV)>O9WNCS2#V@ESB zNAb~|4-(%X-LLlum^RdL3DsBzg)nEg@n%9B-?j^dATug@`w2I752;cywiznG?P#b( z%yQX{EGW3wcz_Fiv(h_KoYzrPD8gPQDlm$ zR<>9_uZAcOu=FxxE%TMuoqoLwGl`nh^HB16I=t%mLb%xyE5+^YnO+>xvvTgRknhBuYh#}miqXCf4ZJnZ~43w2vpWU`Jxf3usmK}oaE)-Idl`W{?&6z9AC%OJ|sqU$py~!eXWj3nLgrZ^6+6On1_gVWH{(XZ)GD#SLt`J{_ zAIZMMTc#w+-zr7KDa@Dp;7U4w@&mGl2FDzY$V2-Y3|j({^uG$01Hm&B(uJ{4%nd4X z?XlvHzg&ney`h7XVb7siYnY^W7q-!lulUcA>k#PJ8`~wAUs>Z$haTnQC(OTKxBhJAm@?8>>B!SRdIdk^FvWkDp z-IV3sAX6*oJGuPOFWXy4xU515Z0Cj70P7JN;y$4-;abocRbU8ZHDX+Bwg0N1SK#_- zL#Np$rwK9p=^~|M;H5IDhiJh|3cD%igQ&%!;*(!G1dWHEoFoMe2V>B{%2<-ED9cIJn*{PxX8j-+~wu^5!c8rm|Y z=71`I&WLb)t)iL+bh9oPJH6zYwuz?2RD;j;ec!b#)vk^c8*5UBj#X0_u1ucjbCUGL zx>fb;V>rOgv{z5@d8x*k%%A|=M~ck|Q%&xo=7o4;HFmW8KJw&Vu6wwC{%(xHc@w$G zw^!~?R++d^jHWGkxiBip#80;YHJ|w0)I^l68HdQ$NyS~~DYZjN_M&c$hnObY74J!t zWgGj6{D`p@SO=T!r;*~?hYMlv^Ylw|Vy{SXe~qYiBoO>IZlBqP&hF&RoyLWl@OP&L z-jupuh#Zb!mm06eRrcNMR%`u0bQ%5-y@@}wrb@mmK>~GUhxn|Ut1N2ZU-+S-x*ywZ zXf8&AU3F}sr0f)(aDjbgDRR4|J(7jHc*f8vhEB>Pej@&BCUK^JvL-2Px>ONpQdw7L2busm`& z5zFl2yHeghAGo=o&PW8VLrJ5d{_H|Kr-xZBw~d9I#GZE*C3!4cjc-CvNMn_ar>gQoy)|Z{X)qg=*Nt z@Wo!W;wz%7L)DPXP^H|hC6}{4G9n!X{0>}diB|!>{d80BfVAEFDx;w%1WW`8te4!7N^FCePX06R)GD^n>S`BXSOIxsh+(8b<%dL~I zkl5ZtS(!UCU)~#-@Mw+{S}yXP6}M?rFYASnpsR&b>+_)-+y0|fB zn?XZeolBo5>iBr3DrDfPyAd#Hsi7NDydR`1>L6`{r^DRC00#?k#Z7Fb#b7qD2zpy1 zTWYp?p&m#>)eS#{!(^!sYH8x^1|Xv=V-*hDw=2^r2*vuP&+u%fsn+Ht)+ren)epW| zqrhy%_5JPql_y)1unLR86O~0fE?!NGbH@X0}4>wfX}fDC|y1{-BuiEdo^~3qYEO{W9#3$9yvV8&A3fY~e~cYGE5SagLX6 zgBxl%>Fv0rTT*rJVSLBonC3#o;|>yp4cm-F%Sqo|8ExE#QU1!Pklj2sdU4Ia&%Bmu z*HL0{TUtFMzSJk6%(zkC%x@v@v65nP3$DGPpgl_5{f=pqZ2t0$Q<~n9F|ITsMdYsg zHOx;8Q}IOm?*=L($iLsix&BC5+xotf(`*p+9Z1phBT|xyG$jFm_=?(ku(Rh!l~7Bj z`H@y~XUE%IS5R}ke{?n2eQYsCBNh)HFxrk;v$X%|Z^Ur-D5P==q|vbS*^5lea6O#! z?`E77XtrOc*U>()<~2THGGkCJdqG<8HAwz}HCFm>21+#EyanS>rYy31Zq4+l8+26V zJbDs8?fZDVoO~@lTYlNme+Z+n<_|Y*3;&~G)a*E1uKyL8Ub4AP_={a4ISAM#-#)Eu z*V3!tT7uRtHz&9axx=|fl|r#5JFTw2QxT`mp8+zUtX)Yx4lix84`g?^%fB{JD|$Sx z+_F6}X(Y%v1b0IwO1IR(NE8zONw3|=NrK9YMtfzZw*M_g>dW8)x+ZhTUDV>kQfHb} zEio*A55;&{D!y_iRIPo7Z6RMfuZL$@^#Cv9d&8hFHSmST`YZdabHY<6_p}TD;sQ+5 z%48pvel|GBpbg4Y9Ka$O1$;E(`Fv$RErIwIQT? z%Wf{*_a-0p?L<;cHu}3ssC9P_-}>ip|6572PRaHDeVySs{K>=Ga+e&^hauLDa7gj< zmfa@*Cq)Le2$hz6Q$m3Nk+2|)$Ra6&IcEFgK)5|H`&-4m~wHZ?qjBt5Bq zbQz{mQyUs@?^C+611N6(i2EO_-BDZ1L-q)sMJNE?VP0JxGy?%lfWEvA5tHWRYZVbBN zLZ8uW*+a0?%|1y9_NF3HSc$^C_!gl08Kg^%JeG+ln0pS09AYj=qnaDmy$*OPUm7( zUIPkQS)3$yLrG`H3*^p^fv4MU&ozEB`_b%63l71efa#lNKTJBFOqDBlzsoP~wrLON zf=FcVPJ*WRNe-0}AwYcC=@<-QmUPSE$v7gm>$MC@+Vq<>pNJ6LPi@KP1*#X}VS*zS z{t1_Gl{XrhS!5oSE`!?o1GT+%vL>F+XG%fc1i#C~0+IN+doQa?6)@qR5YpHu#H585 za7g$jI+88$DGc~BRNw!S8F;dXa9daKT+5%BvDyrkyNNSA9$RUC5>4tEq$3)6jvkFR zys(v*>mp{f8j>indlLPRi7H3Va@yX_Wz!z2_V$w>$_pWO@Q2-e@HNyo#|FEkyomaj zp}d;}M@mp7H~oeau1~c%{J@y3Zoggxcyf1|uM^V;qTY%BQNc z-=_tV=Gz&Qmmh7M3tHueAHaTS{g!1MPbjO@I}HY1J>Y=od&jNwD^zMKSDfgEfRKJm*Rjav6{(?uXN) ziw9A38P(d;dtGQGbJq>Z((UsK=_2T4z084Y?2qxCL5HYEOUBN31F1{zuzzncCmYR{ zHSHu92iMMozGYW4wx0HwD9v{*I?h&_i2L5V*1U({qh|hj!{llHUXgxv@}vi@l&je& z3*{^=ceoW>@}yj0G@xSI-PxbYVoIeD2aOB6%-co8WI#{E8fA?p;QGZK-+A}Bs2GEI#bWO_30+EP6x+{EY)GSARx4svX|>DB5ak2#!}rs~?WI7^s_vib1 z04t6e$PcwwD0O3&?|bGaNk(S&F0k>fC41Sk4v*`#+Cp5_H-2(mEwhfKHLV6_fk3Hq zF3DAMjSzo-?ify)ZB@l}Y8&0nbef92H@wlK(;!8xvm{B_bgnU!3X9vQ_Kb-~F~1mM zh)AZZ>b6=n((JHKFu6`9lX8T%9BrI2Twgg+_&Doj(j{#5UMXb?fC=If>(rKcvAN~_t&ec|YPBh{VaqMfz5+7F5aug<-` zc=5#bt3$6}ym&Q~>E`K4J2fThR`8kgb=(yh!g98npV7*4w^GR+?CMV!B35SX~_Oajc z`>NWMq$Iawt1TZg`d8#DQ69VvN7$w>wFcAD?$c~-`@8AJA;;bWlic>T{qO{V z&cK5`>(AL|U@cP6!$X;G6KnB_>$}ov{^~Q?L_AijU11J+*tarMkkPc+#p_%^>n0m2 zTKXBt+^oJ}qn~OV-DSp*S)E> z+&Oq}t#75(?(Ok6wTK&<9Uiio;(*P6e9T^6f|_#OAp$+q23%6#Z(hFrFl1}V^aLCd z5jMbyYJ;?;^Q}^lJp9c=4P>E5W+OgD291$(YjLDq&a`Zoqrj6klEv$z5u3Z7;RQ<1 zIe)G(dmK(JtnTgIYEdq=Y=#^B@=Snqj|A8vO{*R1(yFKZ6R^p5M~d`>k2FS(-{>|d zxpQ)_X~f+t5NXz*F+-_E+(obR`5kyZQ|tJYeOoh(GC+KY6!w8yt>3ydYTh}F=b*`* z5U-8z74Kc!b(Z0vKH)ljcB@%No&5lI;y_t^p8D(`DKBcjCS{jen0yyJ4yno{G zT@AsnKrmWryvW*?ckNZ158S;|8LiQ$Q>XY(Tp6+c>2pCalkJ9O``f!u-B9_xttFxt zHw@lN*j5o%Hh1X0CBi)8z<`u&<4?)-GgIT#gQj8#4&<>Td>C74j`%VAuLg7DR6y95JtR_0J)~9icfgYbEfAran9t<@Qfi%CG zE*UIEB0pcGUyc*{C1}Av6u?heUL6E&T0 z$Am2v-fM-NWf)M3hl~{2=XVZB2)B&y>76paW|Ps^R~arZXM^U+Q@omgR<~4F>9!t% zFPwotUdDVilILqStL)kJH35!y&(;Mbw{^O0dT$!nIJyEq*`Y`b^^*4<#^-uuyy%zPWbe_{Z9`$9!Cq41tG1Mz_`<9j~;* z6Ox3If@R#F&bJ(rRLN55YgCHQ6!d;S)34oO3_Q;mkaB*%K4Kf#pO>S){a)Zp7S00l zlX;d%K44ET|FnEAGLRu<8_D$09l!5w*yvH}hunj4MA?V~n~|%F=fGx9He^}-cTbRe z;6U0_|M&kt^#UA|W4afW{>Lou?=~qfJ&Lr~3&Ys7fk-w?+e=|PWa;gdh ze#oj{&t0A$TIe3i)}vyveQg}dXI`Q9vpq^sHGw~YX05CiP5_F^Sm5fLGS|3^y;k5a zvw*6Xtih3aXK|qWsL(eJ2EO~9Q{SSPxPd;XX=S3t{6PfM<@3R2_W}ZMs3+@A z&(vu#2%0mWx6Xg}kFS1S7(5cE;gf+#-zs2zt&%S7Jzv@H(Ya+P%qI19M_vxQaWzTA zK0Ehev;qUa8J}^DbNOJ=y&D4OZm7il!N6~FWpVWAVAs6fV>LNd(*2R_Sc4v|AM9$E zcS+=(00FBcvNJn^{{l^Xc8Y`_e3WpTpJ?u_ zz%CuXz$wzCG3Kf_iapiXwi$CBDzfGkhCjdvn9lcP z*!IZUYv;vN&@eyTVU@VyU14=m&`esmY*$n6h>vP=(%g&FTIMv#+pLRsq_~+=uxb4z zR+oKuZ6<-&0MQ{HO554wO_=X%mWtez6bxgL_Ij=BQ=C2D5#7dGuuB#qcQ1=VT6@It zPFj<%KhSyZ+Evr@qw6;2^0aau#PCD(bM`J~*7AGfM6V3*+5t@19WMjpG#r zn;Q#Uo5!m-_)(xw!UK9Ou}4nCU(w?TJp)Pp?(fg%asiyVISK1mX?;c6>+UZFHyTA4 za-N|JnA?#gyNK_fK9|Uox+S%_R%*OC0haTPhQ(?v5H8S8%^_*xYSk}5b~yl&zS~3s z<2$R)n9k3@yzfHJgI^=@6E%+f)?M-L#`#l?p53oH>De-5F04`7kJptqX=lF3Rf}%j z-v?G3uGkc))y{tX^sin_`U9*~hDA@uAb9Ky#HE6aT!WeEr+ua$J{42 zyqPNC8jO^m(is%V6)>!LH7AWOGZzQn%g$v7i2Q}Pt8k|1Cn4^q+wH~C*ISOdfp zm1T4p^n@|Z>fzxYRL8%WZi?*l1_b3TIVF98hk2?AH}r~hUH5jJ>U}YhH`L-fRtlbO zyk$ZygbVK8izHmaD(k|%&TWa#!}-jlRidTq3kkr1aq?Au!xG$WWmv`ea4(bJdwhR; z(@lV;hu5I+*|K(T73ZU=YJsW)M=%6oQN~ej)O)|YV);>j9tK?lTkK(iIj_DL+CvX` zg92!C0c|cBmVC!wfl0{gqg7w(*uL7tD}Ut}ZcXQW??DeC-%-A?4WdB%UH#0m8s|XHT49&WJp6nUYNRfUlX?sSPVRfQ5fhs{jvai${0T@$rzHSHK zXjGDKys@`kzGI6w86%Eq09Vv{g1Xcs@S9fYXg!~>IZ~o;x;)~Gt{Ti&Pw8B3teuLV zNsw>pfGFkafrMVfUR#+vZPfecmp8o<0mKDHCTXjOiQ>1GM+&*unht7m5Y?5!-~w2z zFX|gF6nfruBf&LzcM@bCX__k#@dXSUNV4xp} z^Q;wAXdv;WPi$m)O+I`in;g8tFe}iqi+!;yeDwa-O}6n`@jb_bdHSRli$JE;B2Guq zIzI`h87zX9P4eE$mcrQlj>>*nUb1VbTDsWRh5c(VOv)#>wr>#?ZbWUu7`fN@!3SKw zNs^WgtWqAs`%Qn8U*!6L`oXi6I(>MG#1oVM&R(Y=@#Np1PK2rAGNzD^WnzrG9irQocz6!PA*LS?-Zb2nQ0o)t9TlY~0e4pQ0} zbIqe=JM>|dDW*Y^fH4H!+qaf~crErA=GxZcW4P3rC4BLzRcix%TEja|7=eW=#^a)br=?z31DM`!rR-5smztJkg1+T*(tYAYDtnK&t>42Ip#C6i#{Na z5Y4bRwNcB^0<#0`ru_y7>XdNt`DbMsJQ9FzCZ$%fVF$aYU6S=#@rD7yWD_t>AFU6Z zVS9lWg9ais+(p2A6V$8+Aj2o!^^Kn+ZfLx(?PXDC9ta@xiVHc;2L*?^^FCSETN#48hu(`ULjI7~6R z&J%!MJ*Zd0$ymz3MvsinrX(Y!|{l(kkP zGYEm;bIrF64v${%Xn|oAD+R}&cI*>v+z=X=&Dl03F=KHk&%(5y4she;c1k-j{8K6H zQG$SJn~AuW#m5`3Im~OEXBcey@aa=N9Y7Ak#XRhR{r>qmJOC{2`iyUF`eb8(IhRJ_ z522q=H1D~0>3Z)h+0FwGNwJ&~cNp!Pgt8|BIQ9^t{_(2H`-*BbGNOM}Ei%&y4n%z* z4d8r1*kt99@o`IBvd&jab$n0^IV-b2JA7wWuYs*>y>Hv66RjTlF652Vc-8ZA^o32y z)v%BvFlowNt?_5sdzdNBNUcI7t1FL2l908!OJPdn&`dcblvqhYViK~71Mmr7J7 zk%iiYVIt1M9dF~ycJYmoJjNyRa&7}qHb-#u&OHb8ARfg~X{;M6@&cyz^p-7&OR*df zz*s2RBt5r&z1k2RK*yGt)U=-3RkD{h!OcC2^4YpPQ zMP4jrOuMdHP&_!$q}gw_Sel}dCM7lfad0V!LEwpnN;%EOwf}Z2uY#1y z&3>q}s!*qQm=a~%7q==^^X76Y$X@1`U|=Z(@sh%Jq8n^SlfTmV|^P3KFo6duA+af zKV=f~`!F^oaRU#`AyP@(x{0WsR9T&&e3iLPu9uDSa@n{xoy;;6eYM@lbu~lLvpum= zo{5FQO=ER?lW7KCn9gL0l()s1IR_7tlKewUL~obKA5DP3`QzGW|BYaDyi9N%m{=1nK7EURQT;rwRX?#L4Yti9 za@G^!H8qnVA29Yak1O?PJ&4X~cIfM(t12V^-D^#av;qf{Bn`>_BPd*Xi$5zkoH3c8 z>s?%dWf1@-Kv5#jqhE5eYUKld)=}CtWGjNK7R*3sO;wemTkK7PpB^nW7wqQfBd)UISHIUQ=JzH#dsHjvc?M=u7>*R{38Un& z*K;Htj1?^);jiV*+xjc4yAqdPxY6zo?7IBwxWKgOa_+jh!ZS!*8LSQ`Fc5_$eWb_I zt;h|iMa^s4hRKr^X&75{|LPN>w!x_G%|7=MG>XTV zS?q%#7yvDTs=*G+_pNH|E)A6DSe861S?@Y>+UFq0eSNv!nWA`{e-gd`LK;3sc-4airfL@v`EjK;d}hvjvv`?}B` zwBa6Z6-4-w5Wg;B_a=}{r%CE8;_n|po2{3t>r?vVH5V3y|6!r}dg{a}lGrjjK|TP# zQqMJ9`5?2#Y2-my9M2qTL=3(a{Ysiuf@S(RXGp}m6+xt3zWSi|zG&PCj{rXWAtF_x z_aS;95P7)=6huuZ-RX|!=LYnAF_$uYQn z;c_6d#cY~6ML@ntrw!Y*rEh`A>55{PxlK@1U=6Hi;{_W5Id>WJHOifKV_L>1IeKrL znCGN7@vWs6s=A+fRrqGOGsspv#=&NJ{#m_1n-*cF_efZ}_SPvhTHd^%pNtR`78HVH zL&-UuL4a$j!DFM4wJ&^Y(d%v~HH+l%ucA3(IL2D5LjjeQkK^&H*@H!4wivL+Gr^a6 zIelhVsYijn&+D0EBphsDDqBi z&y+$(pAFLH0PCAQ-S(A#&cb|7#>^hb>|MkfA0OM}@Vg4oZU)>H^ZcrH{ahgbOH#5F zwaaL6xt}P3FXi!6I75;YCzKcp_deV^JW25|cyl4|k2e>8Iu~8!y$5Qh#J6nXeSF{T zoG#p5h@?P{CuQm!bDFNW*;6GL$z!$8ngw-t!zwfev1_#a1TReo>2X2_?bfQ7H$-_9 zBgqz96T~`J*&Pl}#CZGHmctl!8J|R-TFV91Oz7#kzMS^bOam#;ZxI<+<{}-R06uW+`M08)rgg_N3J@RwD<1j%@wq!-dE`rRMlXQN|LXCpy#;o(!PM zfjiBm{{YX_V?3c&zQr1~sEt%OjnbL`b0jiw@7eyxZ~IU5=;{tYOD8^9LIeH3Z87yK zl=X#I>Vu*SnCLuvFpLvnEpV8@;npz({>@dBKOS z<~BxeGv9Gv9s1g%#NqP_Y4(vcF|yFSJ1u(63`DiwINB`6&wM)hB~0bT#%0rRlLsoe z{t}HqaWBLXgwOQh8|^dT7RP+ ztARBVCqZC}-p)e5UXh=PH1JZNNEVc7`se~qY>cG4!x$2tqLv|w(w_k3&y@Pyram!j z2)vRyUwCv1x)A-INpi-7FUAnfdow|zWp#%==nEu|i3*BVFu_zT0bOgD+A?Qt7N9s@ z<(#tY~b72u<{*girAk6Ds|j35cGKc(6lx0-km4T#7D8AD_d6x0DcUm zWoza8RJ^=I@7>64hv%QyWHrUf(YrT{OT|;VL4e%HK`lPsqE?u*s{$D$dM_U?y zB$j|;z_W#FDEATn9AGn1rL2)wgp-7N_g)UBclF!;1vUN~0X^UN8yhURjKyU!I|v#! z=*kA%TdV3D+wYrzrf5}yg6aL>+_!rOi_-_pJ*$)TiiNjWv3Vk>2S`9X`51r-U{PE90^WV#J7Vl|>{~UMf_L&~x)emlbxLr4B+52pe zIi$?E)=bRrKjG;J;|Z_6Rerd>Pb3WG9|UQfgQnEV z%K;3>rrhj6;1CR86oQTNDTlsBu4eU1!VgzwK$9VoX`#;K&9j@iihDcXOq6#a1(SKa z2|a{zZ^EL@hU1ie$rB!&*rB;?dLt$>4dkH6HVBo+L;^TGp6lULjn=a-gYya8kVVHK z;Q#R(DT;Fd@K9VVcYv4@%GPslXZ6PPTvQ4sk04?0J^=| z<_h_bBY!?AN6r0q562_6hTC^+Xk(`Wh1*!n2>Ef7NeEG6R zK(fUf+%-M@$s$h6XM+wXx3;B6Uz;3M3JetPmZhB~9Wi6`fjfZz(SaxM3a+e2T)qg# z;!UZHi`f66d>kU-CQ|HdMAu-4HSle5ozwt*U;OzC+gX@5+y)otJ7bbw7)=1fEFCg}90{?L zpUSouMqRUcuRie#U&dLv-Ftj$u`dO4#U0NB#%`=uSOz)i`k!pI<8a;av4aYu?Eak5rfK=jL z=j_|zQE|rG`X4OPkCS9bd&_#1KAS42r`aippu1PSEE2B+{MfWmtGR(lj7*cV8h^W# zp8R%B7(_7qlctAS_ESaG-n90_ZNozMBIpThK3lM)fj{=KkyKofF2XV0n$$chs$7D} zx$Li*EYz8oI{PmuEHEfH9?CpZ`21RmgA1t{;*pFE#xyr4r|@Ot=>KetQHH` zy!=h$pOLKichiX+$<9s!3V;fk15y8$Nj))O?i-DS|9)K$9c46(v9DgnH_gaB*Dm@x z*Cq7&+Kh4Se3Y;)d920L2ZJF7(rFagPY)J_0U*HvzaC4RZga zJCjqxvdqaf_G4YR>xrbA`)_`JT>#F&LV-odi1h^n^G;7N9TJQ=x}#*=Wc9|6xSg=r ze@H>1oa2tu=Qng?%oI?Pdh%c}M>$1Yxx%XRS*ziy{5?3SDhaVT5Uuz$%&_PW9zy(! z$YJ#PU(0ew9pyR7y(y=HC}>2|%+28k)qF-39jrKhaJu$E(CPsodXjx$6)SU{KXHLQ zNemW}(`33-WleGF97u^OA?ayZC_7o}~O74JnSOBdg?gjbd5{t4k&z_0|ANOPU^3DJFajfLk74kj``*#-bw?y!K=xIRn za9M!i{_&Rm^^<{|@|hg>+?7=R5yAiZ0zW^3HLxQ5od4@6iK{N)jXha_xW)4i*y+y} zraUXd)q$wC_x+`0TEl-_%>VVP{@%maZJ@Y*Ubk8A{vRINZ=@vf6Zy%dHe~1i$3H<{ z%=0HN-W9K2X-%%H^W%@uig6lIX?UU4$0rP z@&6r?e;lX(pNAyp<0fz;zAg$Gj%^ae!~#y^t)_|&&?H>wm{qLsm^vrny@|QiEXGKt z2L1gY{(gnM+`yq%7T;Q;W?rOY5|Zo++8+o;Sp@v}WcG?AJHjmW?_phkdx7tg->0zU zY}=?q?e!4_EjQ8r1Tea~>@AZN--Ebdg}+lJ{{7bd;*!r?OF?n2Rl6;X+i|2pr(Q)4 zm6^9Ja?a3m|NUzC_p)D0f_2`iFl#2AOyC_sn2LrW_J0Ts!VhjW8~%fK@>lryqCozo zqG2rUaLyh{>r0RIe|!euzIeh5URxzSmZ?A=H*>kL#Cq!g922jehkyW{^T8oU>Twhv zr+h&;;r_qZ_hwC6E~v@{0gBv$##P42JZ={D${#RE>0RM_*iX_+^Sg6YIH!0zathVNPR)ZIlgpyvEfkuJ1$ zsAOwhC8IMgpX9xPc+UzLvI+qqSUqe_LnQu|jajn%%#n}1NlTV)p^+Y9|DaX`GT3uO z|3}+}{#@S&`5H;jkaS?yTkKM$%>zZAx*LDr@j%d}=1|(c%oNxlrxb<7*@Cn`gpTbt zCH>8qVuP}-0>F0UHm>OiR%KKQ0!Ay{-Q9vMjxUpe&2h&s75aT1!~~pjizDeB{U4R` zrOTqZzy{jJ%71EiSqWzU48K6Rbx@WfPrbD*fEz*Z+7IQwe-B1h1LXCf<$2`)d6occ zEfVA>%38t7Zkc#!IVXvv7!*n#sXGrCHPKJolsh!J1E_U!JxV7!SfFutVXvZC#5H!< zzhj`~raRmN0XL9Wyj{X_dFH$144zqyUC(*EHVQXz&@B@Hq>h9Tb!Me+H(4>%R>mjb zM=1|7DM*JiJj#E6htJpk-mXJV$;Z33-!FI{fW}pl{YCbG{pVvJ{hI-sL3;9MPx@U| z?E(#cG9xEiBV1P?wdE7vSxHdytEAp2Z!&AB%JsfoorJ)2z31Khil3_RT6z{0=uc*- zeILkLOdJ7&FpETsO?CpVPt#Ypd_JL)cqhvd3>ZifwT)M<(dE%rA+u8V7aQ1LIFoih z10c&9fFg^lCm}GiOSJ}Tvf5dG65hlGKO54uG!N`#JjHj_RImT@G2dy7`XuDK*9_Ot)a( zxYXLi5NakhmeB?|pREsSDYB-{hrYr+3tb7kg3vete!zH20A$yc+Dif^byop66+^?} z=;Eu{a0?H3>v=#qyJ_<6?czPB1cwV17Wc1EX~M8LLZ{x1*+CqRcQwud8C)?wlowY=43>G6EPqdJ>Re6GpqRW zTu|lW&FDAuTn~$M3+EHSm;z^+#>Ti(`?-AWrPA6Sm<|P4QqmUl zqKX=JFkrrEr;twBzxbk!2+r9x&snYU0y*m)(jDtFf4>~BYk}MC;`203w&=iywCl?A z+bl?9^J#w!8xat5@}AFex$fatUBZXU;mSOE6}gms$+Zr%-}}$$Yo<%{0J8>GMy$06s>bs_XPt9?b(p(Bf79~Y+dnDIWvUX>x6QSHSkvZZnHhYqqcc`6 z)L6rG(zD~oXD$o$y2(l1)msl^ZoJ`%qUcu)rHMraq>5o&Xtx3YTte?)M3w4r=A|=f z#o@4pY6kz5~nr(vm05L(l2@ z2;~90Q=US04?lK;S>iJ2Q>$4~uuzNOBT^DYCApW?8N($?Q45`kmw;iqB2A@OLmlo( zT6LT!v)2}Fecpzoj$g<&BQ1VQ+?e|f!@+pnM#XaO=i_q%kX{?vZp-6iD>^^kpBSof zGFI96s58HOB&VGHictUero;GP=^j$bQeqvcOchs;Xg`Wrp)x0S*# zJ->q8{YoTWl=k|V(}qw>su;?*=B-={yWGu2#Ra)4=lbQW4*pLDR2~?moiEJ${O6ZN zV%|&;aG+?r1hghC)4SEBYY9^gJG+J#%`%~w#`AjCJG%XT{~+L6k+1ri^)`aCRi<-6 z2Z-5t03t`X1}^~CXr5v_Eqizlr#6eIgVl+i@$Kb%JJo=AoAH{v}Y_= z6{Eq$H+;U?Iz>OyauT#%nMI?!z-M-_`ym*h{2lvArxufkRV+q`_XLEdw<@tIN3cFcpt3BL$!v9`%l~3$K7$P zP)WPc`@oN1{h=3Ne6_ZwApxCu?APqx;y&~FTF1fGruSJndyQ^u4*t%kt4Ox1 z#|%GO1ghRD<$PYCmgI=o227i-sw82xGjBau-B!M9ghtd67G`NhI}igUp_~2)8HBT&93_* z$DR7L3iR0sHZMjSFFP3TUNOs*%Y+7WCJ1KigOOsu2ISe;HcbN$5bdI292a&MaCd~z2)*uCs~rAY(Q}f) zRF~r!dQ(zp8#$FDJ1h#;YPa`?ggo&>U!^Bro}rGB_TEg~BTp|eX>je-YEI9ed{fj= zL*N51U@gJ)BT9BPZVcs_+W+@t{S337p0%RIR>$wjR^olhEtPA-3ZI$H`oV0nSH69-@CZ+bj>ff7DtZmg^gEqu=GlP- z6tluM{V`4xvZ81Q&@J)Ctky2{Bnd|f0g6$4@w7Rlpkn#(a@YF=GQm)Bq6T8StkCxP zG?hZnhb46&hrAZf*pO=EMB2sK`it2OT$LcT-A%Ya8mCpzWU~Sd?~WyQ8Q%~?R-H9H z<9T;SQ?z9SNPW%y019?DK`Wl4N%;HLR><~Qjs zs&O)8Bq@*W5$SE6$6PYGADJQStHC5We_#3gmOaX#{ z+RoNtg~PD#svvt7KDSrnK9-ckdY{|MDMB2;KX$HWDtPw)WADAAnoPU5 z(Gj~0A|jw5f)x<~5$Poe3MwT^@2Ch!jkM4c8O26ZQHpdaks6U+qexL|=p{g;C$vx! zN+8L(Gw;m&-jSJezCXTozV)ref6OeCJkMQrx%RcM4Z?(1c`53#wBL5KyN2kq!)^_< zom7vJON~bj3NM;+5QlPNeI!*hil6L%sm9}blM(n@24=Zr`1#wdl%xFY!-H||Nu+^W zcV=Nbr{twHz`XgUa6n42OK-z+H1QskZS$+)j;OOWrxv6#QO02}Txmpnh!%lo2-{B$c#r8v(z zJXIxpmLz`x^D-_vvJvRbW(5x(XSA%_KUIHU^V|lAE88|4hXu!l@k`ZdRUY!KDfHCI znv2I58JFFE?!#I=NZsZe$2I40SpLr972IqKzG!$aPA>@$UWwvdQtn9|w6*q90TIcFco}qDSt*lS-1uI4a9gpOVYca=umw1@pN`Uw zcToQhO_b9sBK6vax$_Xrg594Y8f(rCOrN%rfgQN-mkU~>ef(}S31YmlSXk|2dII&L zDt@Z7lN7MNpMRiA)HMo)&cP3u-NwBZ z91b?#;fy<~S8gnnH+Pc!=30W-h-gy0f?saxjhOZ(=iHQBHJE&ORZRZI@F98m(=Wn4 z_h|RnVLx6XrytJ?!+Iq&)aYsGdjG!7-@oW3LHxg6$QR5dkIDh_+`>^EbJ_WpF8e=Wd)5ke7nT;PCQbqN>Tdsbkpv$@gNbvo87E5O~6J zeLw8}{DJRah~gSF{zV29g9s~BjB6bi#xj$H6_fYlUOWUGO{~{G5#BJf5ZLhaC)jJx zl}`ZHIknvk75+_SQ0LXjMJgaT%jjXR)EuR9a1ew-5^pluhN{&DfY31I>3FZMllWT9 zLf(e~*o+J9waTxE6-$(nIdA@4*fVVU`UV&b*5ZkVm=3LuQCJczsIw)x zyj%tOCo4_6(O>z&4;4vp*Sa6N$HYN@g!|wOol`*>P}u@Mc*w9N{{8O=LUOw3C!HCv zKMl+z?!C<)`{SHQkL*J=+!NZAxm^dD+w0qEiP9&wdn#NF`!;R1Br*MLHNE*=X;TV+ znG-+G>y7Sd&3C_`O&TY{!e@764Tl19v1!T!q%6XmTint~)s^3#RDn&0xX80T6?-3Z z(hn~A-uTfsF!TW1CU;v13+u`bsxYalkPYu=w>Ts6p?g{iHdHN_P^m*|% z5;QulfoW;galEx;I~TRJZKhy=&Oc>ray{|V|MaKt%SGu#8rHui48 zJ(VWIAKg(e|GG>%w;eoH3444juJo8remNIaK0grUXL{(P(r!N~hAnQ`_{t8s zh6$uqN^vatTnWP#&((IU4Cy7G3E_uTJuM@8A!@#HgaswXhcmlt3@n~jvBO#q4&wjY z?u9=4U$A><)-aufOTtYfadQn)j#MqqXHSoLItdw54iU^)H68A+*78_IbL2s27Hs5n=tqcri@w$^9z4zFKYe0T!^wZfKa2$D}oq>8%y&O4*=oB}JEp`#SH|G!9 zFiV|!OWL%GM-|^{C9|M!0AkA+xLs}wXv`_-+`xznb-&l)Qn$)em!XQn$PIeAd5K4x z4UeOP!0hdh6Qtq^kk@g>j}WE`v+n3*rM%!gb>mchmBKl@)_98i3;(H9;YBd%upwFP z={>@;-LSLX{T^3l!I0OcyWbppa^G@g8Tl&I_|L)0^E;#!xoY&1VA`DHuKQ_EI~JK% zf&Okfpl`2Mj+9t+Z1|8)QldnFdW`Iy--u3bmcRB}B;hN;Fe2TOk873%4jm0B_}w7F zAKakFa&=MrcTXoZaEV-xIHRqFO41>^gXS{jx0k&f20rHLXPBF~43?cnZY(_Y>G%QW zC`O140_{McX7g9%vi4t*%er~s%(Fki@jnZJr$9|hgC(Kh6Zif!uHeG4Flz^ny@?5kBJxG4g zSos`gC&~^Kr;yG|@u~rUSDcE7k+h8#4lD9seBKlzQ$K!~aunhcN1SgHe&}ybox_4+!0xn6>t>r50>sLUGyO$o zrG*9D2|wq3vhLl;4UD6xSovb#;cx1G@EsR)& z?fmTd)XC)2bwJHwud)L}PnYA%?|u7m`?&z5%D=!q;=FOe=W)eu)q%O8IFL#){hZ7X zU|;}c(JQI zsR687r^k&M?z-o8XB%6%|1( z)b4u1trRVd$Gga^s;WmlUxK8=zP%*xNPLBLJ)B9@L?HZT)aZM5cm&G`LKgm3fX(?pLpdvKK+nVK4i{`E)bA!bJpRhBp z#Jy6U()31`3Rfp^kU>zSR1b)6<3T)@7vkLj6deV?*$X{d5$6goIrZLTCZychIWR|Z ztrR{Ma#W()C7$z-?Hn^i*n%cT&c2%A}>Z(!tCz2_D69|L4W@C~St{7P0d?HI& z$+HNanF6SS%`)*z!zPB&6(&dH)R!m9&R2F%(YX~a<}&)T(APmObNcGySh_5)p?!4f z<=8@^z5+l__e%Qq>wKJYeq0r{JPQcOr3H{Bn`t_4G@Lll*YrJSB{^_v&~N3G{p(Y< z^$@7{k(TnXP zQkE{Sw5h-e*yFFk&bf#94wm{Ey*{n&zyhBgJ0jx}m!el4g*8!IwbhP&cfbI>w$im* zZ_RkJGvodfHi5Z|HqUi#>F25Lbk}zF^VPyeK;QXx&H_cOi-O)sk3Q@Co`Ekea}ylr z`!QA!A1V>mZ13^7WGG6fFg4z~jJvkezGDoKkHQ*P@A6s3Y)Ap~qYQe4B-_nNmu2`p z(Ui1VV_X%Jtw$RK6)G?}sYw|Co%h|@S}4eVpVWF{qGyNa4nPGIkIFT78?XdCJGjd7 zTvwyVGXk7O^;}z7cGlqQaew;w@wcLQ9RFk{9qyT7RvadbDsx=dd~+&KF=EbsNr@uF z45z0335gV+_cqr627V+E2}*fOMU53NeI}@YbMzMA-S0?Wfbf#dq@6-@YvK0W)B<}Z zr+DoTxF&3IYUvJ8yR6g2g+#i3eG>Y~WmG;Ns^3`{Z+9I$Sv={t_+@)kQ$p3~{~PG3 zo-_w-J2_32Kdsr!D|HH-=rbQXepvL-pvH`T**(`O7x&+&UfCO~#1WmNY*s0ry zTUAwa+*_MUO>y(o_v&`)(|YE#a~-7<|GIFxyENoUz#A}EBPC@vm`B96n+Gds)r?r9 z7_H5JWp6$tHS8QAq;>JjI#ZsCRUmB6YpCa4xVf5@$PV&29n7KYPt68#9Jk61MJ0Ky z)O^_FrBWI6UI`#C@OhSxhl%eo%a86yE?(Q%u~+MM_V6^bT1s3@6B{q;m?Q6?7qCHx zK(mdKVbAXS10Rw>tye=gUCX|I7}Ap)b2W_O{7e#=;9B|^T{Qf*QNUdxFvHRa1#vLO z1m0_oGdXxl>uj;h(3=QM+-n07qw84W!$Y8Sag%+upuy!aoe{C05w)y&o^A!Fvz&#}-wW|1}0 z>BExx?*(Z<8mNj~y0zSKtAwfYb5ajD4sP3_X$?7<`hTMlwK0L9t5OI4!z4HtofC9! zP_*g@SI;Ex&#B~}5!gK-PN9*W+fn)lsX*EtK!ewEOr@I1QloX@QRrm^S6|fF#=m|3 z4fFFHUjHE)e7if>Rf#%u_U7lNwOkx%@8w^5pwTTQC;l^H^~bjz;0NTYTdBwX^Sqy; z6})DgxMP4EMEX%>f8%tm_;X%!gU_=YT`hmkwElo!1^Ypukn&8qY&(Teg*YVPe&pZ& zVK?(RZ*>73bwj`leot^n5O)Wx{0u9MZOB>Hpr1?LfAK2jwL$BI3`%1t>kqO?xPQVk z*)Q|}KbxlBI~=a(S^|IC$iM#cz*Xkk?TD9i=LX%V9tGj;J#A~-cQ3yNyy>v_3X>QA zWleu!;W!TVk>iDOf|?rboz&Fzp{S*+J|EGS>94*%y*FFN^>|IWo2UF=*)hy4^i0<- zo1ep~zg|j35b(eP-tGFAZ2@$dZGzwletCO^<6k*#2L!Pwwx5gie-Uj25BvgNe%sCK zhdBO)`~y~;C7}PW#rmI5{`uF|BQRHYYZd;NS79ZeZOoU*1qr|H^)FW7m%wPj2Qvg2 ziR8mYjerRz#2;9dczfpG$e%R_z!8OroUmU!ggEwP?q9F#9>LFhwvO`Fh5p07ZkaWv zcm6-Q3P0L*xY&uEXS^XU-WM7s7z@vL4#?*%R+VJ*3Io5UF#Wb%^?#hS}EMr`Y0y%Dr9G%i+73YHh-o7akq$rQ)61#2-Mfj68ov zVVJ&G8(c0ubRNHIl!8fm^Lft(V5jVVj$;f-BdZY8^71<1w=QjBJAtzjXe@rKP%j!D6;q9=_sK`2WyTGNBd-%b%#V%Di}6U z9DDdrR>rsq*>=B;Y?wOV0WpiWX~E0TO1b9hyI!sDph^_ebT9scLkZk-(7RIw0P5_% z7X$A#m|RO}HN1Io(%+z6Iacbo8{h3un1Z)>e>G*&zq`P&Ww;97UQ^2fzOb!hSLwNb z*bE>P*ULthx>k_h-6jappkI6~f;9OaiSj-anV{Ln1;p$`c-+HTX^|mII39`OP7b4s zpshK7OzWS{n}q&nog~uHqqfZ!`im8YCT=`udDKWR@0N0c){Dy7>b3QZlN51AfZXy> zc1Ju1q5iqU#F@>(USNsn4`u}===tl^m;P7ruX=_)2wi`j_5$~8oA(Nc@Qm#dhfMHE z*I!)>8hs`bg|4AqbqD0q`+_t7)Q55)ngwVC2uGkUvAQY8ACEnB&cMu4Y+2{_(4{nP za9rE=WRtRooSoPydfu@hqd@I5A?6@K7A+D(tcKRz@Al!kSw)nO#&NMcy?72h=r7)EGb9 ztB6t#L<~4|42W*tr~^VfnZ?fo(dV>x`HarOUrQqUWQm7xF7H$zMO`0O*7MTw&vTNM z1FKe~KOg(2&DkdQLlnz|kZXc@pbb?Ww=6~0Eg#}VYm0%o8MQV0s7jme$!(;V^;mPU z1R}CCbzFA+Lm2%z{vk>Q&l}Hh8$PF7IIQxE^(%m7ozK*QxSR%Zkjy=iJ5kh(f+mqP z)NUL7(;5DJXb&K^RBzZx+C(jSQlGaZwsQM{hp>iPQ2m7RJe9#RBF$LV|KZN<*%9}! zScMT7{;~e^*dv4}j$o&D!-33SB?n zTKQ6A$VFOJH`P~O$G9df7T6%oLXSZM=Rdgs{L7ZsqyL}Zm!In*3P#SSzR4Z9@dDGO zs3Tib$owuxsbkBCoSkmgjo>Q+`Y&zX(T6J8JP&vXlLOc7Qzb z&GgyeP>Bs8a`_vwuPy%Jy7dYluuNjuHj6Fx^O2O>wG%gH3&@9u%}^?n6WVE4zWf&M zp`@^zlVBA!U7X8T#`HYL{fnSy60eIyqR$$fGji=?&}r+7Buw#GKV^DxDIC%ul!#y! zxIFz@6vfBio(bjqJ_{E2Ij2xVDeI(v75vKV;U1O%&tUD~(Tl2(_f>l(2#UZEf4F!e z?`&h#`3S%Q*2E)dW2@oy0cGv05~E`Pxr<_ltg3K}S#@=_sPf=~eRO7~e%@=KQ2Bao?YEuBKaqYQ6uQ=~)z{PldE@33XcFUEGsO0oR+50^ z^y&ASSAn?7`#AQ$UtU3JR&e}TkRs|Whob-MOTl#5}TU*0=f+N z2-6=n;QYAKPSu4ISJU(}+FvUN_?99W3E4vcS>l_4M8c#?l(*}c|XOGmRJ%4bY_V$#2C)*dX&ws!n`=9C4&J7zo4e|6Pw#k|`H!-eD^nUy-Uj z&5BfaTD}Ds-E}}WZ<2@-w~B04W|&)g5AXCjZKdpk991`|@VAe)X0}GuzTlFChbt{d zo0s{UFsZ3P54ocIMqy@GDiD0Ll?Vp(Dx)qo-Li?%4j}d4=i6q*Tzc4p!5y|ErM|a<=<2>1wEw^ZGI= z3TV8^!DBAEej*-OE{F%^oqn$evj*L?Sy_U3(1{?{#z`yx<6s%1z}W8_MNWMoqd=F` z20Y(I>*W-N&3DI>cQyrRaXSDtu?tDNIplI^I|xUfPn)Y{V!H(rdjigRgV#P)KYdSF&(4WXY(6zCP#K*}Bm;+wBRpb*iWRxKxO zf_Wl9LwxDcQAJf`+iKpYm2VttGt-rBQ^g&N`VDUu`$g7LRWn|{RabXFv$IT$?s#Uaw$9+P`Tyx!FitZ=I z{Hw#2XLH2^5uZ+dzlZ%C`1-<~6Ki)@cs}`$yY0R_tQwSd=h2f13=V#u?@7A;!fV}Z zZ&y(94&9-2 zR)8!F4({S*25(NtG|V{%ba|qZk_1a#N7FYJ!At@6vsn$(u+L^rf@g1Ey>&}}hAVXN z@RhupgMOqFy&~{Ow?iv$lXx9F4@Z1>UT1tz^U{}tQrucvHNWjUclZZvt!hPUPWEfl z&0X5wK8g8}n$do(rGAk07MmQr+=>!XfV)+33DpaP{jvhmm(xp#he1Gj#8!MYy`)fu z0aOD%RCgWnMv~h!Bznxk+r> ziVI@`e6+=!^5wENj1}hGXC2P~MF;(+p{fAScENZL0ybGKKal>sKK=k0w&6xG%|IP$ z@?v(u_%EbI-TjgDoMG+TpQL9EhS6j?*b~RDL*4-q?71CKC8fxjwiN0^KcK*!wQ`yISx1PEPG7;)yVmmyjAD*jVesFD;tn~} zISJzVD_kf_vLy(j2*u(tev)5^? zc@m~$cX&-5PC%+lT&^&Xo+)x(0L<_3+*m`4UtI~}4ncro;OD8JQlgpXE*xZF*h7tMbFGsvQb4> z$z?bAb{qSQ%Q}&iVjol4Aq!7S$Pt)Y+!Jb)3J)B0q==dI9}1hMdyJ-UU!WMsQ2rn9`Q)4J3)sfMjoxYK8ka^8%Xaq zqn(u|bd|rOFlhMIYmVerX|wfnLN8Y3j|I%+mC#ynd@)WR5$ImGMp5hZ7~3%Ms#VN6 zcP%-KesSloPuPOpTOg=5WpGnET9rfl6jBE!rVlH?%`a83e|AJ;xOvPyE~n7Wa1+Ff z$!>W#mvRHSo2D-J$pncuF!0D>`TJB}{W0e^Mrqr7Tn-ErIhRj))`ii$-g-7GE}fUK zZ6e&qSE*6l0}-QE+l>gtBi6~>1e8TbS5K6cj}PtE{$32y>EoAO1|uj4zy8LMRQNM>R46U@1J@k>s!rxVqx|%|%*9=@czFWY&asi1TRZ{H8?eQ_;iX+dA z{TFISDLq!<)==48MB?7fBRjkbhC2CGP`b@rJ0&z^4wjQgj!WSEmui8;Fuz@#kFEdw@b0eB-DCwpU)qn| zUAwW5jR>UD>AAc$ZZmzOdaHnDsfAUfrQ@tr&kNf6oK?U)-fddcwMWUTST;T$-YbkK z=rS~@G_nZrR9qyS6jd&U8~9!HAFkyY{jysigvZi5dEY?C-e?qq>_MxVwRPsgcy18F;+^D^;*uhFRXX{`pm%4ByHaid-M;7wJN|Uy+G4^bN)S)xF#!p;d*(h%IbC~$C_TnX`-j}a)pv;Qq{23608p#awT*Q)%xQ7$ef6|!1+SZwc(FuJM=s5my^=h_ z>N-p5sqbB|w$5#FuTAiq;RqHj^7aQKrd{hy?CR!+N&5Ag_#KB=M+G$Y)#&|#{K_U% z^9`zQ817s--rZKdvWr7PZD1{5fzQUSPz9nSzLQQ|c+=$(oufAM@oQL{`&)xd>j5vk z_y`@}8Gq@9nPwoOj7l4efaV$|UMn2*?$BOX#telWv5S^i1|sl;(i^Rr%g5E8)MTM6 zO_3FWLTuS$9y)!!K;%phxy?C|ZCxKP8|~R$221zn3q;84H0X60Hj49AewU;Mt6;dk zW&$G|V&vXfXs$f=Y3kgx6@ALcb&##63!_@5>$@x`U$=b)kH6O!>E0q=Zu;J~`WLVH z&zJL>E;iNxmt{RqL`i2#2ZWASvx`Wa4IUR z)Fz-^TcO*;tMZ&j<=ynhlZU_WHz*;L3hhsLoZU-5iH z_c8st;+(svJ?5U;9#g#%E~Vq$XEZs}#_bR#eYRaszqi%zk0yAuKW$od9p$t5GBmIs zST}LCr*#>ehjmsr$X^O-Ct_vz`cdpC&~KbAFX0BXDZ7y0n)>Wp2*BCleN)D5ay`09ijX7~%HQ3WHl zzp&pS5aDa}tNd)I_u4q7Xr#DF>^~msPuKMW$F&OELCqN9in1)LtuDqnbr}0D)J_SY zcT(NqQ->zw9{792RnEN_>xXwa7uRmbAkR}tpWMloUh&jK$kGNYbGM(Taa)~_H2b8H z5} z;l53mIzkqvtp)|y74>;Oq0-<*19uyz_N{w-aTIMr$8@tSCAYGNitw3VFl8k}sD!VF zy&HtJBgf=iKIi3uIG}!h`0Hq=WYzbRX8Xv~I>*Ritq=h{i~04oZ{fV5QaQ41X^k5T zQOU^XS-GW|zD_w^Js}oegE>_9vX3jzA(^;F=9V{WOfC)cVlYQMw6L*YG^55DBKr8kFk@sc$~OYbRC3D%*J z%B9Phj+wu#eG)+fA}-ZA!a4^tL~Vn2n~Wwu706|uVwzH39n;9u@#fbWi{rf8uZQLy zNYqE!vt@Y{`uozzqJiX`NAX@+>w-7TX7?>cR`gS5T7i$$z*p&riO%>_( z8cdmsDjM#tT)*qO9AhUX=W`*$RA|{-6bo4{86Ba>SS1pYTjXg?Q)cY!kc|iV(-rKB zKDWPw4!e)-WZ10gl|pmXsV zA(Qqa-G$xel6C%cADYbJGv_zTepfTLzm#xZr@w<8s$Qu4+B47~nxj90sNykjMM z(dOHFAK*@=Q>7e?2+%1NA%3@;*#%7(DbTE))U2Em)uUiSOvE5^ZJsnzrhw!wcPX^l zxId~KA@5IF1qceSAG>T3)ty?T;>@=GB#wJuLXBm642 z^QTtw1HFOH&djV_Za>m3FMJ)d^LuDx{o_vQ{3Cbj@^uZ=yORFD*2^dK0<0^ zZ^ES?-?kY@uQX(#>uT8|O%pn`U1etTuNQJ78glk4jbN!MMTZDII=^gCKn(tkD@t0p zum}%i?fD)-1G5nXBi~@pC^{+MSX87eVm99-a4n&BS&>F4i2T=*G zrV-XMJOo&Nmvr7zi-Pv2@PhP>Wrf)b-zT+OIDxssAZYIfRJ^Ncj+%S4r%C|+crVVK zUK*Ov?L`W(9#2q8aYyMi56!3DNy6MBv1N@1wTC`j!67l%E6wx6kx_y4PT5Y3_?b~Y zjvR*poxhvABrcYYMQvAbc}WZ!X>P_4za*sC^Q0WjlKv6p4UEWR1qMlC1)knq`M)+_R)sqs-jC+X^i<@WEZ-Sb^YXiB}Hy4 z|K|?RN%^ggWg|f(|{8DvogFwH8E!etOG>Q5V`6@EKYbj*)h^!W%X@fbMHUY-&CJ(ikY8UiL3 zZ}IR*le_r>^pD@UsD(e4Tak44IQRrn`xIC^uU`0iFJgT*ON88EQW{z0Q3tP%IG%IZ zps?}H4yuv4uaC`zjY==;nMr)M7i8EGRk#YH616FiT#M4g$kS9Dp3W@*QmKH(A6d@` z`EBZ5&4KRw$Y#?oRd!Hw0#N%9|L&Yl1zzG)GkQzE{aDTZ|rWM?w2bml6t9GI$T?ND7CEA`Rn)_A#Q@uQ&DN$Mc-v}xrqcn z(1i86z1f91E3#y?MPY=-L6SK!tpl}5qf(raRRBM;uYYb$HZ73O z!s-#q8Lp7*8?PzGr;CYm(+s5h*d-c?$|z+xlL@U;OI7{FyU%3jz<&)yG`?e@U9aqT z=i{#%NYB#{;LOS?GF4<$vr2sU@>)U0(o4-#`dp4ZMdcCSjnt?QJF7)SOWlcFQT3pI zapFb4pb(J?zrYwy&?C=Sl_#XuHp3eG&F=2irOsAK>CK z-7PwY{1I3}?avaLe#MH*($&M6{+aXU}O|H zq;<)LXceSx#FFgaEdPc$)A09r&x8>Cs+}c9L$srUV_x$za3Cajq?~gV)8de&j~mHR6TzzDeuuV7zZK&Gzm@2`<`48J zeY>VOf3w9-yh}IS^?NOkk>==3IUb?ijYX~|KOJco5l34hv7hzs_Sr<{{6^N>#pZBO zKGA>Vd)~-Cks1N|YP~G!w3!%ha}#vJ|X=*}`+LdUw`G#A!`ID&g)jYtAK0;85)5e{g%_Hcdbue@vEu zeH5E>r09KTHQ!|7{pX|y~1c9n}#gFQ)gS{sqv{O&mk*HUVi;cMl)@RG1AX;r& zh{Y=!C!%J{MqBc_`=<*^_6Im_r}Y=Jw##hSomc^rj0`E@JJ=P8^%A6trCNZ^9ptg{ zT{wl`P2B58stDHB^Yr9MJ?c{B?$P3apej(}q;~OA?foWV>_)QP5xNX@Z7*j_ z*IF*?nch6tnpYa#NVYRO`(y?l1Qe#z)G#z>b9fh5m-14J5^Sb)GNJ28F}W+cKGFMx zSP9`b>S#{!a#@$QLC5YPey@RJkn+j7sk}pn1YB1E0-08r9wt9Qpay`TOy*azLi3Pt zi#+gGaxtL8Ng?dmg``^t5WQcX)|E}?nu+GScbh<-%RK2CR4no$v00;Ly`JgB%Nq=@ zg_U2jWB|oZu@^hJjg!iVb3eYIy777I4{7W5yZD@L@{bNBG8A1Z#wV!M{Yr$F#;{6C z=4pG7kwtp{C}!`5M7osajfN?xn?^ApU{Kkf-CW%fH%6@YOgZ`(SIfr^n7>IQZ5}Qo6&ZpFmj8crEk&<}8$rF_r zAiRE%lXb{ZNbAXde%?n=#zfdCF;!PnNX|jV3CQQJjINGCKaiZO158J6@;y{RexeU9 zp85lE{3GxcXVp4PC`PXL8IQEk+x(`#-fbQ#*D{b|%&K7U&xH(RB9$l~PFfRdc^viE zgVA)+U6v2-Q1T-q2jtdQN8hhkvr6@o9)&}~wnJs*s;Jbgl9_@p5+rl?F38XT7uBAP zkpfjXNndDD$YY2p)T&SC;%eq;;gA>0`9+~Xg`x)%8M*z2BSpr`>LEFm-28~l_PDYL zCynU+VaAffkA38oz}KqiW|kt7|1|NISFMk;*^d&Jh=5gXs?)((#)GAESqk$n%d&d; zTqIjASRaM=-pMmwsm=7iF!(6%jtamwklzBI*63=Aa*?VODsHvtDJhw(pC9w587&@IwxNgj-HMPmF_<~ zhn%NVc^zfgbi$0xeS8914E?H2v(QJ%=gQ0|J+RUA65&VMxZMJX3w`+shKYoIObZPU zLX`C@2h9^FN>bEAUx1-C8YlpETM_(_3&HA+*{CSFBp9YQ$%{0_rS~k>MZeuOOfHi$ z+kx*epmBKx)-dxB%eb^D%@K`F;!Hz03l$th+h&!RzgmO z%f2r=LehgOaoHt$wa5sVTW~+q+sz&p#;;&_+(&*cJHJUcC!g}2feh6*o05uun{}O` zB-u>C`e zBvc`V>U+z##g9~Jq-5w9@A3Xj(A+4-$zLtDe_IjGw!HTa2p~&SeL{~Ym6a`Af2XF2 z7rSOB0|iqgr)qSkoWaDyuEY+1cDb>a>N0FDp=z1hN%$A+T;)Gu_hJ|BB;l8HP}Ujc z>@RGLi2P0J)RMEm@0P_0Sr8 zeL^PAOQ*07(XY%8yt2H53S}~gCmOPUAYjGV|5cfwA871$?bC*YPFf-4q>cS@WN#N+ z@cA$cHA0F2Lvii4i84CUerJ7#KP%^>|69FO|FOb5uv#0L!K3+wiK4ss5yo$GQk_@V z1!|HX?b0YWv3)KefVh9YXug_)rfi6yYchD)GIY+39VVd5yUX(E)am+-rBH1}yvjB%xy&e<-Y_+RK2tTB#e?`O0g}+jUz$Mv)J>!C*6?v1N z*Jmc8xQ-R|+2W>JTWM>hCF4bAnH6^N09sZKkPa)IcvtRwe$aSjV3&wnXXL>3@U&nL zQrWcK!UX=Q0nY(aL5EJ>;tfxH%j#O?N(~I469F`E&4kZGY(GD|-{VrWx;h#@(gO1E zRt(v{5I-^)^~|}rFTKo*@EC6>OI(dOm$fp46c;New^PkC*QcW+5KC zg;fyKWj8*4&E(v(GT;tP5t-^h5U+d*Q!_%* z*NyEok0i1|x^q#VJU6jBXG$Ud-ZdeS1iuNWE3H{Z+9$`O+nrRo686^6JkJ+FeRvK3 z_*x~Bv5pU~E%QR_bc1}W!^UNK&<{`O%eV8G>&$SwCL_LPEsyJOn$}vawoj<^rDRrJ zentPX;Y21(J!wSAxJQiiR}s|$z*=MtX%YoG`q^HHV@6(_(Uj26Q1fLAM2INs&-@*# z$CrM797xapn(!ixBquB1+;_TY+RM_z?*r3Jk#hrUrJoR zJXz0IZ(v0|V5C6Mt1pB=E7=zt6@E6^-bWbW141wwT92N zAe$rByk}zUGV9xeVvu~&*}gi(xfY{iYWp+Abe&2-o{XB@SRBUJdV0~beSLexEbe|0 zvpCTel{)}Mzl|iB+#!|)Uv$J-Lm`|zmR(Otd8K;D2jc0%U80+_?%85ufVFRtZw8@DQt9rY{ zuAC(>FMNIK3NSzd^{3UITr&OSmvKz97O_5O7Qg4PjEj?J8?1r5V^Jw1+=IiRKV2G| zm=KA}81hQ&w9rya!~F^h(e*ugWqQ8!8GeOA+hTi%#Os5HyxyH#MjAJbtBGalcntVX zi&8Xq(%2gp+W(HXJXHC@jIQn3Xov3R7GKA&(kIpnO;LA}AQ_Kl@|wHzYM*$D$QDRr zq3ej%g?d*8Ng&iN0jaFKM$RYX5#AffR@t^1j7*fzg}qAAsDI*!=5Jj7pm6IJE174i z{d9}+!m9TO2x8k%j6+{R9c5o|uPS5vHJdC>o~33BueYm@aU!q^4t6>{&wCh#%u*c~_a#5Gbj&%D=)Y_dhioo1 z*+DH#XzyATeyPJ_y5Da!banPCd;R>%6B*n}s>_S=TcprT8xN4==bpnJ0|iU}MWWEc zKz?g2`q-*LgmqDTnzN??O;=OQAPDe97HmQeFI?WBuY}it0tu&*U#_Xe0l$U}R42&{ zkobJ48LN{RI+r6lU#Cf_$DE1!E)eY!>ofsGUOa$sjOcl1oRpXlmz$0E=+L$vc44h| z$}dE#uC=NoxnUIx>H|KL@f4XQXYU_#gFL|k&UrST$q%Ny21;BPTIPorKGRk-0OFMO z4s14Bv{=$%>;tz^b#x~Q@X$&?#iio_0r@Dh8zc{_cNah#q||FIObCiyxsa;iI-h~6 z!}2^SQtkh#knr%AK$r%z4o7$N%8DE@_HG-uZVb>t=XMusTe0Amd8r*yEo8j*?9>L) zrZoPN1h5Z7#Vj-!w!MD=c>d5>m#R{_;}zmW-gZ!31dX~z)71jr-!A9eEMomz(ht1% z4;<%xF>K;=CjWc)`j)Pp&nt@*1-s|+dx}?n3%jJfXAc?mTwEP z(7{p|Gj+b1#9-@J_M7F5b*gZsJSu20pqgr#+6oQskj|-m+g;}iw$3csiRW)|HnYTE zEQHlfvMRqb!7>;bqO1@HjQGU`sd-H0i=#x$aX~Btb`~0omZ;2$x6%W?XoPkK3k&I< z-pyBYCV*PQ!g~H@ad%g5-a>dWKDWE>U1zhi*%#R6pE<+|vN_cDuH1Gj*U)C`;6Z4@ zh&Ap`Ki2^Qv^;-JOe8D*gC_Wy?09i3Lkuqwrv3L~DBvSp(B2DJ0O%kj81nes(v1J0 z81FMs!57s?TXwG71wT z^XsCX7cp}~!n$4y8CryWrGkmR<@o03Z@ihS0?>tb#$OLe>|AH15RL?k=c{{6=b1M3 zc;>m?K`pd^*I9jdCcmP;U7kp^_lYlf`>Ceo@f8-@cmED=-k;b0p-L5t9v?x5X9ks6 zn3&?;q`(-Ffb|?}v-wi{zQdeVebR^fSy8@cNQ|9*taz`7E`V6km(zb#6{Y<^M4H%T zu+KX}#HGHWDdO1HX{8*~pReUFfrfkh%>of$Z)5h66$)TQ?~d5T30G|_1{>--M8Wg` zJba&%qmY5JW>;E-hFacDb2nO-wrbC-+8%gl1eNnRc1Y(1GzlVnJe>2t89l!*%sugW z=iOAc%k6H8g`yU>;)?n|3HE)p83J7h=xyDTr6~ci(@j0@yWK`|qKMI?M+O^(b?>mISc~H*(+nZr4G-U;y?t=mz#XDYT7w z=5<9jpP#H90MNfHa#tu2!C{@?@(#S8OiJgTP5(KcAmV`=R~n71*Sd zJHl`O_EvwrX9F;&Q3`(Xn|F$t8>*;-)eCu&roU;SfB)IDMk+~Gb2FZsZRhU`a%Arw zvLtV$?q-X~{P`qv@T#TxJ#w4g;P)rJ$6VX{#y2*vE%S5+x`DkLZ?T-*yqe7a^K$E@ zncMfbE#KPdO$Tl3c>jDz_8i!{)0<7omLdC_CE2pxn=Hwe%i8kOe;tag;N+*!V=E&2 z>umh@A6pUGf3F79t8674o5J?3h-}j#0nTy+Jb{OqfR^aY+>M=>JD4E^;T~E`=Qv1$TmCM zt%z)sv)wB3Y<9L=C7#XBb_-DWTWjb{d6<7K!nXVV+%ys zbV#;9gw1iy7KpGpuGs<+HpMkt?7@F1GHy{O|KaXz1t**Q&Q?CX+3#%S)0^+kRzAJS z@BH6Gcn@Us3H|h5Kh0#3Y)at@=SVdU+{O}0Z&Yh`yKk#hh8vo@4xBiaHDh?_) zKOf+Db%N(W2G5%vZ{0Xpo8fy)58SLc#At^`dkC!%Cru^~?FYWr3%ld!k(fH-WC!<@)lG34ugDrAl!&Gm1}-;xXbAt%dyEbEQ&ZO4CW7 z%5d-~XiX%JbY$9jzV3eP1!3I`{d?_XH)r0LObqOko`Q-{p6@%(04-7>S z>d8yXO??a%xiu8~iS5CDId+dS;LB#M38q<OYY!B_FuFh5c z(`FZ^WR}k&N79N`WtbG+Pg8#{SNG>p5H3;9fOU_E_^9=Dg-Q|{zB;lvb_E*+v%1S~ zWV#`TXsrwR(FQs�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 literal 0 HcmV?d00001 diff --git a/website/public/devtools_extension/extension_tab.png b/website/public/devtools_extension/extension_tab.png new file mode 100644 index 0000000000000000000000000000000000000000..c86c5b7217571d38f0104d7c43aa040ad493464b GIT binary patch literal 6695 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 literal 0 HcmV?d00001 diff --git a/website/public/devtools_extension/reactter_extension.png b/website/public/devtools_extension/reactter_extension.png new file mode 100644 index 0000000000000000000000000000000000000000..221c207c77ad9e4e0c680598170a6b50954ec992 GIT binary patch literal 119584 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 Ct>26Q literal 0 HcmV?d00001 diff --git a/website/src/components/Highlight/index.ts b/website/src/components/Highlight/index.ts index b37d04a8..bc97c5c4 100644 --- a/website/src/components/Highlight/index.ts +++ b/website/src/components/Highlight/index.ts @@ -2,5 +2,5 @@ export { default as HE } from "./HE.astro"; export { default as HK } from "./HK.astro"; export { default as HM } from "./HM.astro"; export { default as HS } from "./HS.astro"; -export { default as HN } from "./HN.astro"; +export { default as HN, default as HB } from "./HN.astro"; export { default as HT } from "./HT.astro"; diff --git a/website/src/content/docs/api/extensions/build_context_extensions.mdx b/website/src/content/docs/api/extensions/build_context_extensions.mdx index 76a5d9b2..9de81035 100644 --- a/website/src/content/docs/api/extensions/build_context_extensions.mdx +++ b/website/src/content/docs/api/extensions/build_context_extensions.mdx @@ -235,7 +235,7 @@ V context.select<T, V>( - `context`: The <HT>`BuildContext`</HT> object, which provides information about the current widget in the tree. - <HT>`T`</HT>: The type of the dependency you want to select. It is used to locate the dependency from the nearest ancestor provider. - <HT>`V`</HT>: The type of the value you want to compute. It is the return type of the <HM>`computeValue`</HM> function. -- <HM>`computeValue`</HM>: A function that computes the value based on the selected states. It takes two arguments: +- <HM>`computeValue`</HM>: A function that computes the value based on the selected states. It takes two parameters: - `instance`: The instance of the dependency of type <HT>`T`</HT>. - <HM>`select`</HM>: A function that allows you to wrap the state (<HT>`RtState`</HT>) to be listened for changes and returns it. - `id`: An optional identifier for the <HT>`T`</HT> dependency. If omitted, the dependency will be located by its type (<HT>`T`</HT>). diff --git a/website/src/content/docs/api/hooks/use_async_state.mdx b/website/src/content/docs/api/hooks/use_async_state.mdx index ab48ca28..4a43df89 100644 --- a/website/src/content/docs/api/hooks/use_async_state.mdx +++ b/website/src/content/docs/api/hooks/use_async_state.mdx @@ -4,7 +4,7 @@ description: UseAsyncState hook documentation. sidebar: order: 2 --- -import { HE, HK, HM, HT } from '@/components/Highlight'; +import { HB, HE, HK, HM, HT } from '@/components/Highlight'; import MDXRedry from '@/components/MDXRedry.astro'; import StateMethodsLite from '@/content/docs/shareds/state_methods_lite.mdx'; import * as NoteLateState from '@/content/docs/shareds/note_late_state.mdx'; @@ -17,57 +17,91 @@ import * as ListeningChangesState from '@/content/docs/shareds/listening_changes ```dart showLineNumbers=false UseAsyncState<T>( - T initialValue, Future<T> asyncFunction(), + T initialValue, + { String? debugLabel }, ); // UseAsyncState with arguments UseAsyncState<T, A>.withArg( - T initialValue, Future<T> asyncFunction(A arg), + T initialValue, + { String? debugLabel }, ); ``` -<HT>`UseAsyncState`</HT> accepts these arguments: +<HT>`UseAsyncState`</HT> accepts these parameters: -- `initialValue`: Initial value of <HT>`T`</HT> type that it will hold. -- <HM>`asyncFunction`</HM>: A function that returns a <HT>`Future<T>`</HT> to update the state asynchronously. +- <HM>**`asyncFunction`**</HM>: A function that returns a <HT>`Future<T>`</HT> to update the state asynchronously. This function is called by the <HM>`resolve`</HM> method and sets the `value` property. +- **`initialValue`**: Initial value of <HT>`T`</HT> type that it will hold. +- **`debugLabel`**: A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). ## Properties & Methods -<HT>`UseAsyncState`</HT> provides the following properties and methods: - -- `value`: A getter that allows to read its state. -- `status`: A getter that allows to read its status. It can be: - - <HE>`UseAsyncStateStatus.standby`</HE>: Represents the standby status, indicating that the state is idle. - - <HE>`UseAsyncStateStatus.loading`</HE>: Denotes the loading status, indicating that an asynchronous operation is in progress. - - <HE>`UseAsyncStateStatus.done`</HE>: Indicates that the asynchronous operation has been successfully completed. - - <HE>`UseAsyncStateStatus.error`</HE>: Signifies that an error has occurred during the asynchronous operation. -- `error`: A getter that allows to get the error object when the <HM>`asyncFunction`</HM> fails. -- <HM>`resolve`</HM>: A method that updates the state asynchronously by calling the <HM>`asyncFunction`</HM> function. - - Syntax: +The <HT>`UseAsyncState`</HT> utility provides the following properties and methods to manage asynchronous states effectively: + +- **`value`**: + A getter to retrieve the current state of <HT>`T`</HT> type. + The `value` can either be the `initialValue` or the resolved value returned by the <HM>`asyncFunction`</HM>. +- **`status`**: + A getter to access the current status of the asynchronous state. The possible statuses are: + - <HE>**`UseAsyncStateStatus.idle`**</HE>: Indicates that the state is in stand by and no operation is in progress. + - <HE>**`UseAsyncStateStatus.loading`**</HE>: Indicates that an asynchronous operation is currently running. + - <HE>**`UseAsyncStateStatus.done`**</HE>: Indicates that the asynchronous operation has completed successfully. + - <HE>**`UseAsyncStateStatus.error`**</HE>: Indicates that an error occurred during the asynchronous operation. +- **`isLoading`**: +A boolean getter that returns <HB>`true`</HB> if the <HM>`asyncFunction`</HM> is currently in progress. +- **`isDone`**: + A boolean getter that returns <HB>`true`</HB> if the <HM>`asyncFunction`</HM> has successfully completed. +- **`isError`**: + A boolean getter that returns <HB>`true`</HB> if the <HM>`asyncFunction`</HM> has failed. +- **`error`**: + A getter that returns the error object if the <HM>`asyncFunction`</HM> has failed. +- **`future`**: + A getter that returns the <HT>`Future`</HT> of the asynchronous operation. +- **`uValue`**: + A reactive state object that provides the current value and notifies listeners when the value changes. +- **`uStatus`**: + A reactive state object that provides the current status and notifies listeners when the status changes. +- **`uIsLoading`**: + A reactive state object that provides a boolean indicating whether the <HM>`asyncFunction`</HM> is in progress, with notifications for changes. +- **`uIsDone`**: + A reactive state object that provides a boolean indicating whether the <HM>`asyncFunction`</HM> has completed, with notifications for changes. +- **`uIsError`**: + A reactive state object that provides a boolean indicating whether the <HM>`asyncFunction`</HM> has failed, with notifications for changes. +- **`uError`**: + A reactive state object that provides the error information and notifies listeners if an error occurs. +- **<HM>`resolve`</HM>**: + Executes the <HM>`asyncFunction`</HM> and updates the state asynchronously. + - **Syntax**: ```dart showLineNumbers=false FutureOr<T?> resolve(); - // for UseAsyncState.withArg + // For UseAsyncState.withArg FutureOr<T?> resolve(A arg); ``` -- <HM>`when`</HM>: A method that allows to computed a value depending on its status. - - Syntax: +- **<HM>`cancel`</HM>**: + Cancels the currently running asynchronous operation, if applicable. + - **Syntax**: + ```dart showLineNumbers=false + void cancel(); + ``` +- **<HM>`when`</HM>**: + Evaluates and returns a value based on the current state. Useful for rendering or handling state-specific logic. + - **Syntax**: ```dart showLineNumbers=false R? when<R>({ - WhenValueReturn<T, R>? standby, + WhenValueReturn<T, R>? idle, WhenValueReturn<T, R>? loading, WhenValueReturn<T, R>? done, WhenErrorReturn<R>? error, }); ``` - - Arguments: - - <HM>`standby`</HM>: A function that returns a value when the state is <HE>`UseAsyncStateStatus.standby`</HE>. - - <HM>`loading`</HM>: A function that returns a value when the state is <HE>`UseAsyncStateStatus.loading`</HE>. - - <HM>`done`</HM>: A function that returns a value when the state is <HE>`UseAsyncStateStatus.done`</HE>. - - <HM>`error`</HM>: A function that returns a value when the state is <HE>`UseAsyncStateStatus.error`</HE>. - + - **parameters**: + - **`idle`**: A callback function invoked when the state is <HE>`UseAsyncStateStatus.idle`</HE>. + - **`loading`**: A callback function invoked when the state is <HE>`UseAsyncStateStatus.loading`</HE>. + - **`done`**: A callback function invoked when the state is <HE>`UseAsyncStateStatus.done`</HE>. + - **`error`**: A callback function invoked when the state is <HE>`UseAsyncStateStatus.error`</HE>. <StateMethodsLite /> ## Usage @@ -77,7 +111,7 @@ UseAsyncState<T, A>.withArg( <HT>`UseAsyncState`</HT> can be initialized using the constructor class: ```dart showLineNumbers=false "UseAsyncState" "asyncFunction" -final uAsyncState = UseAsyncState<String>('Initial value', asyncFunction); +final uAsyncState = UseAsyncState<String>(asyncFunction, 'Initial value'); Future<String> asyncFunction() async { await Future.delayed(Duration(seconds: 2)); @@ -92,7 +126,7 @@ class UserController { final String userId; late final uUser = Rt.lazyState( - () => UseAsyncState<User?>.withArg(null, this.getUser), + () => UseAsyncState<User?>.withArg(this.getUser, null), this, ); @@ -130,8 +164,8 @@ print("${uAsyncState.value}"); // Resolved value ```dart showLineNumbers=false "UseAsyncState.withArg" "asyncFunctionWithArg" final uAsyncStateWithArg = UseAsyncState.withArg<String, int>( + asyncFunctionWithArg, 'Initial value', - asyncFunctionWithArg ); Future<String> asyncFunctionWithArg(int arg) async { @@ -158,8 +192,8 @@ or <HT>[`Args`](/reactter/api/classes/args)</HT>(A generic arguments provided by ```dart showLineNumbers=false "UseAsyncState.withArg" "asyncFunctionWithArgs" ".resolve" ".value" final uAsyncStateWithArgs = UseAsyncState.withArg<String, ArgsX3<String>>( + asyncFunctionWithArgs, 'Initial value', - asyncFunctionWithArgs ); Future<String> asyncFunctionWithArgs(ArgsX3<String> args) async { @@ -172,13 +206,12 @@ await uAsyncStateWithArgs.resolve(ArgsX3('arg1', 'arg2', 'arg3')); print("${uAsyncStateWithArgs.value}"); // Resolved value with args: arg1, arg2, arg3 ``` -### Using with Memo +### Caching the value -<HT>`UseAsyncState`</HT> does not cache the resolving `value`, meaning that it will resolve the `value` every time <HM>`resolve`</HM> is called, potentially impacting performance, especially if the <HM>`asyncFunction`</HM> is expensive. In this case, you should consider using <HT>[`Memo`](/reactter/api/classes/memo)</HT> to cache the resolving `value`, e.g.: +<HT>`UseAsyncState`</HT> doesn't cache the resolving `value` by default, meaning that it will resolve the `value` every time <HM>`resolve`</HM> is called, potentially impacting performance, especially if the <HM>`asyncFunction`</HM> is expensive. In this case, you should consider using <HT>[`Memo`](/reactter/api/classes/memo)</HT> to cache the resolving `value`, e.g.: ```dart "UseAsyncState" "Memo.inline" final translateState = UseAsyncState.withArg<String?, ArgsX3<String>>( - null, /// `Memo` stores the value resolved in cache, /// and retrieving that same value from the cache the next time /// it's needed instead of resolving it again. @@ -192,6 +225,7 @@ final translateState = UseAsyncState.withArg<String?, ArgsX3<String>>( }, AsyncMemoSafe(), // avoid to save in cache when throw a error ), + null, ); ``` @@ -201,7 +235,7 @@ final translateState = UseAsyncState.withArg<String?, ArgsX3<String>>( ```dart showLineNumbers=false ".when" final result = uAsyncState.when( - standby: (value) => "Standby", + idle: (value) => "Standby", loading: (value) => "Loading", done: (value) => "Done", error: (error) => "Error: $error", @@ -220,7 +254,7 @@ Builder( ); return myController.uAsyncState.when( - standby: (value) => Text("Standby: $value"), + idle: (value) => Text("Standby: $value"), loading: (value) => CircularProgressIndicator(), done: (value) => Text("Done: $value"), error: (error) => Text("Error: $error"), @@ -242,10 +276,10 @@ uAsyncState.update((value) { }); ``` -Use <HM>`refresh`</HM> method to force to notify changes. +Use <HM>`notify`</HM> method to force to notify changes. -```dart showLineNumbers=false ".refresh" -uAsyncState.refresh(); +```dart showLineNumbers=false ".notify" +uAsyncState.notify(); ``` ### Listening to changes diff --git a/website/src/content/docs/api/hooks/use_compute.mdx b/website/src/content/docs/api/hooks/use_compute.mdx index 95bcc88e..b3f3f039 100644 --- a/website/src/content/docs/api/hooks/use_compute.mdx +++ b/website/src/content/docs/api/hooks/use_compute.mdx @@ -21,13 +21,15 @@ import * as ListeningChangesState from '@/content/docs/shareds/listening_changes UseCompute<T>( T computeValue(), List<RtState> dependencies, + { String? debugLabel }, ); ``` -<HT>`UseCompute`</HT> accepts these arguments: +<HT>`UseCompute`</HT> accepts these parameters: - <HM>`computeValue`</HM>: A function that returns a value based on the current state of the dependencies. - `dependencies`: A list of state(<HT>`RtState`</HT>, learn about it [here](/reactter/core_concepts/state_management/#state)) that the computation depends on. When any of these dependencies change, the `value` is recalculated. +- `debugLabel`: A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). ## Properties & Methods diff --git a/website/src/content/docs/api/hooks/use_dependency.mdx b/website/src/content/docs/api/hooks/use_dependency.mdx index c00f5f91..faf8981f 100644 --- a/website/src/content/docs/api/hooks/use_dependency.mdx +++ b/website/src/content/docs/api/hooks/use_dependency.mdx @@ -20,13 +20,19 @@ It's recommended read that first if you're new in Reactter. - <HT>`UseDependency`</HT>: Default constructor to get the <HT>`T`</HT> dependency. It's similar to using <HM>`Rt.find`</HM>. ```dart showLineNumbers=false - UseDependency<T>([String? id]); + UseDependency<T>({ + String? id, + String? debugLabel + }); ``` - <HT>`UseDependency.get`</HT>: Get a <HT>`T`</HT> dependency inmediately. It's similar to using <HM>`Rt.get`</HM>. ```dart showLineNumbers=false - UseDependency<T>.get([String? id]); + UseDependency<T>.get({ + String? id, + String? debugLabel + }); ``` - <HT>`UseDependency.register`</HT>: Register a builder function to create a <HT>`T`</HT> dependency. It's similar to using <HM>`Rt.register`</HM>. @@ -35,6 +41,7 @@ It's recommended read that first if you're new in Reactter. UseDependency<T>.register(T Function() builder, { String? id, DependencyMode mode = DependencyMode.builder, + String? debugLabel, }); ``` @@ -44,25 +51,35 @@ It's recommended read that first if you're new in Reactter. UseDependency<T>.create(T Function() builder, { String? id, DependencyMode mode = DependencyMode.builder, + String? debugLabel, }); ``` - <HT>`UseDependency.lazyBuilder`</HT>: Registers a builder function, to create a instance of the <HT>`T`</HT> dependency as [Builder](/reactter/core_concepts/dependency_injection/#builder) mode. It's similar to using <HM>`Rt.lazyBuilder`</HM>. ```dart showLineNumbers=false - UseDependency<T>.lazyBuilder(T Function() builder, [String? id]); + UseDependency<T>.lazyBuilder(T Function() builder, { + String? id, + String? debugLabel + }); ``` - <HT>`UseDependency.lazyFactory`</HT>: Registers a builder function, to create a instance of the <HT>`T`</HT> dependency as [Factory](/reactter/core_concepts/dependency_injection/#factory) mode. It's similar to using <HM>`Rt.lazyFactory`</HM>. ```dart showLineNumbers=false - UseDependency<T>.lazyFactory(T Function() builder, [String? id]); + UseDependency<T>.lazyFactory(T Function() builder, { + String? id, + String? debugLabel + }); ``` - <HT>`UseDependency.lazySingleton`</HT>: Registers a builder function, to create a instance of the <HT>`T`</HT> dependency as [Singleton](/reactter/core_concepts/dependency_injection/#singleton) mode. It's similar to using <HM>`Rt.lazySingleton`</HM>. ```dart showLineNumbers=false - UseDependency<T>.lazySingleton(T Function() builder, [String? id]); + UseDependency<T>.lazySingleton(T Function() builder, { + String? id, + String? debugLabel + }); ``` - <HT>`UseDependency.builder`</HT>: Registers, creates and gets a <HT>`T`</HT> dependency inmediately as [Builder](/reactter/core_concepts/dependency_injection/#builder) mode. It's similar to using <HM>`Rt.builder`</HM>. @@ -71,6 +88,7 @@ It's recommended read that first if you're new in Reactter. UseDependency<T>.builder(T Function() builder, { String? id, DependencyMode mode = DependencyMode.builder, + String? debugLabel, }); ``` @@ -80,6 +98,7 @@ It's recommended read that first if you're new in Reactter. UseDependency<T>.factory(T Function() builder, { String? id, DependencyMode mode = DependencyMode.builder, + String? debugLabel, }); ``` @@ -89,22 +108,24 @@ It's recommended read that first if you're new in Reactter. UseDependency<T>.singleton(T Function() builder, { String? id, DependencyMode mode = DependencyMode.builder, + String? debugLabel, }); ``` -### Arguments +### Parameters -- `id`: An optional <HT>`String`</HT> value to identify the dependency of <HT>`T`</HT> type. -- <HM>`builder`</HM>: A function that returns an instance of the dependency of <HT>`T`</HT> type. -- `mode`: An optional <HT>`DependencyMode`</HT> value to define the dependency mode. Default value is <HE>`DependencyMode.builder`</HE>. -Learn more about it in [DependencyMode](/reactter/core_concepts/dependency_injection#dependency-modes). +- **`id`**: *(Optional)* A <HT>`String`</HT> used to identify the dependency of type <HT>`T`</HT>. +- **`builder`**: A function that returns an instance of the dependency of type <HT>`T`</HT>. +- **`mode`**: *(Optional)* A <HT>`DependencyMode`</HT> that defines the dependency mode. Defaults to <HE>`DependencyMode.builder`</HE>. + Learn more in [DependencyMode](/reactter/core_concepts/dependency_injection#dependency-modes). +- **`debugLabel`**: *(Optional)* A <HT>`String`</HT> used to identify the hook in the [DevTools extension](/reactter/devtools_extension). :::note -Some constructors and factories don't have all the arguments. For example, -<HM>`builder`</HM> is not available in <HT>`UseDependency`</HT> and <HT>`UseDependency.get`</HT>, and `mode` is available only in <HT>`UseDependency.register`</HT> and <HT>`UseDependency.create`</HT>. +Some constructors and factories do not include all parameters. For example: +- **`builder`** is not available in <HT>`UseDependency`</HT> and <HT>`UseDependency.get`</HT>. +- **`mode`** is available only in <HT>`UseDependency.register`</HT> and <HT>`UseDependency.create`</HT>. ::: - ## Properties and methods - `instance`: A getter property to get the dependency instance of <HT>`T`</HT> type. diff --git a/website/src/content/docs/api/hooks/use_effect.mdx b/website/src/content/docs/api/hooks/use_effect.mdx index f467fc2c..37227519 100644 --- a/website/src/content/docs/api/hooks/use_effect.mdx +++ b/website/src/content/docs/api/hooks/use_effect.mdx @@ -22,18 +22,20 @@ In this case, we will refer to the _**instance**_ as the _**dependency**_ which UseEffect( void | Function callback(), List<RtState> dependencies, + { String? debugLabel }, ); // UseEffect run on init UseEffect.runOnInit( void | Function callback(), List<RtState> dependencies, + { String? debugLabel }, ); ``` -<HT>`UseEffect`</HT> accepts these arguments: +<HT>`UseEffect`</HT> accepts these parameters: -- <HM>`callback`</HM>: A function that performs side effects. This function is executed when the `dependencies` argument changes or the _**instance**_ trigger <HE>`Lifecycle.didMount`</HE> event. +- <HM>`callback`</HM>: A function that performs side effects. This function is executed when the `dependencies` trigger <HE>`Lifecycle.didUpdate`</HE> event or the _**instance**_ trigger <HE>`Lifecycle.didMount`</HE> event. If the `callback` returns a <HT>`Function`</HT>(considers as an _**effect cleanup**_), it will be called before the next effect is run or the _**instance**_ trigger <HE>`Lifecycle.willUnmount`</HE> event. :::tip The _**effect cleanup**_ is useful for cleaning up any resources or subscriptions that the effect may have created. @@ -43,6 +45,7 @@ UseEffect.runOnInit( The <HM>`callback`</HM> and _**effect cleanup**_ function may be called by <HT>[`Lifecycle`](/reactter/core_concepts/lifecycle)</HT> events, such as <HE>`Lifecycle.didMount`</HE> and <HE>`Lifecycle.willUnmount`</HE>. However, they work only if the instance is provided to the widget tree using the API of _**flutter_reactter**_ package. ::: - `dependencies`: A list of state(<HT>`RtState`</HT>, learn about it [here](/reactter/core_concepts/state_management/#state)) that the effect depends on. +- `debugLabel`: A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). ## Properties & Methods diff --git a/website/src/content/docs/api/hooks/use_reducer.mdx b/website/src/content/docs/api/hooks/use_reducer.mdx index f400885e..67a87e45 100644 --- a/website/src/content/docs/api/hooks/use_reducer.mdx +++ b/website/src/content/docs/api/hooks/use_reducer.mdx @@ -27,13 +27,15 @@ An alternative to <HT>[`UseState`](/reactter/api/hooks/use_state)</HT>. UseReducer<T>( T reducer(T state, RtAction action), T initialValue, + { String? debugLabel }, ); ``` -<HT>`UseReducer`</HT> accepts these arguments: +<HT>`UseReducer`</HT> accepts these parameters: - <HM>`reducer`</HM>: A function that takes the current `state` and an `action`, and returns a new state. - `initialState`: Initial value of <HT>`T`</HT> type that it will hold. +- `debugLabel`: A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). ## Properties & Methods diff --git a/website/src/content/docs/api/hooks/use_state.mdx b/website/src/content/docs/api/hooks/use_state.mdx index 9158c5f8..fff01a14 100644 --- a/website/src/content/docs/api/hooks/use_state.mdx +++ b/website/src/content/docs/api/hooks/use_state.mdx @@ -17,18 +17,19 @@ import * as ListeningChangesState from '@/content/docs/shareds/listening_changes ## Syntax ```dart showLineNumbers=false -UseState<T>(T initialValue); +UseState<T>(T initialValue, { String? debugLabel }); ``` -<HT>`UseState`</HT> accepts this argument: +<HT>`UseState`</HT> accepts these parameters: -- `initialValue`: Initial value of <HT>`T`</HT> type that it will hold. +- **`initialValue`**: Initial value of <HT>`T`</HT> type that it will hold. +- **`debugLabel`**: *(Optional)* A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). ## Properties & Methods <HT>`UseState`</HT> provides the following properties and methods: -- `value`: A getter/setter that allows to read and write its state. +- **`value`**: A getter/setter that allows to read and write its state. <StateMethodsLite/> ## Usage diff --git a/website/src/content/docs/api/methods/core_methods.mdx b/website/src/content/docs/api/methods/core_methods.mdx deleted file mode 100644 index c7e8c74a..00000000 --- a/website/src/content/docs/api/methods/core_methods.mdx +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Core Methods 🚧 -description: The core methods of Reactter. -sidebar: - order: 1 ---- \ No newline at end of file diff --git a/website/src/content/docs/api/methods/debugging_methods.mdx b/website/src/content/docs/api/methods/debugging_methods.mdx new file mode 100644 index 00000000..70545a04 --- /dev/null +++ b/website/src/content/docs/api/methods/debugging_methods.mdx @@ -0,0 +1,16 @@ +--- +title: Debugging Methods 🚧 +description: Reactter debugging methods +sidebar: + order: 4 +--- + +import { HM } from '@/components/Highlight'; + +Reactter provides a set of debugging methods to help you debug your states and dependencies in the finest detail. + +## <HM>`Rt.initializeDevTools`</HM> + +## <HM>`Rt.initializeLogger`</HM> + +## <HM>`Rt.addObserver`</HM> \ No newline at end of file diff --git a/website/src/content/docs/api/methods/dependency_injection_methods.mdx b/website/src/content/docs/api/methods/dependency_injection_methods.mdx index 00b4b9c8..7d47c759 100644 --- a/website/src/content/docs/api/methods/dependency_injection_methods.mdx +++ b/website/src/content/docs/api/methods/dependency_injection_methods.mdx @@ -2,10 +2,10 @@ title: Dependency Injection Methods description: Documentation of Dependency Injection Methods in Reactter. sidebar: - order: 3 + order: 2 --- -import { HE, HK, HM, HN, HS, HT } from '@/components/Highlight'; +import { HB, HE, HK, HM, HN, HS, HT } from '@/components/Highlight'; import CodeTabs from '@/components/CodeTabs.astro'; import { Tabs, TabItem } from "@/components/Tabs"; import ZappButton from "@/components/ZappButton.astro"; @@ -71,7 +71,7 @@ These methods are used to create and manipulate dependencies in a more organized ## <HM>`Rt.register`</HM> The <HM>`Rt.register`</HM> method registers a new dependency. -It returns <HN>`true`</HN> if the dependency is registered successfully, otherwise return <HN>`false`</HN>. +It returns <HB>`true`</HB> if the dependency is registered successfully, otherwise return <HB>`false`</HB>. #### Syntax @@ -112,7 +112,7 @@ See the example below to understand how to use the <HM>`Rt.register`</HM> method ## <HM>`Rt.lazyBuilder`</HM> The <HM>`Rt.lazyBuilder`</HM> method registers a new dependency as [<HE>`DependencyMode.builder`</HE>](/reactter/core_concepts/dependency_injection/#builder). -It returns <HN>`true`</HN> if the dependency is registered successfully, otherwise return <HN>`false`</HN>. +It returns <HB>`true`</HB> if the dependency is registered successfully, otherwise return <HB>`false`</HB>. This method is similar to the <HM>`Rt.register`</HM> method but with the <HE>`DependencyMode.builder`</HE> mode only. @@ -152,7 +152,7 @@ See the example below to understand how to use the <HM>`Rt.lazyBuilder`</HM> met ## <HM>`Rt.lazyFactory`</HM> The <HM>`Rt.lazyFactory`</HM> method registers a new dependency as [<HE>`DependencyMode.factory`</HE>](/reactter/core_concepts/dependency_injection/#factory). -It returns <HN>`true`</HN> if the dependency is registered successfully, otherwise return <HN>`false`</HN>. +It returns <HB>`true`</HB> if the dependency is registered successfully, otherwise return <HB>`false`</HB>. This method is similar to the <HM>`Rt.register`</HM> method but with the <HE>`DependencyMode.factory`</HE> mode only. @@ -192,7 +192,7 @@ See the example below to understand how to use the <HM>`Rt.lazyFactory`</HM> met ## <HM>`Rt.lazySingleton`</HM> The <HM>`Rt.lazySingleton`</HM> method registers a new dependency as [<HE>`DependencyMode.singleton`</HE>](/reactter/core_concepts/dependency_injection/#singleton). -It returns <HN>`true`</HN> if the dependency is registered successfully, otherwise return <HN>`false`</HN>. +It returns <HB>`true`</HB> if the dependency is registered successfully, otherwise return <HB>`false`</HB>. This method is similar to the <HM>`Rt.register`</HM> method but with the <HE>`DependencyMode.singleton`</HE> mode. @@ -449,7 +449,7 @@ See the example below to understand how to use the <HM>`Rt.find`</HM> method in ## <HM>`Rt.exists`</HM> -The <HM>`Rt.exists`</HM> method checks if the dependency instance exists in Reactter. It returns <HN>`true`</HN> if the dependency exists, otherwise return <HN>`false`</HN>. +The <HM>`Rt.exists`</HM> method checks if the dependency instance exists in Reactter. It returns <HB>`true`</HB> if the dependency exists, otherwise return <HB>`false`</HB>. #### Syntax @@ -516,7 +516,7 @@ See the example below to understand how to use the <HM>`Rt.getDependencyMode`</H ## <HM>`Rt.delete`</HM> The <HM>`Rt.delete`</HM> method removes the dependency instance and its registration from the Reactter based on the dependency mode. -It returns <HN>`true`</HN> if the dependency is successfully deleted, otherwise return <HN>`false`</HN>. +It returns <HB>`true`</HB> if the dependency is successfully deleted, otherwise return <HB>`false`</HB>. :::note The behavior of the <HM>`Rt.delete`</HM> method depends on the dependency mode: @@ -559,7 +559,7 @@ See the example below to understand how to use the <HM>`Rt.delete`</HM> method i ## <HM>`Rt.destroy`</HM> The <HM>`Rt.destroy`</HM> method forcibly removes the dependency instance and its registration from the Reactter. -It returns <HN>`true`</HN> if the dependency is successfully destroyed, otherwise return <HN>`false`</HN>. +It returns <HB>`true`</HB> if the dependency is successfully destroyed, otherwise return <HB>`false`</HB>. #### Syntax @@ -573,7 +573,7 @@ bool Rt.destroy<T>({ #### Parameters - `id`: An optional identifier for the <HT>`T`</HT> dependency. If not provided, the default instance of the <HT>`T`</HT> dependency is used. -- `onlyDependency`: An optional parameter to delete the dependency instance only. The default value is <HN>`false`</HN>. +- `onlyDependency`: An optional parameter to delete the dependency instance only. The default value is <HB>`false`</HB>. #### Example @@ -597,7 +597,7 @@ See the example below to understand how to use the <HM>`Rt.destroy`</HM> method ## <HM>`Rt.unregister`</HM> The <HM>`Rt.unregister`</HM> method is used to unregister the dependency from the Reactter. -It returns <HN>`true`</HN> if the dependency is unregistered successfully, otherwise return <HN>`false`</HN>. +It returns <HB>`true`</HB> if the dependency is unregistered successfully, otherwise return <HB>`false`</HB>. #### Syntax @@ -631,7 +631,7 @@ See the example below to understand how to use the <HM>`Rt.unregister`</HM> meth ## <HM>`Rt.isActive`</HM> The <HM>`Rt.isActive`</HM> method is used to check if the dependency is registered in Reactter. -It returns <HN>`true`</HN> if the dependency is registered, otherwise return <HN>`false`</HN>. +It returns <HB>`true`</HB> if the dependency is registered, otherwise return <HB>`false`</HB>. #### Syntax diff --git a/website/src/content/docs/api/methods/event_handler_methods.mdx b/website/src/content/docs/api/methods/event_handler_methods.mdx index eded9879..4e5413b3 100644 --- a/website/src/content/docs/api/methods/event_handler_methods.mdx +++ b/website/src/content/docs/api/methods/event_handler_methods.mdx @@ -2,7 +2,7 @@ title: Event Handler Methods description: Documentation of Event Handler Methods in Reactter. sidebar: - order: 4 + order: 3 --- import { HE, HK, HM, HN, HS, HT } from '@/components/Highlight'; diff --git a/website/src/content/docs/api/methods/state_management_methods.mdx b/website/src/content/docs/api/methods/state_management_methods.mdx index a78fd05c..024d689a 100644 --- a/website/src/content/docs/api/methods/state_management_methods.mdx +++ b/website/src/content/docs/api/methods/state_management_methods.mdx @@ -2,7 +2,7 @@ title: State Management Methods description: Documentation of State Management Methods in Reactter. sidebar: - order: 2 + order: 1 --- import { HE, HK, HM, HN, HS, HT } from '@/components/Highlight'; diff --git a/website/src/content/docs/api/widgets/rt_component.mdx b/website/src/content/docs/api/widgets/rt_component.mdx index ac1a7fb6..033dd8cc 100644 --- a/website/src/content/docs/api/widgets/rt_component.mdx +++ b/website/src/content/docs/api/widgets/rt_component.mdx @@ -4,7 +4,7 @@ description: Learn how to use the RtComponent in Reactter. sidebar: order: 7 --- -import { HM, HT, HN } from '@/components/Highlight'; +import { HB, HM, HT } from '@/components/Highlight'; import CodeTabs from '@/components/CodeTabs.astro'; import { Tabs, TabItem } from "@/components/Tabs"; import ZappButton from "@/components/ZappButton.astro"; @@ -46,8 +46,8 @@ abstract class RtComponent<T> extends StatelessWidget { It exposes the instance of the <HT>`T`</HT> dependency as argument of the function. If omitted, the `listenAll` getter is checked. - `listenAll`: A boolean value that determines whether to listen to all states(<HT>`RtState`</HT>) of the <HT>`T`</HT> dependency for rebuilds the widget tree defined in the <HM>`render`</HM> method. - For default, it is set to <HN>`false`</HN>. -- <HM>`render`</HM>: A required function that builds the widget tree based on the instance of the <HT>`T`</HT> dependency. It receives the following arguments: + For default, it is set to <HB>`false`</HB>. +- <HM>`render`</HM>: A required function that builds the widget tree based on the instance of the <HT>`T`</HT> dependency. It receives the following parameters: - `context`: The <HT>`BuildContext`</HT> of the <HT>`RtComponent`</HT> widget. - `inst`: The instance of the <HT>`T`</HT> dependency. diff --git a/website/src/content/docs/api/widgets/rt_consumer.mdx b/website/src/content/docs/api/widgets/rt_consumer.mdx index aea954b6..c56c1033 100644 --- a/website/src/content/docs/api/widgets/rt_consumer.mdx +++ b/website/src/content/docs/api/widgets/rt_consumer.mdx @@ -4,7 +4,7 @@ description: Learn how to use the RtConsumer in Reactter. sidebar: order: 4 --- -import { HM, HN, HS, HT } from '@/components/Highlight'; +import { HB, HM, HS, HT } from '@/components/Highlight'; import CodeTabs from '@/components/CodeTabs.astro'; import { Tabs, TabItem } from "@/components/Tabs"; import ZappButton from "@/components/ZappButton.astro"; @@ -74,13 +74,13 @@ RtConsumer<T>({ - `child`: An optional <HT>`Widget`</HT> that remains static while the widget tree is rebuilt. It is passed to the <HM>`builder`</HM> function if it is defined. - `listenAll`: A boolean that determines whether to listen to all states provided by the <HT>`T`</HT> dependency. - If set to <HN>`true`</HN>, the <HM>`listenStates`</HM> function is ignored. - The default value is <HN>`false`</HN>. + If set to <HB>`true`</HB>, the <HM>`listenStates`</HM> function is ignored. + The default value is <HB>`false`</HB>. - <HM>`listenStates`</HM>: An optional function that returns a list of state(<HT>`RtState`</HT>) to listen to. It takes the instance of the <HT>`T`</HT> dependency as an argument. If omitted, the `listenAll` property is checked. - <HM>`builder`</HM>: A function that rebuilds a widget depending on the <HT>`RtConsumer`</HT>. - It receives the following arguments: + It receives the following parameters: - `context`: The <HT>`BuildContext`</HT> of the <HT>`RtConsumer`</HT> widget. - `instance`: The instance of <HT>`T`</HT> dependency provided by the closest <HT>`RtProvider`</HT> widget. - `child`: The `child` widget passed to <HT>`RtConsumer`</HT>. @@ -126,12 +126,12 @@ Consequently, it does not rebuild whenever there are changes in the `count` stat :::note For specific state listening, you can use the <HM>`listenStates`</HM> property(see the [Listening to specific states](#listening-to-specific-states) section). -Alternatively, you can set the `listenAll` property to <HN>`true`</HN> to listen to all states provided by the <HT>`CounterController`</HT> dependency(see the [Listening to all states](#listening-to-all-states) section). +Alternatively, you can set the `listenAll` property to <HB>`true`</HB> to listen to all states provided by the <HT>`CounterController`</HT> dependency(see the [Listening to all states](#listening-to-all-states) section). ::: ### Listening to all state -To listen to all states, set the `listenAll` property to <HN>`true`</HN>. This ensures that <HT>`RtConsumer`</HT> rebuilds whenever any state in the dependency changes. +To listen to all states, set the `listenAll` property to <HB>`true`</HB>. This ensures that <HT>`RtConsumer`</HT> rebuilds whenever any state in the dependency changes. :::caution Using the `listenAll` property indiscriminately is not recommended, especially when some states are not directly involved in rendering the widget subtree. diff --git a/website/src/content/docs/api/widgets/rt_multi_provider.mdx b/website/src/content/docs/api/widgets/rt_multi_provider.mdx index 61384a67..44cae67c 100644 --- a/website/src/content/docs/api/widgets/rt_multi_provider.mdx +++ b/website/src/content/docs/api/widgets/rt_multi_provider.mdx @@ -43,7 +43,7 @@ RtMultiProvider( - `child`: An optional <HT>`Widget`</HT> that remains static while the widget tree is rebuilt. It is passed to the <HM>`builder`</HM> function if it is defined. - <HM>`builder`</HM>: An optional function which builds a widget depending on the <HT>`RtMultiProvider`</HT>. If it not defined, the <HT>`child`</HT> widget is returned. -It receives the following arguments: +It receives the following parameters: - `context`: The <HM>`BuildContext`</HM> of the widget. A handle to the location of <HT>`RtMultiProvider`</HT> in the widget tree. - `child`: The `child` widget passed to the <HT>`RtMultiProvider`</HT> widget. diff --git a/website/src/content/docs/api/widgets/rt_provider.mdx b/website/src/content/docs/api/widgets/rt_provider.mdx index 0e4e050a..4d8f9c4b 100644 --- a/website/src/content/docs/api/widgets/rt_provider.mdx +++ b/website/src/content/docs/api/widgets/rt_provider.mdx @@ -119,7 +119,7 @@ The <HT>`RtProvider`</HT> class has three constructors, each catering to differe It is passed to the <HM>`builder`</HM> function if it is defined. - <HM>`builder`</HM>: An optional function which builds a widget based on the dependency. If it not defined, the <HT>`child`</HT> widget is returned. -It receives the following arguments: +It receives the following parameters: - `context`: The <HM>`BuildContext`</HM> of the widget. A handle to the location of <HT>`RtProvider`</HT> in the widget tree. - `instance`: The instance of the <HT>`T`</HT> dependency. It is available only to default constructor. - `child`: The `child` widget passed to the <HT>`RtProviders`</HT> widget. diff --git a/website/src/content/docs/api/widgets/rt_selector.mdx b/website/src/content/docs/api/widgets/rt_selector.mdx index c29c8b54..24a5c70b 100644 --- a/website/src/content/docs/api/widgets/rt_selector.mdx +++ b/website/src/content/docs/api/widgets/rt_selector.mdx @@ -61,11 +61,11 @@ RtSelector<T, V>({ - `child`: An optional <HT>`Widget`</HT> that remains static while the widget tree is rebuilt. If defined, it is passed to the <HM>`builder`</HM> function. - <HM>`selector`</HM>: A function that computes a value <HT>`V`</HT> from one or more states and listens for changes to rebuild the widget tree when the value computed changes. - It receives the following arguments: + It receives the following parameters: - `instance`: The instance of <HT>`T`</HT> dependency provided by the closest <HT>`RtProvider`</HT> widget. - <HM>`select`</HM>: A function that allows you to wrap the state(<HT>`RtState`</HT>) to be listened for changes and returns it. - <HM>`builder`</HM>: A function that rebuilds a widget depending on the <HT>`RtSelector`</HT>. -It receives the following arguments: +It receives the following parameters: - `context`: The <HT>`BuildContext`</HT> of the <HT>`RtSelector`</HT> widget. - `instance`: The instance of <HT>`T`</HT> dependency provided by the closest <HT>`RtProvider`</HT> widget. - `value`: The selected value computed <HT>`V`</HT> by the <HM>`selector`</HM> function. diff --git a/website/src/content/docs/api/widgets/rt_signal_watcher.mdx b/website/src/content/docs/api/widgets/rt_signal_watcher.mdx index 5c9527b4..79a76cb0 100644 --- a/website/src/content/docs/api/widgets/rt_signal_watcher.mdx +++ b/website/src/content/docs/api/widgets/rt_signal_watcher.mdx @@ -42,7 +42,7 @@ RtSignalWatcher({ - `child`: An optional <HT>`Widget`</HT> that remains static while the widget tree is rebuilt. It is passed to the <HM>`builder`</HM> function if it is defined. - <HM>`builder`</HM>: A required function that builds a widget tree based on the current state of the signals. - It receives the following arguments: + It receives the following parameters: - `context`: The <HT>`BuildContext`</HT> of the <HT>`RtSignalWatcher`</HT> widget. - `child`: The `child` widget passed to <HT>`RtSignalWatcher`</HT>. diff --git a/website/src/content/docs/devtools_extension.mdx b/website/src/content/docs/devtools_extension.mdx index 9e034ce4..bd6782e9 100644 --- a/website/src/content/docs/devtools_extension.mdx +++ b/website/src/content/docs/devtools_extension.mdx @@ -1,4 +1,48 @@ --- -title: DevTools extension 🚧 +title: DevTools extension description: Reactter DevTools Extension ---- \ No newline at end of file +--- +import { HM } from '@/components/Highlight'; + +Now you can integrate Reactter into the DevTools tooling suite as an extension to inspect states and dependencies in the finest detail. + +![Reactter DevTools extension](/public/devtools_extension/devtools.png) + +## Enable the extension + +:::note +Ensure that you have the Reactter [installed](/reactter/getting_started#instalaci%C3%B3n) in your project before enabling the extension. +::: + +To install the Reactter DevTools extension, you need to follow these steps: + +- Open the DevTools in your browser. +Visit the [official documentation](https://docs.flutter.dev/tools/devtools#start) to learn how to open the DevTools in your browser. + +- Go to the Extensions tab. +![Extension tab](/public/devtools_extension/extension_tab.png) + +- Click on the "Enable" button on the Reactter DevTools extension. +![Enable button](/public/devtools_extension/enable_button.png) + +Once you have enabled the Reactter DevTools extension, you can start using it here. + +![Reactter DevTools extension tab](/public/devtools_extension/reactter_extension.png) + +## Usage + +To use the Reactter DevTools extension, it is necessary to call <HM>`Rt.initializeDevTools`</HM> in your main file. + +```dart title="main.dart" +import 'package:reactter/reactter.dart'; + +void main() { + Rt.initializeDevtools(); + runApp(MyApp()); +} +``` + +:::tip +Use `debugLabel` and `debugInfo` property in your states and dependencies for better visualization in the DevTools extension. +::: + diff --git a/website/src/content/docs/shareds/listening_changes_state.mdx b/website/src/content/docs/shareds/listening_changes_state.mdx index ac23e6cc..313167b4 100644 --- a/website/src/content/docs/shareds/listening_changes_state.mdx +++ b/website/src/content/docs/shareds/listening_changes_state.mdx @@ -8,8 +8,8 @@ import { HE, HM, HT } from '@/components/Highlight'; When `value` has changed, the <HT>`[[stateName]]`</HT> will emit the following events(learn about it [here](/reactter/core_concepts/lifecycle/)): -- <HE>`Lifecycle.willUpdate`</HE> event is triggered before the `value` change or <HM>`update`</HM>, <HM>`refresh`</HM> methods have been invoked. -- <HE>`Lifecycle.didUpdate`</HE> event is triggered after the `value` change or <HM>`update`</HM>, <HM>`refresh`</HM> methods have been invoked. +- <HE>`Lifecycle.willUpdate`</HE> event is triggered before the `value` change or <HM>`update`</HM>, <HM>`notify`</HM> methods have been invoked. +- <HE>`Lifecycle.didUpdate`</HE> event is triggered after the `value` change or <HM>`update`</HM>, <HM>`notify`</HM> methods have been invoked. Example of listening to changes: diff --git a/website/src/content/docs/shareds/state_methods_lite.mdx b/website/src/content/docs/shareds/state_methods_lite.mdx index 16597a4c..54a2bea5 100644 --- a/website/src/content/docs/shareds/state_methods_lite.mdx +++ b/website/src/content/docs/shareds/state_methods_lite.mdx @@ -2,15 +2,15 @@ title: State Methods --- -import { HM, HT } from '@/components/Highlight'; +import { HM, HN, HT } from '@/components/Highlight'; - Methods inherited from <HT>`RtState`</HT>(Learn more [here](/reactter/core_concepts/state_management/#state-methods)): - - <HM>`update`</HM>: A method to notify changes after run a set of instructions. - - <HM>`notify`</HM>: A method to force to notify changes. - - *<HM>`bind`</HM>: A method to bind an instance to it. - - *<HM>`unbind`</HM>: A method to unbind an instance to it. - - *<HM>`dispose`</HM>: A method to remove all listeners and free resources. + - <HM>**`update`**</HM>: A method to notify changes after run a set of instructions. + - <HM>**`notify`**</HM>: A method to force to notify changes. + - <HN>*</HN><HM>**`bind`**</HM>: A method to bind an instance to it. + - <HN>*</HN><HM>**`unbind`**</HM>: A method to unbind an instance to it. + - <HN>*</HN><HM>**`dispose`**</HM>: A method to remove all listeners and free resources. :::note - \* These methods are unnecessary when is declared within a dependency(class registered via the [dependecy injection](/reactter/core_concepts/dependency_injection)). + <HN>*</HN> These methods are unnecessary when is declared within a dependency(class registered via the [dependecy injection](/reactter/core_concepts/dependency_injection)). ::: From 1100b35c569505be4296feb71aa77de714ad21df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Sat, 1 Feb 2025 02:26:34 -0600 Subject: [PATCH 104/141] refactor(website): Update documentation for hooks --- .../docs/api/classes/auto_dispatch_effect.mdx | 6 - .../docs/api/hooks/use_async_state.mdx | 6 +- .../content/docs/api/hooks/use_compute.mdx | 9 +- .../content/docs/api/hooks/use_dependency.mdx | 249 ++++++++++-------- .../src/content/docs/api/hooks/use_effect.mdx | 73 +++-- .../content/docs/api/hooks/use_reducer.mdx | 20 +- .../src/content/docs/api/hooks/use_state.mdx | 6 +- .../docs/shareds/tip_dependency_checking.mdx | 29 ++ 8 files changed, 240 insertions(+), 158 deletions(-) delete mode 100644 website/src/content/docs/api/classes/auto_dispatch_effect.mdx create mode 100644 website/src/content/docs/shareds/tip_dependency_checking.mdx diff --git a/website/src/content/docs/api/classes/auto_dispatch_effect.mdx b/website/src/content/docs/api/classes/auto_dispatch_effect.mdx deleted file mode 100644 index 262adc34..00000000 --- a/website/src/content/docs/api/classes/auto_dispatch_effect.mdx +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: AutoDispatchEffect 🚧 -description: A mixin to execute the effect on initialization. -sidebar: - order: 4 ---- \ No newline at end of file diff --git a/website/src/content/docs/api/hooks/use_async_state.mdx b/website/src/content/docs/api/hooks/use_async_state.mdx index 4a43df89..6277c84d 100644 --- a/website/src/content/docs/api/hooks/use_async_state.mdx +++ b/website/src/content/docs/api/hooks/use_async_state.mdx @@ -35,7 +35,7 @@ UseAsyncState<T, A>.withArg( - <HM>**`asyncFunction`**</HM>: A function that returns a <HT>`Future<T>`</HT> to update the state asynchronously. This function is called by the <HM>`resolve`</HM> method and sets the `value` property. - **`initialValue`**: Initial value of <HT>`T`</HT> type that it will hold. -- **`debugLabel`**: A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). +- **`debugLabel`**: *(Optional)* A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). ## Properties & Methods @@ -211,7 +211,7 @@ print("${uAsyncStateWithArgs.value}"); // Resolved value with args: arg1, arg2, <HT>`UseAsyncState`</HT> doesn't cache the resolving `value` by default, meaning that it will resolve the `value` every time <HM>`resolve`</HM> is called, potentially impacting performance, especially if the <HM>`asyncFunction`</HM> is expensive. In this case, you should consider using <HT>[`Memo`](/reactter/api/classes/memo)</HT> to cache the resolving `value`, e.g.: ```dart "UseAsyncState" "Memo.inline" -final translateState = UseAsyncState.withArg<String?, ArgsX3<String>>( +final uTranslateState = UseAsyncState.withArg<String?, ArgsX3<String>>( /// `Memo` stores the value resolved in cache, /// and retrieving that same value from the cache the next time /// it's needed instead of resolving it again. @@ -227,6 +227,8 @@ final translateState = UseAsyncState.withArg<String?, ArgsX3<String>>( ), null, ); + +await uTranslateState.resolve(ArgsX3('Hello', 'en', 'es')); ``` ### Using `when` method diff --git a/website/src/content/docs/api/hooks/use_compute.mdx b/website/src/content/docs/api/hooks/use_compute.mdx index b3f3f039..63b32f0f 100644 --- a/website/src/content/docs/api/hooks/use_compute.mdx +++ b/website/src/content/docs/api/hooks/use_compute.mdx @@ -13,7 +13,8 @@ import * as NoteLateState from '@/content/docs/shareds/note_late_state.mdx'; import * as NoteStateValueNotifies from '@/content/docs/shareds/note_state_value_notifies.mdx'; import * as ListeningChangesState from '@/content/docs/shareds/listening_changes_state.mdx'; -<HT><a href="https://pub.dev/documentation/reactter/latest/reactter/UseCompute-class.html" target="_blank">`UseCompute`</a></HT> is a [hook](/reactter/core_concepts/hooks) that allows you to define a computation method (<HM>`computeValue`</HM>) to derive a value based on the current state of one or more dependencies. When any of these dependencies change, the hook recalculates the `value` and provides the updated result. +<HT><a href="https://pub.dev/documentation/reactter/latest/reactter/UseCompute-class.html" target="_blank">`UseCompute`</a></HT> is a [hook](/reactter/core_concepts/hooks) that allows you to define a computation method (<HM>`computeValue`</HM>) to derive a value based on the current state of one or more dependencies. +When any of these `dependencies` notify to change, the hook recalculates the `value` and provides the updated result. ## Syntax @@ -27,9 +28,9 @@ UseCompute<T>( <HT>`UseCompute`</HT> accepts these parameters: -- <HM>`computeValue`</HM>: A function that returns a value based on the current state of the dependencies. -- `dependencies`: A list of state(<HT>`RtState`</HT>, learn about it [here](/reactter/core_concepts/state_management/#state)) that the computation depends on. When any of these dependencies change, the `value` is recalculated. -- `debugLabel`: A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). +- <HM>**`computeValue`**</HM>: A function that returns a value based on the current state of the dependencies. +- **`dependencies`**: A list of state(<HT>`RtState`</HT>, learn about it [here](/reactter/core_concepts/state_management/#state)) that the computation depends on. When any of these `dependencies` trigger <HE>`Lifecycle.didUpdate`</HE> event, the `value` is recalculated. +- **`debugLabel`**: *(Optional)* A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). ## Properties & Methods diff --git a/website/src/content/docs/api/hooks/use_dependency.mdx b/website/src/content/docs/api/hooks/use_dependency.mdx index faf8981f..4179f915 100644 --- a/website/src/content/docs/api/hooks/use_dependency.mdx +++ b/website/src/content/docs/api/hooks/use_dependency.mdx @@ -5,19 +5,22 @@ sidebar: order: 6 --- +import CodeTabs from '@/components/CodeTabs.astro'; +import { Tabs, TabItem } from "@/components/Tabs"; import { HE, HM, HN, HS, HT } from '@/components/Highlight'; import StateMethodsLite from '@/content/docs/shareds/state_methods_lite.mdx'; +import TipDependencyChecking from '@/content/docs/shareds/tip_dependency_checking.mdx'; :::tip This documentation assumes you've already read the [Dependency Injection](/reactter/core_concepts/dependency_injection). It's recommended read that first if you're new in Reactter. ::: -<HT><a href="https://pub.dev/documentation/reactter/latest/reactter/UseDependency-class.html" target="_blank">`UseDependency`</a></HT> is a [RtHook](https://pub.dev/documentation/reactter/latest/reactter/RtHook-class.html) that allows to manage a dependency. +<HT><a href="https://pub.dev/documentation/reactter/latest/reactter/UseDependency-class.html" target="_blank">`UseDependency`</a></HT> is a [hook](/reactter/core_concepts/hooks) that allows to manage a dependency. -## Constructors and factories +## Syntax -- <HT>`UseDependency`</HT>: Default constructor to get the <HT>`T`</HT> dependency. It's similar to using <HM>`Rt.find`</HM>. +- <HT>**`UseDependency`**</HT>: Default constructor to get the <HT>`T`</HT> dependency. It's similar to using <HM>`Rt.find`</HM>. ```dart showLineNumbers=false UseDependency<T>({ @@ -26,7 +29,7 @@ It's recommended read that first if you're new in Reactter. }); ``` -- <HT>`UseDependency.get`</HT>: Get a <HT>`T`</HT> dependency inmediately. It's similar to using <HM>`Rt.get`</HM>. +- <HT>**`UseDependency.get`**</HT>: Get a <HT>`T`</HT> dependency inmediately. It's similar to using <HM>`Rt.get`</HM>. ```dart showLineNumbers=false UseDependency<T>.get({ @@ -35,81 +38,97 @@ It's recommended read that first if you're new in Reactter. }); ``` -- <HT>`UseDependency.register`</HT>: Register a builder function to create a <HT>`T`</HT> dependency. It's similar to using <HM>`Rt.register`</HM>. +- <HT>**`UseDependency.register`**</HT>: Register a builder function to create a <HT>`T`</HT> dependency. It's similar to using <HM>`Rt.register`</HM>. ```dart showLineNumbers=false - UseDependency<T>.register(T Function() builder, { - String? id, - DependencyMode mode = DependencyMode.builder, - String? debugLabel, - }); + UseDependency<T>.register( + T Function() builder, { + String? id, + DependencyMode mode = DependencyMode.builder, + String? debugLabel, + }, + ); ``` -- <HT>`UseDependency.create`</HT>: Registers, creates and gets a <HT>`T`</HT> dependency inmediately. It's similar to using <HM>`Rt.create`</HM>. +- <HT>**`UseDependency.create`**</HT>: Registers, creates and gets a <HT>`T`</HT> dependency inmediately. It's similar to using <HM>`Rt.create`</HM>. ```dart showLineNumbers=false - UseDependency<T>.create(T Function() builder, { - String? id, - DependencyMode mode = DependencyMode.builder, - String? debugLabel, - }); + UseDependency<T>.create( + T Function() builder, { + String? id, + DependencyMode mode = DependencyMode.builder, + String? debugLabel, + }, + ); ``` -- <HT>`UseDependency.lazyBuilder`</HT>: Registers a builder function, to create a instance of the <HT>`T`</HT> dependency as [Builder](/reactter/core_concepts/dependency_injection/#builder) mode. It's similar to using <HM>`Rt.lazyBuilder`</HM>. +- <HT>**`UseDependency.lazyBuilder`**</HT>: Registers a builder function, to create a instance of the <HT>`T`</HT> dependency as [Builder](/reactter/core_concepts/dependency_injection/#builder) mode. It's similar to using <HM>`Rt.lazyBuilder`</HM>. ```dart showLineNumbers=false - UseDependency<T>.lazyBuilder(T Function() builder, { - String? id, - String? debugLabel - }); + UseDependency<T>.lazyBuilder( + T Function() builder, { + String? id, + String? debugLabel + }, + ); ``` -- <HT>`UseDependency.lazyFactory`</HT>: Registers a builder function, to create a instance of the <HT>`T`</HT> dependency as [Factory](/reactter/core_concepts/dependency_injection/#factory) mode. It's similar to using <HM>`Rt.lazyFactory`</HM>. +- <HT>**`UseDependency.lazyFactory`**</HT>: Registers a builder function, to create a instance of the <HT>`T`</HT> dependency as [Factory](/reactter/core_concepts/dependency_injection/#factory) mode. It's similar to using <HM>`Rt.lazyFactory`</HM>. ```dart showLineNumbers=false - UseDependency<T>.lazyFactory(T Function() builder, { - String? id, - String? debugLabel - }); + UseDependency<T>.lazyFactory( + T Function() builder, { + String? id, + String? debugLabel + }, + ); ``` -- <HT>`UseDependency.lazySingleton`</HT>: Registers a builder function, to create a instance of the <HT>`T`</HT> dependency as [Singleton](/reactter/core_concepts/dependency_injection/#singleton) mode. It's similar to using <HM>`Rt.lazySingleton`</HM>. +- <HT>**`UseDependency.lazySingleton`**</HT>: Registers a builder function, to create a instance of the <HT>`T`</HT> dependency as [Singleton](/reactter/core_concepts/dependency_injection/#singleton) mode. It's similar to using <HM>`Rt.lazySingleton`</HM>. ```dart showLineNumbers=false - UseDependency<T>.lazySingleton(T Function() builder, { - String? id, - String? debugLabel - }); + UseDependency<T>.lazySingleton( + T Function() builder, { + String? id, + String? debugLabel + }, + ); ``` -- <HT>`UseDependency.builder`</HT>: Registers, creates and gets a <HT>`T`</HT> dependency inmediately as [Builder](/reactter/core_concepts/dependency_injection/#builder) mode. It's similar to using <HM>`Rt.builder`</HM>. +- <HT>**`UseDependency.builder`**</HT>: Registers, creates and gets a <HT>`T`</HT> dependency inmediately as [Builder](/reactter/core_concepts/dependency_injection/#builder) mode. It's similar to using <HM>`Rt.builder`</HM>. ```dart showLineNumbers=false - UseDependency<T>.builder(T Function() builder, { - String? id, - DependencyMode mode = DependencyMode.builder, - String? debugLabel, - }); + UseDependency<T>.builder( + T Function() builder, { + String? id, + DependencyMode mode = DependencyMode.builder, + String? debugLabel, + }, + ); ``` -- <HT>`UseDependency.factory`</HT>: Registers, creates and gets a <HT>`T`</HT> dependency inmediately as [Factory](/reactter/core_concepts/dependency_injection/#factory) mode. It's similar to using <HM>`Rt.factory`</HM>. +- <HT>**`UseDependency.factory`**</HT>: Registers, creates and gets a <HT>`T`</HT> dependency inmediately as [Factory](/reactter/core_concepts/dependency_injection/#factory) mode. It's similar to using <HM>`Rt.factory`</HM>. ```dart showLineNumbers=false - UseDependency<T>.factory(T Function() builder, { - String? id, - DependencyMode mode = DependencyMode.builder, - String? debugLabel, - }); + UseDependency<T>.factory( + T Function() builder, { + String? id, + DependencyMode mode = DependencyMode.builder, + String? debugLabel, + }, + ); ``` -- <HT>`UseDependency.singleton`</HT>: Registers, creates and gets a <HT>`T`</HT> dependency inmediately as [Singleton](/reactter/core_concepts/dependency_injection/#singleton) mode. It's similar to using <HM>`Rt.singleton`</HM>. +- <HT>**`UseDependency.singleton`**</HT>: Registers, creates and gets a <HT>`T`</HT> dependency inmediately as [Singleton](/reactter/core_concepts/dependency_injection/#singleton) mode. It's similar to using <HM>`Rt.singleton`</HM>. ```dart showLineNumbers=false - UseDependency<T>.singleton(T Function() builder, { - String? id, - DependencyMode mode = DependencyMode.builder, - String? debugLabel, - }); + UseDependency<T>.singleton( + T Function() builder, { + String? id, + DependencyMode mode = DependencyMode.builder, + String? debugLabel, + }, + ); ``` ### Parameters @@ -128,14 +147,14 @@ Some constructors and factories do not include all parameters. For example: ## Properties and methods -- `instance`: A getter property to get the dependency instance of <HT>`T`</HT> type. +- **`instance`**: A getter property to get the dependency instance of <HT>`T`</HT> type. <StateMethodsLite /> ## Usage ### Finding a dependency -You can use the default constructor to obtain the dependency of <HT>`T`</HT> type, e.g.: +Use the default constructor to obtain the dependency of <HT>`T`</HT> type, e.g.: ```dart collapse={1-3, 5-9} "UseDependency<MyDependency>" import 'package:reactter/reactter.dart'; @@ -157,31 +176,11 @@ To register and create the dependency instance, using the <HT>`UseDependency.cre If only you need to retrieve the dependency instance, using the <HT>`UseDependency.get`</HT> hook instead. Learn more about it in [Getting a dependency](/reactter/api/hooks/use_dependency#getting-a-dependency). ::: -:::tip -The <HT>`UseDependency`</HT> hook is ideal for checking if a dependency has been created and waiting for it to become available using <HT>`UseEffect`</HT> hook, e.g.: - -```dart title="my_controller.dart" "UseDependency<MyDependency>" "UseEffect" -import 'package:reactter/reactter.dart'; - -class MyController { - final uDependency = UseDependency<MyDependency>(); - - const MyController() { - UseEffect.runOnInit(() { - print( - uDependency.instance != null - ? "MyDependency is available" - : "MyDependency is not available yet." - ); - }, [uDependency]); - } -} -``` -::: +<TipDependencyChecking /> ### Identifying a dependency -You can use the `id` argument to identify the dependency. For example, to obtain a dependency by <HS>`uniqueId`</HS>: +Use the `id` parameter to identify the dependency. For example, to obtain a dependency by <HS>`uniqueId`</HS>: ```dart collapse={1-3, 5-9} "UseDependency<MyDependency>" ins="uniqueId" import 'package:reactter/reactter.dart'; @@ -196,9 +195,13 @@ class MyController { ``` :::tip -The `id` argument is ideal for identifying dependencies, when you need to use the same dependency type in different places, e.g.: +The `id` parameter is ideal for identifying dependencies, when you need to use the same dependency type in different places, e.g.: -```dart title="my_controller.dart" "UseDependency<MyDependency>.get" +<CodeTabs> + <Tabs> + <TabItem> + <HM single slot="label">my_controller.dart</HM> +```dart "UseDependency<MyDependency>.get" import 'package:reactter/reactter.dart'; class MyController { @@ -211,8 +214,10 @@ class MyController { } } ``` - -```dart title="my_app.dart" "RtMultiProvider" "RtProvider" + </TabItem> + <TabItem> + <HM single slot="label">my_app.dart</HM> +```dart "RtMultiProvider" "RtProvider" import 'package:flutter_reactter/flutter_reactter.dart'; class MyApp extends StatelessWidget { @@ -231,11 +236,14 @@ class MyApp extends StatelessWidget { } } ``` + </TabItem> + </Tabs> +</CodeTabs> ::: ### Using the dependency -You can use the `instance` property to access the dependency. +Use the `instance` property to access the dependency. ```dart collapse={1-6, 8-9} "UseDependency<MyDependency>" ".instance" import 'package:reactter/reactter.dart'; @@ -249,31 +257,11 @@ class MyController { } ``` -:::tip -If the dependency is not registered and created, the `instance` property will be <HN>`null`</HN>. - -To avoid this, you can use the <HT>`UseEffect`</HT> hook to get the dependency when it is created, e.g.: - -```dart title="my_controller.dart" "UseDependency<MyDependency>" "UseEffect" -import 'package:reactter/reactter.dart'; - -class MyController { - final uDependency = UseDependency<MyDependency>(); - - const MyController() { - UseEffect.runOnInit(() { - if (uDependency.instance != null) { - print("Dependency instance: ${uDependency.instance}"); - } - }, [uDependency]); - } -} -``` -::: +<TipDependencyChecking /> ### Getting a dependency -You can use the <HT>`UseDependency.get`</HT> hook to get the dependency instance, e.g.: +Ue the <HT>`UseDependency.get`</HT> hook to get the dependency instance, e.g.: ```dart collapse={1-3, 5-9} "UseDependency" "UseDependency<MyDependency>.get" import 'package:reactter/reactter.dart'; @@ -296,7 +284,11 @@ To ensure that the dependency is created, you can using the <HT>`UseDependency.c :::tip The <HT>`UseDependency.get`</HT> hook is ideal for accessing a dependency, when you are sure that it is already registered before, e.g.: -```dart title="my_app.dart" "RtMultiProvider" "RtProvider" +<CodeTabs> + <Tabs> + <TabItem> + <HM single slot="label">my_app.dart</HM> +```dart "RtMultiProvider" "RtProvider" import 'package:flutter_reactter/flutter_reactter.dart'; class MyApp extends StatelessWidget { @@ -314,8 +306,10 @@ class MyApp extends StatelessWidget { } } ``` - -```dart title="my_controller.dart" "UseDependency<MyDependency>.get" + </TabItem> + <TabItem> + <HM single slot="label">my_controller.dart</HM> +```dart "UseDependency<MyDependency>.get" import 'package:flutter_reactter/flutter_reactter.dart'; class MyController { @@ -326,11 +320,14 @@ class MyController { } } ``` + </TabItem> + </Tabs> +</CodeTabs> ::: ### Registering a dependency -You can use the <HT>`UseDependency.register`</HT> hook to register a builder function to create the dependency instance, e.g.: +Use the <HT>`UseDependency.register`</HT> hook to register a builder function to create the dependency instance, e.g.: ```dart collapse={1-3, 5-9} "UseDependency<MyDependency>.register" import 'package:reactter/reactter.dart'; @@ -368,7 +365,11 @@ Also, you can create the dependency instance inmediately using the <HT>`UseDepen :::tip The <HT>`UseDependency.register`</HT> hook is ideal for registering dependencies in a single location, e.g.: -```dart title="dependencies_registers.dart" "UseDependency<MyDependency>.register" "UseDependency<MyOtherDependency>.register" +<CodeTabs> + <Tabs> + <TabItem> + <HM single slot="label">dependencies_registers.dart</HM> +```dart "UseDependency<MyDependency>.register" "UseDependency<MyOtherDependency>.register" import 'package:reactter/reactter.dart'; class DependenciesRegisters { @@ -379,8 +380,10 @@ class DependenciesRegisters { } } ``` - -```dart title="my_app.dart" "RtMultiProvider" "RtProvider" "DependenciesRegisters" + </TabItem> + <TabItem> + <HM single slot="label">my_app.dart</HM> +```dart "RtMultiProvider" "RtProvider" "DependenciesRegisters" import 'package:flutter_reactter/flutter_reactter.dart'; class MyApp extends StatelessWidget { @@ -395,11 +398,14 @@ class MyApp extends StatelessWidget { } } ``` + </TabItem> + </Tabs> +</CodeTabs> ::: ### Creating a dependency -You can use the <HT>`UseDependency.create`</HT> hook to create a dependency instance inmediately, e.g.: +Use the <HT>`UseDependency.create`</HT> hook to create a dependency instance inmediately, e.g.: ```dart collapse={1-3, 5-9} "UseDependency<MyDependency>.create" import 'package:reactter/reactter.dart'; @@ -419,7 +425,7 @@ The <HT>`UseDependency.create`</HT> hook is ideal for immediately creating the d ### Defining the dependency mode -You can use the `mode` argument to define the dependency mode. For example, to create a singleton dependency: +Use the `mode` parameter to define the [dependency mode](/reactter/core_concepts/dependency_injection#dependency-modes). For example, to create a singleton dependency: ```dart collapse={1-3, 8-12} "UseDependency<MyDependency>.create" "DependencyMode.singleton" import 'package:reactter/reactter.dart'; @@ -437,12 +443,16 @@ class MyController { ``` :::note -The `mode` argument is available only in <HT>`UseDependency.register`</HT> and <HT>`UseDependency.create`</HT> hooks. +The `mode` parameter is available only in <HT>`UseDependency.register`</HT> and <HT>`UseDependency.create`</HT> hooks. If you need to define the dependency mode directly, use the <HT>`UseDependency.lazyBuilder`</HT>, <HT>`UseDependency.lazyFactory`</HT>, <HT>`UseDependency.lazySingleton`</HT> hooks for registering a dependency in its respective mode. -Likewise, use the <HT>`UseDependency.builder`</HT>, <HT>`UseDependency.factory`</HT> and <HT>`UseDependency.singleton`</HT> hooks instead, for creating a dependency in its respective mode. Learn more about it in [Dependency Modes](/reactter/core_concepts/dependency_injection#dependency-modes), e.g.: +Likewise, use the <HT>`UseDependency.builder`</HT>, <HT>`UseDependency.factory`</HT> and <HT>`UseDependency.singleton`</HT> hooks instead, for creating a dependency in its respective mode, e.g.: -```dart title="dependencies_registers.dart" "UseDependency<MyDependency>.lazyFactory" "UseDependency<MyOtherDependency>.lazySingleton" "UseDependency<MyController>.builder" +<CodeTabs> + <Tabs> + <TabItem> + <HM single slot="label">my_controller.dart</HM> +```dart "UseDependency<MyDependency>.lazyFactory" "UseDependency<MyOtherDependency>.lazySingleton" "UseDependency<MyController>.builder" import 'package:reactter/reactter.dart'; class DependenciesRegisters { @@ -453,8 +463,10 @@ class DependenciesRegisters { } } ``` - -```dart title="my_app.dart" "RtMultiProvider" "RtProvider" "DependenciesRegisters" + </TabItem> + <TabItem> + <HM single slot="label">my_app.dart</HM> +```dart "RtMultiProvider" "RtProvider" "DependenciesRegisters" import 'package:flutter_reactter/flutter_reactter.dart'; class MyApp extends StatelessWidget { @@ -469,8 +481,10 @@ class MyApp extends StatelessWidget { } } ``` - -```dart title="my_controller.dart" "UseDependency<MyDependency>.get" "UseDependency<MyOtherDependency>.get" + </TabItem> + <TabItem> + <HM single slot="label">my_controller.dart</HM> +```dart "UseDependency<MyDependency>.get" "UseDependency<MyOtherDependency>.get" import 'package:reactter/reactter.dart'; class MyController { @@ -485,4 +499,7 @@ class MyController { } } ``` + </TabItem> + </Tabs> +</CodeTabs> ::: \ No newline at end of file diff --git a/website/src/content/docs/api/hooks/use_effect.mdx b/website/src/content/docs/api/hooks/use_effect.mdx index 37227519..54b517b8 100644 --- a/website/src/content/docs/api/hooks/use_effect.mdx +++ b/website/src/content/docs/api/hooks/use_effect.mdx @@ -9,10 +9,10 @@ import { HE, HN, HM, HT } from '@/components/Highlight'; import StateMethodsLite from '@/content/docs/shareds/state_methods_lite.mdx'; import * as ListeningChangesState from '@/content/docs/shareds/listening_changes_state.mdx'; -<HT><a href="https://pub.dev/documentation/reactter/latest/reactter/UseEffect-class.html" target="_blank">`UseEffect`</a></HT> is a [RtHook](https://pub.dev/documentation/reactter/latest/reactter/RtHook-class.html) that allows to manage side-effect, and its functionality is closely related to <HT>[Lifecycle](/reactter/core_concepts/lifecycle)</HT> of the _**instance**_ and its dependencies. +<HT><a href="https://pub.dev/documentation/reactter/latest/reactter/UseEffect-class.html" target="_blank">`UseEffect`</a></HT> is a [hook](/reactter/core_concepts/hooks) that allows to manage side-effect, and its functionality is closely related to <HT>[Lifecycle](/reactter/core_concepts/lifecycle)</HT> of its `dependencies` or the bound **_instance_**. :::note -The _**instance**_(or dependency) is the instance of the class where the hook is used. +The bound _**instance**_(or dependency) is the instance of the class where the hook is used. In this case, we will refer to the _**instance**_ as the _**dependency**_ which is managed by [dependency injection](/reactter/core_concepts/dependency_injection). ::: @@ -35,8 +35,8 @@ UseEffect.runOnInit( <HT>`UseEffect`</HT> accepts these parameters: -- <HM>`callback`</HM>: A function that performs side effects. This function is executed when the `dependencies` trigger <HE>`Lifecycle.didUpdate`</HE> event or the _**instance**_ trigger <HE>`Lifecycle.didMount`</HE> event. - If the `callback` returns a <HT>`Function`</HT>(considers as an _**effect cleanup**_), it will be called before the next effect is run or the _**instance**_ trigger <HE>`Lifecycle.willUnmount`</HE> event. +- <HM>**`callback`**</HM>: A function that performs side effects. This function is executed when the `dependencies` trigger <HE>`Lifecycle.didUpdate`</HE> event or the bound _**instance**_ trigger <HE>`Lifecycle.didMount`</HE> event. + If the `callback` returns a <HT>`Function`</HT>(considers as an _**effect cleanup**_), it will be called before the next effect is run or the bound _**instance**_ trigger <HE>`Lifecycle.willUnmount`</HE> event. :::tip The _**effect cleanup**_ is useful for cleaning up any resources or subscriptions that the effect may have created. ::: @@ -44,8 +44,8 @@ UseEffect.runOnInit( :::note The <HM>`callback`</HM> and _**effect cleanup**_ function may be called by <HT>[`Lifecycle`](/reactter/core_concepts/lifecycle)</HT> events, such as <HE>`Lifecycle.didMount`</HE> and <HE>`Lifecycle.willUnmount`</HE>. However, they work only if the instance is provided to the widget tree using the API of _**flutter_reactter**_ package. ::: -- `dependencies`: A list of state(<HT>`RtState`</HT>, learn about it [here](/reactter/core_concepts/state_management/#state)) that the effect depends on. -- `debugLabel`: A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). +- **`dependencies`**: A list of state(<HT>`RtState`</HT>, learn about it [here](/reactter/core_concepts/state_management/#state)) that the effect depends on. +- **`debugLabel`**: *(Optional)* A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). ## Properties & Methods @@ -66,12 +66,14 @@ class MyCounter { const MyCounter() { final timer = Timer.periodic(Duration(seconds: 1), (_) => uCount.value++); - UseEffect(() { - // Execute by `uCount` state changed or 'Lifecycle.didMount' event + UseEffect(() { // Effect callback + // Executed by `Lifecycle.didUpdate` event of `uCount` state + // or `Lifecycle.didMount` event of `MyCounter` instance print("Count: ${uCount.value}"); - return () { - // Effect cleanup - Execute before `uCount` state changed or 'Lifecycle.willUnmount' event + return () { // Effect cleanup + // Executed by `Lifecycle.willUpdate` event of `uCount` state + // or `Lifecycle.willUnmount` event of `MyCounter` instance if (uCount.value == 10 && timer.isActive) { timer.cancel(); print("Counter stopped"); @@ -86,19 +88,23 @@ In the example above, the <HT>`UseEffect`</HT> hook is used to print the `value` ### Running it inmediately -Sometime you may want to execute inmediately the <HT>`UseEffect`</HT> is initialized, you can use <HM>`UseEffect.runOnInit`</HM> to do that, e.g.: +Sometimes you may want to execute the <HT>`UseEffect`</HT> immediately upon initialization. +You can use <HM>`UseEffect.runOnInit`</HM> or the <HT>`AutoDispatchEffect`</HT> mixin to archive this. +Here's an example: + +#### Using <HM>`UseEffect.runOnInit`</HM> ```dart collapse={1-3, 14-14} "UseEffect.runOnInit" /(uCount)(?!\u0060)/ final uCount = UseState(0); void main() { - UseEffect.runOnInit(() { - // Execute by `uCount` state changed or 'Lifecycle.didMount' event + UseEffect.runOnInit(() { // Effect callback + // Executed at the initiation and by `Lifecycle.didUpdate` event of `uCount` state. print("Count: ${uCount.value}"); Future.delayed(const Duration(seconds: 1), () => uCount.value++); - return () { - // Effect cleanup - Execute before `uCount` state changed or 'Lifecycle.willUnmount' event + return () { // Effect cleanup + // Executed by `Lifecycle.willUpdate` event of `uCount` state. print("Cleanup executed"); }; }, [uCount]); @@ -108,5 +114,38 @@ void main() { In the example above, the <HT>`UseEffect.runOnInit`</HT> hook is used to print the `value` of the `uCount` state inmediately and increment the value every second. :::tip -The <HM>`UseEffect.runOnInit`</HM> is useful when you want to execute the `callback` function inmediately without waiting for <HE>`Lifecycle.didMount`</HE> event of _**instance**_. -::: \ No newline at end of file +The <HM>`UseEffect.runOnInit`</HM> is useful when you want to execute the <HM>`callback`</HM> function inmediately without waiting for <HE>`Lifecycle.didMount`</HE> event of _**instance**_. +::: + +#### Using <HT>`AutoDispatchEffect`</HT> mixin. + +The <HT>`AutoDispatchEffect`</HT> mixin can be used to automatically execute the effect when the class is initialized. +This is particularly useful when you want to ensure that the side effect runs as soon as the instance is created. Here's an example: + +```dart collapse={1-3, 14-14} "AutoDispatchEffect" /(uCount)(?!\u0060)/ +class MyCounter with AutoDispatchEffect { + final uCount = UseState(0); + + MyCounter() { + final timer = Timer.periodic(Duration(seconds: 1), (_) => uCount.value++); + + UseEffect(() { // Effect callback + // Executed by `Lifecycle.didUpdate` event of `uCount` state + // or `Lifecycle.didMount` event of `MyCounter` instance + print("Count: ${uCount.value}"); + + return () { // Effect cleanup + // Executed by `Lifecycle.willUpdate` event of `uCount` state + // or `Lifecycle.willUnmount` event of `MyCounter` instance + if (uCount.value == 10 && timer.isActive) { + timer.cancel(); + print("Counter stopped"); + } + }; + }, [uCount]); + } +} +``` + +In this example, the <HT>`AutoDispatchEffect`</HT> mixin ensures that the <HT>`UseEffect`</HT> <HM>`callback`</HM> is executed immediately when the <HT>`MyCounter`</HT> instance is created. +This eliminates the need to manually call <HM>`UseEffect.runOnInit`</HM>. \ No newline at end of file diff --git a/website/src/content/docs/api/hooks/use_reducer.mdx b/website/src/content/docs/api/hooks/use_reducer.mdx index 67a87e45..d8afd1a6 100644 --- a/website/src/content/docs/api/hooks/use_reducer.mdx +++ b/website/src/content/docs/api/hooks/use_reducer.mdx @@ -33,17 +33,17 @@ UseReducer<T>( <HT>`UseReducer`</HT> accepts these parameters: -- <HM>`reducer`</HM>: A function that takes the current `state` and an `action`, and returns a new state. -- `initialState`: Initial value of <HT>`T`</HT> type that it will hold. -- `debugLabel`: A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). +- <HM>**`reducer`**</HM>: A function that takes the current `state` and an `action`, and returns a new state. +- **`initialState`**: Initial value of <HT>`T`</HT> type that it will hold. +- **`debugLabel`**: *(Optional)* A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). ## Properties & Methods <HT>`UseReducer`</HT> provides the following properties and methods: -- `value`: A getter that allows to read its state. -- <HM>`dispatch`</HM>: A method that allows to dispatch an action to update the state. - - Syntax: +- **`value`**: A getter that allows to read its state. +- <HM>**`dispatch`**</HM>: A method that allows to dispatch an action to update the state. + - **Syntax**: ```dart void dispatch(RtAction action); ``` @@ -64,7 +64,7 @@ class UserController { final String userId; late final uUser = Rt.lazyState( - () => UseReducer<User?>.withArg(this.reducer, null), + () => UseReducer<User?>(this.reducer, null), this, ); @@ -161,10 +161,10 @@ uState.update((value) { }); ``` -Use <HM>`refresh`</HM> method to force to notify changes. +Use <HM>`notify`</HM> method to force to notify changes. -```dart showLineNumbers=false ".refresh" -userState.refresh(); +```dart showLineNumbers=false ".notify" +userState.notify(); ``` ### Listening to changes diff --git a/website/src/content/docs/api/hooks/use_state.mdx b/website/src/content/docs/api/hooks/use_state.mdx index fff01a14..e9bc196c 100644 --- a/website/src/content/docs/api/hooks/use_state.mdx +++ b/website/src/content/docs/api/hooks/use_state.mdx @@ -83,10 +83,10 @@ uState.update((value) { }); ``` -Use <HM>`refresh`</HM> method to force to notify changes. +Use <HM>`notify`</HM> method to force to notify changes. -```dart showLineNumbers=false ".refresh" -userState.refresh(); +```dart showLineNumbers=false ".notify" +userState.notify(); ``` ### Listening to changes diff --git a/website/src/content/docs/shareds/tip_dependency_checking.mdx b/website/src/content/docs/shareds/tip_dependency_checking.mdx new file mode 100644 index 00000000..b917216f --- /dev/null +++ b/website/src/content/docs/shareds/tip_dependency_checking.mdx @@ -0,0 +1,29 @@ +--- +title: Tip about the UseDependency hook +--- +import { HN, HT } from '@/components/Highlight'; + +:::tip + +If the dependency is not registered and created, the `instance` property will be <HN>`null`</HN>. + +To avoid this, you can use the <HT>[`UseEffect`](/reactter/api/hooks/use_effect)</HT> hook to get the dependency when it is created, e.g.: + +```dart title="my_controller.dart" "UseDependency<MyDependency>" "UseEffect" +import 'package:reactter/reactter.dart'; + +class MyController { + final uDependency = UseDependency<MyDependency>(); + + const MyController() { + UseEffect.runOnInit(() { + print( + uDependency.instance != null + ? "MyDependency is available" + : "MyDependency is not available yet." + ); + }, [uDependency]); + } +} +``` +::: \ No newline at end of file From 1ca28956e5e6f79353d7535a2c12ec3f07749a1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Sun, 2 Feb 2025 00:05:01 -0600 Subject: [PATCH 105/141] doc: Add contributors to README --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 13e79a9a..263d8373 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,8 @@ You can: Any idean is welcome! -## Authors +## Contributors -- **[Carlos León](https://twitter.com/CarLeonDev)** - <carleon.dev@gmail.com> -- **[Leo Castellanos](https://twitter.com/leoocast10)** - <leoocast.dev@gmail.com> +<a href="https://github.com/2devs-team/reactter/graphs/contributors"> + <img src="https://contrib.rocks/image?repo=2devs-team/reactter" /> +</a> From 272dbc76231df85239fcba7bafc6c53b0d02e729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Sun, 2 Feb 2025 00:24:35 -0600 Subject: [PATCH 106/141] refactor(website): Improve documentation for `Signal` class properties and methods --- website/src/content/docs/api/classes/signal.mdx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/website/src/content/docs/api/classes/signal.mdx b/website/src/content/docs/api/classes/signal.mdx index 03fa3b39..1245b79b 100644 --- a/website/src/content/docs/api/classes/signal.mdx +++ b/website/src/content/docs/api/classes/signal.mdx @@ -22,13 +22,22 @@ import * as ListeningChangesState from '@/content/docs/shareds/listening_changes <HT>`Signal`</HT> accepts this property: -- `initialValue`: Initial value of <HT>`T`</HT> type that the it will hold. +- **`initialValue`**: Initial value of <HT>`T`</HT> type that the it will hold. ## Properties & Methods <HT>`Signal`</HT> provides the following properties and methods: -- `value`: A getter/setter that allows to read and write its state. +- **`value`**: A getter/setter that allows to read and write its state. + +- Various properties and methods depending on the type of <HT>`T`</HT>. For example, if <HT>`T`</HT> is a <HT>`List`</HT>, you can use the methods and properties of the <HT>`List`</HT> class: + + ```dart showLineNumbers=false + final listSignal = Signal<List<int>>([]); + listSignal.addAll([1, 2, 3]); + listSignal.removeAt(0); + listSignal.clear(); + ``` <StateMethodsLite/> ## Usage @@ -97,10 +106,10 @@ userSignal.update((user) { }); ``` -Use <HM>`refresh`</HM> method to force to notify changes. +Use <HM>`notify`</HM> method to force to notify changes. ```dart showLineNumbers=false -userSignal.refresh(); +userSignal.notify(); ``` ### Listening to changes From fa915e127d762a5247b3b8e614c3d544ab5cd59c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Sun, 2 Feb 2025 03:06:17 -0600 Subject: [PATCH 107/141] refactor(memo): Rename `MemoMultiInterceptor` to `MultiMemoInterceptor` for clarity. --- CHANGELOG.md | 10 +++++----- .../controllers/github_search_controller.dart | 2 +- ...ti_interceptor.dart => multi_memo_interceptor.dart} | 4 ++-- packages/reactter/lib/src/memo/memo.dart | 2 +- packages/reactter/test/memo_test.dart | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) rename packages/reactter/lib/src/memo/interceptors/{memo_multi_interceptor.dart => multi_memo_interceptor.dart} (89%) diff --git a/CHANGELOG.md b/CHANGELOG.md index d312f552..345a7825 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,7 @@ - Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/Rt.html) instead. - Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtStateBase-class.html) instead. -- Replace [`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html) to `UseAsyncStateStatus.idle`. +- Replace [`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html) to [`UseAsyncStateStatus.idle`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/UseAsyncStateStatus.html). - Rename [`DispatchEffect`](https://pub.dev/documentation/reactter/7.3.0/reactter/DispatchEffect-class.html) to [`AutoDispatchEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/DispatchEffect-class.html). - Move `asyncFunction` to the first parameter of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/UseAsyncState/withArg.html). - Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. @@ -44,7 +44,7 @@ - Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/UseDependency-class.html) instead. - Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtAction-class.html) instead. - Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtActionCallable-class.html) instead. -- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MemoMultiInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/MemoMultiInterceptor-class.html) instead. +- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MultiMemoInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/MultiMemoInterceptor-class.html) instead. - Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/MemoSafeAsyncInterceptor-class.html) instead. - Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/MemoTemporaryCacheInterceptor-class.html) instead. - Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/MemoWrapperInterceptor-class.html) instead. @@ -59,7 +59,7 @@ - Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.19/flutter_reactter/RtDependencyNotFoundException-class.html) instead. - Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.19/flutter_reactter/RtScopeNotFoundException-class.html) instead. -## Fixes +### Fixes - Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/UseEffect-class.html) causing dependencies to not be unwatched. - Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/register.html) method. @@ -106,7 +106,7 @@ ## 7.3.1 -## Fixes +### Fixes - **fix:** Update `RtComponentWidget` typedef to `ReactterComponent`. @@ -132,7 +132,7 @@ - Deprecate [`ReactterAction`](https://pub.dev/documentation/reactter/7.2.0/reactter/ReactterAction-class.html), use [`RtAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtAction-class.html) instead. - Deprecate [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.2.0/reactter/ReactterActionCallable-class.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtActionCallable-class.html) instead. - **refactor:** Update memo interceptors to use more descriptive names. - - Deprecate [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.2.0/reactter/MemoInterceptors-class.html), use [`MemoMultiInterceptor`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoMultiInterceptor-class.html) instead. + - Deprecate [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.2.0/reactter/MemoInterceptors-class.html), use [`MultiMemoInterceptor`](https://pub.dev/documentation/reactter/7.3.0/reactter/MultiMemoInterceptor-class.html) instead. - Deprecate [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.2.0/reactter/AsyncMemoSafe-class.html) , use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoSafeAsyncInterceptor-class.html) instead. - Deprecate [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.2.0/reactter/TemporaryCacheMemo-class.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoTemporaryCacheInterceptor-class.html) instead. - Deprecate [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.2.0/reactter/MemoInterceptorWrapper-class.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoWrapperInterceptor-class.html) instead. diff --git a/packages/flutter_reactter/example/lib/examples/4_github_search/controllers/github_search_controller.dart b/packages/flutter_reactter/example/lib/examples/4_github_search/controllers/github_search_controller.dart index 31114d82..595c1a76 100644 --- a/packages/flutter_reactter/example/lib/examples/4_github_search/controllers/github_search_controller.dart +++ b/packages/flutter_reactter/example/lib/examples/4_github_search/controllers/github_search_controller.dart @@ -15,7 +15,7 @@ class GithubSearchController { late final uEntity = Rt.lazyState(() { final getEntityMemo = Memo.inline<Future<Object?>, String>( _getEntity, - const MemoMultiInterceptor([ + const MultiMemoInterceptor([ MemoSafeAsyncInterceptor(), MemoTemporaryCacheInterceptor(Duration(seconds: 30)), ]), diff --git a/packages/reactter/lib/src/memo/interceptors/memo_multi_interceptor.dart b/packages/reactter/lib/src/memo/interceptors/multi_memo_interceptor.dart similarity index 89% rename from packages/reactter/lib/src/memo/interceptors/memo_multi_interceptor.dart rename to packages/reactter/lib/src/memo/interceptors/multi_memo_interceptor.dart index 661d0a02..9ada7e61 100644 --- a/packages/reactter/lib/src/memo/interceptors/memo_multi_interceptor.dart +++ b/packages/reactter/lib/src/memo/interceptors/multi_memo_interceptor.dart @@ -3,11 +3,11 @@ part of '../memo.dart'; /// {@template reactter.memo_multi_interceptor} /// It allows multiple memoization interceptors to be used together. /// {@endtemplate} -class MemoMultiInterceptor<T, A> extends MemoInterceptor<T, A> { +class MultiMemoInterceptor<T, A> extends MemoInterceptor<T, A> { final List<MemoInterceptor<T, A>> interceptors; /// {@macro reactter.memo_multi_interceptor} - const MemoMultiInterceptor(this.interceptors); + const MultiMemoInterceptor(this.interceptors); @override void onInit(Memo<T, A> memo, A arg) { diff --git a/packages/reactter/lib/src/memo/memo.dart b/packages/reactter/lib/src/memo/memo.dart index bb4c1048..7f48695f 100644 --- a/packages/reactter/lib/src/memo/memo.dart +++ b/packages/reactter/lib/src/memo/memo.dart @@ -7,5 +7,5 @@ part 'memo_impl.dart'; part 'interceptors/memo_safe_async_interceptor.dart'; part 'interceptors/memo_wrapper_interceptor.dart'; part 'interceptors/memo_interceptor.dart'; -part 'interceptors/memo_multi_interceptor.dart'; +part 'interceptors/multi_memo_interceptor.dart'; part 'interceptors/memo_temporary_cache_interceptor.dart'; diff --git a/packages/reactter/test/memo_test.dart b/packages/reactter/test/memo_test.dart index 48a43306..5f95a38a 100644 --- a/packages/reactter/test/memo_test.dart +++ b/packages/reactter/test/memo_test.dart @@ -103,7 +103,7 @@ void main() { test( "shouldn't memoize when an error Future occurs " - "using AsyncMemoSafe interceptor", () async { + "using MemoSafeAsyncInterceptor interceptor", () async { final memo = Memo<Future<dynamic>, Args1<Error>>( (Args1 args) { return Future.error(args.arg1); @@ -165,7 +165,7 @@ void main() { final nInterceptors = 2; - final memoInterceptors = MemoMultiInterceptor<dynamic, Args1>([ + final memoInterceptors = MultiMemoInterceptor<dynamic, Args1>([ FakeInterceptorForCoverage(), ...List.generate( nInterceptors, From 103988ed0fa11ec2ded4d5cc2d23de1634e5e307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Sun, 2 Feb 2025 03:06:38 -0600 Subject: [PATCH 108/141] refactor(website): Add Migration section and update Memo interceptor references --- website/astro.config.mjs | 9 + website/src/content/docs/api/classes/memo.mdx | 2 +- .../docs/api/hooks/use_async_state.mdx | 2 +- .../docs/migration/v7.3.0_to_v8.0.0.mdx | 248 ++++++++++++++++++ 4 files changed, 259 insertions(+), 2 deletions(-) create mode 100644 website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx diff --git a/website/astro.config.mjs b/website/astro.config.mjs index 5ef217cf..4cf58e54 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -91,6 +91,15 @@ export default defineConfig({ directory: "core_concepts", }, }, + { + label: "Migration", + translations: { + es: "Migración", + }, + autogenerate: { + directory: "migration", + }, + }, { label: "API", translations: { diff --git a/website/src/content/docs/api/classes/memo.mdx b/website/src/content/docs/api/classes/memo.mdx index f28b0250..109046a9 100644 --- a/website/src/content/docs/api/classes/memo.mdx +++ b/website/src/content/docs/api/classes/memo.mdx @@ -27,7 +27,7 @@ Memo<T, A>( - <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/MemoSafeAsyncInterceptor-class.html" target="_blank">`MemoSafeAsyncInterceptor`</a></HT>: prevents caching if the <HT>`Future`</HT> calculation function throws an error during execution. - <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/MemoTemporaryCacheInterceptor-class.html" target="_blank">`MemoTemporaryCacheInterceptor`</a></HT>: removes memoized values from the cache after a specified duration. - <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/MemoWrapperInterceptor-class.html" target="_blank">`MemoWrapperInterceptor`</a></HT>: Wraps a memoized function, enabling the definition of callbacks for initialization, successful completion, error handling, and finishing. - - <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/MemoMultiInterceptor-class.html" target="_blank">`MemoMultiInterceptor`</a></HT>: allows multiple memoization interceptors to be used together. + - <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/MultiMemoInterceptor-class.html" target="_blank">`MultiMemoInterceptor`</a></HT>: allows multiple memoization interceptors to be used together. ## Methods diff --git a/website/src/content/docs/api/hooks/use_async_state.mdx b/website/src/content/docs/api/hooks/use_async_state.mdx index 6277c84d..77f9ee0d 100644 --- a/website/src/content/docs/api/hooks/use_async_state.mdx +++ b/website/src/content/docs/api/hooks/use_async_state.mdx @@ -223,7 +223,7 @@ final uTranslateState = UseAsyncState.withArg<String?, ArgsX3<String>>( // this is fake code, which simulates a request to API return await api.translate(text, from, to); }, - AsyncMemoSafe(), // avoid to save in cache when throw a error + MemoSafeAsyncInterceptor(), // avoid to save in cache when throw a error ), null, ); diff --git a/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx b/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx new file mode 100644 index 00000000..7dbebc2f --- /dev/null +++ b/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx @@ -0,0 +1,248 @@ +--- +title: v7.3.0 to v8.0.0 +description: Migration Guide from v7.3.0 to v8.0.0 +--- +import { HE, HM, HT } from '@/components/Highlight'; + +This guide will help you migrate your project from Reactter **v7.3.0** to **v8.0.0**. +The new version introduces several **enhancements**, **breaking changes**, and **fixes** to improve stability, performance, and developer experience. +Below, we'll walk you through the necessary steps to update your codebase. + +By following this guide, you should be able to successfully migrate your project to Reactter v8.0.0. +If you encounter any issues, refer to the [official documentation](https://pub.dev/documentation/reactter/8.0.0) or report them as an [issue on Github](https://github.com/2devs-team/reactter/issues).. + +## 1. Overview of Changes + +### Enhancements + +- Add Reactter devtools extension. +- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/removeObserver.html) methods to manage the observers. +- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). +- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). +- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. +- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. +- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtStateBase/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtStateBase/debugInfo.html) to states and hooks to get info for debugging. +- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. +- Add [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtStateBase-class.html) abstract class to implement the base logic of the state. +- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/createState.html) method to create a new state. +- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. +- Add [`initHook`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtHook/initHook.html) method to `RtHook` to call when hook is created. +- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. +- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. +- Enhance event handling and notifier logic for improved stability and performance. +- Enhance state management and error handling. +- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtComponent-class.html). +- Enhance dependency management to ensure to re-build when is detected changes while building. + +### Breakings + +- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0/reactter/Rt.html) instead. +- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtStateBase-class.html) instead. +- Replace [`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html) to [`UseAsyncStateStatus.idle`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseAsyncStateStatus.html). +- Rename [`DispatchEffect`](https://pub.dev/documentation/reactter/7.3.0/reactter/DispatchEffect-class.html) to [`AutoDispatchEffect`](https://pub.dev/documentation/reactter/8.0.0/reactter/DispatchEffect-class.html). +- Move `asyncFunction` to the first parameter of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseAsyncState/withArg.html). +- Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. +- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/isActive.html) instead. +- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/getDependencyMode.html) instead. +- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0/reactter/DependencyMode.html) instead. +- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0/reactter/Lifecycle.html) instead. +- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0/reactter/Lifecycle.html) instead. +- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`LifecycleObserver.onCreated`](https://pub.dev/documentation/reactter/8.0.0//reactter/LifecycleObserver/onCreated.html) instead. +- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtDependency-class.html) instead. +- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtHook-class.html) instead. +- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtInterface-class.html) instead. +- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtState/notify.html) instead. +- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseDependency-class.html) instead. +- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtAction-class.html) instead. +- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtActionCallable-class.html) instead. +- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MultiMemoInterceptor`](https://pub.dev/documentation/reactter/8.0.0/reactter/MultiMemoInterceptor-class.html) instead. +- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0/reactter/MemoSafeAsyncInterceptor-class.html) instead. +- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0/reactter/MemoTemporaryCacheInterceptor-class.html) instead. +- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0/reactter/MemoWrapperInterceptor-class.html) instead. +- Remove [`Obj`](https://pub.dev/documentation/reactter/7.3.0/reactter/Obj-class.html). +- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtProvider/RtProvider.init.html) instead. +- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtComponent-class.html) instead. +- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtConsumer-class.html) instead. +- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtProvider-class.html) instead. +- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtMultiProvider-class.html) instead. +- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtScope-class.html) instead. +- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtSignalWatcher-class.html) instead. +- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0/flutter_reactter/RtDependencyNotFoundException-class.html) instead. +- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0/flutter_reactter/RtScopeNotFoundException-class.html) instead. + +### Fixes + +- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseEffect-class.html) causing dependencies to not be unwatched. +- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/register.html) method. +- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/batch.html) method to work correctly. +- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0/reactter/Notifier-class.html) class. +- Fix to propagate state param to `boundInstance`. +- Remove listeners correctly from single-use listeners and handle null cases. + +## 2. Implement the changes + +- Replace all occurrences of <HT>`Reactter`</HT> with <HT>`Rt`</HT>. + + ```dart showLineNumbers=false del={1,3} del="Reactter" ins={2,4} ins="Rt" + Reactter.createState(...); + Rt.createState(...); + ReactterProvider(...); + RtProvider(...); + ``` + +- If you're using <HT>`ReactterStateImpl`</HT> or <HT>`RtStateImpl`</HT>, migrate to <HT>`RtStateBase`</HT> with <HT>`RtContextMixin`</HT>. + + ```dart showLineNumbers=false del={1} del="ReactterStateImpl" ins={2} ins="RtStateBase with RtContextMixin" + class MyState extends ReactterStateImpl { + class MyState extends RtStateBase with RtContextMixin { + // Implementation + } + ``` + +- Replace <HE>`UseAsyncStateStatus.standby`</HE> with <HE>`UseAsyncStateStatus.idle`</HE>. + + Before: + ```dart showLineNumbers=false del={1} del="standby" ins={2} ins="idle" + UseAsyncStateStatus.standby; + UseAsyncStateStatus.idle; + ``` + +- Replace <HT>`DispatchEffect`</HT> with <HT>`AutoDispatchEffect`</HT>. + + ```dart showLineNumbers=false del={1} del="DispatchEffect" ins={2} ins="AutoDispatchEffect" + class MyClass with DispatchEffect { + class MyClass with AutoDispatchEffect { + // Implementation + } + ``` + +- Move <HM>`asyncFunction`</HM> to the first parameter of <HT>`UseAsyncState`</HT> and <HT>`UseAsyncState.withArg`</HT>. + + ```dart showLineNumbers=false del={1,3} del="initialValue, asyncFunction" ins={2,4} ins="asyncFunction, initialValue" + final uAsyncState = UseAsyncState(initialValue, asyncFunction); + final uAsyncState = UseAsyncState(asyncFunction, initialValue); + final uAsyncStateWithArg = UseAsyncState.withArg(initialValue, asyncFunction); + final uAsyncStateWithArg = UseAsyncState.withArg(asyncFunction, initialValue); + ``` + +- Replace <HM>`Rt.isRegistered`</HM> with <HM>`Rt.isActive`</HM>. + + ```dart showLineNumbers=false del={1} del="isRegistered" ins={2} ins="isActive" + Rt.isRegistered(instance); + Rt.isActive(instance); + ``` + +- Replace <HM>`Rt.getInstanceManageMode`</HM> with <HM>`Rt.getDependencyMode`</HM>. + + ```dart showLineNumbers=false del={1} del="getInstanceManageMode" ins={2} ins="getDependencyMode" + Rt.getInstanceManageMode(instance); + Rt.getDependencyMode(instance); + ``` + +- Replace <HM>`RtState.refresh`</HM> with <HM>`RtState.notify`</HM>. + + ```dart showLineNumbers=false del={1} del="refresh" ins={2} ins="notify" + state.refresh(); + state.notify(); + ``` + +- Replace <HT>`UseInstance`</HT> with <HT>`UseDependency`</HT>. + + ```dart showLineNumbers=false del={1} del="UseInstance" ins={2} ins="UseDependency" + final uDependency = UseInstance<MyDependency>(); + final uDependency = UseDependency<MyDependency>(); + ``` + +- Replace deprecated <HT>`Lifecycle`</HT> enum values with their new counterparts. + + ```dart showLineNumbers=false del={1,3,5} del="initialized" del="destroyed" del="onInitialized" ins={2,4,6} ins="created" ins="deleted" ins="onCreated" + Lifecycle.initialized; + Lifecycle.created; + Lifecycle.destroyed; + Lifecycle.deleted; + ``` + +- Replace <HM>`LifecycleObserver.onInitialized`</HM> with <HM>`LifecycleObserver.onCreated`</HM>. + + ```dart showLineNumbers=false del={2} del="onInitialized" ins={3} ins="onCreated" + class MyClass extends LifecycleObserver { + void onInitialized() { + void onCreated() { + // Implementation + } + } + ``` + +- Replace <HT>`MemoInterceptors`</HT> with <HT>`MultiMemoInterceptor`</HT>. + + ```dart showLineNumbers=false del={3} del="MemoInterceptors" ins={4} ins="MultiMemoInterceptor" + final memo = Memo( + computeValue, + MemoInterceptors([ + MultiMemoInterceptor([ + // Interceptors + ]), + ); + ``` + +- Replace <HT>`AsyncMemoSafe`</HT> with <HT>`MemoSafeAsyncInterceptor`</HT>. + + ```dart showLineNumbers=false del={3} del="AsyncMemoSafe" ins={4} ins="MemoSafeAsyncInterceptor" + final memo = Memo( + computeValue, + AsyncMemoSafe(), + MemoSafeAsyncInterceptor(), + ); + ``` + +- Replace <HT>`TemporaryCacheMemo`</HT> with <HT>`MemoTemporaryCacheInterceptor`</HT>. + + ```dart showLineNumbers=false del={3} del="TemporaryCacheMemo" ins={4} ins="MemoTemporaryCacheInterceptor" + final memo = Memo( + computeValue, + TemporaryCacheMemo(...), + MemoTemporaryCacheInterceptor(...), + ); + ``` + +- Replace <HT>`MemoInterceptorWrapper`</HT> with <HT>`MemoWrapperInterceptor`</HT>. + + ```dart showLineNumbers=false del={3} del="MemoInterceptorWrapper" ins={4} ins="MemoWrapperInterceptor" + final memo = Memo( + computeValue, + MemoInterceptorWrapper(...), + MemoWrapperInterceptor(...), + ); + ``` + +- Replace <HM>`init`</HM> property with <HM>`RtProvider.init`</HM> constructor. + + ```dart showLineNumbers=false del={1,2} del="init: true," ins={3} ins="RtProvider.init" + RtProvider( + init: true, + RtProvider.init( + ... + ); + + ``` + +- Replace <HT>`ReactterProviders`</HT> or <HT>`RtProviders`</HT> with <HT>`RtMultiProvider`</HT>. + + ```dart showLineNumbers=false del={1} del="RtProviders" ins={2} ins="RtMultiProvider" + RtProviders(...) + RtMultiProvider(...) + ``` + +- Replace <HT>`ReactterWatcher`</HT> with <HT>`RtSignalWatcher`</HT>. + + ```dart showLineNumbers=false del={1} del="ReactterWatcher" ins={2} ins="RtSignalWatcher" + ReactterWatcher(...) + RtSignalWatcher(...) + ``` + + +## **3. Verify Your Code** +After making the above changes, thoroughly test your application to ensure everything works as expected. Pay special attention to: +- State and dependency lifecycles. +- Event handling and notifier logic. +- Debugging and logging outputs. \ No newline at end of file From 3ca95555bb9954707cbde0ce95e8bd52e4c510ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Mon, 3 Feb 2025 00:30:38 -0600 Subject: [PATCH 109/141] refactor(website): Enhance documentation for Memo class. --- website/src/content/docs/api/classes/memo.mdx | 21 ++++++++----------- .../docs/migration/v7.3.0_to_v8.0.0.mdx | 1 - 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/website/src/content/docs/api/classes/memo.mdx b/website/src/content/docs/api/classes/memo.mdx index 109046a9..95f93896 100644 --- a/website/src/content/docs/api/classes/memo.mdx +++ b/website/src/content/docs/api/classes/memo.mdx @@ -21,30 +21,27 @@ Memo<T, A>( <HT>`Memo`</HT> accepts these properties: -- <HM>`computeValue`</HM>: A method that takes an argument of type <HT>`A`</HT> and returns a value of <HT>`T`</HT> type. This is the core function that will be memoized. -- `interceptor`: A <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/MemoInterceptor-class.html" target="_blank">`MemoInterceptor`</a></HT> that allows you to intercept the memoization function calls and modify the memoization process. +- <HM>**`computeValue`**</HM>: A method that takes an argument of type <HT>`A`</HT> and returns a value of <HT>`T`</HT> type. This is the core function that will be memoized. +- **`interceptor`**: A <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/MemoInterceptor-class.html" target="_blank">`MemoInterceptor`</a></HT> that allows you to intercept the memoization function calls and modify the memoization process. Reactter providers some interceptors: - - <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/MemoSafeAsyncInterceptor-class.html" target="_blank">`MemoSafeAsyncInterceptor`</a></HT>: prevents caching if the <HT>`Future`</HT> calculation function throws an error during execution. - - <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/MemoTemporaryCacheInterceptor-class.html" target="_blank">`MemoTemporaryCacheInterceptor`</a></HT>: removes memoized values from the cache after a specified duration. - - <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/MemoWrapperInterceptor-class.html" target="_blank">`MemoWrapperInterceptor`</a></HT>: Wraps a memoized function, enabling the definition of callbacks for initialization, successful completion, error handling, and finishing. - - <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/MultiMemoInterceptor-class.html" target="_blank">`MultiMemoInterceptor`</a></HT>: allows multiple memoization interceptors to be used together. + - <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/MemoSafeAsyncInterceptor-class.html" target="_blank">**`MemoSafeAsyncInterceptor`**</a></HT>: prevents caching if the <HT>`Future`</HT> calculation function throws an error during execution. + - <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/MemoTemporaryCacheInterceptor-class.html" target="_blank">**`MemoTemporaryCacheInterceptor`**</a></HT>: removes memoized values from the cache after a specified duration. + - <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/MemoWrapperInterceptor-class.html" target="_blank">**`MemoWrapperInterceptor`**</a></HT>: Wraps a memoized function, enabling the definition of callbacks for initialization, successful completion, error handling, and finishing. + - <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/MultiMemoInterceptor-class.html" target="_blank">**`MultiMemoInterceptor`**</a></HT>: allows multiple memoization interceptors to be used together. ## Methods <HT>`Memo`</HT> provides the following methods that will help you manipulate the cache as you wish: -- <HM>`get`</HM>: Returns the cached value by argument passed. - - Syntax: +- <HM>**`get`**</HM>: Returns the cached value by argument passed. ```dart showLineNumbers=false T? get(A arg); ``` -- <HM>`remove`</HM>: Removes the cached value by argument passed. - - Syntax: +- <HM>**`remove`**</HM>: Removes the cached value by argument passed. ```dart showLineNumbers=false T? remove(A arg); ``` -- <HM>`clear`</HM>: Removes all cached data. - - Syntax: +- <HM>**`clear`**</HM>: Removes all cached data. ```dart showLineNumbers=false void clear(); ``` diff --git a/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx b/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx index 7dbebc2f..ae87dd5a 100644 --- a/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx +++ b/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx @@ -101,7 +101,6 @@ If you encounter any issues, refer to the [official documentation](https://pub.d - Replace <HE>`UseAsyncStateStatus.standby`</HE> with <HE>`UseAsyncStateStatus.idle`</HE>. - Before: ```dart showLineNumbers=false del={1} del="standby" ins={2} ins="idle" UseAsyncStateStatus.standby; UseAsyncStateStatus.idle; From a9ef75d3b3e20dd5a5c9e6dd5dba3bc0761091bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Mon, 3 Feb 2025 00:31:52 -0600 Subject: [PATCH 110/141] refactor(website): Update formatting for arguments documentation in Args class --- website/src/content/docs/api/classes/args.mdx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/website/src/content/docs/api/classes/args.mdx b/website/src/content/docs/api/classes/args.mdx index aa770425..658847b2 100644 --- a/website/src/content/docs/api/classes/args.mdx +++ b/website/src/content/docs/api/classes/args.mdx @@ -36,11 +36,11 @@ Reactter provides these generic arguments classes: In each of the methods it provides these properties and methods: -- `arguments`: A property to get the list of arguments. -- `arg1`: A property to get the first argument. -- `arg2`(<HT>`Args2`</HT>, <HT>`Args3`</HT>, <HT>`ArgsX2`</HT>, <HT>`ArgsX3`</HT> only): A property to get the second argument. -- `arg3`(<HT>`Args3`</HT>, <HT>`ArgsX3`</HT> only): A property to get the third argument. -- <HM>`toList()`</HM>: A method to get the list of arguments <HT>`T`</HT> type. +- **`arguments`**: A property to get the list of arguments. +- **`arg1`**: A property to get the first argument. +- **`arg2`**(<HT>`Args2`</HT>, <HT>`Args3`</HT>, <HT>`ArgsX2`</HT>, <HT>`ArgsX3`</HT> only): A property to get the second argument. +- **`arg3`**(<HT>`Args3`</HT>, <HT>`ArgsX3`</HT> only): A property to get the third argument. +- <HM>**`toList`**</HM>: A method to get the list of arguments <HT>`T`</HT> type. ## Usage From 670d74830938ae76637e0151249d8731ae390c49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Mon, 3 Feb 2025 00:42:18 -0600 Subject: [PATCH 111/141] refactor(website): Enhance documentation for LifecycleObserver class with detailed methods and examples. --- .../docs/api/classes/lifecycle_observer.mdx | 91 ++++++++++++++++++- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/website/src/content/docs/api/classes/lifecycle_observer.mdx b/website/src/content/docs/api/classes/lifecycle_observer.mdx index d5b753d8..6b489a68 100644 --- a/website/src/content/docs/api/classes/lifecycle_observer.mdx +++ b/website/src/content/docs/api/classes/lifecycle_observer.mdx @@ -1,6 +1,93 @@ --- -title: LifecycleObserver 🚧 +title: LifecycleObserver description: The base class for all lifecycle observers in Reactter. sidebar: order: 5 ---- \ No newline at end of file +--- +import { HM, HT } from '@/components/Highlight'; + +:::tip +This documentation assumes you've already read the [Lifecycle](/reactter/core_concepts/lifecycle/). +It's recommended read that first if you're new in Reactter. +::: + +<HT><a href="https://pub.dev/documentation/reactter/latest/reactter/LifecycleObserver-class.html" target="_blank">`LifecycleObserver`</a></HT> is a class that provides a way to observe the lifecycle of a dependency. + +## Syntax + +```dart showLineNumbers=false +abstract class LifecycleObserver { + /// This method is called when the dependency instance is created. + void onCreated() {} + + /// This method is called when the dependency is going to be mounted + /// in the widget tree(exclusive to `flutter_reactter`). + void onWillMount() {} + + /// This method is called when the dependency has been successfully mounted + /// in the widget tree(exclusive to `flutter_reactter`). + void onDidMount() {} + + /// This method is called when the dependency's state is about to be updated. + /// The parameter is a [IState]. + void onWillUpdate(covariant IState? state) {} + + /// This method is called when the dependency's state has been updated. + /// The parameter is a [IState]. + void onDidUpdate(covariant IState? state) {} + + /// This method is called when the dependency is going to be unmounted + /// from the widget tree(exclusive to `flutter_reactter`). + void onWillUnmount() {} + + /// This method is called when the dependency has been successfully unmounted + /// from the widget tree(exclusive to `flutter_reactter`). + void onDidUnmount() {} +} +``` + +## Example + +Here's an example of a simple `LifecycleObserver`: + +```dart +class MyDependency extends LifecycleObserver { + @override + void onCreated() { + print('MyObserver created'); + } + + @override + void onWillMount() { + print('MyObserver will mount'); + } + + @override + void onDidMount() { + print('MyObserver did mount'); + } + + @override + void onWillUpdate(RtState? state) { + print('MyObserver will update with state: $state'); + } + + @override + void onDidUpdate(RtState? state) { + print('MyObserver did update with state: $state'); + } + + @override + void onWillUnmount() { + print('MyObserver will unmount'); + } + + @override + void onDidUnmount() { + print('MyObserver did unmount'); + } +} +``` + +In the above example, the `MyDependency` class extends `LifecycleObserver` and overrides all the lifecycle methods. +When an instance of `MyDependency` is created, the lifecycle methods are called in the order they are defined. From 426d0f84d75c3854b8a568e42c594acaff6625f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Mon, 3 Feb 2025 01:22:11 -0600 Subject: [PATCH 112/141] refactor(website): Improve `LifecycleObserver` documentation and examples for clarity and usability. --- .../docs/api/classes/lifecycle_observer.mdx | 109 ++++++++---------- .../content/docs/core_concepts/lifecycle.mdx | 2 +- .../lib/counter_controller.dart | 25 ++-- 3 files changed, 67 insertions(+), 69 deletions(-) diff --git a/website/src/content/docs/api/classes/lifecycle_observer.mdx b/website/src/content/docs/api/classes/lifecycle_observer.mdx index 6b489a68..add69a34 100644 --- a/website/src/content/docs/api/classes/lifecycle_observer.mdx +++ b/website/src/content/docs/api/classes/lifecycle_observer.mdx @@ -5,6 +5,16 @@ sidebar: order: 5 --- import { HM, HT } from '@/components/Highlight'; +import CodeTabs from '@/components/CodeTabs.astro'; +import { Tabs, TabItem } from "@/components/Tabs"; +import ZappButton from "@/components/ZappButton.astro"; +import { Code } from "@astrojs/starlight/components"; +import { marks } from '@/examples/marks.ts' + +import counterControllerLifecycleObserverCode from '@/examples/lifecycle_observer/lib/counter_controller.dart?raw'; +import counterLifecycleObserverCode from '@/examples/lifecycle_observer/lib/counter.dart?raw'; +import counterViewLifecycleObserverCode from '@/examples/lifecycle_observer/lib/counter_view.dart?raw'; +import counterMainLifecycleObserverCode from '@/examples/lifecycle_observer/lib/main.dart?raw'; :::tip This documentation assumes you've already read the [Lifecycle](/reactter/core_concepts/lifecycle/). @@ -17,77 +27,58 @@ It's recommended read that first if you're new in Reactter. ```dart showLineNumbers=false abstract class LifecycleObserver { - /// This method is called when the dependency instance is created. void onCreated() {} - - /// This method is called when the dependency is going to be mounted - /// in the widget tree(exclusive to `flutter_reactter`). void onWillMount() {} - - /// This method is called when the dependency has been successfully mounted - /// in the widget tree(exclusive to `flutter_reactter`). void onDidMount() {} - - /// This method is called when the dependency's state is about to be updated. - /// The parameter is a [IState]. - void onWillUpdate(covariant IState? state) {} - - /// This method is called when the dependency's state has been updated. - /// The parameter is a [IState]. - void onDidUpdate(covariant IState? state) {} - - /// This method is called when the dependency is going to be unmounted - /// from the widget tree(exclusive to `flutter_reactter`). + void onWillUpdate(RtState state) {} + void onDidUpdate(RtState state) {} void onWillUnmount() {} - - /// This method is called when the dependency has been successfully unmounted - /// from the widget tree(exclusive to `flutter_reactter`). void onDidUnmount() {} } ``` -## Example - -Here's an example of a simple `LifecycleObserver`: - -```dart -class MyDependency extends LifecycleObserver { - @override - void onCreated() { - print('MyObserver created'); - } +## Methods + +- <HM>**`onCreated`**</HM>: Called when the dependency is created. +This is the first lifecycle method invoked. +- <HM>**`onWillMount`**</HM>(only in **flutter_reactter**): Called just before the dependency is mounted. +Useful for pre-mount setup. +- <HM>**`onDidMount`**</HM>(only in **flutter_reactter**): Called immediately after the dependency is mounted. +Ideal for post-mount initialization. +- <HM>**`onWillUpdate`**</HM>: Called before the dependency is updated. +The parameter is the state (RtState) that triggered the update. +Use this to react to state changes before they take effect. +- <HM>**`onDidUpdate`**</HM>: Called after the dependency is updated. +The parameter is the state (RtState) that triggered the update. +Use this to react to state changes after they take effect. +- <HM>**`onWillUnmount`**</HM>(only in **flutter_reactter**): Called just before the dependency is unmounted. +Useful for cleanup tasks. +- <HM>**`onDidUnmount`**</HM>(only in **flutter_reactter**): Called immediately after the dependency is unmounted. +Ideal for final cleanup or resource release. - @override - void onWillMount() { - print('MyObserver will mount'); - } +## Example - @override - void onDidMount() { - print('MyObserver did mount'); - } +Here's an example of a simple LifecycleObserver implementation: - @override - void onWillUpdate(RtState? state) { - print('MyObserver will update with state: $state'); - } +<CodeTabs> + <ZappButton path="examples/lifecycle_observer"/> - @override - void onDidUpdate(RtState? state) { - print('MyObserver did update with state: $state'); - } + <Tabs> + <TabItem> + <HM single slot="label">counter_controller.dart</HM> + <Code code={counterControllerLifecycleObserverCode} lang="dart" mark={[...marks, {range: "14-17"}, {range: "19-22"}, {range: "24-27"}, {range: "29-32"}, {range: "34-37"}, {range: "39-42"}, {range: "44-47"}]}/> + </TabItem> - @override - void onWillUnmount() { - print('MyObserver will unmount'); - } + <TabItem label="counter_view.dart"> + <Code code={counterViewLifecycleObserverCode} lang="dart" mark={marks} /> + </TabItem> - @override - void onDidUnmount() { - print('MyObserver did unmount'); - } -} -``` + <TabItem label="counter.dart"> + <Code code={counterLifecycleObserverCode} lang="dart" mark={marks} /> + </TabItem> -In the above example, the `MyDependency` class extends `LifecycleObserver` and overrides all the lifecycle methods. -When an instance of `MyDependency` is created, the lifecycle methods are called in the order they are defined. + <TabItem label="main.dart"> + <Code code={counterMainLifecycleObserverCode} lang="dart" mark={marks} /> + </TabItem> + </Tabs> +</CodeTabs> \ No newline at end of file diff --git a/website/src/content/docs/core_concepts/lifecycle.mdx b/website/src/content/docs/core_concepts/lifecycle.mdx index 4f6867e0..f6c4003a 100644 --- a/website/src/content/docs/core_concepts/lifecycle.mdx +++ b/website/src/content/docs/core_concepts/lifecycle.mdx @@ -94,7 +94,7 @@ and use its methods to observe the lifecycle events. e.g: <Tabs> <TabItem> <HM single slot="label">counter_controller.dart</HM> - <Code code={counterControllerLifecycleObserverCode} lang="dart" mark={[...marks, {range: "6-8"}, {range: "10-12"}, {range: "14-16"}, {range: "18-20"}, {range: "22-24"}, {range: "26-28"}, {range: "30-32"}]}/> + <Code code={counterControllerLifecycleObserverCode} lang="dart" mark={[...marks, {range: "14-17"}, {range: "19-22"}, {range: "24-27"}, {range: "29-32"}, {range: "34-37"}, {range: "39-42"}, {range: "44-47"}]}/> </TabItem> <TabItem label="counter_view.dart"> diff --git a/website/src/examples/lifecycle_observer/lib/counter_controller.dart b/website/src/examples/lifecycle_observer/lib/counter_controller.dart index 9f6d656b..b9718ade 100644 --- a/website/src/examples/lifecycle_observer/lib/counter_controller.dart +++ b/website/src/examples/lifecycle_observer/lib/counter_controller.dart @@ -3,39 +3,46 @@ import 'package:flutter_reactter/flutter_reactter.dart'; class CounterController extends LifecycleObserver { final count = Signal(0); - void onInitialized() { + void increment() { + count.value++; + } + + void decrement() { + count.value--; + } + + @override + void onCreated() { print('CounterController initialized'); } + @override void onDidMount() { print('CounterController mounted'); } + @override void onWillMount() { print('CounterController will mount'); } + @override void onWillUpdate(RtState state) { print('CounterController will update by ${state.runtimeType}'); } + @override void onDidUpdate(RtState state) { print('CounterController did update by ${state.runtimeType}'); } + @override void onWillUnmount() { print('CounterController will unmount'); } + @override void onDidUnmount() { print('CounterController did unmount'); } - - void increment() { - count.value++; - } - - void decrement() { - count.value--; - } } From 0c92a5a223bcf11fa9b85dc345284e9e7714cd6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Mon, 3 Feb 2025 02:16:38 -0600 Subject: [PATCH 113/141] refactor(website): Add documentation for RtHook. --- .../src/content/docs/api/classes/rt_hooks.mdx | 52 ++++++++++++++++- .../src/content/docs/core_concepts/hooks.mdx | 58 ++----------------- .../docs/shareds/creating_custom_hook.mdx | 30 ++++++++++ .../docs/shareds/using_custom_hook.mdx | 47 +++++++++++++++ 4 files changed, 132 insertions(+), 55 deletions(-) create mode 100644 website/src/content/docs/shareds/creating_custom_hook.mdx create mode 100644 website/src/content/docs/shareds/using_custom_hook.mdx diff --git a/website/src/content/docs/api/classes/rt_hooks.mdx b/website/src/content/docs/api/classes/rt_hooks.mdx index 88cfa31c..9b46ce47 100644 --- a/website/src/content/docs/api/classes/rt_hooks.mdx +++ b/website/src/content/docs/api/classes/rt_hooks.mdx @@ -1,6 +1,54 @@ --- -title: RtHook 🚧 +title: RtHook description: Aprende aceca de los Hooks en Reactter. sidebar: order: 6 ---- \ No newline at end of file +--- + +import { HM, HK, HT } from '@/components/Highlight'; +import StateMethodsLite from '@/content/docs/shareds/state_methods_lite.mdx'; +import CreatingCustomHook from '@/content/docs/shareds/creating_custom_hook.mdx'; +import UsingCustomHook from '@/content/docs/shareds/using_custom_hook.mdx'; + +:::tip +This documentation assumes you've already read the [hooks](/reactter/core_concepts/hooks/). +It's recommended read that first if you're new in Reactter. +::: + +<HT><a href="https://pub.dev/documentation/reactter/latest/reactter/RtHook-class.html" target="_blank">`RtHook`</a></HT> is a class that provides a way to create a custom hook. + +## Syntax + +```dart showLineNumbers=false +abstract class RtHook with RtContextMixin, RtStateBase<RtHook> { + static get $register => HookBindingZone<RtHook>(); + HookBindingZone<RtHook> get $; + void initHook() + void update([Function()? fnUpdate]) +} +``` + +## Properties & Methods + +- **`$register`**: A static getter to allow access to <HT>`HookBindingZone`</HT>. +It's responsible for registering a <HT>`RtHook`</HT> and attaching collected states to it. +- **`$`**: A required getter to access the <HT>`HookBindingZone`</HT> instance. +It must be defined has a <HK>`final`</HK> property on first line of the class for registering the hook and attach the states below it, like this: + + ```dart showLineNumbers=false + final $ = RtHook.$register; + ``` +- <HM>**`initHook`**</HM>: Called when the hook is initialized. +Useful for setting up state dependencies or performing side effects when the hook is first created. + +<StateMethodsLite/> + +## Usage + +### Creating a custom hook + +<CreatingCustomHook/> + +### Using the hook + +<UsingCustomHook/> \ No newline at end of file diff --git a/website/src/content/docs/core_concepts/hooks.mdx b/website/src/content/docs/core_concepts/hooks.mdx index 51a8df3d..75770e5d 100644 --- a/website/src/content/docs/core_concepts/hooks.mdx +++ b/website/src/content/docs/core_concepts/hooks.mdx @@ -13,6 +13,8 @@ import { Code } from "@astrojs/starlight/components"; import { marks } from '@/examples/marks.ts' import StateMethodsLite from '@/content/docs/shareds/state_methods_lite.mdx'; +import CreatingCustomHook from '@/content/docs/shareds/creating_custom_hook.mdx'; +import UsingCustomHook from '@/content/docs/shareds/using_custom_hook.mdx'; import MainCode from '@/examples/custom_hook/lib/main.dart?raw'; import MyCustomFormCode from '@/examples/custom_hook/lib/my_custom_form.dart?raw'; @@ -53,58 +55,8 @@ There are several advantages to using _**custom hooks**_: ### Creating a custom hook -To create a _**custom hook**_, you need to create a class that extends <HT>[`RtHook`](/reactter/api/classes/rt_hook)</HT> and follow the naming convention with the <HT>`Use`</HT> prefix. +<CreatingCustomHook/> -Here's an example of a _**custom hook**_ that manages the state of a text input: +### Using the hook -<Code code={UseTextInputCode} lang="dart" mark={[...marks, {range: "5-6", label: "* This line is REQUIRED! "}]} /> - -:::caution[Attention!!] -To create a _**hook**_, you need to register it by adding the following line: - -```dart showLineNumbers=false -final $ = RtHook.$register; -``` - -It's important to note that the states and hooks defined above this line are not linked to the hook. -To avoid this, it's recommended to place this line as the first line in the class body. -::: - -As shown in the example above, we can utilize other hooks such as <HT>[`UseEffect`](/reactter/api/hooks/use_effect)</HT> to monitor changes in the text input's controller and ensure it is disposed when the hook is destroyed. - -The <HM>`update`</HM> method is used to set the internal `_value` to the current text in the controller, which keeps the state synchronized with the input. -This methods is a part of the <HT>`RtHook`</HT> class that allows you to update the state of the hook. - -### Using a custom hook - -You can then call that _**Custom Hook**_ from anywhere in the code and get access to its shared logic: - -<CodeTabs> - <ZappButton path="examples/custom_hook"/> - - <Tabs> - <TabItem> - <HM single slot="label">my_controller.dart</HM> - <Code code={MyControllerCode} lang="dart" mark={[...marks, {range: "5-6"}, 11, 12]}/> - </TabItem> - - <TabItem> - <HM single slot="label">my_custom_form.dart</HM> - <Code code={MyCustomFormCode} lang="dart" mark={[...marks, 43, 49]}/> - </TabItem> - - <TabItem label="use_text_input.dart"> - <Code code={UseTextInputCode} lang="dart" mark={[...marks]}/> - </TabItem> - - <TabItem label="main.dart"> - <Code code={MainCode} lang="dart" mark={marks} /> - </TabItem> - </Tabs> -</CodeTabs> - -In the previous example, the form captures the input from the name and surname fields, combines them into a full name, and displays the result. -The <HT>`MyController`</HT> component uses the <HT>`UseTextInput`</HT> hook (_**custom hook**_ created previously) to manage the name and lastname inputs. - -The `fullName` state is defined using <HT>[`UseCompute`](/reactter/api/hooks/use_compute)</HT>, which calculates the full name based on the values of `firstNameInput` and `lastNameInput`. -This ensures that the full name updates automatically whenever either input changes. \ No newline at end of file +<UsingCustomHook/> diff --git a/website/src/content/docs/shareds/creating_custom_hook.mdx b/website/src/content/docs/shareds/creating_custom_hook.mdx new file mode 100644 index 00000000..05fbaca8 --- /dev/null +++ b/website/src/content/docs/shareds/creating_custom_hook.mdx @@ -0,0 +1,30 @@ +--- +title: Creating a custom hook +--- + +import { HT, HM } from '@/components/Highlight'; +import { Code } from "@astrojs/starlight/components"; +import { marks } from '@/examples/marks.ts' +import UseTextInputCode from '@/examples/custom_hook/lib/use_text_input.dart?raw'; + +To create a [custom hook](/reactter/core_concepts/hooks/#custom-hook), you need to create a class that extends <HT>[`RtHook`](/reactter/api/classes/rt_hook)</HT> and follow the naming convention with the <HT>`Use`</HT> prefix. + +Here's an example of a [custom hook](/reactter/core_concepts/hooks/#custom-hook) that manages the state of a text input: + +<Code code={UseTextInputCode} lang="dart" mark={[...marks, {range: "5-6", label: "* This line is REQUIRED! "}]} /> + +:::caution[Attention!!] +To create a _**hook**_, you need to register it by adding the following line: + +```dart showLineNumbers=false +final $ = RtHook.$register; +``` + +It's important to note that the states and hooks defined above this line are not linked to the hook. +To avoid this, it's recommended to place this line as the first line in the class body. +::: + +As shown in the example above, we can utilize other hooks such as <HT>[`UseEffect`](/reactter/api/hooks/use_effect)</HT> to monitor changes in the text input's controller and ensure it is disposed when the hook is destroyed. + +The <HM>`update`</HM> method is used to set the internal `_value` to the current text in the controller, which keeps the state synchronized with the input. +This methods is a part of the <HT>`RtHook`</HT> class that allows you to update the state of the hook. diff --git a/website/src/content/docs/shareds/using_custom_hook.mdx b/website/src/content/docs/shareds/using_custom_hook.mdx new file mode 100644 index 00000000..2a3a2515 --- /dev/null +++ b/website/src/content/docs/shareds/using_custom_hook.mdx @@ -0,0 +1,47 @@ +--- +title: Using the hook +--- + +import { HT, HM } from '@/components/Highlight'; +import CodeTabs from '@/components/CodeTabs.astro'; +import { Tabs, TabItem } from "@/components/Tabs"; +import ZappButton from "@/components/ZappButton.astro"; +import { Code } from "@astrojs/starlight/components"; +import { marks } from '@/examples/marks.ts' + +import MainCode from '@/examples/custom_hook/lib/main.dart?raw'; +import MyCustomFormCode from '@/examples/custom_hook/lib/my_custom_form.dart?raw'; +import MyControllerCode from '@/examples/custom_hook/lib/my_controller.dart?raw'; +import UseTextInputCode from '@/examples/custom_hook/lib/use_text_input.dart?raw'; + +You can then call that [custom hook](/reactter/core_concepts/hooks/#custom-hook) from anywhere in the code and get access to its shared logic: + +<CodeTabs> + <ZappButton path="examples/custom_hook"/> + + <Tabs> + <TabItem> + <HM single slot="label">my_controller.dart</HM> + <Code code={MyControllerCode} lang="dart" mark={[...marks, {range: "5-6"}, 11, 12]}/> + </TabItem> + + <TabItem> + <HM single slot="label">my_custom_form.dart</HM> + <Code code={MyCustomFormCode} lang="dart" mark={[...marks, 43, 49]}/> + </TabItem> + + <TabItem label="use_text_input.dart"> + <Code code={UseTextInputCode} lang="dart" mark={[...marks]}/> + </TabItem> + + <TabItem label="main.dart"> + <Code code={MainCode} lang="dart" mark={marks} /> + </TabItem> + </Tabs> +</CodeTabs> + +In the previous example, the form captures the input from the name and surname fields, combines them into a full name, and displays the result. +The <HT>`MyController`</HT> component uses the <HT>`UseTextInput`</HT> hook (_**custom hook**_ created previously) to manage the name and lastname inputs. + +The `fullName` state is defined using <HT>[`UseCompute`](/reactter/api/hooks/use_compute)</HT>, which calculates the full name based on the values of `firstNameInput` and `lastNameInput`. +This ensures that the full name updates automatically whenever either input changes. \ No newline at end of file From 7258fcb4e26a084347b1c860ed075b88039990aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Wed, 5 Feb 2025 10:55:18 -0600 Subject: [PATCH 114/141] refactor(website): Correct typos and improve documentation clarity across multiple files --- packages/reactter/lib/src/hooks/use_reducer.dart | 2 +- website/src/content/docs/api/classes/args.mdx | 8 +++++--- website/src/content/docs/api/widgets/rt_selector.mdx | 2 +- website/src/content/docs/getting_started.mdx | 2 +- website/src/examples/custom_hook/lib/my_custom_form.dart | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/reactter/lib/src/hooks/use_reducer.dart b/packages/reactter/lib/src/hooks/use_reducer.dart index b8851fb2..9baf7d03 100644 --- a/packages/reactter/lib/src/hooks/use_reducer.dart +++ b/packages/reactter/lib/src/hooks/use_reducer.dart @@ -5,7 +5,7 @@ part of 'hooks.dart'; /// /// [UseReducer] accepts a [reducer] method /// and returns the current state paired with a [dispatch] method. -/// (If you’re familiar with Redux, you already know how this works.) +/// (If you're familiar with Redux, you already know how this works.) /// /// Contains a [value] of type [T] which represents the current state. /// diff --git a/website/src/content/docs/api/classes/args.mdx b/website/src/content/docs/api/classes/args.mdx index 658847b2..0945ba52 100644 --- a/website/src/content/docs/api/classes/args.mdx +++ b/website/src/content/docs/api/classes/args.mdx @@ -44,6 +44,8 @@ In each of the methods it provides these properties and methods: ## Usage +### Define Arguments + To use it, you can create an instance and provide the specified types of arguments, e.g.: ```dart @@ -81,7 +83,7 @@ void myFunction(ArgsX3<int> args) { myFunction(ArgsX3(1, 2, 3)); ``` -## Type compatibility +### Type compatibility The <HT>`Args`</HT> classes are compatible with each other, so you can pass an <HT>`Args`</HT> instance to a function that expects an <HT>`Args`</HT> instance with fewer arguments, e.g.: @@ -114,7 +116,7 @@ myFunction(Args2(1, "foo")); myFunction(Args3(1, "bar", false)); ``` -## Equality Comparation +### Equality Comparation The <HT>`Args`</HT> classes are comparable, so you can compare them with each other. Two Args instances are considered equal if they contain the same number of arguments and the values in each corresponding position match, e.g.: @@ -140,7 +142,7 @@ print(Args3(1, "foo", false) == Args([1, "foo", false, "bar"])); // false print(ArgsX3(3, "bar", false) == Args3("bar", 3, false)); // false ``` -## Ary Function +### Ary Function > The _**arity**_ or _**adicity**_ of a function is the number of arguments (i.e. inputs or parameters) it takes. diff --git a/website/src/content/docs/api/widgets/rt_selector.mdx b/website/src/content/docs/api/widgets/rt_selector.mdx index 24a5c70b..2b1c1190 100644 --- a/website/src/content/docs/api/widgets/rt_selector.mdx +++ b/website/src/content/docs/api/widgets/rt_selector.mdx @@ -90,7 +90,7 @@ RtSelector<UserController, String>( To use <HT>`RtSelector`</HT>, wrap it around the widget that you want to rebuild when the selected value changes. -Here’s an example of how to use it: +Here's an example of how to use it: <CodeTabs> <ZappButton path="examples/rt_selector"/> diff --git a/website/src/content/docs/getting_started.mdx b/website/src/content/docs/getting_started.mdx index 1d810ea0..1ebada3c 100644 --- a/website/src/content/docs/getting_started.mdx +++ b/website/src/content/docs/getting_started.mdx @@ -40,7 +40,7 @@ Before anything, you need to be aware that Reactter is distributed on two packag ## Installation -To follow this guide, you’ll need an existing Dart/Flutter project. +To follow this guide, you'll need an existing Dart/Flutter project. If you don't get one yet, you can create a Dart app <a href="https://dart.dev/tutorials/server/get-started" target="_blank">here</a> or a Flutter app <a href="https://docs.flutter.dev/get-started/install" target="_blank">here</a>. Once you have your project set up, proceed to install the package using one of the following methods: diff --git a/website/src/examples/custom_hook/lib/my_custom_form.dart b/website/src/examples/custom_hook/lib/my_custom_form.dart index 6456d34c..a9e79a33 100644 --- a/website/src/examples/custom_hook/lib/my_custom_form.dart +++ b/website/src/examples/custom_hook/lib/my_custom_form.dart @@ -12,7 +12,7 @@ class MyCustomForm extends StatelessWidget { builder: (context, myController, child) { return Scaffold( appBar: AppBar( - title: const Text("Retrieve Text Input"), + title: const Text("Custom hook example"), ), body: Padding( padding: const EdgeInsets.all(16), From 18d06c4c17aeb568076c18225e6afd4fdc654e13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Wed, 5 Feb 2025 18:19:44 -0600 Subject: [PATCH 115/141] chore(dependencies): Update reactter to version 8.0.0-dev.20 --- CHANGELOG.md | 102 +++++++++--------- .../flutter_reactter/example/pubspec.yaml | 2 +- packages/flutter_reactter/pubspec.yaml | 4 +- packages/reactter/pubspec.yaml | 2 +- .../docs/api/classes/rt_context_mixin.mdx | 6 -- .../src/examples/build_context/pubspec.yaml | 2 +- .../build_context_select/pubspec.yaml | 2 +- website/src/examples/counter/pubspec.yaml | 2 +- website/src/examples/custom_hook/pubspec.yaml | 2 +- .../dependency_injection_builder/pubspec.yaml | 2 +- .../dependency_injection_create/pubspec.yaml | 2 +- .../dependency_injection_delete/pubspec.yaml | 2 +- .../dependency_injection_destroy/pubspec.yaml | 2 +- .../dependency_injection_exists/pubspec.yaml | 2 +- .../dependency_injection_factory/pubspec.yaml | 2 +- .../dependency_injection_find/pubspec.yaml | 2 +- .../dependency_injection_get/pubspec.yaml | 2 +- .../pubspec.yaml | 2 +- .../pubspec.yaml | 2 +- .../pubspec.yaml | 2 +- .../pubspec.yaml | 2 +- .../pubspec.yaml | 2 +- .../pubspec.yaml | 2 +- .../pubspec.yaml | 2 +- .../pubspec.yaml | 2 +- .../examples/event_handler_off/pubspec.yaml | 2 +- .../event_handler_off_all/pubspec.yaml | 2 +- .../event_handler_on_emit/pubspec.yaml | 2 +- .../examples/event_handler_one/pubspec.yaml | 2 +- .../lifecycle_event_handler/pubspec.yaml | 2 +- .../examples/lifecycle_observer/pubspec.yaml | 2 +- .../lifecycle_use_effect/pubspec.yaml | 2 +- .../src/examples/rt_component/pubspec.yaml | 2 +- website/src/examples/rt_consumer/pubspec.yaml | 2 +- .../examples/rt_consumer_child/pubspec.yaml | 2 +- .../src/examples/rt_consumer_id/pubspec.yaml | 2 +- .../rt_consumer_listen_all/pubspec.yaml | 2 +- .../rt_consumer_listen_all_2/pubspec.yaml | 2 +- .../rt_consumer_listen_states/pubspec.yaml | 2 +- .../examples/rt_multi_provider/pubspec.yaml | 2 +- .../examples/rt_provider_child/pubspec.yaml | 2 +- .../src/examples/rt_provider_id/pubspec.yaml | 2 +- .../examples/rt_provider_init/pubspec.yaml | 2 +- .../examples/rt_provider_lazy/pubspec.yaml | 2 +- .../examples/rt_provider_mode/pubspec.yaml | 2 +- website/src/examples/rt_scope/pubspec.yaml | 2 +- website/src/examples/rt_selector/pubspec.yaml | 2 +- .../examples/rt_selector_child/pubspec.yaml | 2 +- .../src/examples/rt_selector_id/pubspec.yaml | 2 +- .../examples/rt_signal_watcher/pubspec.yaml | 2 +- website/src/examples/rt_watcher/pubspec.yaml | 2 +- 51 files changed, 101 insertions(+), 107 deletions(-) delete mode 100644 website/src/content/docs/api/classes/rt_context_mixin.mdx diff --git a/CHANGELOG.md b/CHANGELOG.md index 345a7825..90e4c8bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,70 +1,70 @@ # Reactter -## 8.0.0-dev.19 +## 8.0.0-dev.20 ### Enhancements - Add Reactter devtools extension. -- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/removeObserver.html) methods to manage the observers. -- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). -- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). -- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. -- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. -- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtStateBase/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtStateBase/debugInfo.html) to states and hooks to get info for debugging. -- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. -- Add [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtStateBase-class.html) abstract class to implement the base logic of the state. -- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/createState.html) method to create a new state. -- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. -- Add [`initHook`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtHook/initHook.html) method to `RtHook` to call when hook is created. -- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. -- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. +- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtInterface/removeObserver.html) methods to manage the observers. +- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). +- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). +- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. +- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. +- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtStateBase/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtStateBase/debugInfo.html) to states and hooks to get info for debugging. +- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. +- Add [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtStateBase-class.html) abstract class to implement the base logic of the state. +- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtInterface/createState.html) method to create a new state. +- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. +- Add [`initHook`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtHook/initHook.html) method to `RtHook` to call when hook is created. +- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. +- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. - Enhance event handling and notifier logic for improved stability and performance. - Enhance state management and error handling. -- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.19/flutter_reactter/RtComponent-class.html). +- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.20/flutter_reactter/RtComponent-class.html). - Enhance dependency management to ensure to re-build when is detected changes while building. ### Breakings -- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/Rt.html) instead. -- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtStateBase-class.html) instead. -- Replace [`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html) to [`UseAsyncStateStatus.idle`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/UseAsyncStateStatus.html). -- Rename [`DispatchEffect`](https://pub.dev/documentation/reactter/7.3.0/reactter/DispatchEffect-class.html) to [`AutoDispatchEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/DispatchEffect-class.html). -- Move `asyncFunction` to the first parameter of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/UseAsyncState/withArg.html). -- Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. -- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/isActive.html) instead. -- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/getDependencyMode.html) instead. -- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/DependencyMode.html) instead. -- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/Lifecycle.html) instead. -- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/Lifecycle.html) instead. -- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`LifecycleObserver.onCreated`](https://pub.dev/documentation/reactter/8.0.0-dev.19//reactter/LifecycleObserver/onCreated.html) instead. -- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.19//reactter/RtDependency-class.html) instead. -- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0-dev.19//reactter/RtHook-class.html) instead. -- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0-dev.19//reactter/RtInterface-class.html) instead. -- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtState/notify.html) instead. -- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/UseDependency-class.html) instead. -- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtAction-class.html) instead. -- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtActionCallable-class.html) instead. -- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MultiMemoInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/MultiMemoInterceptor-class.html) instead. -- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/MemoSafeAsyncInterceptor-class.html) instead. -- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/MemoTemporaryCacheInterceptor-class.html) instead. -- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/MemoWrapperInterceptor-class.html) instead. +- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/Rt.html) instead. +- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtStateBase-class.html) instead. +- Replace [`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html) to [`UseAsyncStateStatus.idle`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/UseAsyncStateStatus.html). +- Rename [`DispatchEffect`](https://pub.dev/documentation/reactter/7.3.0/reactter/DispatchEffect-class.html) to [`AutoDispatchEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/DispatchEffect-class.html). +- Move `asyncFunction` to the first parameter of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/UseAsyncState/withArg.html). +- Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. +- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtInterface/isActive.html) instead. +- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtInterface/getDependencyMode.html) instead. +- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/DependencyMode.html) instead. +- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/Lifecycle.html) instead. +- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/Lifecycle.html) instead. +- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`LifecycleObserver.onCreated`](https://pub.dev/documentation/reactter/8.0.0-dev.20//reactter/LifecycleObserver/onCreated.html) instead. +- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.20//reactter/RtDependency-class.html) instead. +- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0-dev.20//reactter/RtHook-class.html) instead. +- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0-dev.20//reactter/RtInterface-class.html) instead. +- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtState/notify.html) instead. +- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/UseDependency-class.html) instead. +- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtAction-class.html) instead. +- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtActionCallable-class.html) instead. +- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MultiMemoInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/MultiMemoInterceptor-class.html) instead. +- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/MemoSafeAsyncInterceptor-class.html) instead. +- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/MemoTemporaryCacheInterceptor-class.html) instead. +- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/MemoWrapperInterceptor-class.html) instead. - Remove [`Obj`](https://pub.dev/documentation/reactter/7.3.0/reactter/Obj-class.html). -- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.19/flutter_reactter/RtProvider/RtProvider.init.html) instead. -- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.19/flutter_reactter/RtComponent-class.html) instead. -- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.19/flutter_reactter/RtConsumer-class.html) instead. -- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.19/flutter_reactter/RtProvider-class.html) instead. -- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.19/flutter_reactter/RtMultiProvider-class.html) instead. -- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.19/flutter_reactter/RtScope-class.html) instead. -- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.19/flutter_reactter/RtSignalWatcher-class.html) instead. -- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.19/flutter_reactter/RtDependencyNotFoundException-class.html) instead. -- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.19/flutter_reactter/RtScopeNotFoundException-class.html) instead. +- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.20/flutter_reactter/RtProvider/RtProvider.init.html) instead. +- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.20/flutter_reactter/RtComponent-class.html) instead. +- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.20/flutter_reactter/RtConsumer-class.html) instead. +- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.20/flutter_reactter/RtProvider-class.html) instead. +- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.20/flutter_reactter/RtMultiProvider-class.html) instead. +- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.20/flutter_reactter/RtScope-class.html) instead. +- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.20/flutter_reactter/RtSignalWatcher-class.html) instead. +- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.20/flutter_reactter/RtDependencyNotFoundException-class.html) instead. +- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.20/flutter_reactter/RtScopeNotFoundException-class.html) instead. ### Fixes -- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/UseEffect-class.html) causing dependencies to not be unwatched. -- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/register.html) method. -- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/RtInterface/batch.html) method to work correctly. -- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0-dev.19/reactter/Notifier-class.html) class. +- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/UseEffect-class.html) causing dependencies to not be unwatched. +- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtInterface/register.html) method. +- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtInterface/batch.html) method to work correctly. +- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/Notifier-class.html) class. - Fix to propagate state param to `boundInstance`. - Remove listeners correctly from single-use listeners and handle null cases. diff --git a/packages/flutter_reactter/example/pubspec.yaml b/packages/flutter_reactter/example/pubspec.yaml index c424bcc8..6c40f06b 100644 --- a/packages/flutter_reactter/example/pubspec.yaml +++ b/packages/flutter_reactter/example/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: http: ^0.13.6 url_launcher: ^6.1.2 intl: ^0.17.0 - flutter_reactter: ^8.0.0-dev.19 + flutter_reactter: ^8.0.0-dev.20 dev_dependencies: custom_lint: ^0.5.0 diff --git a/packages/flutter_reactter/pubspec.yaml b/packages/flutter_reactter/pubspec.yaml index eb4ff2d4..c23554e4 100644 --- a/packages/flutter_reactter/pubspec.yaml +++ b/packages/flutter_reactter/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://2devs-team.github.io/reactter/ documentation: https://2devs-team.github.io/reactter/ repository: https://github.com/2devs-team/reactter/tree/master/packages/flutter_reactter license: MIT License -version: 8.0.0-dev.19 +version: 8.0.0-dev.20 topics: - reactive @@ -26,7 +26,7 @@ dependencies: flutter: sdk: flutter meta: ^1.7.0 - reactter: ^8.0.0-dev.17 + reactter: ^8.0.0-dev.20 dev_dependencies: flutter_test: diff --git a/packages/reactter/pubspec.yaml b/packages/reactter/pubspec.yaml index 7aaa5321..877e40bd 100644 --- a/packages/reactter/pubspec.yaml +++ b/packages/reactter/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://2devs-team.github.io/reactter/ documentation: https://2devs-team.github.io/reactter/ repository: https://github.com/2devs-team/reactter/tree/master/packages/reactter license: MIT License -version: 8.0.0-dev.17 +version: 8.0.0-dev.20 topics: - reactive diff --git a/website/src/content/docs/api/classes/rt_context_mixin.mdx b/website/src/content/docs/api/classes/rt_context_mixin.mdx deleted file mode 100644 index af577f38..00000000 --- a/website/src/content/docs/api/classes/rt_context_mixin.mdx +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: RtContextMixin 🚧 -description: The base class for all context mixins in Reactter. -sidebar: - order: 11 ---- \ No newline at end of file diff --git a/website/src/examples/build_context/pubspec.yaml b/website/src/examples/build_context/pubspec.yaml index 647e0d7f..126d118d 100644 --- a/website/src/examples/build_context/pubspec.yaml +++ b/website/src/examples/build_context/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/build_context_select/pubspec.yaml b/website/src/examples/build_context_select/pubspec.yaml index c6ae390c..bb14edd6 100644 --- a/website/src/examples/build_context_select/pubspec.yaml +++ b/website/src/examples/build_context_select/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/counter/pubspec.yaml b/website/src/examples/counter/pubspec.yaml index b303886b..27670a8f 100644 --- a/website/src/examples/counter/pubspec.yaml +++ b/website/src/examples/counter/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/custom_hook/pubspec.yaml b/website/src/examples/custom_hook/pubspec.yaml index 09a2e0bb..45b3bb5f 100644 --- a/website/src/examples/custom_hook/pubspec.yaml +++ b/website/src/examples/custom_hook/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.19 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_builder/pubspec.yaml b/website/src/examples/dependency_injection_builder/pubspec.yaml index 008ebc63..6717e131 100644 --- a/website/src/examples/dependency_injection_builder/pubspec.yaml +++ b/website/src/examples/dependency_injection_builder/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_create/pubspec.yaml b/website/src/examples/dependency_injection_create/pubspec.yaml index 74818dda..6458556f 100644 --- a/website/src/examples/dependency_injection_create/pubspec.yaml +++ b/website/src/examples/dependency_injection_create/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_delete/pubspec.yaml b/website/src/examples/dependency_injection_delete/pubspec.yaml index 4357618b..40be9cbc 100644 --- a/website/src/examples/dependency_injection_delete/pubspec.yaml +++ b/website/src/examples/dependency_injection_delete/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_destroy/pubspec.yaml b/website/src/examples/dependency_injection_destroy/pubspec.yaml index 858e2b30..90f67f42 100644 --- a/website/src/examples/dependency_injection_destroy/pubspec.yaml +++ b/website/src/examples/dependency_injection_destroy/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_exists/pubspec.yaml b/website/src/examples/dependency_injection_exists/pubspec.yaml index 831d6860..0ca0243a 100644 --- a/website/src/examples/dependency_injection_exists/pubspec.yaml +++ b/website/src/examples/dependency_injection_exists/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_factory/pubspec.yaml b/website/src/examples/dependency_injection_factory/pubspec.yaml index 2bca9da4..7f15361d 100644 --- a/website/src/examples/dependency_injection_factory/pubspec.yaml +++ b/website/src/examples/dependency_injection_factory/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_find/pubspec.yaml b/website/src/examples/dependency_injection_find/pubspec.yaml index 3de884a7..c1092bca 100644 --- a/website/src/examples/dependency_injection_find/pubspec.yaml +++ b/website/src/examples/dependency_injection_find/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_get/pubspec.yaml b/website/src/examples/dependency_injection_get/pubspec.yaml index 0034bba6..ed101da7 100644 --- a/website/src/examples/dependency_injection_get/pubspec.yaml +++ b/website/src/examples/dependency_injection_get/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_get_dependency_mode/pubspec.yaml b/website/src/examples/dependency_injection_get_dependency_mode/pubspec.yaml index 8a044440..faeb722e 100644 --- a/website/src/examples/dependency_injection_get_dependency_mode/pubspec.yaml +++ b/website/src/examples/dependency_injection_get_dependency_mode/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_is_active/pubspec.yaml b/website/src/examples/dependency_injection_is_active/pubspec.yaml index bec3fb48..478f5fe0 100644 --- a/website/src/examples/dependency_injection_is_active/pubspec.yaml +++ b/website/src/examples/dependency_injection_is_active/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_lazy_builder/pubspec.yaml b/website/src/examples/dependency_injection_lazy_builder/pubspec.yaml index cc94bf46..31dbf7bd 100644 --- a/website/src/examples/dependency_injection_lazy_builder/pubspec.yaml +++ b/website/src/examples/dependency_injection_lazy_builder/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_lazy_factory/pubspec.yaml b/website/src/examples/dependency_injection_lazy_factory/pubspec.yaml index 9fe67fa1..a0c7386a 100644 --- a/website/src/examples/dependency_injection_lazy_factory/pubspec.yaml +++ b/website/src/examples/dependency_injection_lazy_factory/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_lazy_singleton/pubspec.yaml b/website/src/examples/dependency_injection_lazy_singleton/pubspec.yaml index 5fc6a8a2..86bb80ee 100644 --- a/website/src/examples/dependency_injection_lazy_singleton/pubspec.yaml +++ b/website/src/examples/dependency_injection_lazy_singleton/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_register/pubspec.yaml b/website/src/examples/dependency_injection_register/pubspec.yaml index 1d3775d2..c9967c43 100644 --- a/website/src/examples/dependency_injection_register/pubspec.yaml +++ b/website/src/examples/dependency_injection_register/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_singleton/pubspec.yaml b/website/src/examples/dependency_injection_singleton/pubspec.yaml index 0ebebac9..19fa3f73 100644 --- a/website/src/examples/dependency_injection_singleton/pubspec.yaml +++ b/website/src/examples/dependency_injection_singleton/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_unregister/pubspec.yaml b/website/src/examples/dependency_injection_unregister/pubspec.yaml index 391e4649..199ded38 100644 --- a/website/src/examples/dependency_injection_unregister/pubspec.yaml +++ b/website/src/examples/dependency_injection_unregister/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/event_handler_off/pubspec.yaml b/website/src/examples/event_handler_off/pubspec.yaml index 7bdd0915..3ad079d2 100644 --- a/website/src/examples/event_handler_off/pubspec.yaml +++ b/website/src/examples/event_handler_off/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/event_handler_off_all/pubspec.yaml b/website/src/examples/event_handler_off_all/pubspec.yaml index 20a3d4bb..369541cc 100644 --- a/website/src/examples/event_handler_off_all/pubspec.yaml +++ b/website/src/examples/event_handler_off_all/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/event_handler_on_emit/pubspec.yaml b/website/src/examples/event_handler_on_emit/pubspec.yaml index e82982c8..ad53b0ac 100644 --- a/website/src/examples/event_handler_on_emit/pubspec.yaml +++ b/website/src/examples/event_handler_on_emit/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/event_handler_one/pubspec.yaml b/website/src/examples/event_handler_one/pubspec.yaml index e9fdb74b..cf6ff6d5 100644 --- a/website/src/examples/event_handler_one/pubspec.yaml +++ b/website/src/examples/event_handler_one/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/lifecycle_event_handler/pubspec.yaml b/website/src/examples/lifecycle_event_handler/pubspec.yaml index 9c77499e..d8dd59cd 100644 --- a/website/src/examples/lifecycle_event_handler/pubspec.yaml +++ b/website/src/examples/lifecycle_event_handler/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/lifecycle_observer/pubspec.yaml b/website/src/examples/lifecycle_observer/pubspec.yaml index 66d70fe3..0fb844dd 100644 --- a/website/src/examples/lifecycle_observer/pubspec.yaml +++ b/website/src/examples/lifecycle_observer/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/lifecycle_use_effect/pubspec.yaml b/website/src/examples/lifecycle_use_effect/pubspec.yaml index b3a31e74..3368612a 100644 --- a/website/src/examples/lifecycle_use_effect/pubspec.yaml +++ b/website/src/examples/lifecycle_use_effect/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/rt_component/pubspec.yaml b/website/src/examples/rt_component/pubspec.yaml index 596f383d..0ca2c60f 100644 --- a/website/src/examples/rt_component/pubspec.yaml +++ b/website/src/examples/rt_component/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/rt_consumer/pubspec.yaml b/website/src/examples/rt_consumer/pubspec.yaml index 595aa10f..ce2bc8f1 100644 --- a/website/src/examples/rt_consumer/pubspec.yaml +++ b/website/src/examples/rt_consumer/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/rt_consumer_child/pubspec.yaml b/website/src/examples/rt_consumer_child/pubspec.yaml index 71758022..d117e7e3 100644 --- a/website/src/examples/rt_consumer_child/pubspec.yaml +++ b/website/src/examples/rt_consumer_child/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/rt_consumer_id/pubspec.yaml b/website/src/examples/rt_consumer_id/pubspec.yaml index 7e3e8cd0..5f87df3b 100644 --- a/website/src/examples/rt_consumer_id/pubspec.yaml +++ b/website/src/examples/rt_consumer_id/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/rt_consumer_listen_all/pubspec.yaml b/website/src/examples/rt_consumer_listen_all/pubspec.yaml index 12f73c06..1f44c29b 100644 --- a/website/src/examples/rt_consumer_listen_all/pubspec.yaml +++ b/website/src/examples/rt_consumer_listen_all/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/rt_consumer_listen_all_2/pubspec.yaml b/website/src/examples/rt_consumer_listen_all_2/pubspec.yaml index 980a0c4a..53e24026 100644 --- a/website/src/examples/rt_consumer_listen_all_2/pubspec.yaml +++ b/website/src/examples/rt_consumer_listen_all_2/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/rt_consumer_listen_states/pubspec.yaml b/website/src/examples/rt_consumer_listen_states/pubspec.yaml index 6665d73c..b0c4ae59 100644 --- a/website/src/examples/rt_consumer_listen_states/pubspec.yaml +++ b/website/src/examples/rt_consumer_listen_states/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/rt_multi_provider/pubspec.yaml b/website/src/examples/rt_multi_provider/pubspec.yaml index 3aeef3c2..f3d4f355 100644 --- a/website/src/examples/rt_multi_provider/pubspec.yaml +++ b/website/src/examples/rt_multi_provider/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/rt_provider_child/pubspec.yaml b/website/src/examples/rt_provider_child/pubspec.yaml index b4455925..469a8514 100644 --- a/website/src/examples/rt_provider_child/pubspec.yaml +++ b/website/src/examples/rt_provider_child/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/rt_provider_id/pubspec.yaml b/website/src/examples/rt_provider_id/pubspec.yaml index 5746b3dd..6db9b2e9 100644 --- a/website/src/examples/rt_provider_id/pubspec.yaml +++ b/website/src/examples/rt_provider_id/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/rt_provider_init/pubspec.yaml b/website/src/examples/rt_provider_init/pubspec.yaml index 182cb422..1b3fda41 100644 --- a/website/src/examples/rt_provider_init/pubspec.yaml +++ b/website/src/examples/rt_provider_init/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/rt_provider_lazy/pubspec.yaml b/website/src/examples/rt_provider_lazy/pubspec.yaml index c15f9230..c8d9635f 100644 --- a/website/src/examples/rt_provider_lazy/pubspec.yaml +++ b/website/src/examples/rt_provider_lazy/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/rt_provider_mode/pubspec.yaml b/website/src/examples/rt_provider_mode/pubspec.yaml index 20093fca..07515f27 100644 --- a/website/src/examples/rt_provider_mode/pubspec.yaml +++ b/website/src/examples/rt_provider_mode/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/rt_scope/pubspec.yaml b/website/src/examples/rt_scope/pubspec.yaml index 7917dc05..396500dc 100644 --- a/website/src/examples/rt_scope/pubspec.yaml +++ b/website/src/examples/rt_scope/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/rt_selector/pubspec.yaml b/website/src/examples/rt_selector/pubspec.yaml index 21771a8f..9e8574d1 100644 --- a/website/src/examples/rt_selector/pubspec.yaml +++ b/website/src/examples/rt_selector/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/rt_selector_child/pubspec.yaml b/website/src/examples/rt_selector_child/pubspec.yaml index e71ea568..a186ff11 100644 --- a/website/src/examples/rt_selector_child/pubspec.yaml +++ b/website/src/examples/rt_selector_child/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/rt_selector_id/pubspec.yaml b/website/src/examples/rt_selector_id/pubspec.yaml index 7db20128..84206ac9 100644 --- a/website/src/examples/rt_selector_id/pubspec.yaml +++ b/website/src/examples/rt_selector_id/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/rt_signal_watcher/pubspec.yaml b/website/src/examples/rt_signal_watcher/pubspec.yaml index 00f86734..2cb45353 100644 --- a/website/src/examples/rt_signal_watcher/pubspec.yaml +++ b/website/src/examples/rt_signal_watcher/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true diff --git a/website/src/examples/rt_watcher/pubspec.yaml b/website/src/examples/rt_watcher/pubspec.yaml index d661ec0f..3d9ed3cd 100644 --- a/website/src/examples/rt_watcher/pubspec.yaml +++ b/website/src/examples/rt_watcher/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^7.3.1 + flutter_reactter: ^8.0.0-dev.20 flutter: uses-material-design: true From 408ceffbe28aa7770702df87b3b9a9ac901767e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Thu, 6 Feb 2025 00:39:55 -0600 Subject: [PATCH 116/141] chore: Update `flutter_reactter` to version 8.0.0-dev.22 and enhance example code --- CHANGELOG.md | 102 +++++++++--------- ROADMAP.md | 29 ----- .../flutter_reactter/example/lib/main.dart | 3 +- .../flutter_reactter/example/pubspec.yaml | 2 +- packages/flutter_reactter/pubspec.yaml | 2 +- 5 files changed, 54 insertions(+), 84 deletions(-) delete mode 100644 ROADMAP.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 90e4c8bf..bfc87f1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,70 +1,70 @@ # Reactter -## 8.0.0-dev.20 +## 8.0.0-dev.22 ### Enhancements - Add Reactter devtools extension. -- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtInterface/removeObserver.html) methods to manage the observers. -- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). -- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). -- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. -- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. -- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtStateBase/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtStateBase/debugInfo.html) to states and hooks to get info for debugging. -- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. -- Add [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtStateBase-class.html) abstract class to implement the base logic of the state. -- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtInterface/createState.html) method to create a new state. -- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. -- Add [`initHook`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtHook/initHook.html) method to `RtHook` to call when hook is created. -- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. -- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. +- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtInterface/removeObserver.html) methods to manage the observers. +- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). +- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). +- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. +- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. +- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtStateBase/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtStateBase/debugInfo.html) to states and hooks to get info for debugging. +- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. +- Add [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtStateBase-class.html) abstract class to implement the base logic of the state. +- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtInterface/createState.html) method to create a new state. +- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. +- Add [`initHook`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtHook/initHook.html) method to `RtHook` to call when hook is created. +- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. +- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. - Enhance event handling and notifier logic for improved stability and performance. - Enhance state management and error handling. -- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.20/flutter_reactter/RtComponent-class.html). +- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.22/flutter_reactter/RtComponent-class.html). - Enhance dependency management to ensure to re-build when is detected changes while building. ### Breakings -- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/Rt.html) instead. -- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtStateBase-class.html) instead. -- Replace [`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html) to [`UseAsyncStateStatus.idle`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/UseAsyncStateStatus.html). -- Rename [`DispatchEffect`](https://pub.dev/documentation/reactter/7.3.0/reactter/DispatchEffect-class.html) to [`AutoDispatchEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/DispatchEffect-class.html). -- Move `asyncFunction` to the first parameter of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/UseAsyncState/withArg.html). -- Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. -- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtInterface/isActive.html) instead. -- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtInterface/getDependencyMode.html) instead. -- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/DependencyMode.html) instead. -- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/Lifecycle.html) instead. -- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/Lifecycle.html) instead. -- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`LifecycleObserver.onCreated`](https://pub.dev/documentation/reactter/8.0.0-dev.20//reactter/LifecycleObserver/onCreated.html) instead. -- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.20//reactter/RtDependency-class.html) instead. -- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0-dev.20//reactter/RtHook-class.html) instead. -- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0-dev.20//reactter/RtInterface-class.html) instead. -- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtState/notify.html) instead. -- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/UseDependency-class.html) instead. -- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtAction-class.html) instead. -- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtActionCallable-class.html) instead. -- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MultiMemoInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/MultiMemoInterceptor-class.html) instead. -- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/MemoSafeAsyncInterceptor-class.html) instead. -- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/MemoTemporaryCacheInterceptor-class.html) instead. -- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/MemoWrapperInterceptor-class.html) instead. +- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/Rt.html) instead. +- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtStateBase-class.html) instead. +- Replace [`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html) to [`UseAsyncStateStatus.idle`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/UseAsyncStateStatus.html). +- Rename [`DispatchEffect`](https://pub.dev/documentation/reactter/7.3.0/reactter/DispatchEffect-class.html) to [`AutoDispatchEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/DispatchEffect-class.html). +- Move `asyncFunction` to the first parameter of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/UseAsyncState/withArg.html). +- Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. +- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtInterface/isActive.html) instead. +- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtInterface/getDependencyMode.html) instead. +- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/DependencyMode.html) instead. +- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/Lifecycle.html) instead. +- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/Lifecycle.html) instead. +- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`LifecycleObserver.onCreated`](https://pub.dev/documentation/reactter/8.0.0-dev.22//reactter/LifecycleObserver/onCreated.html) instead. +- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.22//reactter/RtDependency-class.html) instead. +- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0-dev.22//reactter/RtHook-class.html) instead. +- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0-dev.22//reactter/RtInterface-class.html) instead. +- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtState/notify.html) instead. +- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/UseDependency-class.html) instead. +- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtAction-class.html) instead. +- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtActionCallable-class.html) instead. +- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MultiMemoInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/MultiMemoInterceptor-class.html) instead. +- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/MemoSafeAsyncInterceptor-class.html) instead. +- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/MemoTemporaryCacheInterceptor-class.html) instead. +- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/MemoWrapperInterceptor-class.html) instead. - Remove [`Obj`](https://pub.dev/documentation/reactter/7.3.0/reactter/Obj-class.html). -- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.20/flutter_reactter/RtProvider/RtProvider.init.html) instead. -- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.20/flutter_reactter/RtComponent-class.html) instead. -- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.20/flutter_reactter/RtConsumer-class.html) instead. -- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.20/flutter_reactter/RtProvider-class.html) instead. -- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.20/flutter_reactter/RtMultiProvider-class.html) instead. -- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.20/flutter_reactter/RtScope-class.html) instead. -- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.20/flutter_reactter/RtSignalWatcher-class.html) instead. -- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.20/flutter_reactter/RtDependencyNotFoundException-class.html) instead. -- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.20/flutter_reactter/RtScopeNotFoundException-class.html) instead. +- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.22/flutter_reactter/RtProvider/RtProvider.init.html) instead. +- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.22/flutter_reactter/RtComponent-class.html) instead. +- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.22/flutter_reactter/RtConsumer-class.html) instead. +- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.22/flutter_reactter/RtProvider-class.html) instead. +- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.22/flutter_reactter/RtMultiProvider-class.html) instead. +- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.22/flutter_reactter/RtScope-class.html) instead. +- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.22/flutter_reactter/RtSignalWatcher-class.html) instead. +- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.22/flutter_reactter/RtDependencyNotFoundException-class.html) instead. +- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.22/flutter_reactter/RtScopeNotFoundException-class.html) instead. ### Fixes -- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/UseEffect-class.html) causing dependencies to not be unwatched. -- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtInterface/register.html) method. -- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/RtInterface/batch.html) method to work correctly. -- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0-dev.20/reactter/Notifier-class.html) class. +- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/UseEffect-class.html) causing dependencies to not be unwatched. +- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtInterface/register.html) method. +- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtInterface/batch.html) method to work correctly. +- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/Notifier-class.html) class. - Fix to propagate state param to `boundInstance`. - Remove listeners correctly from single-use listeners and handle null cases. diff --git a/ROADMAP.md b/ROADMAP.md deleted file mode 100644 index cdb23e21..00000000 --- a/ROADMAP.md +++ /dev/null @@ -1,29 +0,0 @@ - -# Roadmap - -We want to keeping adding features for `Reactter`, those are some we have in mind order by priority: - -- Improve performance and do benchmark. - -# Contribute - -If you want to contribute don't hesitate to create an [issue](https://github.com/2devs-team/reactter/issues/new) or [pull-request](https://github.com/2devs-team/reactter/pulls) in **[Reactter repository](https://github.com/2devs-team/reactter).** - -You can: - -- Provide new features. -- Report bugs. -- Report situations difficult to implement. -- Report an unclear error. -- Report unclear documentation. -- Add a new custom hook. -- Add a new widget. -- Add examples. -- Write articles or make videos teaching how to use **[Reactter](https://github.com/2devs-team/reactter)**. - -Any idea is welcome! - -# Authors - -- **[Leo Castellanos](https://twitter.com/leoocast10)** - <leoocast.dev@gmail.com> -- **[Carlos León](_blank)** - <carleon.dev@gmail.com> diff --git a/packages/flutter_reactter/example/lib/main.dart b/packages/flutter_reactter/example/lib/main.dart index b3b7ea50..3f256007 100644 --- a/packages/flutter_reactter/example/lib/main.dart +++ b/packages/flutter_reactter/example/lib/main.dart @@ -12,7 +12,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_reactter/flutter_reactter.dart'; // Global state for theme mode -final uThemeMode = UseState(ThemeMode.dark); +final uThemeMode = UseState(ThemeMode.dark, debugLabel: 'uThemeMode'); class IndexPage extends StatelessWidget { const IndexPage({Key? key}) : super(key: key); @@ -97,6 +97,5 @@ class MyApp extends StatelessWidget { } void main() { - Rt.initializeDevTools(); runApp(const MyApp()); } diff --git a/packages/flutter_reactter/example/pubspec.yaml b/packages/flutter_reactter/example/pubspec.yaml index 6c40f06b..b151ac96 100644 --- a/packages/flutter_reactter/example/pubspec.yaml +++ b/packages/flutter_reactter/example/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: http: ^0.13.6 url_launcher: ^6.1.2 intl: ^0.17.0 - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev.22 dev_dependencies: custom_lint: ^0.5.0 diff --git a/packages/flutter_reactter/pubspec.yaml b/packages/flutter_reactter/pubspec.yaml index c23554e4..fdbeeb14 100644 --- a/packages/flutter_reactter/pubspec.yaml +++ b/packages/flutter_reactter/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://2devs-team.github.io/reactter/ documentation: https://2devs-team.github.io/reactter/ repository: https://github.com/2devs-team/reactter/tree/master/packages/flutter_reactter license: MIT License -version: 8.0.0-dev.20 +version: 8.0.0-dev.22 topics: - reactive From cbf15267f507c46363c28ec455b3bce1c0b2715e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Sat, 8 Feb 2025 00:49:44 -0600 Subject: [PATCH 117/141] refactor: Replace `RtStateObserver` and `RtDependencyObserver` with `IStateObserver` and `IDependencyObserver` for improved interface consistency. --- .../lib/src/core/dependency_injection.dart | 2 +- .../reactter/lib/src/core/event_handler.dart | 2 +- packages/reactter/lib/src/devtools.dart | 2 +- packages/reactter/lib/src/framework.dart | 3 +- .../src/framework/rt_dependency_observer.dart | 158 ++++++++----- .../lib/src/framework/rt_interface.dart | 16 +- .../lib/src/framework/rt_state_base.dart | 10 +- .../lib/src/framework/rt_state_observer.dart | 118 +++++++--- .../reactter/lib/src/interfaces/observer.dart | 148 +++++++++++++ packages/reactter/lib/src/logger.dart | 2 +- .../rt_dependency_observer_test.dart | 209 ++++++++---------- .../framework/rt_state_observer_test.dart | 176 ++++++++------- 12 files changed, 547 insertions(+), 299 deletions(-) diff --git a/packages/reactter/lib/src/core/dependency_injection.dart b/packages/reactter/lib/src/core/dependency_injection.dart index cdc3b7d8..d144260b 100644 --- a/packages/reactter/lib/src/core/dependency_injection.dart +++ b/packages/reactter/lib/src/core/dependency_injection.dart @@ -554,7 +554,7 @@ abstract class DependencyInjection implements IContext { DependencyFail fail, ) { for (final observer - in RtDependencyObserver._observers.toList(growable: false)) { + in IDependencyObserver._observers.toList(growable: false)) { observer.onDependencyFailed(dependencyRef, fail); } } diff --git a/packages/reactter/lib/src/core/event_handler.dart b/packages/reactter/lib/src/core/event_handler.dart index b300fa27..c0e96c79 100644 --- a/packages/reactter/lib/src/core/event_handler.dart +++ b/packages/reactter/lib/src/core/event_handler.dart @@ -176,7 +176,7 @@ abstract class EventHandler implements IContext { if (dependencyRef == null) return; for (final observer - in RtDependencyObserver._observers.toList(growable: false)) { + in IDependencyObserver._observers.toList(growable: false)) { switch (lifecycle) { case Lifecycle.registered: observer.onDependencyRegistered(dependencyRef); diff --git a/packages/reactter/lib/src/devtools.dart b/packages/reactter/lib/src/devtools.dart index 7a306e13..98c86133 100644 --- a/packages/reactter/lib/src/devtools.dart +++ b/packages/reactter/lib/src/devtools.dart @@ -25,7 +25,7 @@ extension RtDevToolsExt on RtInterface { /// To access the Reactter devtools, open the devtools extension /// and navigate to the Reactter tab. @internal -class RtDevTools with RtStateObserver, RtDependencyObserver { +class RtDevTools implements IStateObserver, IDependencyObserver { static RtDevTools? instance; final LinkedList<_Node> _nodes = LinkedList(); diff --git a/packages/reactter/lib/src/framework.dart b/packages/reactter/lib/src/framework.dart index 51018938..0d87c5e1 100644 --- a/packages/reactter/lib/src/framework.dart +++ b/packages/reactter/lib/src/framework.dart @@ -10,8 +10,7 @@ export 'internals.dart' RtHook, RtInterface, RtState, - RtStateBase, - RtStateObserver; + RtStateBase; part 'framework/rt_context.dart'; part 'framework/rt_dependency.dart'; diff --git a/packages/reactter/lib/src/framework/rt_dependency_observer.dart b/packages/reactter/lib/src/framework/rt_dependency_observer.dart index 33ab450a..7b44c45a 100644 --- a/packages/reactter/lib/src/framework/rt_dependency_observer.dart +++ b/packages/reactter/lib/src/framework/rt_dependency_observer.dart @@ -1,66 +1,116 @@ part of '../internals.dart'; -enum DependencyFail { - alreadyRegistered, - alreadyCreated, - alreadyDeleted, - alreadyUnregistered, - missingInstanceBuilder, - builderRetainedAsFactory, - dependencyRetainedAsSingleton, - cannotUnregisterActiveInstance, -} - /// {@template reactter.rt_dependency_observer} -/// An abstract class that defines the interface for observing dependency changes. -/// Implementations of this class can be used to monitor the lifecycle of dependencies. +/// A class that implements the [IDependencyObserver] interface. +/// This class can be used to monitor the lifecycle of dependencies. +/// +/// It provides a set of callback functions that are called when a dependency is +/// registered, created, mounted, unmounted, deleted, unregistered, or failed. +/// +/// This observer should be added to Reactter's observers for it to work, using the [Rt.addObserver] method, like so: +/// ```dart +/// final dependencyObserver = RtDependencyObserver( +/// onRegistered: (dependency) { +/// print('Dependency registered: $dependency'); +/// }, +/// onCreated: (dependency, instance) { +/// print('Dependency created: $dependency, instance: $instance'); +/// }, +/// onMounted: (dependency, instance) { +/// print('Dependency mounted: $dependency, instance: $instance'); +/// }, +/// onUnmounted: (dependency, instance) { +/// print('Dependency unmounted: $dependency, instance: $instance'); +/// }, +/// onDeleted: (dependency, instance) { +/// print('Dependency deleted: $dependency, instance: $instance'); +/// }, +/// onUnregistered: (dependency) { +/// print('Dependency unregistered: $dependency'); +/// }, +/// onFailed: (dependency, fail) { +/// print('Dependency failed: $dependency, fail: $fail'); +/// }, +/// ); +/// +/// Rt.addObserver(dependencyObserver); +/// ``` +/// +/// See also: +/// - [IDependencyObserver] - An abstract class that defines the interface for observing dependency changes. +/// - [Rt.addObserver] - A method that adds an observer to Reactter's observers. /// {@endtemplate} -abstract class RtDependencyObserver implements IObserver { - /// A set of all registered dependency observers. - static final _observers = <RtDependencyObserver>{}; +class RtDependencyObserver implements IDependencyObserver { + /// {@macro reactter.i_dependency_observer.on_dependency_registered} + final void Function(DependencyRef)? onRegistered; + + /// {@macro reactter.i_dependency_observer.on_dependency_created} + final void Function(DependencyRef, Object?)? onCreated; + + /// {@macro reactter.i_dependency_observer.on_dependency_mounted} + final void Function(DependencyRef, Object?)? onMounted; + + /// {@macro reactter.i_dependency_observer.on_dependency_unmounted} + final void Function(DependencyRef, Object?)? onUnmounted; + + /// {@macro reactter.i_dependency_observer.on_dependency_deleted} + final void Function(DependencyRef, Object?)? onDeleted; + + /// {@macro reactter.i_dependency_observer.on_dependency_unregistered} + final void Function(DependencyRef)? onUnregistered; + + /// {@macro reactter.i_dependency_observer.on_dependency_failed} + final void Function(DependencyRef, DependencyFail)? onFailed; + + RtDependencyObserver({ + this.onRegistered, + this.onCreated, + this.onMounted, + this.onUnmounted, + this.onDeleted, + this.onUnregistered, + this.onFailed, + }); - /// Called when a dependency is registered. - /// [dependency] - The dependency that was registered. - void onDependencyRegistered(covariant DependencyRef dependency); + /// {@macro reactter.i_dependency_observer.on_dependency_registered} + @override + void onDependencyRegistered(DependencyRef dependency) { + onRegistered?.call(dependency); + } - /// Called when a dependency is created. - /// [dependency] - The dependency that was created. - /// [instance] - The instance of the dependency. - void onDependencyCreated( - covariant DependencyRef dependency, - Object? instance, - ); + /// {@macro reactter.i_dependency_observer.on_dependency_created} + @override + void onDependencyCreated(DependencyRef dependency, Object? instance) { + onCreated?.call(dependency, instance); + } - /// Called when a dependency is mounted. - /// [dependency] - The dependency that was mounted. - /// [instance] - The instance of the dependency. - void onDependencyMounted( - covariant DependencyRef dependency, - Object? instance, - ); + /// {@macro reactter.i_dependency_observer.on_dependency_mounted} + @override + void onDependencyMounted(DependencyRef dependency, Object? instance) { + onMounted?.call(dependency, instance); + } - /// Called when a dependency is unmounted. - /// [dependency] - The dependency that was unmounted. - /// [instance] - The instance of the dependency. - void onDependencyUnmounted( - covariant DependencyRef dependency, - Object? instance, - ); + /// {@macro reactter.i_dependency_observer.on_dependency_unmounted} + @override + void onDependencyUnmounted(DependencyRef dependency, Object? instance) { + onUnmounted?.call(dependency, instance); + } - /// Called when a dependency is deleted. - /// [dependency] - The dependency that was deleted. - /// [instance] - The instance of the dependency. - void onDependencyDeleted( - covariant DependencyRef dependency, - Object? instance, - ); + /// {@macro reactter.i_dependency_observer.on_dependency_deleted} + @override + void onDependencyDeleted(DependencyRef dependency, Object? instance) { + onDeleted?.call(dependency, instance); + } - /// Called when a dependency is unregistered. - /// [dependency] - The dependency that was unregistered. - void onDependencyUnregistered(covariant DependencyRef dependency); + /// {@macro reactter.i_dependency_observer.on_dependency_unregistered} + @override + void onDependencyUnregistered(DependencyRef dependency) { + onUnregistered?.call(dependency); + } - void onDependencyFailed( - covariant DependencyRef dependency, - DependencyFail fail, - ); + /// {@macro reactter.i_dependency_observer.on_dependency_failed} + @override + void onDependencyFailed(DependencyRef dependency, DependencyFail fail) { + onFailed?.call(dependency, fail); + } } diff --git a/packages/reactter/lib/src/framework/rt_interface.dart b/packages/reactter/lib/src/framework/rt_interface.dart index 06a099c4..36bd9a57 100644 --- a/packages/reactter/lib/src/framework/rt_interface.dart +++ b/packages/reactter/lib/src/framework/rt_interface.dart @@ -23,23 +23,23 @@ class RtInterface @override void addObserver(covariant IObserver observer) { - if (observer is RtStateObserver) { - RtStateObserver._observers.add(observer); + if (observer is IStateObserver) { + IStateObserver._observers.add(observer); } - if (observer is RtDependencyObserver) { - RtDependencyObserver._observers.add(observer); + if (observer is IDependencyObserver) { + IDependencyObserver._observers.add(observer); } } @override void removeObserver(covariant IObserver observer) { - if (observer is RtStateObserver) { - RtStateObserver._observers.remove(observer); + if (observer is IStateObserver) { + IStateObserver._observers.remove(observer); } - if (observer is RtDependencyObserver) { - RtDependencyObserver._observers.remove(observer); + if (observer is IDependencyObserver) { + IDependencyObserver._observers.remove(observer); } } } diff --git a/packages/reactter/lib/src/framework/rt_state_base.dart b/packages/reactter/lib/src/framework/rt_state_base.dart index d79d56ad..f4b6e28d 100644 --- a/packages/reactter/lib/src/framework/rt_state_base.dart +++ b/packages/reactter/lib/src/framework/rt_state_base.dart @@ -194,25 +194,25 @@ abstract class RtStateBase<E extends RtStateBase<E>> implements RtState { } void _notifyCreated() { - for (final observer in RtStateObserver._observers.toList(growable: false)) { + for (final observer in IStateObserver._observers.toList(growable: false)) { observer.onStateCreated(this); } } void _notifyBound(Object instance) { - for (final observer in RtStateObserver._observers.toList(growable: false)) { + for (final observer in IStateObserver._observers.toList(growable: false)) { observer.onStateBound(this, instance); } } void _notifyUnbound() { - for (final observer in RtStateObserver._observers.toList(growable: false)) { + for (final observer in IStateObserver._observers.toList(growable: false)) { observer.onStateUnbound(this, _boundInstance!); } } void _notifyUpdated() { - for (final observer in RtStateObserver._observers.toList(growable: false)) { + for (final observer in IStateObserver._observers.toList(growable: false)) { observer.onStateUpdated(this); if (boundInstance is RtState) { @@ -222,7 +222,7 @@ abstract class RtStateBase<E extends RtStateBase<E>> implements RtState { } void _notifyDisponsed() { - for (final observer in RtStateObserver._observers.toList(growable: false)) { + for (final observer in IStateObserver._observers.toList(growable: false)) { observer.onStateDisposed(this); } } diff --git a/packages/reactter/lib/src/framework/rt_state_observer.dart b/packages/reactter/lib/src/framework/rt_state_observer.dart index 93e8ca90..5414f571 100644 --- a/packages/reactter/lib/src/framework/rt_state_observer.dart +++ b/packages/reactter/lib/src/framework/rt_state_observer.dart @@ -1,37 +1,91 @@ part of '../internals.dart'; /// {@template reactter.rt_state_observer} -/// An abstract class that defines the interface for observing state changes. -/// Implementations of this class can be used to monitor the lifecycle of states. +/// A class that implements the [IStateObserver] interface. +/// +/// It provides a set of callback functions that can be used to observe +/// the lifecycle of states. +/// +/// This observer should be added to Reactter's observers for it to work, +/// using the [Rt.addObserver] method, like so: +/// +/// ```dart +/// final stateObserver = RtStateObserver( +/// onCreated: (state) { +/// print('State created: $state'); +/// }, +/// onBound: (state, instance) { +/// print('State bound: $state, instance: $instance'); +/// }, +/// onUnbound: (state, instance) { +/// print('State unbound: $state, instance: $instance'); +/// }, +/// onUpdated: (state) { +/// print('State updated: $state'); +/// }, +/// onDisposed: (state) { +/// print('State disposed: $state'); +/// }, +/// ); +/// +/// Rt.addObserver(stateObserver); +/// ``` +/// +/// See also: +/// - [IStateObserver] - An abstract class that defines the interface for observing lifecycle of states. +/// - [Rt.addObserver] - A method that adds an observer to Reactter's observers. /// {@endtemplate} -abstract class RtStateObserver implements IObserver { - /// A set of all registered state observers. - static final _observers = <RtStateObserver>{}; - - /// Called when a state is created. - /// - /// [state] - The state that was created. - void onStateCreated(covariant RtState state); - - /// Called when a state is bound to an instance. - /// - /// [state] - The state that was bound. - /// [instance] - The instance to which the state was bound. - void onStateBound(covariant RtState state, Object instance); - - /// Called when a state is unbound from an instance. - /// - /// [state] - The state that was unbound. - /// [instance] - The instance from which the state was unbound. - void onStateUnbound(covariant RtState state, Object instance); - - /// Called when a state is updated. - /// - /// [state] - The state that was updated. - void onStateUpdated(covariant RtState state); - - /// Called when a state is disposed. - /// - /// [state] - The state that was disposed. - void onStateDisposed(covariant RtState state); +class RtStateObserver implements IStateObserver { + /// {@macro reactter.i_state_observer.on_state_bound} + final void Function(RtState state, Object instance)? onBound; + + /// {@macro reactter.i_state_observer.on_state_created} + final void Function(RtState state)? onCreated; + + /// {@macro reactter.i_state_observer.on_state_mounted} + final void Function(RtState state)? onDisponsed; + + /// {@macro reactter.i_state_observer.on_state_unbound} + final void Function(RtState state, Object instance)? onUnbound; + + /// {@macro reactter.i_state_observer.on_state_updated} + final void Function(RtState state)? onUpdated; + + RtStateObserver({ + this.onBound, + this.onCreated, + this.onDisponsed, + this.onUnbound, + this.onUpdated, + }); + + /// {@macro reactter.i_state_observer.on_state_bound} + @override + void onStateBound(RtState state, Object instance) { + onBound?.call(state, instance); + } + + /// {@macro reactter.i_state_observer.on_state_created} + @override + void onStateCreated(RtState state) { + onCreated?.call(state); + } + + /// {@macro reactter.i_state_observer.on_state_mounted} + @override + void onStateDisposed(RtState state) { + onDisponsed?.call(state); + } + + /// {@macro reactter.i_state_observer.on_state_unbound} + @override + void onStateUnbound(RtState state, Object instance) { + onUnbound?.call(state, instance); + } + + /// {@macro reactter.i_state_observer.on_state_updated} + @override + void onStateUpdated(RtState state) { + onUpdated?.call(state); + } } diff --git a/packages/reactter/lib/src/interfaces/observer.dart b/packages/reactter/lib/src/interfaces/observer.dart index 32789485..0aea99b5 100644 --- a/packages/reactter/lib/src/interfaces/observer.dart +++ b/packages/reactter/lib/src/interfaces/observer.dart @@ -1,4 +1,152 @@ part of '../internals.dart'; +/// {@template reactter.i_observer} /// An abstract class representing an observer. +/// {@endtemplate} +@internal abstract class IObserver {} + +/// {@template reactter.i_state_observer} +/// An abstract class that defines the interface for observing lifecycle of states. +/// {@endtemplate} +@internal +abstract class IStateObserver implements IObserver { + /// A set of all registered state observers. + static final _observers = <IStateObserver>{}; + + /// {@template reactter.i_state_observer.on_state_created} + /// Called when a state is created. + /// + /// [state] - The state that was created. + /// {@endtemplate} + void onStateCreated(covariant IState state); + + /// {@template reactter.i_state_observer.on_state_bound} + /// Called when a state is bound to an instance. + /// + /// [state] - The state that was bound. + /// + /// [instance] - The instance to which the state was bound. + /// {@endtemplate} + void onStateBound(covariant IState state, Object instance); + + /// {@template reactter.i_state_observer.on_state_unbound} + /// Called when a state is unbound from an instance. + /// + /// [state] - The state that was unbound. + /// + /// [instance] - The instance from which the state was unbound. + /// {@endtemplate} + void onStateUnbound(covariant IState state, Object instance); + + /// {@template reactter.i_state_observer.on_state_updated} + /// Called when a state is updated. + /// + /// [state] - The state that was updated. + /// {@endtemplate} + void onStateUpdated(covariant IState state); + + /// {@template reactter.i_state_observer.on_state_mounted} + /// Called when a state is disposed. + /// + /// [state] - The state that was disposed. + /// {@endtemplate} + void onStateDisposed(covariant IState state); +} + +/// {@template reactter.i_dependency_observer} +/// An abstract class that defines the interface for observing lifecycle of dependencies. +/// {@endtemplate} +@internal +abstract class IDependencyObserver implements IObserver { + /// A set of all registered dependency observers. + static final _observers = <IDependencyObserver>{}; + + /// {@template reactter.i_dependency_observer.on_dependency_registered} + /// Called when a dependency is registered. + /// + /// [dependency] - The dependency that was registered. + /// {@endtemplate} + void onDependencyRegistered(covariant DependencyRef dependency); + + /// {@template reactter.i_dependency_observer.on_dependency_created} + /// Called when a dependency is created. + /// + /// [dependency] - The dependency that was created. + /// + /// [instance] - The instance of the dependency. + /// {@endtemplate} + void onDependencyCreated( + covariant DependencyRef dependency, + Object? instance, + ); + + /// {@template reactter.i_dependency_observer.on_dependency_mounted} + /// Called when a dependency is mounted. + /// + /// [dependency] - The dependency that was mounted. + /// + /// [instance] - The instance of the dependency. + /// {@endtemplate} + void onDependencyMounted( + covariant DependencyRef dependency, + Object? instance, + ); + + /// {@template reactter.i_dependency_observer.on_dependency_unmounted} + /// Called when a dependency is unmounted. + /// + /// [dependency] - The dependency that was unmounted. + /// + /// [instance] - The instance of the dependency. + /// {@endtemplate} + void onDependencyUnmounted( + covariant DependencyRef dependency, + Object? instance, + ); + + /// {@template reactter.i_dependency_observer.on_dependency_deleted} + /// Called when a dependency is deleted. + /// + /// [dependency] - The dependency that was deleted. + /// + /// [instance] - The instance of the dependency. + /// {@endtemplate} + void onDependencyDeleted( + covariant DependencyRef dependency, + Object? instance, + ); + + /// {@template reactter.i_dependency_observer.on_dependency_unregistered} + /// Called when a dependency is unregistered. + /// + /// [dependency] - The dependency that was unregistered. + /// {@endtemplate} + void onDependencyUnregistered(covariant DependencyRef dependency); + + /// {@template reactter.i_dependency_observer.on_dependency_failed} + /// Called when a dependency fails. + /// + /// [dependency] - The dependency that failed. + /// + /// [fail] - The reason for the failure. + /// {@endtemplate} + void onDependencyFailed( + covariant DependencyRef dependency, + DependencyFail fail, + ); +} + +/// {@template reactter.dependency_fail} +/// An enum representing the reasons for a dependency failure. +/// {@endtemplate} +enum DependencyFail { + alreadyRegistered, + alreadyCreated, + alreadyDeleted, + alreadyUnregistered, + missingInstanceBuilder, + builderRetainedAsFactory, + dependencyRetainedAsSingleton, + cannotUnregisterActiveInstance, +} diff --git a/packages/reactter/lib/src/logger.dart b/packages/reactter/lib/src/logger.dart index 8c4ef9dd..9bd743d2 100644 --- a/packages/reactter/lib/src/logger.dart +++ b/packages/reactter/lib/src/logger.dart @@ -30,7 +30,7 @@ extension RtLoggerExt on RtInterface { } @internal -class RtLogger with RtStateObserver, RtDependencyObserver { +class RtLogger implements IStateObserver, IDependencyObserver { static RtLogger? instance; static void initialize({ diff --git a/packages/reactter/test/framework/rt_dependency_observer_test.dart b/packages/reactter/test/framework/rt_dependency_observer_test.dart index bd3b824d..b00b6766 100644 --- a/packages/reactter/test/framework/rt_dependency_observer_test.dart +++ b/packages/reactter/test/framework/rt_dependency_observer_test.dart @@ -3,212 +3,199 @@ import 'package:reactter/reactter.dart'; import 'package:reactter/src/internals.dart'; import '../shareds/test_controllers.dart'; - -class RtDependencyObserverTest extends RtDependencyObserver { - int onDependencyRegisteredCalledCount = 0; - - int onDependencyCreatedCalledCount = 0; - Object? lastInstanceCreated; - - int onDependencyMountedCalledCount = 0; - Object? lastInstanceMounted; - - int onDependencyUnmountedCalledCount = 0; - Object? lastInstanceUnmounted; - - int onDependencyDeletedCalledCount = 0; - Object? lastInstanceDeleted; - - int onDependencyUnregisteredCalledCount = 0; - - int onDependencyFailedCalledCount = 0; - DependencyFail? lastFail; - - @override - void onDependencyRegistered(covariant DependencyRef<Object?> dependency) { - onDependencyRegisteredCalledCount++; - } - - @override - void onDependencyCreated( - covariant DependencyRef<Object?> dependency, - Object? instance, - ) { - onDependencyCreatedCalledCount++; - lastInstanceCreated = instance; - } - - @override - void onDependencyMounted( - covariant DependencyRef<Object?> dependency, - Object? instance, - ) { - onDependencyMountedCalledCount++; - lastInstanceMounted = instance; - } - - @override - void onDependencyUnmounted( - covariant DependencyRef<Object?> dependency, - Object? instance, - ) { - onDependencyUnmountedCalledCount++; - lastInstanceUnmounted = instance; - } - - @override - void onDependencyDeleted( - covariant DependencyRef<Object?> dependency, - Object? instance, - ) { - onDependencyDeletedCalledCount++; - lastInstanceDeleted = instance; - } - - @override - void onDependencyUnregistered(covariant DependencyRef<Object?> dependency) { - onDependencyUnregisteredCalledCount++; - } - - @override - void onDependencyFailed( - covariant DependencyRef<Object?> dependency, - DependencyFail fail, - ) { - onDependencyFailedCalledCount++; - lastFail = fail; - } -} - void main() { group("RtDependencyObserver", () { test("should be observed when a dependency is registered", () { - final observer = RtDependencyObserverTest(); + bool onRegisteredCalled = false; + + final observer = RtDependencyObserver( + onRegistered: (dependencyRef) { + expect(dependencyRef, isA<DependencyRegister<TestController>>()); + onRegisteredCalled = true; + }, + ); Rt.addObserver(observer); Rt.register<TestController>(() => TestController()); - expect(observer.onDependencyRegisteredCalledCount, 1); + expect(onRegisteredCalled, isTrue); - Rt.addObserver(observer); + Rt.removeObserver(observer); Rt.unregister<TestController>(); }); test("should be observed when a dependency is created", () { - final observer = RtDependencyObserverTest(); + bool onCreatedCalled = false; + dynamic instanceCreated; + + final observer = RtDependencyObserver( + onCreated: (dependencyRef, instance) { + expect(dependencyRef, isA<DependencyRegister<TestController>>()); + expect(instance, isA<TestController>()); + onCreatedCalled = true; + instanceCreated = instance; + }, + ); Rt.addObserver(observer); final instance = Rt.create(() => TestController()); - expect(observer.onDependencyCreatedCalledCount, 1); - expect(observer.lastInstanceCreated, instance); + expect(onCreatedCalled, isTrue); + expect(instanceCreated, instance); Rt.removeObserver(observer); Rt.destroy<TestController>(); }); test("should be observed when a dependency is mounted", () { - final observer = RtDependencyObserverTest(); + bool onMountedCalled = false; + dynamic instanceMounted; + + final observer = RtDependencyObserver( + onMounted: (dependencyRef, instance) { + expect(dependencyRef, isA<RtDependency<TestController>>()); + expect(instance, isA<TestController>()); + onMountedCalled = true; + instanceMounted = instance; + }, + ); Rt.addObserver(observer); final instance = Rt.create(() => TestController()); Rt.emit(RtDependency<TestController>(), Lifecycle.didMount, instance); - expect(observer.onDependencyMountedCalledCount, 1); - expect(observer.lastInstanceMounted, instance); + expect(onMountedCalled, isTrue); + expect(instanceMounted, instance); Rt.removeObserver(observer); Rt.destroy<TestController>(); }); test("should be observed when a dependency is unmounted", () { - final observer = RtDependencyObserverTest(); + bool onUnmountedCalled = false; + dynamic instanceUnmounted; + + final observer = RtDependencyObserver( + onUnmounted: (dependencyRef, instance) { + expect(dependencyRef, isA<RtDependency<TestController>>()); + expect(instance, isA<TestController>()); + onUnmountedCalled = true; + instanceUnmounted = instance; + }, + ); Rt.addObserver(observer); final instance = Rt.create(() => TestController()); Rt.emit(RtDependency<TestController>(), Lifecycle.didUnmount, instance); - expect(observer.onDependencyUnmountedCalledCount, 1); - expect(observer.lastInstanceUnmounted, instance); + expect(onUnmountedCalled, isTrue); + expect(instanceUnmounted, instance); Rt.removeObserver(observer); Rt.destroy<TestController>(); }); test("should be observed when a dependency is deleted", () { - final observer = RtDependencyObserverTest(); + bool onDeletedCalled = false; + dynamic instanceDeleted; + + final observer = RtDependencyObserver( + onDeleted: (dependencyRef, instance) { + expect(dependencyRef, isA<DependencyRegister<TestController>>()); + expect(instance, isA<TestController>()); + onDeletedCalled = true; + instanceDeleted = instance; + }, + ); Rt.addObserver(observer); final instance = Rt.create(() => TestController()); Rt.delete<TestController>(); - expect(observer.onDependencyDeletedCalledCount, 1); - expect(observer.lastInstanceDeleted, instance); + expect(onDeletedCalled, isTrue); + expect(instanceDeleted, instance); Rt.removeObserver(observer); }); test("should be observed when a dependency is unregistered", () { - final observer = RtDependencyObserverTest(); + bool onUnregisteredCalled = false; + + final observer = RtDependencyObserver( + onUnregistered: (dependencyRef) { + expect(dependencyRef, isA<DependencyRegister<TestController>>()); + onUnregisteredCalled = true; + }, + ); Rt.addObserver(observer); Rt.register<TestController>(() => TestController()); Rt.unregister<TestController>(); - expect(observer.onDependencyUnregisteredCalledCount, 1); + expect(onUnregisteredCalled, isTrue); Rt.removeObserver(observer); }); test("should be observed when a dependency is failed", () { - final observer = RtDependencyObserverTest(); + int onFailedCalledCount = 0; + DependencyFail? lastFail; + + final observer = RtDependencyObserver( + onFailed: (dependencyRef, fail) { + expect(dependencyRef is DependencyRef<TestController?>, isTrue); + expect(fail, isA<DependencyFail>()); + onFailedCalledCount++; + lastFail = fail; + }, + ); Rt.addObserver(observer); Rt.create(() => TestController()); Rt.register(() => TestController()); - expect(observer.onDependencyFailedCalledCount, 1); - expect(observer.lastFail, DependencyFail.alreadyRegistered); + expect(onFailedCalledCount, 1); + expect(lastFail, DependencyFail.alreadyRegistered); Rt.create(() => TestController()); - expect(observer.onDependencyFailedCalledCount, 3); // before the last fail, it should be `DependencyFail.alreadyRegistered` again. - expect(observer.lastFail, DependencyFail.alreadyCreated); + expect(onFailedCalledCount, 3); + expect(lastFail, DependencyFail.alreadyCreated); Rt.delete<TestController>(); Rt.delete<TestController>(); - expect(observer.onDependencyFailedCalledCount, 4); - expect(observer.lastFail, DependencyFail.alreadyDeleted); + expect(onFailedCalledCount, 4); + expect(lastFail, DependencyFail.alreadyDeleted); Rt.unregister<TestController>(); - expect(observer.onDependencyFailedCalledCount, 5); - expect(observer.lastFail, DependencyFail.alreadyUnregistered); + expect(onFailedCalledCount, 5); + expect(lastFail, DependencyFail.alreadyUnregistered); Rt.get<TestController>(); - expect(observer.onDependencyFailedCalledCount, 6); - expect(observer.lastFail, DependencyFail.missingInstanceBuilder); + expect(onFailedCalledCount, 6); + expect(lastFail, DependencyFail.missingInstanceBuilder); Rt.create(() => TestController(), mode: DependencyMode.factory); Rt.delete<TestController>(); - expect(observer.onDependencyFailedCalledCount, 7); - expect(observer.lastFail, DependencyFail.builderRetainedAsFactory); + expect(onFailedCalledCount, 7); + expect(lastFail, DependencyFail.builderRetainedAsFactory); Rt.destroy<TestController>(); Rt.create(() => TestController(), mode: DependencyMode.singleton); Rt.delete<TestController>(); - expect(observer.onDependencyFailedCalledCount, 8); - expect(observer.lastFail, DependencyFail.dependencyRetainedAsSingleton); + expect(onFailedCalledCount, 8); + expect(lastFail, DependencyFail.dependencyRetainedAsSingleton); Rt.unregister<TestController>(); - expect(observer.onDependencyFailedCalledCount, 9); - expect(observer.lastFail, DependencyFail.cannotUnregisterActiveInstance); + expect(onFailedCalledCount, 9); + expect(lastFail, DependencyFail.cannotUnregisterActiveInstance); Rt.removeObserver(observer); Rt.destroy<TestController>(); diff --git a/packages/reactter/test/framework/rt_state_observer_test.dart b/packages/reactter/test/framework/rt_state_observer_test.dart index a49a6be6..addd5f80 100644 --- a/packages/reactter/test/framework/rt_state_observer_test.dart +++ b/packages/reactter/test/framework/rt_state_observer_test.dart @@ -1,99 +1,82 @@ import 'package:reactter/reactter.dart'; +import 'package:reactter/src/internals.dart'; import 'package:test/test.dart'; -class RtStateObserverTest extends RtStateObserver { - int onStateCreatedCalledCount = 0; - String? lastStateCreated; - - int onStateBoundCalledCount = 0; - String? lastStateBound; - Object? lastInstanceBound; - - int onStateUnboundCalledCount = 0; - String? lastStateUnbound; - - int onStateUpdatedCalledCount = 0; - String? lastStateUpdated; - - int onStateDisposedCalledCount = 0; - String? lastStateDisposed; - - @override - void onStateCreated(covariant RtState state) { - lastStateCreated = state.debugLabel; - onStateCreatedCalledCount++; - } - - @override - void onStateBound(covariant RtState state, Object instance) { - lastStateBound = state.debugLabel; - lastInstanceBound = instance; - onStateBoundCalledCount++; - } - - @override - void onStateUnbound(covariant RtState state, Object instance) { - lastStateUnbound = state.debugLabel; - onStateUnboundCalledCount++; - } - - @override - void onStateUpdated(covariant RtState state) { - lastStateUpdated = state.debugLabel; - onStateUpdatedCalledCount++; - } - - @override - void onStateDisposed(covariant RtState state) { - lastStateDisposed = state.debugLabel; - onStateDisposedCalledCount++; - } -} - void main() { group("RtStateObserver", () { test("should be observed when a state is created", () { - final observer = RtStateObserverTest(); + int onCreatedCalledCount = 0; + String? lastStateCreated; + + final observer = RtStateObserver( + onCreated: (state) { + expect(state, isA<RtState>()); + onCreatedCalledCount++; + lastStateCreated = state.debugLabel; + }, + ); Rt.addObserver(observer); UseState(0, debugLabel: "stateA"); - expect(observer.onStateCreatedCalledCount, 1); - expect(observer.lastStateCreated, "stateA"); + expect(onCreatedCalledCount, 1); + expect(lastStateCreated, "stateA"); UseState(1, debugLabel: "stateB"); - expect(observer.onStateCreatedCalledCount, 2); - expect(observer.lastStateCreated, "stateB"); + expect(onCreatedCalledCount, 2); + expect(lastStateCreated, "stateB"); Rt.removeObserver(observer); }); test("should be observed when a state is bound", () { - final observer = RtStateObserverTest(); + int onBoundCalledCount = 0; + String? lastStateBound; + dynamic lastInstanceBound; + + final observer = RtStateObserver( + onBound: (state, instance) { + expect(state, isA<RtState>()); + onBoundCalledCount++; + lastStateBound = state.debugLabel; + lastInstanceBound = instance; + }, + ); Rt.addObserver(observer); final stateA = UseState(0, debugLabel: "stateA"); final instanceA = Object(); stateA.bind(instanceA); - expect(observer.onStateBoundCalledCount, 1); - expect(observer.lastStateBound, "stateA"); - expect(observer.lastInstanceBound, instanceA); + expect(onBoundCalledCount, 1); + expect(lastStateBound, "stateA"); + expect(lastInstanceBound, instanceA); final stateB = UseState(1, debugLabel: "stateB"); final instanceB = Object(); stateB.bind(instanceB); - expect(observer.onStateBoundCalledCount, 2); - expect(observer.lastStateBound, "stateB"); - expect(observer.lastInstanceBound, instanceB); + expect(onBoundCalledCount, 2); + expect(lastStateBound, "stateB"); + expect(lastInstanceBound, instanceB); Rt.removeObserver(observer); }); test("should be observed when a state is unbound", () { - final observer = RtStateObserverTest(); + int onUnboundCalledCount = 0; + String? lastStateUnbound; + dynamic lastInstanceUnbound; + + final observer = RtStateObserver( + onUnbound: (state, instance) { + expect(state, isA<RtState>()); + onUnboundCalledCount++; + lastStateUnbound = state.debugLabel; + lastInstanceUnbound = instance; + }, + ); Rt.addObserver(observer); final stateA = UseState(0, debugLabel: "stateA"); @@ -101,62 +84,89 @@ void main() { stateA.bind(instanceA); stateA.unbind(); - expect(observer.onStateUnboundCalledCount, 1); - expect(observer.lastStateUnbound, "stateA"); - expect(observer.lastInstanceBound, instanceA); + expect(onUnboundCalledCount, 1); + expect(lastStateUnbound, "stateA"); + expect(lastInstanceUnbound, instanceA); final stateB = UseState(1, debugLabel: "stateB"); final instanceB = Object(); stateB.bind(instanceB); stateB.unbind(); - expect(observer.onStateUnboundCalledCount, 2); - expect(observer.lastStateUnbound, "stateB"); - expect(observer.lastInstanceBound, instanceB); + expect(onUnboundCalledCount, 2); + expect(lastStateUnbound, "stateB"); + expect(lastInstanceUnbound, instanceB); Rt.removeObserver(observer); }); test("should be observed when a state is updated", () { - final observer = RtStateObserverTest(); + int onUpdatedCalledCount = 0; + String? lastStateUpdated; + + final observer = RtStateObserver( + onUpdated: (state) { + expect(state, isA<RtState>()); + onUpdatedCalledCount++; + lastStateUpdated = state.debugLabel; + }, + ); Rt.addObserver(observer); final stateA = UseState(0, debugLabel: "stateA"); stateA.value = 1; - expect(observer.onStateUpdatedCalledCount, 1); - expect(observer.lastStateUpdated, "stateA"); + expect(onUpdatedCalledCount, 1); + expect(lastStateUpdated, "stateA"); final stateB = UseState(1, debugLabel: "stateB"); stateB.value = 2; - expect(observer.onStateUpdatedCalledCount, 2); - expect(observer.lastStateUpdated, "stateB"); + expect(onUpdatedCalledCount, 2); + expect(lastStateUpdated, "stateB"); Rt.removeObserver(observer); }); test("should be observed when a state is disposed", () { - final observer = RtStateObserverTest(); + int onDisposedCalledCount = 0; + String? lastStateDisposed; + + final observer = RtStateObserver( + onDisponsed: (state) { + expect(state, isA<RtState>()); + onDisposedCalledCount++; + lastStateDisposed = state.debugLabel; + }, + ); Rt.addObserver(observer); final stateA = UseState(0, debugLabel: "stateA"); stateA.dispose(); - expect(observer.onStateDisposedCalledCount, 1); - expect(observer.lastStateDisposed, "stateA"); + expect(onDisposedCalledCount, 1); + expect(lastStateDisposed, "stateA"); final stateB = UseState(1, debugLabel: "stateB"); stateB.dispose(); - expect(observer.onStateDisposedCalledCount, 2); - expect(observer.lastStateDisposed, "stateB"); + expect(onDisposedCalledCount, 2); + expect(lastStateDisposed, "stateB"); Rt.removeObserver(observer); }); test("should be observed when a nested state is updated", () { - final observer = RtStateObserverTest(); + int onStateUpdatedCalledCount = 0; + String? lastStateUpdated; + + final observer = RtStateObserver( + onUpdated: (state) { + expect(state, isA<RtState>()); + onStateUpdatedCalledCount++; + lastStateUpdated = state.debugLabel; + }, + ); Rt.addObserver(observer); final stateA = UseState(0, debugLabel: "stateA"); @@ -165,13 +175,13 @@ void main() { stateB.value = 2; - expect(observer.onStateUpdatedCalledCount, 2); - expect(observer.lastStateUpdated, "stateA"); + expect(onStateUpdatedCalledCount, 2); + expect(lastStateUpdated, "stateA"); stateA.value = 3; - expect(observer.onStateUpdatedCalledCount, 3); - expect(observer.lastStateUpdated, "stateA"); + expect(onStateUpdatedCalledCount, 3); + expect(lastStateUpdated, "stateA"); Rt.removeObserver(observer); }); From e452cef94cb1ab50b58a6fa74659ddb38291cd21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Mon, 10 Feb 2025 00:44:55 -0600 Subject: [PATCH 118/141] refactor(website): Enhance debugging methods documentation with detailed descriptions and usage examples. --- .../docs/api/methods/debugging_methods.mdx | 158 +++++++++++++++++- 1 file changed, 155 insertions(+), 3 deletions(-) diff --git a/website/src/content/docs/api/methods/debugging_methods.mdx b/website/src/content/docs/api/methods/debugging_methods.mdx index 70545a04..77c4a4c8 100644 --- a/website/src/content/docs/api/methods/debugging_methods.mdx +++ b/website/src/content/docs/api/methods/debugging_methods.mdx @@ -1,16 +1,168 @@ --- -title: Debugging Methods 🚧 +title: Debugging Methods description: Reactter debugging methods sidebar: order: 4 --- -import { HM } from '@/components/Highlight'; +import { HN, HM, HT, HS } from '@/components/Highlight'; Reactter provides a set of debugging methods to help you debug your states and dependencies in the finest detail. ## <HM>`Rt.initializeDevTools`</HM> +The <HM>`Rt.initializeDevTools`</HM> method initializes the [Reactter DevTools](http://localhost:4321/reactter/devtools_extension) for debugging purposes. +Only works in development mode. + +### Syntax + +```dart showLineNumbers=false +Rt.initializeDevTools(); +``` + +:::tip +This method should be called at the beginning of your application. +::: + ## <HM>`Rt.initializeLogger`</HM> -## <HM>`Rt.addObserver`</HM> \ No newline at end of file +The <HM>`Rt.initializeLogger`</HM> method initializes the Reactter logger for debugging purposes. Only works in development mode. + +### Syntax + +```dart showLineNumbers=false +Rt.initializeLogger({ + String name = 'REACTTER', + LogOutput output = dev.log, +}); + +typedef LogOutput = void Function( + String message, { + required String name, + required int level, + StackTrace? stackTrace, +}); +``` + +### Parameters + +- **`name`**: *(Optional)* The name of the logger. Default is <HS>`'REACTTER'`</HS>. +- <HM>**`output`**</HM>: *(Optional)* The output function for the logger. Default is <HM>`dev.log`</HM>. + +The Parameters of the output function(<HT>`LogOutput`</HT>) are: + - **`message`**: The message to log. + - **`name`**: The name of the logger. + - **`level`**: The level of the log message. + The levels are(Copy from [package:logging](https://pub.dev/packages/logging)): + - <HN>`0`</HN>: Special key to turn on logging for all levels. + - <HN>`300`</HN>: Key for highly detailed tracing. + - <HN>`400`</HN>: Key for fairly detailed tracing. + - <HN>`500`</HN>: Key for tracing information. + - <HN>`700`</HN>: Key for static configuration messages. + - <HN>`800`</HN>: Key for informational messages. + - <HN>`900`</HN>: Key for potential problems. + - <HN>`1000`</HN>: Key for serious failures. + - <HN>`1200`</HN>: Key for extra debugging loudness. + - <HN>`2000`</HN>: Special key to turn off all logging. + - **`stackTrace`**: *(Optional)* The stack trace of the log message. + +### Usage + +You can customize the logger output function to log messages in your preferred way, e.g.: + +```dart showLineNumbers=false +Rt.initializeLogger( + output: (String message, {required String name, required int level, StackTrace? stackTrace}) { + print('[$name] $message'); + }, +); +``` + +Or use another package like [logging](https://pub.dev/packages/logging) to log messages, e.g.: + +```dart showLineNumbers=false +void initializeLogger() { + final logger = Logger('REACTTER'); + final levelMapper = { for (final level in Level.LEVELS) level.value: level }; + + Rt.initializeLogger( + output: (String message, {required String name, required int level, StackTrace? stackTrace}) { + logger.log( + levelMapper[level] ?? Level.INFO, + message, + name: name, + stackTrace: stackTrace, + ) + }, + ); +} + +void main() { + initializeLogger(); + runApp(MyApp()); +} +``` + +## <HM>`Rt.addObserver`</HM> + +The <HM>`Rt.addObserver`</HM> method adds an observer to the Reactter store to listen to the state or dependency lifecycle. + +### Syntax + +```dart showLineNumbers=false +Rt.addObserver(IObserver observer); +``` + +### Parameters + +- **`observer`**: The observer to add like <HT>`RtStateObserver`</HT> or <HT>`RtDependencyObserver`</HT>. + +### Usage + +You can add a <HT>`RtStateObserver`</HT> to listen to the state lifecycle, e.g.: + +```dart showLineNumbers=false +final stateObserver = RtStateObserver( + onChanged: (state) { + print('State changed: $state'); + }, +); + +Rt.addObserver(stateObserver); +``` + +Or add a <HT>`RtDependencyObserver`</HT> to listen to the dependency lifecycle, e.g.: + +```dart showLineNumbers=false +final dependencyObserver = RtDependencyObserver( + onCreated: (dependency, instance) { + print('Dependency created: $dependency'); + }, +); + +Rt.addObserver(dependencyObserver); +``` + +## <HM>`Rt.removeObserver`</HM> + +The <HM>`Rt.removeObserver`</HM> method removes an observer from the Reactter store. + +### Syntax + +```dart showLineNumbers=false +Rt.removeObserver(IObserver observer); +``` + +### Parameters + +- **`observer`**: The observer to remove. + +### Usage + +You can remove the observer added before, e.g.: + +```dart showLineNumbers=false +Rt.removeObserver(stateObserver); +``` + + From 28cdfa830f8013bc4b32e7ed2174dff784ecc830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Wed, 12 Feb 2025 23:26:09 -0600 Subject: [PATCH 119/141] refactor: Replace `RtStateBase` with `RtState` for improved state management consistency. --- .../lib/examples/6_tree/states/tree_list.dart | 3 +- .../lib/examples/6_tree/states/tree_node.dart | 3 +- .../lib/src/core/state_management.dart | 2 +- packages/reactter/lib/src/framework.dart | 5 +- ...{rt_context.dart => rt_context_mixin.dart} | 0 .../reactter/lib/src/framework/rt_hook.dart | 12 +- .../reactter/lib/src/framework/rt_state.dart | 256 +++++++++++++++++- .../lib/src/framework/rt_state_base.dart | 229 ---------------- .../reactter/lib/src/interfaces/hook.dart | 3 +- packages/reactter/lib/src/internals.dart | 1 - packages/reactter/lib/src/signal/signal.dart | 26 +- .../test/framework/rt_state_base_test.dart | 6 +- packages/reactter/test/logger_test.dart | 3 +- .../lib/src/bases/tree_list.dart | 2 +- .../lib/src/bases/tree_node.dart | 2 +- .../test/widget_test.dart | 43 +-- packages/reactter_lint/README.md | 4 +- .../src/rules/rt_invalid_state_creation.dart | 2 +- packages/reactter_lint/lib/src/types.dart | 2 - 19 files changed, 298 insertions(+), 306 deletions(-) rename packages/reactter/lib/src/framework/{rt_context.dart => rt_context_mixin.dart} (100%) delete mode 100644 packages/reactter/lib/src/framework/rt_state_base.dart diff --git a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart index 384cc733..95c931b9 100644 --- a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart +++ b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart @@ -3,8 +3,7 @@ import 'dart:collection'; import 'package:examples/examples/6_tree/states/tree_node.dart'; import 'package:flutter_reactter/reactter.dart'; -class TreeList extends LinkedList<TreeNode> - with RtContextMixin, RtStateBase<TreeList> { +class TreeList extends LinkedList<TreeNode> with RtState<TreeList> { TreeList._(); factory TreeList() { diff --git a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart index c426efff..97d21ecc 100644 --- a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart +++ b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart @@ -4,8 +4,7 @@ import 'dart:collection'; import 'package:flutter_reactter/flutter_reactter.dart'; -class TreeNode extends LinkedListEntry<TreeNode> - with RtStateBase<TreeNode>, RtContextMixin { +class TreeNode extends LinkedListEntry<TreeNode> with RtState<TreeNode> { /// A unique identifier for each instance of [TreeNode]. static int _lastId = 0; static String _getId() => (_lastId++).toString(); diff --git a/packages/reactter/lib/src/core/state_management.dart b/packages/reactter/lib/src/core/state_management.dart index fdf90802..926b0824 100644 --- a/packages/reactter/lib/src/core/state_management.dart +++ b/packages/reactter/lib/src/core/state_management.dart @@ -17,7 +17,7 @@ abstract class StateManagement<S extends IState> implements IContext { /// /// Example usage: /// ```dart - /// class MyState with RtContextMixin, RtStateBase<MyState> { + /// class MyState extends RtState<MyState> { /// int _value = 0; /// int get value => value; /// set value(int n) { diff --git a/packages/reactter/lib/src/framework.dart b/packages/reactter/lib/src/framework.dart index 0d87c5e1..c482e14e 100644 --- a/packages/reactter/lib/src/framework.dart +++ b/packages/reactter/lib/src/framework.dart @@ -9,8 +9,7 @@ export 'internals.dart' RtDependencyObserver, RtHook, RtInterface, - RtState, - RtStateBase; + RtState; -part 'framework/rt_context.dart'; +part 'framework/rt_context_mixin.dart'; part 'framework/rt_dependency.dart'; diff --git a/packages/reactter/lib/src/framework/rt_context.dart b/packages/reactter/lib/src/framework/rt_context_mixin.dart similarity index 100% rename from packages/reactter/lib/src/framework/rt_context.dart rename to packages/reactter/lib/src/framework/rt_context_mixin.dart diff --git a/packages/reactter/lib/src/framework/rt_hook.dart b/packages/reactter/lib/src/framework/rt_hook.dart index e4a8e4ef..f7c9c075 100644 --- a/packages/reactter/lib/src/framework/rt_hook.dart +++ b/packages/reactter/lib/src/framework/rt_hook.dart @@ -40,9 +40,7 @@ part of '../internals.dart'; /// See also: /// * [RtState], adds state management features to [RtHook]. /// {@endtemplate} -abstract class RtHook - with RtContextMixin, RtStateBase<RtHook> - implements IHook { +abstract class RtHook extends RtState<RtHook> implements IHook { /// {@template reactter.rt_hook.register} /// This getter allows access to the [HookBindingZone] instance /// which is responsible for registering a [RtHook] @@ -62,6 +60,7 @@ abstract class RtHook /// to register the hook and attach the collected states. RtHook() { initHook(); + $._bindInstanceToStates(this); } /// Initializes the hook. @@ -82,13 +81,8 @@ abstract class RtHook /// }, /// [state], /// ); - /// - /// super.initHook(); /// } - @mustCallSuper - void initHook() { - $._bindInstanceToStates(this); - } + void initHook() {} @override @mustCallSuper diff --git a/packages/reactter/lib/src/framework/rt_state.dart b/packages/reactter/lib/src/framework/rt_state.dart index 8b41d636..62ba69e8 100644 --- a/packages/reactter/lib/src/framework/rt_state.dart +++ b/packages/reactter/lib/src/framework/rt_state.dart @@ -1,6 +1,258 @@ part of '../internals.dart'; /// {@template reactter.rt_state} -/// A abstract class that represents a stare in Reactter. +/// A base class for creating a state object in Reactter. +/// +/// The state object must be registered using the [Rt.createState] method or +/// registered as a dependency using the dependency injection. e.g. +/// +/// ```dart +/// class MyState extends RtState<MyState> { +/// int _value = 0; +/// int get value => value; +/// set value(int n) { +/// if (n == _value) return; +/// update(() => _value = n); +/// } +/// } +/// +/// final state = Rt.createState<MyState>(() => MyState()); +/// ``` +/// +/// See also: +/// - [Rt.createState], for creating a state object. +/// /// {@endtemplate} -abstract class RtState implements IState {} +abstract class RtState<E extends RtState<E>> implements IState { + /// Debug assertion for registering a state object. + /// + /// This method is used to assert that a state object is being created within + /// a binding zone. If the assertion fails, an [AssertionError] is thrown. + static bool debugAssertRegistering<E>() { + assert(() { + final currentZone = BindingZone.currentZone; + final isRegistering = currentZone != null && + (currentZone is BindingZone<E> || currentZone is BindingZone<E?>) && + !currentZone.isVerified; + + if (!isRegistering) { + throw AssertionError( + "The state($E) must be create within the BindingZone.\n" + "You can use the 'Rt.createState' method or register as dependency " + "using the dependency injection to ensure that the state is registered.", + ); + } + return true; + }()); + + return true; + } + + // ignore: unused_field + final _debugAssertRegistering = debugAssertRegistering<E>(); + + @override + @internal + DependencyInjection get dependencyInjection => Rt; + + @override + @internal + StateManagement get stateManagement => Rt; + + @override + @internal + EventHandler get eventHandler => Rt; + + bool _isRegistered = false; + bool _isUpdating = false; + + /// A label used for debugging purposes. + @override + String? get debugLabel => null; + + /// A map containing information about state for debugging purposes. + @override + Map<String, dynamic> get debugInfo => {}; + + /// The reference instance to the current state. + @override + Object? get boundInstance => _boundInstance; + Object? _boundInstance; + + /// Returns `true` if the state has been disposed. + @override + bool get isDisposed => _isDisposed; + bool _isDisposed = false; + + bool get _hasListeners => + eventHandler._hasListeners(this) || + (_boundInstance != null && eventHandler._hasListeners(_boundInstance)); + + @override + @protected + @mustCallSuper + void _register() { + if (_isRegistered) return; + + BindingZone.recollectState(this); + _notifyCreated(); + _isRegistered = true; + } + + @mustCallSuper + @override + void bind(Object instance) { + assert(!_isDisposed, "Can't bind when it's been disposed"); + assert( + _boundInstance == null, + "Can't bind a new instance because an instance is already.\n" + "You must unbind the current instance before binding a new one.", + ); + + eventHandler.one(instance, Lifecycle.deleted, _onInstanceDeleted); + _boundInstance = instance; + + _notifyBound(instance); + } + + @override + @mustCallSuper + void unbind() { + assert(!_isDisposed, "Can't unbind when it's been disposed"); + + if (_boundInstance == null) return; + + _notifyUnbound(); + + eventHandler.off(_boundInstance!, Lifecycle.deleted, _onInstanceDeleted); + _boundInstance = null; + } + + @override + @mustCallSuper + + /// {@macro reactter.istate.update} + /// + /// The [fnUpdate] must be a function without parameters(Function()). + void update(Function? fnUpdate) { + assert(!_isDisposed, "Can't update when it's been disposed"); + assert( + fnUpdate is Function(), + "The fnUpdate must be a function without parameters", + ); + + if (_isUpdating || !_hasListeners) { + fnUpdate?.call(); + _notifyUpdated(); + return; + } + + try { + _isUpdating = true; + _notify(Lifecycle.willUpdate); + fnUpdate?.call(); + _notify(Lifecycle.didUpdate); + _notifyUpdated(); + } finally { + _isUpdating = false; + } + } + + @override + @mustCallSuper + void notify() { + assert(!_isDisposed, "Can't refresh when it's been disposed"); + + if (_isUpdating || !_hasListeners) { + _notifyUpdated(); + return; + } + + try { + _isUpdating = true; + _notify(Lifecycle.didUpdate); + _notifyUpdated(); + } finally { + _isUpdating = false; + } + } + + @override + @mustCallSuper + void dispose() { + assert(!_isDisposed, "Can't dispose when it's been disposed"); + + eventHandler.emit(this, Lifecycle.deleted); + eventHandler.offAll(this); + + if (_boundInstance != null) { + unbind(); + } + + if (_isDisposed) return; + + _notifyDisponsed(); + _isDisposed = true; + } + + /// When the instance is destroyed, this object is dispose. + void _onInstanceDeleted(_, __) => dispose(); + + /// Notifies the listeners about the specified [event]. + /// If [Rt._isUntrackedRunning] is true, the notification is skipped. + /// If [Rt._isBatchRunning] is true, the notification is deferred until the batch is completed. + /// The [event] is emitted using [Rt.emit] for the current instance and [_boundInstance]. + void _notify(Enum event, [dynamic param]) { + if (stateManagement._isUntrackedRunning) return; + + final emit = stateManagement._isBatchRunning + ? stateManagement._emitDefferred + : eventHandler.emit; + + final finalParam = param ?? this; + + emit(this, event, finalParam); + + if (_boundInstance == null) return; + + if (_boundInstance is RtState && !(_boundInstance as RtState)._isDisposed) { + return (_boundInstance as RtState)._notify(event, finalParam); + } + + emit(_boundInstance!, event, finalParam); + } + + void _notifyCreated() { + for (final observer in IStateObserver._observers.toList(growable: false)) { + observer.onStateCreated(this); + } + } + + void _notifyBound(Object instance) { + for (final observer in IStateObserver._observers.toList(growable: false)) { + observer.onStateBound(this, instance); + } + } + + void _notifyUnbound() { + for (final observer in IStateObserver._observers.toList(growable: false)) { + observer.onStateUnbound(this, _boundInstance!); + } + } + + void _notifyUpdated() { + for (final observer in IStateObserver._observers.toList(growable: false)) { + observer.onStateUpdated(this); + + if (boundInstance is RtState) { + observer.onStateUpdated(boundInstance as RtState); + } + } + } + + void _notifyDisponsed() { + for (final observer in IStateObserver._observers.toList(growable: false)) { + observer.onStateDisposed(this); + } + } +} diff --git a/packages/reactter/lib/src/framework/rt_state_base.dart b/packages/reactter/lib/src/framework/rt_state_base.dart deleted file mode 100644 index f4b6e28d..00000000 --- a/packages/reactter/lib/src/framework/rt_state_base.dart +++ /dev/null @@ -1,229 +0,0 @@ -part of '../internals.dart'; - -/// {@template reactter.rt_state_base} -/// A base class that implements the [IState] interface. -/// -/// This class provides the basic functionality for managing the state. -/// {@endtemplate} -abstract class RtStateBase<E extends RtStateBase<E>> implements RtState { - /// Debug assertion for registering a state object. - /// - /// This method is used to assert that a state object is being created within - /// a binding zone. If the assertion fails, an [AssertionError] is thrown. - static bool debugAssertRegistering<E>() { - assert(() { - final currentZone = BindingZone.currentZone; - final isRegistering = currentZone != null && - (currentZone is BindingZone<E> || currentZone is BindingZone<E?>) && - !currentZone.isVerified; - - if (!isRegistering) { - throw AssertionError( - "The state($E) must be create within the BindingZone.\n" - "You can use the 'Rt.createState' method or register as dependency " - "using the dependency injection to ensure that the state is registered.", - ); - } - return true; - }()); - - return true; - } - - // ignore: unused_field - final _debugAssertRegistering = debugAssertRegistering<E>(); - - bool _isRegistered = false; - bool _isUpdating = false; - - /// A label used for debugging purposes. - @override - String? get debugLabel => null; - - /// A map containing information about state for debugging purposes. - @override - Map<String, dynamic> get debugInfo => {}; - - /// The reference instance to the current state. - @override - Object? get boundInstance => _boundInstance; - Object? _boundInstance; - - /// Returns `true` if the state has been disposed. - @override - bool get isDisposed => _isDisposed; - bool _isDisposed = false; - - bool get _hasListeners => - eventHandler._hasListeners(this) || - (_boundInstance != null && eventHandler._hasListeners(_boundInstance)); - - @override - @protected - @mustCallSuper - void _register() { - if (_isRegistered) return; - - BindingZone.recollectState(this); - _notifyCreated(); - _isRegistered = true; - } - - @mustCallSuper - @override - void bind(Object instance) { - assert(!_isDisposed, "Can't bind when it's been disposed"); - assert( - _boundInstance == null, - "Can't bind a new instance because an instance is already.\n" - "You must unbind the current instance before binding a new one.", - ); - - eventHandler.one(instance, Lifecycle.deleted, _onInstanceDeleted); - _boundInstance = instance; - - _notifyBound(instance); - } - - @override - @mustCallSuper - void unbind() { - assert(!_isDisposed, "Can't unbind when it's been disposed"); - - if (_boundInstance == null) return; - - _notifyUnbound(); - - eventHandler.off(_boundInstance!, Lifecycle.deleted, _onInstanceDeleted); - _boundInstance = null; - } - - @override - @mustCallSuper - - /// {@macro reactter.istate.update} - /// - /// The [fnUpdate] must be a function without parameters(Function()). - void update(covariant Function? fnUpdate) { - assert(!_isDisposed, "Can't update when it's been disposed"); - assert( - fnUpdate is Function(), - "The fnUpdate must be a function without parameters", - ); - - if (_isUpdating || !_hasListeners) { - fnUpdate?.call(); - _notifyUpdated(); - return; - } - - try { - _isUpdating = true; - _notify(Lifecycle.willUpdate); - fnUpdate?.call(); - _notify(Lifecycle.didUpdate); - _notifyUpdated(); - } finally { - _isUpdating = false; - } - } - - @override - @mustCallSuper - void notify() { - assert(!_isDisposed, "Can't refresh when it's been disposed"); - - if (_isUpdating || !_hasListeners) { - _notifyUpdated(); - return; - } - - try { - _isUpdating = true; - _notify(Lifecycle.didUpdate); - _notifyUpdated(); - } finally { - _isUpdating = false; - } - } - - @override - @mustCallSuper - void dispose() { - assert(!_isDisposed, "Can't dispose when it's been disposed"); - - eventHandler.emit(this, Lifecycle.deleted); - eventHandler.offAll(this); - - if (_boundInstance != null) { - unbind(); - } - - if (_isDisposed) return; - - _notifyDisponsed(); - _isDisposed = true; - } - - /// When the instance is destroyed, this object is dispose. - void _onInstanceDeleted(_, __) => dispose(); - - /// Notifies the listeners about the specified [event]. - /// If [Rt._isUntrackedRunning] is true, the notification is skipped. - /// If [Rt._isBatchRunning] is true, the notification is deferred until the batch is completed. - /// The [event] is emitted using [Rt.emit] for the current instance and [_boundInstance]. - void _notify(Enum event, [dynamic param]) { - if (stateManagement._isUntrackedRunning) return; - - final emit = stateManagement._isBatchRunning - ? stateManagement._emitDefferred - : eventHandler.emit; - - final finalParam = param ?? this; - - emit(this, event, finalParam); - - if (_boundInstance == null) return; - - if (_boundInstance is RtStateBase && - !(_boundInstance as RtStateBase)._isDisposed) { - return (_boundInstance as RtStateBase)._notify(event, finalParam); - } - - emit(_boundInstance!, event, finalParam); - } - - void _notifyCreated() { - for (final observer in IStateObserver._observers.toList(growable: false)) { - observer.onStateCreated(this); - } - } - - void _notifyBound(Object instance) { - for (final observer in IStateObserver._observers.toList(growable: false)) { - observer.onStateBound(this, instance); - } - } - - void _notifyUnbound() { - for (final observer in IStateObserver._observers.toList(growable: false)) { - observer.onStateUnbound(this, _boundInstance!); - } - } - - void _notifyUpdated() { - for (final observer in IStateObserver._observers.toList(growable: false)) { - observer.onStateUpdated(this); - - if (boundInstance is RtState) { - observer.onStateUpdated(boundInstance as RtState); - } - } - } - - void _notifyDisponsed() { - for (final observer in IStateObserver._observers.toList(growable: false)) { - observer.onStateDisposed(this); - } - } -} diff --git a/packages/reactter/lib/src/interfaces/hook.dart b/packages/reactter/lib/src/interfaces/hook.dart index a849df8b..b70d310b 100644 --- a/packages/reactter/lib/src/interfaces/hook.dart +++ b/packages/reactter/lib/src/interfaces/hook.dart @@ -1,7 +1,7 @@ part of '../internals.dart'; /// An abstract class that represents a hook in Reactter. -abstract class IHook { +abstract class IHook implements IState { /// This variable is used to register [IHook] /// and attach the [IState] that are defined here. @protected @@ -11,6 +11,7 @@ abstract class IHook { /// /// If [callback] is provided, it will be executed before notifying the listeners. /// If [callback] is not provided, an empty function will be executed. + @override @mustCallSuper void update([Function()? callback]); } diff --git a/packages/reactter/lib/src/internals.dart b/packages/reactter/lib/src/internals.dart index 2516a15a..eed26336 100644 --- a/packages/reactter/lib/src/internals.dart +++ b/packages/reactter/lib/src/internals.dart @@ -19,7 +19,6 @@ part 'framework/rt_dependency_observer.dart'; part 'framework/rt_hook.dart'; part 'framework/rt_interface.dart'; part 'framework/rt_state.dart'; -part 'framework/rt_state_base.dart'; part 'framework/rt_state_observer.dart'; part 'interfaces/context.dart'; part 'interfaces/hook.dart'; diff --git a/packages/reactter/lib/src/signal/signal.dart b/packages/reactter/lib/src/signal/signal.dart index f67f466b..05a2a213 100644 --- a/packages/reactter/lib/src/signal/signal.dart +++ b/packages/reactter/lib/src/signal/signal.dart @@ -86,20 +86,7 @@ enum SignalEvent { onGetValue, onSetValue } /// package on your dependencies and use its Widgets. /// /// {@endtemplate} -class Signal<T> with RtContextMixin, RtStateBase<Signal<T>> { - /// {@macro reactter.signal} - Signal._( - T value, { - String? debugLabel, - }) : _value = value, - _debugLabel = debugLabel; - - factory Signal(T value, {String? debugLabel}) { - return Rt.createState( - () => Signal._(value, debugLabel: debugLabel), - ); - } - +class Signal<T> with RtState<Signal<T>> { bool _shouldGetValueNotify = true; bool _shouldSetValueNotify = true; @@ -130,6 +117,17 @@ class Signal<T> with RtContextMixin, RtStateBase<Signal<T>> { }); } + /// {@macro reactter.signal} + Signal._( + T value, { + String? debugLabel, + }) : _value = value, + _debugLabel = debugLabel; + + factory Signal(T value, {String? debugLabel}) { + return Rt.createState(() => Signal._(value, debugLabel: debugLabel)); + } + /// Gets and/or sets to [value] like a function /// This method doesn't allow setting its value to null. /// If you need to set null as value, use `.value = null`. diff --git a/packages/reactter/test/framework/rt_state_base_test.dart b/packages/reactter/test/framework/rt_state_base_test.dart index 84a428cb..6b5f1431 100644 --- a/packages/reactter/test/framework/rt_state_base_test.dart +++ b/packages/reactter/test/framework/rt_state_base_test.dart @@ -1,7 +1,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:reactter/reactter.dart'; -class CountTest with RtContextMixin, RtStateBase<CountTest> { +class CountTest extends RtState<CountTest> { int _count = 0; int get count => _count; set count(int value) { @@ -21,7 +21,7 @@ class CountTest with RtContextMixin, RtStateBase<CountTest> { }; } -class StateTest with RtContextMixin, RtStateBase<StateTest> { +class StateTest with RtState<StateTest> { StateTest._() { assert(dependencyInjection == Rt); assert(stateManagement == Rt); @@ -37,7 +37,7 @@ class StateTest with RtContextMixin, RtStateBase<StateTest> { } void main() { - group('RtStateBase', () { + group('RtState', () { test('should create a state object within the BindingZone', () { expect(() => CountTest(), throwsA(isA<AssertionError>())); diff --git a/packages/reactter/test/logger_test.dart b/packages/reactter/test/logger_test.dart index 847380a5..6c143bf7 100644 --- a/packages/reactter/test/logger_test.dart +++ b/packages/reactter/test/logger_test.dart @@ -2,8 +2,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:reactter/reactter.dart'; import 'package:reactter/src/logger.dart'; -class StateTest - with RtContextMixin, RtStateBase<StateTest> { +class StateTest with RtState<StateTest> { StateTest._(); factory StateTest() { diff --git a/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart b/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart index 6dd8e9e6..d6c8edd1 100644 --- a/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart +++ b/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart @@ -4,7 +4,7 @@ import 'package:flutter_reactter/reactter.dart'; import 'package:reactter_devtools_extension/src/bases/tree_node.dart'; base class TreeList<E extends TreeNode<E>> extends LinkedList<E> - with RtContextMixin, RtStateBase<TreeList<E>> { + with RtState<TreeList<E>> { final uMaxDepth = UseState(0); TreeList._(); diff --git a/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart b/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart index 643ccb76..717324cd 100644 --- a/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart +++ b/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart @@ -3,7 +3,7 @@ import 'package:flutter_reactter/reactter.dart'; import 'package:reactter_devtools_extension/src/bases/tree_list.dart'; abstract base class TreeNode<E extends TreeNode<E>> extends LinkedListEntry<E> - with RtStateBase<E>, RtContextMixin { + with RtState<E> { final uChildren = UseState(LinkedHashSet<E>()); final uIsExpanded = UseState(false); final uDepth = UseState(0); diff --git a/packages/reactter_devtools_extension/test/widget_test.dart b/packages/reactter_devtools_extension/test/widget_test.dart index 2e966f48..6f5ea248 100644 --- a/packages/reactter_devtools_extension/test/widget_test.dart +++ b/packages/reactter_devtools_extension/test/widget_test.dart @@ -1,30 +1,13 @@ -// // This is a basic Flutter widget test. -// // -// // To perform an interaction with a widget in your test, use the WidgetTester -// // utility in the flutter_test package. For example, you can send tap and scroll -// // gestures. You can also use WidgetTester to find child widgets in the widget -// // tree, read text, and verify that the values of widget properties are correct. - -// import 'package:flutter/material.dart'; -// import 'package:flutter_test/flutter_test.dart'; - -// import 'package:reactter_devtools_extension/main.dart'; - -// void main() { -// testWidgets('Counter increments smoke test', (WidgetTester tester) async { -// // Build our app and trigger a frame. -// await tester.pumpWidget(const MyApp()); - -// // Verify that our counter starts at 0. -// expect(find.text('0'), findsOneWidget); -// expect(find.text('1'), findsNothing); - -// // Tap the '+' icon and trigger a frame. -// await tester.tap(find.byIcon(Icons.add)); -// await tester.pump(); - -// // Verify that our counter has incremented. -// expect(find.text('0'), findsNothing); -// expect(find.text('1'), findsOneWidget); -// }); -// } +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async {}); +} diff --git a/packages/reactter_lint/README.md b/packages/reactter_lint/README.md index 86371894..02029e25 100644 --- a/packages/reactter_lint/README.md +++ b/packages/reactter_lint/README.md @@ -172,7 +172,7 @@ The state must be create under the Reactter context. Cause: The state cannot be created outside Reactter context. ```dart -class MyState with RtStateBase, RtContextMixin {...} +class MyState with RtState<MyState> {...} > final myState = MyState(); ``` @@ -182,7 +182,7 @@ class MyState with RtStateBase, RtContextMixin {...} Fix: Use `Rt.createState` method for creating the state under the Reactter context. ```dart -class MyState with RtStateBase, RtContextMixin {...} +class MyState with RtState<MyState> {...} final myState = Rt.createState(() => MyState()); ``` diff --git a/packages/reactter_lint/lib/src/rules/rt_invalid_state_creation.dart b/packages/reactter_lint/lib/src/rules/rt_invalid_state_creation.dart index ad01bbf3..ac83fb79 100644 --- a/packages/reactter_lint/lib/src/rules/rt_invalid_state_creation.dart +++ b/packages/reactter_lint/lib/src/rules/rt_invalid_state_creation.dart @@ -29,7 +29,7 @@ class RtInvalidStateCreation extends DartLintRule { if (element == null || element.isFactory || rtHookType.isAssignableFromType(element.returnType) || - !rtStateBaseType.isAssignableFromType(element.returnType) || + !rtStateType.isAssignableFromType(element.returnType) || iAutoRegisterStateType.isAssignableFromType(element.returnType)) { return; } diff --git a/packages/reactter_lint/lib/src/types.dart b/packages/reactter_lint/lib/src/types.dart index 0d7c2684..4c3acc42 100644 --- a/packages/reactter_lint/lib/src/types.dart +++ b/packages/reactter_lint/lib/src/types.dart @@ -1,8 +1,6 @@ import 'package:custom_lint_builder/custom_lint_builder.dart'; final rtStateType = TypeChecker.fromName('RtState', packageName: 'reactter'); -final rtStateBaseType = - TypeChecker.fromName('RtStateBase', packageName: 'reactter'); final iAutoRegisterStateType = TypeChecker.fromName('IAutoRegisterState', packageName: 'reactter'); final rtHookType = TypeChecker.fromName('RtHook', packageName: 'reactter'); From 3808b0c3f90f90572e138333e2b5b5bdd320d1c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Sat, 15 Feb 2025 01:50:42 -0600 Subject: [PATCH 120/141] refactor(website): Remove `RtStateBase` and update related documentation for improved clarity and consistency --- website/src/content/docs/api/classes/args.mdx | 2 +- .../docs/api/classes/lifecycle_observer.mdx | 2 +- website/src/content/docs/api/classes/memo.mdx | 2 +- .../docs/api/classes/rt_dependency.mdx | 2 +- .../api/classes/rt_dependency_observer.mdx | 2 +- .../src/content/docs/api/classes/rt_hooks.mdx | 8 +- .../src/content/docs/api/classes/rt_state.mdx | 158 ++++++++++++++++++ .../docs/api/classes/rt_state_base.mdx | 6 - .../docs/api/classes/rt_state_observer.mdx | 2 +- .../src/content/docs/api/classes/signal.mdx | 6 +- .../docs/api/hooks/use_async_state.mdx | 6 +- .../content/docs/api/hooks/use_compute.mdx | 8 +- .../content/docs/api/hooks/use_dependency.mdx | 10 +- .../src/content/docs/api/hooks/use_effect.mdx | 6 +- .../content/docs/api/hooks/use_reducer.mdx | 6 +- .../src/content/docs/api/hooks/use_state.mdx | 6 +- .../docs/api/methods/debugging_methods.mdx | 48 +++--- .../content/docs/api/widgets/rt_consumer.mdx | 4 +- .../docs/api/widgets/rt_multi_provider.mdx | 2 +- .../src/content/docs/core_concepts/hooks.mdx | 4 +- .../docs/core_concepts/state_management.mdx | 39 ++--- .../content/docs/es/core_concepts/hooks.mdx | 4 +- .../es/core_concepts/state_management.mdx | 15 +- .../docs/migration/v7.3.0_to_v8.0.0.mdx | 13 +- .../docs/shareds/listening_changes_state.mdx | 4 +- .../content/docs/shareds/state_methods.mdx | 20 ++- .../docs/shareds/state_methods_lite.mdx | 16 -- .../docs/shareds/state_properties_methods.mdx | 10 ++ .../shareds/state_properties_methods_ref.mdx | 11 ++ website/src/examples/marks.ts | 1 - 30 files changed, 293 insertions(+), 130 deletions(-) create mode 100644 website/src/content/docs/api/classes/rt_state.mdx delete mode 100644 website/src/content/docs/api/classes/rt_state_base.mdx delete mode 100644 website/src/content/docs/shareds/state_methods_lite.mdx create mode 100644 website/src/content/docs/shareds/state_properties_methods.mdx create mode 100644 website/src/content/docs/shareds/state_properties_methods_ref.mdx diff --git a/website/src/content/docs/api/classes/args.mdx b/website/src/content/docs/api/classes/args.mdx index 0945ba52..c7901a32 100644 --- a/website/src/content/docs/api/classes/args.mdx +++ b/website/src/content/docs/api/classes/args.mdx @@ -2,7 +2,7 @@ title: Args description: Learn how to use generic arguments in Reactter. sidebar: - order: 3 + order: 10 --- import { HM, HT } from '@/components/Highlight'; diff --git a/website/src/content/docs/api/classes/lifecycle_observer.mdx b/website/src/content/docs/api/classes/lifecycle_observer.mdx index add69a34..5176ec78 100644 --- a/website/src/content/docs/api/classes/lifecycle_observer.mdx +++ b/website/src/content/docs/api/classes/lifecycle_observer.mdx @@ -2,7 +2,7 @@ title: LifecycleObserver description: The base class for all lifecycle observers in Reactter. sidebar: - order: 5 + order: 6 --- import { HM, HT } from '@/components/Highlight'; import CodeTabs from '@/components/CodeTabs.astro'; diff --git a/website/src/content/docs/api/classes/memo.mdx b/website/src/content/docs/api/classes/memo.mdx index 95f93896..856afc3f 100644 --- a/website/src/content/docs/api/classes/memo.mdx +++ b/website/src/content/docs/api/classes/memo.mdx @@ -2,7 +2,7 @@ title: Memo description: Learn how to use memoization in Reactter. sidebar: - order: 2 + order: 9 --- import { HM, HT } from '@/components/Highlight'; diff --git a/website/src/content/docs/api/classes/rt_dependency.mdx b/website/src/content/docs/api/classes/rt_dependency.mdx index 01661bf2..7b908ca1 100644 --- a/website/src/content/docs/api/classes/rt_dependency.mdx +++ b/website/src/content/docs/api/classes/rt_dependency.mdx @@ -2,5 +2,5 @@ title: RtDependency 🚧 description: Represents dependency managed by Reactter's dependency injection. sidebar: - order: 9 + order: 4 --- \ No newline at end of file diff --git a/website/src/content/docs/api/classes/rt_dependency_observer.mdx b/website/src/content/docs/api/classes/rt_dependency_observer.mdx index 99de85fb..8c47fde4 100644 --- a/website/src/content/docs/api/classes/rt_dependency_observer.mdx +++ b/website/src/content/docs/api/classes/rt_dependency_observer.mdx @@ -2,5 +2,5 @@ title: RtDependencyObserver 🚧 description: The base class for all dependency observers in Reactter. sidebar: - order: 10 + order: 5 --- \ No newline at end of file diff --git a/website/src/content/docs/api/classes/rt_hooks.mdx b/website/src/content/docs/api/classes/rt_hooks.mdx index 9b46ce47..53ea4443 100644 --- a/website/src/content/docs/api/classes/rt_hooks.mdx +++ b/website/src/content/docs/api/classes/rt_hooks.mdx @@ -2,11 +2,11 @@ title: RtHook description: Aprende aceca de los Hooks en Reactter. sidebar: - order: 6 + order: 3 --- import { HM, HK, HT } from '@/components/Highlight'; -import StateMethodsLite from '@/content/docs/shareds/state_methods_lite.mdx'; +import StatePropertiesMethodsRef from '@/content/docs/shareds/state_properties_methods_ref.mdx'; import CreatingCustomHook from '@/content/docs/shareds/creating_custom_hook.mdx'; import UsingCustomHook from '@/content/docs/shareds/using_custom_hook.mdx'; @@ -20,7 +20,7 @@ It's recommended read that first if you're new in Reactter. ## Syntax ```dart showLineNumbers=false -abstract class RtHook with RtContextMixin, RtStateBase<RtHook> { +abstract class RtHook with RtState<RtHook> { static get $register => HookBindingZone<RtHook>(); HookBindingZone<RtHook> get $; void initHook() @@ -41,7 +41,7 @@ It must be defined has a <HK>`final`</HK> property on first line of the class fo - <HM>**`initHook`**</HM>: Called when the hook is initialized. Useful for setting up state dependencies or performing side effects when the hook is first created. -<StateMethodsLite/> +<StatePropertiesMethodsRef/> ## Usage diff --git a/website/src/content/docs/api/classes/rt_state.mdx b/website/src/content/docs/api/classes/rt_state.mdx new file mode 100644 index 00000000..72b15c19 --- /dev/null +++ b/website/src/content/docs/api/classes/rt_state.mdx @@ -0,0 +1,158 @@ +--- +title: RtState +description: The base class for all states in Reactter. +sidebar: + order: 1 +--- + +import { HE, HN, HM, HT, HS } from '@/components/Highlight'; +import StatePropertiesMethods from '@/content/docs/shareds/state_properties_methods.mdx'; + +:::tip +This documentation assumes you've already read the [State Management](/reactter/core_concepts/state_management/). +It's recommended read that first if you're new in Reactter. +::: + +The <HT>`RtState`</HT> class is a foundational class in the Reactter framework, designed to manage state in a Dart application. +It provides a structured way to create, update, and dispose of state, ensuring that state changes are tracked and propagated efficiently. +This class is typically extended to create custom state that can be used within the Reactter ecosystem. + +## Syntax + +```dart showLineNumbers=false +abstract class RtState<E extends RtState<E>> { + String? get debugLabel; + Map<String, dynamic> get debugInfo; + void update(Function? fnUpdate) + void notify() + void bind(Object instance) + void unbind() + void dispose() +} +``` + +## Properties & Methods + +<StatePropertiesMethods/> + +## Usage + +### Declaration + +To create a custom state, extend the <HT>`RtState`</HT> class and define the state properties and methods. +Register the state using <HM>`Rt.createState`</HM> or as a dependency within the Reactter framework, e.g.: + +```dart collapse={13-20} mark={1,12} "RtState" "Rt.createState" "Rt.on" "Lifecycle.didUpdate" /update(?=\u0028)/ +class MyState extends RtState<MyState> { + int _value = 0; + int get value => _value; + set value(int n) { + if (n == _value) return; + update(() => _value = n); // set new value and notify observers + } + + MyState([int value = 0]) : _value = value; +} + +final state = Rt.createState<MyState>(() => MyState()); // Register state + +Rt.on( + state, + Lifecycle.didUpdate, + (_, __) => print('State updated: ${state.value}') +); + +state.value = 42; // Calls update internally +``` + +:::tip +You can also use the factory constructor to create a state, e.g.: + +```dart collapse={2-8, 15-21} mark={11-13} "RtState" "Rt.createState" "Rt.on" "Lifecycle.didUpdate" /update(?=\u0028)/ +class MyState extends RtState<MyState> { + int _value = 0; + int get value => _value; + set value(int n) { + if (n == _value) return; + update(() => _value = n); // set new value and notify observers + } + + MyState._([int value = 0]) : _value = value; + + factory MyState(int value) { + return Rt.createState(() => MyState._(value)); // Register state + } +} + +Rt.on( + state, + Lifecycle.didUpdate, + (_, __) => print('State updated: ${state.value}') +); + +state.value = 42; // Calls update internally +``` + +This approach allows you to create a state directly without the need to call the <HM>`Rt.createState`</HM> method explicitly. +::: + +### Updating the state + +To update the state, call the <HM>`update`</HM> method and provide a callback function that modifies the state, e.g.: + +```dart collapse={2-3, 8-13} mark={6, 22} "RtState" "Rt.createState" "Rt.on" "Lifecycle.didUpdate" /update(?=\u0028)/ +class MyState extends RtState<MyState> { + int _value = 0; + int get value => _value; + set value(int n) { + if (n == _value) return; + update(() => _value = n); // set new value and notify observers + } + + MyState._([int value = 0]) : _value = value; + + factory MyState(int value) { + return Rt.createState(() => MyState._(value)); // Register state + } +} + +Rt.on( + state, + Lifecycle.didUpdate, + (_, __) => print('State updated: ${state.value}') +); + +state.value = 42; // Calls update internally +``` + +### Listening to changes + +When a state is updated, it emits the following lifecycle events: + +- <HE>`Lifecycle.willUpdate`</HE> event is triggered before the callback function of <HM>`update`</HM> method have been executed. +- <HE>`Lifecycle.didUpdate`</HE> event is triggered after the callback function of <HM>`update`</HM> method have been executed or after the <HM>`notify`</HM> method have been invoked. + +```dart collapse={2-3, 8-13} mark={16-20} "RtState" "Rt.createState" "Rt.on" "Lifecycle.didUpdate" /update(?=\u0028)/ +class MyState extends RtState<MyState> { + int _value = 0; + int get value => _value; + set value(int n) { + if (n == _value) return; + update(() => _value = n); // set new value and notify observers + } + + MyState._([int value = 0]) : _value = value; + + factory MyState(int value) { + return Rt.createState(() => MyState._(value)); // Register state + } +} + +Rt.on( + state, + Lifecycle.didUpdate, + (_, __) => print('State updated: ${state.value}') +); + +state.value = 42; // Calls update internally +``` \ No newline at end of file diff --git a/website/src/content/docs/api/classes/rt_state_base.mdx b/website/src/content/docs/api/classes/rt_state_base.mdx deleted file mode 100644 index 719cc7cb..00000000 --- a/website/src/content/docs/api/classes/rt_state_base.mdx +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: RtStateBase 🚧 -description: The base class for all states in Reactter. -sidebar: - order: 7 ---- \ No newline at end of file diff --git a/website/src/content/docs/api/classes/rt_state_observer.mdx b/website/src/content/docs/api/classes/rt_state_observer.mdx index 1232dfde..b80e2adb 100644 --- a/website/src/content/docs/api/classes/rt_state_observer.mdx +++ b/website/src/content/docs/api/classes/rt_state_observer.mdx @@ -2,5 +2,5 @@ title: RtStateObserver 🚧 description: The base class for all state observers in Reactter. sidebar: - order: 8 + order: 2 --- \ No newline at end of file diff --git a/website/src/content/docs/api/classes/signal.mdx b/website/src/content/docs/api/classes/signal.mdx index 1245b79b..54556929 100644 --- a/website/src/content/docs/api/classes/signal.mdx +++ b/website/src/content/docs/api/classes/signal.mdx @@ -2,12 +2,12 @@ title: Signal description: The base class for all signals in Reactter. sidebar: - order: 1 + order: 7 --- import { HM, HT } from '@/components/Highlight'; import MDXRedry from '@/components/MDXRedry.astro'; -import StateMethodsLite from '@/content/docs/shareds/state_methods_lite.mdx'; +import StatePropertiesMethodsRef from '@/content/docs/shareds/state_properties_methods_ref.mdx'; import * as NoteLateState from '@/content/docs/shareds/note_late_state.mdx'; import * as NoteStateValueNotifies from '@/content/docs/shareds/note_state_value_notifies.mdx'; import * as ListeningChangesState from '@/content/docs/shareds/listening_changes_state.mdx'; @@ -38,7 +38,7 @@ import * as ListeningChangesState from '@/content/docs/shareds/listening_changes listSignal.removeAt(0); listSignal.clear(); ``` -<StateMethodsLite/> +<StatePropertiesMethodsRef/> ## Usage diff --git a/website/src/content/docs/api/hooks/use_async_state.mdx b/website/src/content/docs/api/hooks/use_async_state.mdx index 77f9ee0d..31d2a700 100644 --- a/website/src/content/docs/api/hooks/use_async_state.mdx +++ b/website/src/content/docs/api/hooks/use_async_state.mdx @@ -6,7 +6,7 @@ sidebar: --- import { HB, HE, HK, HM, HT } from '@/components/Highlight'; import MDXRedry from '@/components/MDXRedry.astro'; -import StateMethodsLite from '@/content/docs/shareds/state_methods_lite.mdx'; +import StatePropertiesMethodsRef from '@/content/docs/shareds/state_properties_methods_ref.mdx'; import * as NoteLateState from '@/content/docs/shareds/note_late_state.mdx'; import * as NoteStateValueNotifies from '@/content/docs/shareds/note_state_value_notifies.mdx'; import * as ListeningChangesState from '@/content/docs/shareds/listening_changes_state.mdx'; @@ -35,7 +35,7 @@ UseAsyncState<T, A>.withArg( - <HM>**`asyncFunction`**</HM>: A function that returns a <HT>`Future<T>`</HT> to update the state asynchronously. This function is called by the <HM>`resolve`</HM> method and sets the `value` property. - **`initialValue`**: Initial value of <HT>`T`</HT> type that it will hold. -- **`debugLabel`**: *(Optional)* A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). +- **`debugLabel`**: *(optional)* A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). ## Properties & Methods @@ -102,7 +102,7 @@ A boolean getter that returns <HB>`true`</HB> if the <HM>`asyncFunction`</HM> is - **`loading`**: A callback function invoked when the state is <HE>`UseAsyncStateStatus.loading`</HE>. - **`done`**: A callback function invoked when the state is <HE>`UseAsyncStateStatus.done`</HE>. - **`error`**: A callback function invoked when the state is <HE>`UseAsyncStateStatus.error`</HE>. -<StateMethodsLite /> +<StatePropertiesMethodsRef /> ## Usage diff --git a/website/src/content/docs/api/hooks/use_compute.mdx b/website/src/content/docs/api/hooks/use_compute.mdx index 63b32f0f..ca165a00 100644 --- a/website/src/content/docs/api/hooks/use_compute.mdx +++ b/website/src/content/docs/api/hooks/use_compute.mdx @@ -8,7 +8,7 @@ sidebar: import { HE, HM, HT } from '@/components/Highlight'; import { Code } from "@astrojs/starlight/components"; import MDXRedry from '@/components/MDXRedry.astro'; -import StateMethodsLite from '@/content/docs/shareds/state_methods_lite.mdx'; +import StatePropertiesMethodsRef from '@/content/docs/shareds/state_properties_methods_ref.mdx'; import * as NoteLateState from '@/content/docs/shareds/note_late_state.mdx'; import * as NoteStateValueNotifies from '@/content/docs/shareds/note_state_value_notifies.mdx'; import * as ListeningChangesState from '@/content/docs/shareds/listening_changes_state.mdx'; @@ -30,14 +30,14 @@ UseCompute<T>( - <HM>**`computeValue`**</HM>: A function that returns a value based on the current state of the dependencies. - **`dependencies`**: A list of state(<HT>`RtState`</HT>, learn about it [here](/reactter/core_concepts/state_management/#state)) that the computation depends on. When any of these `dependencies` trigger <HE>`Lifecycle.didUpdate`</HE> event, the `value` is recalculated. -- **`debugLabel`**: *(Optional)* A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). +- **`debugLabel`**: *(optional)* A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). ## Properties & Methods <HT>`UseCompute`</HT> provides the following properties and methods: -- `value`: A getter that allows you to read the computed value. -<StateMethodsLite/> +- **`value`**: A getter that allows you to read the computed value. +<StatePropertiesMethodsRef/> ## Usage diff --git a/website/src/content/docs/api/hooks/use_dependency.mdx b/website/src/content/docs/api/hooks/use_dependency.mdx index 4179f915..fce24541 100644 --- a/website/src/content/docs/api/hooks/use_dependency.mdx +++ b/website/src/content/docs/api/hooks/use_dependency.mdx @@ -8,7 +8,7 @@ sidebar: import CodeTabs from '@/components/CodeTabs.astro'; import { Tabs, TabItem } from "@/components/Tabs"; import { HE, HM, HN, HS, HT } from '@/components/Highlight'; -import StateMethodsLite from '@/content/docs/shareds/state_methods_lite.mdx'; +import StatePropertiesMethodsRef from '@/content/docs/shareds/state_properties_methods_ref.mdx'; import TipDependencyChecking from '@/content/docs/shareds/tip_dependency_checking.mdx'; :::tip @@ -133,11 +133,11 @@ It's recommended read that first if you're new in Reactter. ### Parameters -- **`id`**: *(Optional)* A <HT>`String`</HT> used to identify the dependency of type <HT>`T`</HT>. +- **`id`**: *(optional)* A <HT>`String`</HT> used to identify the dependency of type <HT>`T`</HT>. - **`builder`**: A function that returns an instance of the dependency of type <HT>`T`</HT>. -- **`mode`**: *(Optional)* A <HT>`DependencyMode`</HT> that defines the dependency mode. Defaults to <HE>`DependencyMode.builder`</HE>. +- **`mode`**: *(optional)* A <HT>`DependencyMode`</HT> that defines the dependency mode. Defaults to <HE>`DependencyMode.builder`</HE>. Learn more in [DependencyMode](/reactter/core_concepts/dependency_injection#dependency-modes). -- **`debugLabel`**: *(Optional)* A <HT>`String`</HT> used to identify the hook in the [DevTools extension](/reactter/devtools_extension). +- **`debugLabel`**: *(optional)* A <HT>`String`</HT> used to identify the hook in the [DevTools extension](/reactter/devtools_extension). :::note Some constructors and factories do not include all parameters. For example: @@ -148,7 +148,7 @@ Some constructors and factories do not include all parameters. For example: ## Properties and methods - **`instance`**: A getter property to get the dependency instance of <HT>`T`</HT> type. -<StateMethodsLite /> +<StatePropertiesMethodsRef /> ## Usage diff --git a/website/src/content/docs/api/hooks/use_effect.mdx b/website/src/content/docs/api/hooks/use_effect.mdx index 54b517b8..92e04acc 100644 --- a/website/src/content/docs/api/hooks/use_effect.mdx +++ b/website/src/content/docs/api/hooks/use_effect.mdx @@ -6,7 +6,7 @@ sidebar: --- import { HE, HN, HM, HT } from '@/components/Highlight'; -import StateMethodsLite from '@/content/docs/shareds/state_methods_lite.mdx'; +import StatePropertiesMethodsRef from '@/content/docs/shareds/state_properties_methods_ref.mdx'; import * as ListeningChangesState from '@/content/docs/shareds/listening_changes_state.mdx'; <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/UseEffect-class.html" target="_blank">`UseEffect`</a></HT> is a [hook](/reactter/core_concepts/hooks) that allows to manage side-effect, and its functionality is closely related to <HT>[Lifecycle](/reactter/core_concepts/lifecycle)</HT> of its `dependencies` or the bound **_instance_**. @@ -45,13 +45,13 @@ UseEffect.runOnInit( The <HM>`callback`</HM> and _**effect cleanup**_ function may be called by <HT>[`Lifecycle`](/reactter/core_concepts/lifecycle)</HT> events, such as <HE>`Lifecycle.didMount`</HE> and <HE>`Lifecycle.willUnmount`</HE>. However, they work only if the instance is provided to the widget tree using the API of _**flutter_reactter**_ package. ::: - **`dependencies`**: A list of state(<HT>`RtState`</HT>, learn about it [here](/reactter/core_concepts/state_management/#state)) that the effect depends on. -- **`debugLabel`**: *(Optional)* A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). +- **`debugLabel`**: *(optional)* A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). ## Properties & Methods <HT>`UseEffect`</HT> provides the following properties and methods: -<StateMethodsLite/> +<StatePropertiesMethodsRef/> ## Usage diff --git a/website/src/content/docs/api/hooks/use_reducer.mdx b/website/src/content/docs/api/hooks/use_reducer.mdx index d8afd1a6..48b79932 100644 --- a/website/src/content/docs/api/hooks/use_reducer.mdx +++ b/website/src/content/docs/api/hooks/use_reducer.mdx @@ -7,7 +7,7 @@ sidebar: import { HM, HT } from '@/components/Highlight'; import { Code } from "@astrojs/starlight/components"; import MDXRedry from '@/components/MDXRedry.astro'; -import StateMethodsLite from '@/content/docs/shareds/state_methods_lite.mdx'; +import StatePropertiesMethodsRef from '@/content/docs/shareds/state_properties_methods_ref.mdx'; import useReducerExampleCode from '@/examples/use_reducer/use_reducer_example.dart.code?raw'; import { useReducerExampleMark } from '@/examples/use_reducer/marks.ts'; import * as NoteLateState from '@/content/docs/shareds/note_late_state.mdx'; @@ -35,7 +35,7 @@ UseReducer<T>( - <HM>**`reducer`**</HM>: A function that takes the current `state` and an `action`, and returns a new state. - **`initialState`**: Initial value of <HT>`T`</HT> type that it will hold. -- **`debugLabel`**: *(Optional)* A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). +- **`debugLabel`**: *(optional)* A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). ## Properties & Methods @@ -47,7 +47,7 @@ UseReducer<T>( ```dart void dispatch(RtAction action); ``` -<StateMethodsLite /> +<StatePropertiesMethodsRef /> ## Usage diff --git a/website/src/content/docs/api/hooks/use_state.mdx b/website/src/content/docs/api/hooks/use_state.mdx index e9bc196c..747a5a39 100644 --- a/website/src/content/docs/api/hooks/use_state.mdx +++ b/website/src/content/docs/api/hooks/use_state.mdx @@ -7,7 +7,7 @@ sidebar: import { Code } from "@astrojs/starlight/components"; import { HK, HM, HT } from '@/components/Highlight'; import MDXRedry from '@/components/MDXRedry.astro'; -import StateMethodsLite from '@/content/docs/shareds/state_methods_lite.mdx'; +import StatePropertiesMethodsRef from '@/content/docs/shareds/state_properties_methods_ref.mdx'; import * as NoteLateState from '@/content/docs/shareds/note_late_state.mdx'; import * as NoteStateValueNotifies from '@/content/docs/shareds/note_state_value_notifies.mdx'; import * as ListeningChangesState from '@/content/docs/shareds/listening_changes_state.mdx'; @@ -23,14 +23,14 @@ UseState<T>(T initialValue, { String? debugLabel }); <HT>`UseState`</HT> accepts these parameters: - **`initialValue`**: Initial value of <HT>`T`</HT> type that it will hold. -- **`debugLabel`**: *(Optional)* A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). +- **`debugLabel`**: *(optional)* A label to identify the hook in the [DevTools extension](/reactter/devtools_extension). ## Properties & Methods <HT>`UseState`</HT> provides the following properties and methods: - **`value`**: A getter/setter that allows to read and write its state. -<StateMethodsLite/> +<StatePropertiesMethodsRef/> ## Usage diff --git a/website/src/content/docs/api/methods/debugging_methods.mdx b/website/src/content/docs/api/methods/debugging_methods.mdx index 77c4a4c8..5f088143 100644 --- a/website/src/content/docs/api/methods/debugging_methods.mdx +++ b/website/src/content/docs/api/methods/debugging_methods.mdx @@ -46,25 +46,25 @@ typedef LogOutput = void Function( ### Parameters -- **`name`**: *(Optional)* The name of the logger. Default is <HS>`'REACTTER'`</HS>. -- <HM>**`output`**</HM>: *(Optional)* The output function for the logger. Default is <HM>`dev.log`</HM>. - -The Parameters of the output function(<HT>`LogOutput`</HT>) are: - - **`message`**: The message to log. - - **`name`**: The name of the logger. - - **`level`**: The level of the log message. - The levels are(Copy from [package:logging](https://pub.dev/packages/logging)): - - <HN>`0`</HN>: Special key to turn on logging for all levels. - - <HN>`300`</HN>: Key for highly detailed tracing. - - <HN>`400`</HN>: Key for fairly detailed tracing. - - <HN>`500`</HN>: Key for tracing information. - - <HN>`700`</HN>: Key for static configuration messages. - - <HN>`800`</HN>: Key for informational messages. - - <HN>`900`</HN>: Key for potential problems. - - <HN>`1000`</HN>: Key for serious failures. - - <HN>`1200`</HN>: Key for extra debugging loudness. - - <HN>`2000`</HN>: Special key to turn off all logging. - - **`stackTrace`**: *(Optional)* The stack trace of the log message. +- **`name`**: *(optional)* The name of the logger. Default is <HS>`'REACTTER'`</HS>. +- <HM>**`output`**</HM>: *(optional)* The output function(<HT>`LogOutput`</HT>) for the logger. Default is <HM>`dev.log`</HM>. + + The Parameters of the output function(<HT>`LogOutput`</HT>) are: + - **`message`**: The message to log. + - **`name`**: The name of the logger. + - **`level`**: The level of the log message. + The levels are(Copy from [package:logging](https://pub.dev/packages/logging)): + - <HN>`0`</HN>: Special key to turn on logging for all levels. + - <HN>`300`</HN>: Key for highly detailed tracing. + - <HN>`400`</HN>: Key for fairly detailed tracing. + - <HN>`500`</HN>: Key for tracing information. + - <HN>`700`</HN>: Key for static configuration messages. + - <HN>`800`</HN>: Key for informational messages. + - <HN>`900`</HN>: Key for potential problems. + - <HN>`1000`</HN>: Key for serious failures. + - <HN>`1200`</HN>: Key for extra debugging loudness. + - <HN>`2000`</HN>: Special key to turn off all logging. + - **`stackTrace`**: *(optional)* The stack trace of the log message. ### Usage @@ -161,7 +161,15 @@ Rt.removeObserver(IObserver observer); You can remove the observer added before, e.g.: -```dart showLineNumbers=false +```dart showLineNumbers=false collapse={1-8} +final stateObserver = RtStateObserver( + onChanged: (state) { + print('State changed: $state'); + }, +); + +Rt.addObserver(stateObserver); +// {...} Rt.removeObserver(stateObserver); ``` diff --git a/website/src/content/docs/api/widgets/rt_consumer.mdx b/website/src/content/docs/api/widgets/rt_consumer.mdx index c56c1033..d0a6f4ad 100644 --- a/website/src/content/docs/api/widgets/rt_consumer.mdx +++ b/website/src/content/docs/api/widgets/rt_consumer.mdx @@ -237,7 +237,7 @@ Here's an example of how to use it: <Tabs> <TabItem label="counter.dart"> <HM single slot="label">counter.dart</HM> - <Code code={counterIdCode} lang="dart" mark={[...marks, {range: "13-15, 26-32, 40-41"}, {range: "14,27", label: "👉"}]} /> + <Code code={counterIdCode} lang="dart" mark={[...marks, {range: "13-15, 26-32, 40-41"}, {range: "14,27", label: "👉"}]} /> </TabItem> <TabItem label="counter_view.dart"> @@ -279,7 +279,7 @@ Here's an example of how to use it: <Tabs> <TabItem label="counter.dart"> <HM single slot="label">counter.dart</HM> - <Code code={counterChildCode} lang="dart" mark={[...marks, {range: "26-38"}, {range: "29, 33", label: "👉"}]} /> + <Code code={counterChildCode} lang="dart" mark={[...marks, {range: "26-38"}, {range: "29, 33", label: "👉"}]} /> </TabItem> <TabItem label="counter_view.dart"> diff --git a/website/src/content/docs/api/widgets/rt_multi_provider.mdx b/website/src/content/docs/api/widgets/rt_multi_provider.mdx index 44cae67c..2961dde9 100644 --- a/website/src/content/docs/api/widgets/rt_multi_provider.mdx +++ b/website/src/content/docs/api/widgets/rt_multi_provider.mdx @@ -57,7 +57,7 @@ In the following example, we have a simple counter application that uses the <HT <Tabs> <TabItem label="main.dart"> <HM single slot="label">main.dart</HM> - <Code code={mainCode} lang="dart" mark={[...marks, {range: "14-31"}]} /> + <Code code={mainCode} lang="dart" mark={[...marks, {range: "14-31"}]} /> </TabItem> <TabItem label="counter_view.dart"> diff --git a/website/src/content/docs/core_concepts/hooks.mdx b/website/src/content/docs/core_concepts/hooks.mdx index 75770e5d..af76f1fe 100644 --- a/website/src/content/docs/core_concepts/hooks.mdx +++ b/website/src/content/docs/core_concepts/hooks.mdx @@ -12,7 +12,7 @@ import ZappButton from "@/components/ZappButton.astro"; import { Code } from "@astrojs/starlight/components"; import { marks } from '@/examples/marks.ts' -import StateMethodsLite from '@/content/docs/shareds/state_methods_lite.mdx'; +import StatePropertiesMethodsRef from '@/content/docs/shareds/state_properties_methods_ref.mdx'; import CreatingCustomHook from '@/content/docs/shareds/creating_custom_hook.mdx'; import UsingCustomHook from '@/content/docs/shareds/using_custom_hook.mdx'; @@ -41,7 +41,7 @@ Hooks in Reactter are classes that extend <HT>[`RtHook`](/reactter/api/classes/r Hooks in Reactter are essentially stateful entities because <HT>[`RtHook`](/reactter/api/classes/rt_hook)</HT> inherits from <HT>`RtState`</HT> this inheritance allows hooks to manage both state and lifecycle methods efficiently. To manage these aspects, Hooks provide the following: -<StateMethodsLite/> +<StatePropertiesMethodsRef/> ## Custom hook diff --git a/website/src/content/docs/core_concepts/state_management.mdx b/website/src/content/docs/core_concepts/state_management.mdx index bf5fd849..94142eb9 100644 --- a/website/src/content/docs/core_concepts/state_management.mdx +++ b/website/src/content/docs/core_concepts/state_management.mdx @@ -4,6 +4,7 @@ description: Learn about the state management system in Reactter and how it work sidebar: order: 1 --- + import { HE, HM, HT } from '@/components/Highlight'; import StateMethods from '@/content/docs/shareds/state_methods.mdx'; @@ -18,10 +19,10 @@ To continue, we will show you the mechanisms that Reactter offers for state mana Reactter provides a variety of mechanisms for state management, including classes, hooks, and methods: -- Mixins - - <HT>[`RtStateBase`](/reactter/api/classes/RtStateBase)</HT> - Classes - <HT>[`Signal`](/reactter/api/classes/signal)</HT> + - <HT>[`RtState`](/reactter/api/classes/rt_state)</HT> + - <HT>[`RtHook`](/reactter/api/classes/rt_hook)</HT> - Hooks - <HT>[`UseState`](/reactter/api/hooks/use_state)</HT> - <HT>[`UseAsyncState`](/reactter/api/hooks/use_async_state)</HT> @@ -45,14 +46,16 @@ To dive into the concept, let's start by exploring what constitutes a state in R ### State -All states in Reactter are classes that inherit <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/RtState-class.html" target="_blank">`RtState`</a></HT>, +All states in Reactter are classes that inherit <HT>[`RtState`](/reactter/api/classes/rt_state)</HT>, which encapsulates the data and behavior of a particular state, and provides a way to notify observers when the state changes. -Reactter offers three fundamental approaches for creating states: <HT>[`RtStateBase`](/reactter/api/classes/RtStateBase)</HT>, <HT>[`Signal`](/reactter/api/classes/signal)</HT> and <HT>[`Hooks`](/reactter/core_concepts/hooks)</HT>. +:::note +Reactter offers three fundamental approaches for creating states: <HT>[`RtState`](/reactter/api/classes/rt_state)</HT>, <HT>[`Signal`](/reactter/api/classes/signal)</HT> and <HT>[`Hooks`](/reactter/core_concepts/hooks)</HT>. +::: ### State methods -<HT><a href="https://pub.dev/documentation/reactter/latest/reactter/RtState-class.html" target="_blank">`RtState`</a></HT> class provides some methods for managing states, which are: +<HT>[`RtState`](/reactter/api/classes/rt_state)</HT> class provides some methods for managing states. Let's know about them: <StateMethods /> @@ -90,25 +93,14 @@ void countdown(Timer timer) { } } ``` -Ahora veamos que contiene la clase <HT>`Signal`</HT> y cómo se actualiza el estado `count` en el ejemplo anterior. Now let's see what the <HT>`Signal`</HT> class contains and how the `count` state is updated in the example above. -```dart title="signal.dart" "RtContextMixin" "Rt.createState" "RtStateBase" "_value" "update" -class Signal<T> with RtContextMixin, RtStateBase<Signal<T>> { +```dart title="signal.dart" "RtContextMixin" "Rt.createState" "RtState" "_value" "update" +class Signal<T> with RtState<Signal<T>> { // State value T _value; - // Private constructor, only a `Signal` instance can be created through the factory. - Signal._(this._value); - - factory Signal(T value) { - // Register a new state in the Reactter context - return Rt.createState( - () => Signal._(value), - ); - } - T get value => _value; set value(T val) { @@ -119,11 +111,20 @@ class Signal<T> with RtContextMixin, RtStateBase<Signal<T>> { update((_) => _value = val); } + // Private constructor, only a `Signal` instance can be created through the factory. + Signal._(this._value); + + factory Signal(T value) { + // Register a new state in the Reactter context + return Rt.createState( + () => Signal._(value), + ); + } [...] } ``` -During the process, as the `value` of `count` changes, the <HE>`Lifecycle.didUpdate`</HE> event is triggered, which is fired by the <HM>`update`</HM> method (`signal.dart`, line 22). +During the process, as the `value` of `count` changes, the <HE>`Lifecycle.didUpdate`</HE> event is triggered, which is fired by the <HM>`update`</HM> method (`signal.dart`, line 12). This event is listened to by the <HM>`Rt.on`</HM> method (`main.dart`, line 10), which prints the `value` of `count`. This occurs thanks to the reactivity of Reactter, which is responsible for notifying listeners by emitting events related to the **_lifecycle_** of the state. diff --git a/website/src/content/docs/es/core_concepts/hooks.mdx b/website/src/content/docs/es/core_concepts/hooks.mdx index 5232d2e1..0384fa70 100644 --- a/website/src/content/docs/es/core_concepts/hooks.mdx +++ b/website/src/content/docs/es/core_concepts/hooks.mdx @@ -12,7 +12,7 @@ import ZappButton from "@/components/ZappButton.astro"; import { Code } from "@astrojs/starlight/components"; import { marks } from '@/examples/marks.ts' -import StateMethodsLite from '@/content/docs/es/shareds/state_methods_lite.mdx'; +import StatePropertiesMethodsRef from '@/content/docs/es/shareds/state_properties_methods_ref.mdx'; import MainCode from '@/examples/custom_hook/lib/main.dart?raw'; import MyCustomFormCode from '@/examples/custom_hook/lib/my_custom_form.dart?raw'; @@ -41,7 +41,7 @@ Los hooks en Reactter son esencialmente entidades con estado porque <HT>[`RtHook Para gestionar estos aspectos, los Hooks proporcionan lo siguiente: -<StateMethodsLite/> +<StatePropertiesMethodsRef/> ## Hook personalizado diff --git a/website/src/content/docs/es/core_concepts/state_management.mdx b/website/src/content/docs/es/core_concepts/state_management.mdx index c58f70ff..0582104a 100644 --- a/website/src/content/docs/es/core_concepts/state_management.mdx +++ b/website/src/content/docs/es/core_concepts/state_management.mdx @@ -18,10 +18,9 @@ A continuación, te mostramos los mecanimos que Reactter ofrece para la gestión Reactter proporciona una gran variedad de mecanismos para la gestión de estados, incluyendo clases, hooks y métodos: -- Mixins - - <HT>[`RtStateBase`](/reactter/es/api/classes/RtStateBase)</HT> - Clases - <HT>[`Signal`](/reactter/es/api/classes/signal)</HT> + - <HT>[`RtState`](/reactter/es/api/classes/RtState)</HT> - Hooks - <HT>[`UseState`](/reactter/es/api/hooks/use_state)</HT> - <HT>[`UseAsyncState`](/reactter/es/api/hooks/use_async_state)</HT> @@ -46,11 +45,10 @@ Para adentrarnos en el concepto, comencemos explorando qué constituye un estado ### Estado Todos los estados en Reactter son clases que heredan de <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/RtState-class.html" target="_blank">`RtState`</a></HT>, -la cual encapsula los datos y el comportamiento de un estado particular, y proporciona una forma de notificar a los observadores cuando el estado cambia. - -Reactter ofrece tres enfoques fundamentales para crear estados: <HT>[`RtStateBase`](/reactter/es/api/classes/RtStateBase)</HT>, <HT>[`Signal`](/reactter/es/api/classes/signal)</HT> y <HT>[`Hooks`](/reactter/es/core_concepts/hooks)</HT>. +la cual encapsula los datos y el comportamiento de un estado particular, y proporciona una forma de notificar a los observadores cuando el estado cambia ### Metodos del estado +Reactter ofrece tres enfoques fundamentales para crear estados: <HT>[`RtState`](/reactter/es/api/classes/RtState)</HT>, <HT>[`Signal`](/reactter/es/api/classes/signal)</HT> y <HT>[`Hooks`](/reactter/es/core_concepts/hooks)</HT>. La clase <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/RtState-class.html" target="_blank">`RtState`</a></HT> proporciona algunos métodos para gestionar los estados, que son: @@ -91,11 +89,10 @@ void countdown(Timer timer) { } ``` -Ahora veamos que contiene la clase <HT>`Signal`</HT> y cómo se actualiza el estado `count` en el ejemplo anterior. - -```dart title="signal.dart" "RtContextMixin" "Rt.createState" "RtStateBase" "_value" "update" -class Signal<T> with RtContextMixin, RtStateBase<Signal<T>> { +Ahora veamos que contiene la clase <HT>`Signal`</HT> y cómo se actualiza el estado `count` en el ejemplo anterior +class Signal<T> with RtContextMixin, RtState<Signal<T>> { // Valor del estado +```dart title="signal.dart" "RtContextMixin" "Rt.createState" "RtState" "_value" "update" T _value; // Constructor privada, solo se puede crear una instancia de `Signal` a través del factory. diff --git a/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx b/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx index ae87dd5a..7d9f7214 100644 --- a/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx +++ b/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx @@ -21,9 +21,9 @@ If you encounter any issues, refer to the [official documentation](https://pub.d - Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). - Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. - Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. -- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtStateBase/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtStateBase/debugInfo.html) to states and hooks to get info for debugging. +- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtState/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtState/debugInfo.html) to states and hooks to get info for debugging. - Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. -- Add [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtStateBase-class.html) abstract class to implement the base logic of the state. +- Add [`RtState`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtState-class.html) abstract class to implement the base logic of the state. - Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/createState.html) method to create a new state. - Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. - Add [`initHook`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtHook/initHook.html) method to `RtHook` to call when hook is created. @@ -37,11 +37,10 @@ If you encounter any issues, refer to the [official documentation](https://pub.d ### Breakings - Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0/reactter/Rt.html) instead. -- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtStateBase-class.html) instead. +- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtState`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtState-class.html) instead. - Replace [`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html) to [`UseAsyncStateStatus.idle`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseAsyncStateStatus.html). - Rename [`DispatchEffect`](https://pub.dev/documentation/reactter/7.3.0/reactter/DispatchEffect-class.html) to [`AutoDispatchEffect`](https://pub.dev/documentation/reactter/8.0.0/reactter/DispatchEffect-class.html). - Move `asyncFunction` to the first parameter of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseAsyncState/withArg.html). -- Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. - Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/isActive.html) instead. - Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/getDependencyMode.html) instead. - Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0/reactter/DependencyMode.html) instead. @@ -90,11 +89,11 @@ If you encounter any issues, refer to the [official documentation](https://pub.d RtProvider(...); ``` -- If you're using <HT>`ReactterStateImpl`</HT> or <HT>`RtStateImpl`</HT>, migrate to <HT>`RtStateBase`</HT> with <HT>`RtContextMixin`</HT>. +- If you're using <HT>`ReactterStateImpl`</HT> or <HT>`RtStateImpl`</HT>, migrate to <HT>`RtState`</HT>. - ```dart showLineNumbers=false del={1} del="ReactterStateImpl" ins={2} ins="RtStateBase with RtContextMixin" + ```dart showLineNumbers=false del={1} del="ReactterStateImpl" ins={2} ins="RtState" class MyState extends ReactterStateImpl { - class MyState extends RtStateBase with RtContextMixin { + class MyState extends RtState<MyState> { // Implementation } ``` diff --git a/website/src/content/docs/shareds/listening_changes_state.mdx b/website/src/content/docs/shareds/listening_changes_state.mdx index 313167b4..6059404f 100644 --- a/website/src/content/docs/shareds/listening_changes_state.mdx +++ b/website/src/content/docs/shareds/listening_changes_state.mdx @@ -8,8 +8,8 @@ import { HE, HM, HT } from '@/components/Highlight'; When `value` has changed, the <HT>`[[stateName]]`</HT> will emit the following events(learn about it [here](/reactter/core_concepts/lifecycle/)): -- <HE>`Lifecycle.willUpdate`</HE> event is triggered before the `value` change or <HM>`update`</HM>, <HM>`notify`</HM> methods have been invoked. -- <HE>`Lifecycle.didUpdate`</HE> event is triggered after the `value` change or <HM>`update`</HM>, <HM>`notify`</HM> methods have been invoked. +- <HE>`Lifecycle.willUpdate`</HE> event is triggered before the change in `value` or <HM>`update`</HM> method have been invoked. +- <HE>`Lifecycle.didUpdate`</HE> event is triggered after the change in `value` or after <HM>`update`</HM> or <HM>`notify`</HM> methods have been invoked. Example of listening to changes: diff --git a/website/src/content/docs/shareds/state_methods.mdx b/website/src/content/docs/shareds/state_methods.mdx index 30ea6680..710875be 100644 --- a/website/src/content/docs/shareds/state_methods.mdx +++ b/website/src/content/docs/shareds/state_methods.mdx @@ -3,21 +3,23 @@ title: State Methods --- import { HE, HM, HT } from '@/components/Highlight'; -- <HM><a href="https://pub.dev/documentation/reactter/latest/reactter/RtState/update.html" target="_blank">`update`</a></HM>: Executes a callback function and notify its listeners that the state has changed. -When it is invoked, it emits two events to signal the state transition: -<HE>`Lifecycle.willUpdate`</HE> is emitted first, indicating the impending update, followed by <HE>`Lifecycle.didUpdate`</HE> once the update process is complete. -- <HM><a href="https://pub.dev/documentation/reactter/latest/reactter/RtState/notify.html" target="_blank">`notify`</a></HM>: Forces the state to notify its listeners that it has changed. + +- <HM>**`update`**</HM>: Executes a callback function and notify its observers that the state has changed. +When it is invoked, it emits two [lifecycle](/reactter/core_concepts/lifecycle) events to signal the state transition: + - <HE>`Lifecycle.willUpdate`</HE> is emitted first, indicating the impending update. + - <HE>`Lifecycle.didUpdate`</HE> is emitted once the update process is complete. +- <HM>**`notify`**</HM>: Forces the state to notify its observers. Unlike <HM>`update`</HM>, it emits only the <HE>`Lifecycle.didUpdate`</HE> event, as it doesn't involve any preparatory steps before the notification. -- <HM><a href="https://pub.dev/documentation/reactter/latest/reactter/RtState/bind.html" target="_blank">`bind`</a></HM>: Establishes a connection between the state and a specific instance. +- <HM>**`bind`**</HM>: Establishes a connection between the state and a specific instance. This connection allows the instance to reactively update based on changes to the state. By binding the state, the instance becomes aware of changes to the state and can appropriately reflect those changes in its behavior. -- <HM><a href="https://pub.dev/documentation/reactter/latest/reactter/RtState/unbind.html" target="_blank">`unbind`</a></HM>: Releases the connection between the state and the instance. +- <HM>**`unbind`**</HM>: Releases the connection between the state and the instance. When unbinding, the instance will no longer receive updates from the state. This can be useful when an instance is no longer actively using the state or when it needs to detach from the state temporarily or permanently. -- <HM><a href="https://pub.dev/documentation/reactter/latest/reactter/RtState/dispose.html" target="_blank">`dispose`</a></HM>: Is responsible for cleaning up the state and any associated listeners or resources. +- <HM>**`dispose`**</HM>: Is responsible for cleaning up the state and any associated observers or resources. Disposing of the state ensures that it is properly released and no longer consumes memory or processing resources unnecessarily. :::note -The <HM>`bind`</HM>, <HM>`unbind`</HM> and <HM>`dispose`</HM> methods are normally used to manage the <HT>[Lifecycle](/reactter/core_concepts/lifecycle)</HT> of a state. -However, Reactter handles this automatically, when the state is declared inside an instance that exists within Reactter's context via the [dependecy injection](/reactter/core_concepts/dependency_injection/), so you don't need to worry about it(Learn more about [Binding State to Dependency](/reactter/extra_topics/binding_state_to_dependency/)). +While the <HM>`bind`</HM>, <HM>`unbind`</HM> and <HM>`dispose`</HM> methods are normally used to manage the [lifecycle](/reactter/core_concepts/lifecycle) of a state, +Reactter handles this automatically, when the state is declared inside an instance that exists within Reactter's context via the [dependecy injection](/reactter/core_concepts/dependency_injection/), so you don't need to worry about it(Learn more about [Binding State to Dependency](/reactter/extra_topics/binding_state_to_dependency/)). ::: \ No newline at end of file diff --git a/website/src/content/docs/shareds/state_methods_lite.mdx b/website/src/content/docs/shareds/state_methods_lite.mdx deleted file mode 100644 index 54a2bea5..00000000 --- a/website/src/content/docs/shareds/state_methods_lite.mdx +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: State Methods ---- - -import { HM, HN, HT } from '@/components/Highlight'; - -- Methods inherited from <HT>`RtState`</HT>(Learn more [here](/reactter/core_concepts/state_management/#state-methods)): - - <HM>**`update`**</HM>: A method to notify changes after run a set of instructions. - - <HM>**`notify`**</HM>: A method to force to notify changes. - - <HN>*</HN><HM>**`bind`**</HM>: A method to bind an instance to it. - - <HN>*</HN><HM>**`unbind`**</HM>: A method to unbind an instance to it. - - <HN>*</HN><HM>**`dispose`**</HM>: A method to remove all listeners and free resources. - - :::note - <HN>*</HN> These methods are unnecessary when is declared within a dependency(class registered via the [dependecy injection](/reactter/core_concepts/dependency_injection)). - ::: diff --git a/website/src/content/docs/shareds/state_properties_methods.mdx b/website/src/content/docs/shareds/state_properties_methods.mdx new file mode 100644 index 00000000..43a4d11d --- /dev/null +++ b/website/src/content/docs/shareds/state_properties_methods.mdx @@ -0,0 +1,10 @@ +--- +title: Properties & Methods of RtState +--- + +import { HE, HM, HN, HT } from '@/components/Highlight'; +import StateMethods from '@/content/docs/shareds/state_methods.mdx'; + +- **`debugLabel`**: A string that represents the label of the state object for debugging purposes. +- **`debugInfo`**: A map that contains debug information about the state object. +<StateMethods/> diff --git a/website/src/content/docs/shareds/state_properties_methods_ref.mdx b/website/src/content/docs/shareds/state_properties_methods_ref.mdx new file mode 100644 index 00000000..52b33c5b --- /dev/null +++ b/website/src/content/docs/shareds/state_properties_methods_ref.mdx @@ -0,0 +1,11 @@ +--- +title: Properties & Methods of RtState +--- + +import { HT } from '@/components/Highlight'; +import StatePropertiesMethods from '@/content/docs/shareds/state_properties_methods.mdx'; + +<details> + <summary>Properties and methods inherited from <HT>[`RtState`](/reactter/api/classes/rt_state)</HT></summary> + <StatePropertiesMethods/> +</details> \ No newline at end of file diff --git a/website/src/examples/marks.ts b/website/src/examples/marks.ts index f0c15074..6deaa4b1 100644 --- a/website/src/examples/marks.ts +++ b/website/src/examples/marks.ts @@ -27,7 +27,6 @@ export const marks = [ "RtHook", "RtContextMixin", "RtState", - "RtStateBase", "RtDependency", "RtDependencyMode", "RtDependencyMode.builder", From aa956783f6b10cebe1aa2be5f22ef856c9a7cc94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Tue, 18 Feb 2025 16:49:36 -0600 Subject: [PATCH 121/141] refactor: Replace `LifecycleObserver` to `RtDependencyLifecycle` and enhancing class definitions for improved clarity and consistency --- .../lib/examples/6_tree/states/tree_list.dart | 2 +- .../lib/examples/6_tree/states/tree_node.dart | 2 +- .../test/shareds/test_controller.dart | 2 +- .../lib/src/core/dependency_injection.dart | 4 ++ .../reactter/lib/src/core/event_handler.dart | 8 ++- .../lib/src/core/lifecycle_observer.dart | 26 ++++---- .../lib/src/core/observer_manager.dart | 20 +++++- .../lib/src/core/state_management.dart | 4 ++ packages/reactter/lib/src/framework.dart | 17 ++--- .../lib/src/framework/rt_context_mixin.dart | 23 ------- .../framework/rt_dependency_lifecycle.dart | 64 +++++++++++++++++++ .../src/framework/rt_dependency_observer.dart | 2 +- .../reactter/lib/src/framework/rt_hook.dart | 40 +----------- .../lib/src/framework/rt_interface.dart | 40 ++---------- .../reactter/lib/src/framework/rt_state.dart | 12 ++-- .../lib/src/framework/rt_state_observer.dart | 2 +- .../reactter/lib/src/interfaces/hook.dart | 34 ++++++++-- packages/reactter/lib/src/internals.dart | 4 -- packages/reactter/lib/src/signal/signal.dart | 2 +- .../test/core/lifecycle_observer_test.dart | 4 +- .../test/framework/rt_state_base_test.dart | 4 +- packages/reactter/test/logger_test.dart | 2 +- .../test/shareds/test_controllers.dart | 2 +- .../lib/src/bases/tree_list.dart | 2 +- .../lib/src/bases/tree_node.dart | 2 +- 25 files changed, 172 insertions(+), 152 deletions(-) delete mode 100644 packages/reactter/lib/src/framework/rt_context_mixin.dart create mode 100644 packages/reactter/lib/src/framework/rt_dependency_lifecycle.dart diff --git a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart index 95c931b9..024d9b88 100644 --- a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart +++ b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart @@ -3,7 +3,7 @@ import 'dart:collection'; import 'package:examples/examples/6_tree/states/tree_node.dart'; import 'package:flutter_reactter/reactter.dart'; -class TreeList extends LinkedList<TreeNode> with RtState<TreeList> { +class TreeList extends LinkedList<TreeNode> with RtState { TreeList._(); factory TreeList() { diff --git a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart index 97d21ecc..138d8a99 100644 --- a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart +++ b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart @@ -4,7 +4,7 @@ import 'dart:collection'; import 'package:flutter_reactter/flutter_reactter.dart'; -class TreeNode extends LinkedListEntry<TreeNode> with RtState<TreeNode> { +class TreeNode extends LinkedListEntry<TreeNode> with RtState { /// A unique identifier for each instance of [TreeNode]. static int _lastId = 0; static String _getId() => (_lastId++).toString(); diff --git a/packages/flutter_reactter/test/shareds/test_controller.dart b/packages/flutter_reactter/test/shareds/test_controller.dart index 11678305..dc520a36 100644 --- a/packages/flutter_reactter/test/shareds/test_controller.dart +++ b/packages/flutter_reactter/test/shareds/test_controller.dart @@ -16,7 +16,7 @@ Future<String> _resolveStateAsync([bool throwError = false]) async { return "resolved"; } -class TestController extends LifecycleObserver { +class TestController with RtDependencyLifecycle { final signalString = Signal("initial"); final signalInt = Signal(0); final stateBool = UseState(false); diff --git a/packages/reactter/lib/src/core/dependency_injection.dart b/packages/reactter/lib/src/core/dependency_injection.dart index d144260b..34a140e6 100644 --- a/packages/reactter/lib/src/core/dependency_injection.dart +++ b/packages/reactter/lib/src/core/dependency_injection.dart @@ -14,6 +14,10 @@ part of '../internals.dart'; /// initialized, and destroyed. @internal abstract class DependencyInjection implements IContext { + @override + @internal + DependencyInjection get dependencyInjection => this; + /// It stores the dependencies registered. final _dependencyRegisters = HashSet<DependencyRegister>(); diff --git a/packages/reactter/lib/src/core/event_handler.dart b/packages/reactter/lib/src/core/event_handler.dart index c0e96c79..70f1b1fd 100644 --- a/packages/reactter/lib/src/core/event_handler.dart +++ b/packages/reactter/lib/src/core/event_handler.dart @@ -6,6 +6,10 @@ part of '../internals.dart'; /// as well as storing event callbacks. @internal abstract class EventHandler implements IContext { + @override + @internal + EventHandler get eventHandler => this; + final _notifiers = HashSet<EventNotifier>(); /// Puts on to listen [eventName] event. @@ -153,8 +157,8 @@ abstract class EventHandler implements IContext { ? dependencyInjection.getDependencyRegisterByRef(instance)?.instance : instance; - if (instanceObj is LifecycleObserver) { - _executeLifecycleObserver(instanceObj, lifecycle, param); + if (instanceObj is DependencyLifecycle) { + _processLifecycleCallbacks(instanceObj, lifecycle, param); } } diff --git a/packages/reactter/lib/src/core/lifecycle_observer.dart b/packages/reactter/lib/src/core/lifecycle_observer.dart index f92973d8..8ce2c6c4 100644 --- a/packages/reactter/lib/src/core/lifecycle_observer.dart +++ b/packages/reactter/lib/src/core/lifecycle_observer.dart @@ -1,16 +1,16 @@ part of '../internals.dart'; -/// {@template reactter.lifecycle_observer} +/// {@template reactter.dependency_lifecycle} /// It's a mixin that provides a set of methods that can be used to observe /// the lifecycle of a dependency. /// /// Example usage: /// ```dart -/// class MyController with LifecycleObserver { +/// class MyController with DependencyLifecycle { /// final state = UseState('initial'); /// /// @override -/// void onInitialized() { +/// void onCreated() { /// print('MyController has been initialized'); /// } /// @@ -27,37 +27,37 @@ part of '../internals.dart'; /// ``` /// {@endtemplate} -abstract class LifecycleObserver { +abstract class DependencyLifecycle { /// This method is called when the dependency instance is created. - void onCreated() {} + void onCreated(); /// This method is called when the dependency is going to be mounted /// in the widget tree(exclusive to `flutter_reactter`). - void onWillMount() {} + void onWillMount(); /// This method is called when the dependency has been successfully mounted /// in the widget tree(exclusive to `flutter_reactter`). - void onDidMount() {} + void onDidMount(); /// This method is called when the dependency's state is about to be updated. /// The parameter is a [IState]. - void onWillUpdate(covariant IState? state) {} + void onWillUpdate(covariant IState? state); /// This method is called when the dependency's state has been updated. /// The parameter is a [IState]. - void onDidUpdate(covariant IState? state) {} + void onDidUpdate(covariant IState? state); /// This method is called when the dependency is going to be unmounted /// from the widget tree(exclusive to `flutter_reactter`). - void onWillUnmount() {} + void onWillUnmount(); /// This method is called when the dependency has been successfully unmounted /// from the widget tree(exclusive to `flutter_reactter`). - void onDidUnmount() {} + void onDidUnmount(); } -void _executeLifecycleObserver( - LifecycleObserver observer, +void _processLifecycleCallbacks( + DependencyLifecycle observer, Lifecycle lifecycle, [ dynamic param, ]) { diff --git a/packages/reactter/lib/src/core/observer_manager.dart b/packages/reactter/lib/src/core/observer_manager.dart index 2edaf605..604afd18 100644 --- a/packages/reactter/lib/src/core/observer_manager.dart +++ b/packages/reactter/lib/src/core/observer_manager.dart @@ -5,10 +5,26 @@ abstract class ObserverManager { /// Adds an observer to the manager. /// /// The [observer] parameter is the observer to be added. - void addObserver(covariant IObserver observer); + void addObserver(covariant IObserver observer) { + if (observer is IStateObserver) { + IStateObserver._observers.add(observer); + } + + if (observer is IDependencyObserver) { + IDependencyObserver._observers.add(observer); + } + } /// Removes an observer from the manager. /// /// The [observer] parameter is the observer to be removed. - void removeObserver(covariant IObserver observer); + void removeObserver(IObserver observer) { + if (observer is IStateObserver) { + IStateObserver._observers.remove(observer); + } + + if (observer is IDependencyObserver) { + IDependencyObserver._observers.remove(observer); + } + } } diff --git a/packages/reactter/lib/src/core/state_management.dart b/packages/reactter/lib/src/core/state_management.dart index 926b0824..b048161f 100644 --- a/packages/reactter/lib/src/core/state_management.dart +++ b/packages/reactter/lib/src/core/state_management.dart @@ -2,6 +2,10 @@ part of '../internals.dart'; @internal abstract class StateManagement<S extends IState> implements IContext { + @override + @internal + StateManagement<S> get stateManagement => this; + int _batchRunningCount = 0; bool get _isBatchRunning => _batchRunningCount > 0; diff --git a/packages/reactter/lib/src/framework.dart b/packages/reactter/lib/src/framework.dart index c482e14e..e3e0fbe9 100644 --- a/packages/reactter/lib/src/framework.dart +++ b/packages/reactter/lib/src/framework.dart @@ -1,15 +1,12 @@ import 'package:meta/meta.dart'; + import 'internals.dart'; -export 'internals.dart' - show - DependencyMode, - Lifecycle, - LifecycleObserver, - RtDependencyObserver, - RtHook, - RtInterface, - RtState; +export 'internals.dart' show DependencyMode, Lifecycle, RtState; -part 'framework/rt_context_mixin.dart'; part 'framework/rt_dependency.dart'; +part 'framework/rt_dependency_observer.dart'; +part 'framework/rt_hook.dart'; +part 'framework/rt_interface.dart'; +part 'framework/rt_dependency_lifecycle.dart'; +part 'framework/rt_state_observer.dart'; diff --git a/packages/reactter/lib/src/framework/rt_context_mixin.dart b/packages/reactter/lib/src/framework/rt_context_mixin.dart deleted file mode 100644 index a4377644..00000000 --- a/packages/reactter/lib/src/framework/rt_context_mixin.dart +++ /dev/null @@ -1,23 +0,0 @@ -// ignore_for_file: non_constant_identifier_names - -part of '../framework.dart'; - -/// {@template reactter.rt} -/// This class represents the interface for the Reactter framework. -/// It provides methods and properties for interacting with the Reactter framework. -/// {@endtemplate} -final Rt = RtInterface(); - -mixin RtContextMixin implements IContext { - @override - @internal - DependencyInjection get dependencyInjection => Rt; - - @override - @internal - StateManagement get stateManagement => Rt; - - @override - @internal - EventHandler get eventHandler => Rt; -} diff --git a/packages/reactter/lib/src/framework/rt_dependency_lifecycle.dart b/packages/reactter/lib/src/framework/rt_dependency_lifecycle.dart new file mode 100644 index 00000000..1f2370d5 --- /dev/null +++ b/packages/reactter/lib/src/framework/rt_dependency_lifecycle.dart @@ -0,0 +1,64 @@ +part of '../framework.dart'; + +/// {@template reactter.rt_dependency_lifecycle} +/// It's a mixin that provides a set of methods that can be used to observe +/// the lifecycle of a dependency. +/// +/// Example usage: +/// ```dart +/// class MyController with RtDependencyLifecycle { +/// final state = UseState('initial'); +/// +/// @override +/// void onCreated() { +/// print('MyController has been initialized'); +/// } +/// +/// @override +/// void onDidUpdate(RtState? state) { +/// print("$state has been changed"); +/// } +/// } +/// +/// // MyController has been initialized +/// final myController = Rt.create(() => MyController()); +/// // state has been changed +/// myController.state.value = "value changed"; +/// ``` + +/// {@endtemplate} +abstract class RtDependencyLifecycle implements DependencyLifecycle { + /// This method is called when the dependency instance is created. + @override + void onCreated() {} + + /// This method is called when the dependency is going to be mounted + /// in the widget tree(exclusive to `flutter_reactter`). + @override + void onWillMount() {} + + /// This method is called when the dependency has been successfully mounted + /// in the widget tree(exclusive to `flutter_reactter`). + @override + void onDidMount() {} + + /// This method is called when the dependency's state is about to be updated. + /// The parameter is a [IState]. + @override + void onWillUpdate(covariant IState? state) {} + + /// This method is called when the dependency's state has been updated. + /// The parameter is a [IState]. + @override + void onDidUpdate(covariant IState? state) {} + + /// This method is called when the dependency is going to be unmounted + /// from the widget tree(exclusive to `flutter_reactter`). + @override + void onWillUnmount() {} + + /// This method is called when the dependency has been successfully unmounted + /// from the widget tree(exclusive to `flutter_reactter`). + @override + void onDidUnmount() {} +} diff --git a/packages/reactter/lib/src/framework/rt_dependency_observer.dart b/packages/reactter/lib/src/framework/rt_dependency_observer.dart index 7b44c45a..524220c1 100644 --- a/packages/reactter/lib/src/framework/rt_dependency_observer.dart +++ b/packages/reactter/lib/src/framework/rt_dependency_observer.dart @@ -1,4 +1,4 @@ -part of '../internals.dart'; +part of '../framework.dart'; /// {@template reactter.rt_dependency_observer} /// A class that implements the [IDependencyObserver] interface. diff --git a/packages/reactter/lib/src/framework/rt_hook.dart b/packages/reactter/lib/src/framework/rt_hook.dart index f7c9c075..dfa1ec29 100644 --- a/packages/reactter/lib/src/framework/rt_hook.dart +++ b/packages/reactter/lib/src/framework/rt_hook.dart @@ -1,4 +1,4 @@ -part of '../internals.dart'; +part of '../framework.dart'; /// {@template reactter.rt_hook} /// An abstract-class that provides the functionality of [RtState]. @@ -40,7 +40,7 @@ part of '../internals.dart'; /// See also: /// * [RtState], adds state management features to [RtHook]. /// {@endtemplate} -abstract class RtHook extends RtState<RtHook> implements IHook { +abstract class RtHook extends IHook with RtState { /// {@template reactter.rt_hook.register} /// This getter allows access to the [HookBindingZone] instance /// which is responsible for registering a [RtHook] @@ -48,42 +48,6 @@ abstract class RtHook extends RtState<RtHook> implements IHook { /// {@endtemplate} static get $register => HookBindingZone<RtHook>(); - /// This variable is used to register [RtHook] - /// and attach the [RtState] that are defined here. - @override - @protected - HookBindingZone<RtHook> get $; - - /// Initializes a new instance of the [RtHook] class. - /// - /// This constructor calls the `end` method of the [BindingHookZone] instance - /// to register the hook and attach the collected states. - RtHook() { - initHook(); - $._bindInstanceToStates(this); - } - - /// Initializes the hook. - /// This method is called when the hook is created. - /// You can override this method to ensure that the hook is properly - /// initialized and to bind the instance to the states. - /// This is particularly useful for setting up state dependencies or - /// performing side effects when the hook is first created. - /// - /// For example, you can use the [UseEffect] hook to execute a side effect when a state changes: - /// - /// ```dart - /// @override - /// void initHook() { - /// UseEffect( - /// () { - /// print("Executed by state changed"); - /// }, - /// [state], - /// ); - /// } - void initHook() {} - @override @mustCallSuper void update([Function()? fnUpdate]) { diff --git a/packages/reactter/lib/src/framework/rt_interface.dart b/packages/reactter/lib/src/framework/rt_interface.dart index 36bd9a57..ee205c33 100644 --- a/packages/reactter/lib/src/framework/rt_interface.dart +++ b/packages/reactter/lib/src/framework/rt_interface.dart @@ -1,4 +1,4 @@ -part of '../internals.dart'; +part of '../framework.dart'; ///{@template reactter.rt_interface} /// A class that represents the interface for Rt. @@ -10,36 +10,10 @@ class RtInterface StateManagement<RtState>, DependencyInjection, EventHandler, - ObserverManager { - @override - @internal - StateManagement<RtState> get stateManagement => this; - @override - @internal - DependencyInjection get dependencyInjection => this; - @override - @internal - EventHandler get eventHandler => this; + ObserverManager {} - @override - void addObserver(covariant IObserver observer) { - if (observer is IStateObserver) { - IStateObserver._observers.add(observer); - } - - if (observer is IDependencyObserver) { - IDependencyObserver._observers.add(observer); - } - } - - @override - void removeObserver(covariant IObserver observer) { - if (observer is IStateObserver) { - IStateObserver._observers.remove(observer); - } - - if (observer is IDependencyObserver) { - IDependencyObserver._observers.remove(observer); - } - } -} +/// {@template reactter.rt} +/// This class represents the interface for the Reactter framework. +/// It provides methods and properties for interacting with the Reactter framework. +/// {@endtemplate} +final Rt = RtInterface(); diff --git a/packages/reactter/lib/src/framework/rt_state.dart b/packages/reactter/lib/src/framework/rt_state.dart index 62ba69e8..7756d6ae 100644 --- a/packages/reactter/lib/src/framework/rt_state.dart +++ b/packages/reactter/lib/src/framework/rt_state.dart @@ -23,21 +23,19 @@ part of '../internals.dart'; /// - [Rt.createState], for creating a state object. /// /// {@endtemplate} -abstract class RtState<E extends RtState<E>> implements IState { +abstract class RtState implements IState { /// Debug assertion for registering a state object. /// /// This method is used to assert that a state object is being created within /// a binding zone. If the assertion fails, an [AssertionError] is thrown. - static bool debugAssertRegistering<E>() { + static bool debugAssertRegistering() { assert(() { final currentZone = BindingZone.currentZone; - final isRegistering = currentZone != null && - (currentZone is BindingZone<E> || currentZone is BindingZone<E?>) && - !currentZone.isVerified; + final isRegistering = currentZone != null && !currentZone.isVerified; if (!isRegistering) { throw AssertionError( - "The state($E) must be create within the BindingZone.\n" + "The state must be create within the BindingZone.\n" "You can use the 'Rt.createState' method or register as dependency " "using the dependency injection to ensure that the state is registered.", ); @@ -49,7 +47,7 @@ abstract class RtState<E extends RtState<E>> implements IState { } // ignore: unused_field - final _debugAssertRegistering = debugAssertRegistering<E>(); + final _debugAssertRegistering = debugAssertRegistering(); @override @internal diff --git a/packages/reactter/lib/src/framework/rt_state_observer.dart b/packages/reactter/lib/src/framework/rt_state_observer.dart index 5414f571..31e77bb4 100644 --- a/packages/reactter/lib/src/framework/rt_state_observer.dart +++ b/packages/reactter/lib/src/framework/rt_state_observer.dart @@ -1,4 +1,4 @@ -part of '../internals.dart'; +part of '../framework.dart'; /// {@template reactter.rt_state_observer} /// A class that implements the [IStateObserver] interface. diff --git a/packages/reactter/lib/src/interfaces/hook.dart b/packages/reactter/lib/src/interfaces/hook.dart index b70d310b..ba7d305d 100644 --- a/packages/reactter/lib/src/interfaces/hook.dart +++ b/packages/reactter/lib/src/interfaces/hook.dart @@ -7,13 +7,35 @@ abstract class IHook implements IState { @protected HookBindingZone get $; - /// Executes [callback], and notifies the listeners about the update. + /// Initializes a new instance of the [IHook] class. /// - /// If [callback] is provided, it will be executed before notifying the listeners. - /// If [callback] is not provided, an empty function will be executed. - @override - @mustCallSuper - void update([Function()? callback]); + /// This constructor calls the `end` method of the [BindingHookZone] instance + /// to register the hook and attach the collected states. + IHook() { + initHook(); + $._bindInstanceToStates(this); + } + + /// Initializes the hook. + /// This method is called when the hook is created. + /// You can override this method to ensure that the hook is properly + /// initialized and to bind the instance to the states. + /// This is particularly useful for setting up state dependencies or + /// performing side effects when the hook is first created. + /// + /// For example, you can use the [UseEffect] hook to execute a side effect when a state changes: + /// + /// ```dart + /// @override + /// void initHook() { + /// UseEffect( + /// () { + /// print("Executed by state changed"); + /// }, + /// [state], + /// ); + /// } + void initHook() {} } @internal diff --git a/packages/reactter/lib/src/internals.dart b/packages/reactter/lib/src/internals.dart index eed26336..067d4982 100644 --- a/packages/reactter/lib/src/internals.dart +++ b/packages/reactter/lib/src/internals.dart @@ -15,11 +15,7 @@ part 'core/lifecycle.dart'; part 'core/notifier.dart'; part 'core/observer_manager.dart'; part 'core/state_management.dart'; -part 'framework/rt_dependency_observer.dart'; -part 'framework/rt_hook.dart'; -part 'framework/rt_interface.dart'; part 'framework/rt_state.dart'; -part 'framework/rt_state_observer.dart'; part 'interfaces/context.dart'; part 'interfaces/hook.dart'; part 'interfaces/observer.dart'; diff --git a/packages/reactter/lib/src/signal/signal.dart b/packages/reactter/lib/src/signal/signal.dart index 05a2a213..8280540d 100644 --- a/packages/reactter/lib/src/signal/signal.dart +++ b/packages/reactter/lib/src/signal/signal.dart @@ -86,7 +86,7 @@ enum SignalEvent { onGetValue, onSetValue } /// package on your dependencies and use its Widgets. /// /// {@endtemplate} -class Signal<T> with RtState<Signal<T>> { +class Signal<T> with RtState { bool _shouldGetValueNotify = true; bool _shouldSetValueNotify = true; diff --git a/packages/reactter/test/core/lifecycle_observer_test.dart b/packages/reactter/test/core/lifecycle_observer_test.dart index 7da53afd..cb727617 100644 --- a/packages/reactter/test/core/lifecycle_observer_test.dart +++ b/packages/reactter/test/core/lifecycle_observer_test.dart @@ -4,9 +4,9 @@ import 'package:test/test.dart'; import '../shareds/test_controllers.dart'; void main() { - group("LifecycleObserver", () { + group("DependencyLifecycle", () { test( - "should resolve the lifecycle event of a LifecycleObserver instance", + "should resolve the lifecycle event of a DependencyLifecycle instance", () { late TestLifecycleController? testLifecycleController; diff --git a/packages/reactter/test/framework/rt_state_base_test.dart b/packages/reactter/test/framework/rt_state_base_test.dart index 6b5f1431..2589b01b 100644 --- a/packages/reactter/test/framework/rt_state_base_test.dart +++ b/packages/reactter/test/framework/rt_state_base_test.dart @@ -1,7 +1,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:reactter/reactter.dart'; -class CountTest extends RtState<CountTest> { +class CountTest extends RtState { int _count = 0; int get count => _count; set count(int value) { @@ -21,7 +21,7 @@ class CountTest extends RtState<CountTest> { }; } -class StateTest with RtState<StateTest> { +class StateTest with RtState { StateTest._() { assert(dependencyInjection == Rt); assert(stateManagement == Rt); diff --git a/packages/reactter/test/logger_test.dart b/packages/reactter/test/logger_test.dart index 6c143bf7..b6d5c85b 100644 --- a/packages/reactter/test/logger_test.dart +++ b/packages/reactter/test/logger_test.dart @@ -2,7 +2,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:reactter/reactter.dart'; import 'package:reactter/src/logger.dart'; -class StateTest with RtState<StateTest> { +class StateTest with RtState { StateTest._(); factory StateTest() { diff --git a/packages/reactter/test/shareds/test_controllers.dart b/packages/reactter/test/shareds/test_controllers.dart index dcad7dfa..abc76a4f 100644 --- a/packages/reactter/test/shareds/test_controllers.dart +++ b/packages/reactter/test/shareds/test_controllers.dart @@ -151,7 +151,7 @@ class TestController { // } } -class TestLifecycleController extends LifecycleObserver { +class TestLifecycleController with RtDependencyLifecycle { final stateString = UseState("initial"); final stateInt = UseState(0); diff --git a/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart b/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart index d6c8edd1..3e651376 100644 --- a/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart +++ b/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart @@ -4,7 +4,7 @@ import 'package:flutter_reactter/reactter.dart'; import 'package:reactter_devtools_extension/src/bases/tree_node.dart'; base class TreeList<E extends TreeNode<E>> extends LinkedList<E> - with RtState<TreeList<E>> { + with RtState { final uMaxDepth = UseState(0); TreeList._(); diff --git a/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart b/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart index 717324cd..97a8af07 100644 --- a/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart +++ b/packages/reactter_devtools_extension/lib/src/bases/tree_node.dart @@ -3,7 +3,7 @@ import 'package:flutter_reactter/reactter.dart'; import 'package:reactter_devtools_extension/src/bases/tree_list.dart'; abstract base class TreeNode<E extends TreeNode<E>> extends LinkedListEntry<E> - with RtState<E> { + with RtState { final uChildren = UseState(LinkedHashSet<E>()); final uIsExpanded = UseState(false); final uDepth = UseState(0); From 302e35f42b8c0b65dcb1f32507309383be810b75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Tue, 18 Feb 2025 16:54:45 -0600 Subject: [PATCH 122/141] refactor(website): Replace `LifecycleObserver` to `RtDependencyLifecycle`. --- ...server.mdx => rt_dependency_lifecycle.mdx} | 26 +++++++++---------- .../content/docs/core_concepts/lifecycle.mdx | 22 ++++++++-------- .../docs/es/core_concepts/lifecycle.mdx | 20 +++++++------- .../docs/migration/v7.3.0_to_v8.0.0.mdx | 26 +++++++++++++------ website/src/examples/marks.ts | 4 ++- .../lib/counter.dart | 0 .../lib/counter_controller.dart | 2 +- .../lib/counter_view.dart | 2 +- .../lib/main.dart | 0 .../pubspec.yaml | 4 +-- 10 files changed, 59 insertions(+), 47 deletions(-) rename website/src/content/docs/api/classes/{lifecycle_observer.mdx => rt_dependency_lifecycle.mdx} (65%) rename website/src/examples/{lifecycle_observer => rt_dependency_lifecycle}/lib/counter.dart (100%) rename website/src/examples/{lifecycle_observer => rt_dependency_lifecycle}/lib/counter_controller.dart (93%) rename website/src/examples/{lifecycle_observer => rt_dependency_lifecycle}/lib/counter_view.dart (92%) rename website/src/examples/{lifecycle_observer => rt_dependency_lifecycle}/lib/main.dart (100%) rename website/src/examples/{lifecycle_observer => rt_dependency_lifecycle}/pubspec.yaml (70%) diff --git a/website/src/content/docs/api/classes/lifecycle_observer.mdx b/website/src/content/docs/api/classes/rt_dependency_lifecycle.mdx similarity index 65% rename from website/src/content/docs/api/classes/lifecycle_observer.mdx rename to website/src/content/docs/api/classes/rt_dependency_lifecycle.mdx index 5176ec78..f038ea24 100644 --- a/website/src/content/docs/api/classes/lifecycle_observer.mdx +++ b/website/src/content/docs/api/classes/rt_dependency_lifecycle.mdx @@ -1,5 +1,5 @@ --- -title: LifecycleObserver +title: RtDependencyLifecycle description: The base class for all lifecycle observers in Reactter. sidebar: order: 6 @@ -11,22 +11,22 @@ import ZappButton from "@/components/ZappButton.astro"; import { Code } from "@astrojs/starlight/components"; import { marks } from '@/examples/marks.ts' -import counterControllerLifecycleObserverCode from '@/examples/lifecycle_observer/lib/counter_controller.dart?raw'; -import counterLifecycleObserverCode from '@/examples/lifecycle_observer/lib/counter.dart?raw'; -import counterViewLifecycleObserverCode from '@/examples/lifecycle_observer/lib/counter_view.dart?raw'; -import counterMainLifecycleObserverCode from '@/examples/lifecycle_observer/lib/main.dart?raw'; +import counterControllerRtDependencyLifecycleCode from '@/examples/rt_dependency_lifecycle/lib/counter_controller.dart?raw'; +import counterRtDependencyLifecycleCode from '@/examples/rt_dependency_lifecycle/lib/counter.dart?raw'; +import counterViewRtDependencyLifecycleCode from '@/examples/rt_dependency_lifecycle/lib/counter_view.dart?raw'; +import counterMainRtDependencyLifecycleCode from '@/examples/rt_dependency_lifecycle/lib/main.dart?raw'; :::tip This documentation assumes you've already read the [Lifecycle](/reactter/core_concepts/lifecycle/). It's recommended read that first if you're new in Reactter. ::: -<HT><a href="https://pub.dev/documentation/reactter/latest/reactter/LifecycleObserver-class.html" target="_blank">`LifecycleObserver`</a></HT> is a class that provides a way to observe the lifecycle of a dependency. +<HT><a href="https://pub.dev/documentation/reactter/latest/reactter/RtDependencyLifecycle-class.html" target="_blank">`RtDependencyLifecycle`</a></HT> is a class that provides a way to observe the lifecycle of a dependency. ## Syntax ```dart showLineNumbers=false -abstract class LifecycleObserver { +abstract class RtDependencyLifecycle { void onCreated() {} void onWillMount() {} void onDidMount() {} @@ -58,27 +58,27 @@ Ideal for final cleanup or resource release. ## Example -Here's an example of a simple LifecycleObserver implementation: +Here's an example of a simple RtDependencyLifecycle implementation: <CodeTabs> - <ZappButton path="examples/lifecycle_observer"/> + <ZappButton path="examples/"/> <Tabs> <TabItem> <HM single slot="label">counter_controller.dart</HM> - <Code code={counterControllerLifecycleObserverCode} lang="dart" mark={[...marks, {range: "14-17"}, {range: "19-22"}, {range: "24-27"}, {range: "29-32"}, {range: "34-37"}, {range: "39-42"}, {range: "44-47"}]}/> + <Code code={counterControllerRtDependencyLifecycleCode} lang="dart" mark={[...marks, {range: "14-17"}, {range: "19-22"}, {range: "24-27"}, {range: "29-32"}, {range: "34-37"}, {range: "39-42"}, {range: "44-47"}]}/> </TabItem> <TabItem label="counter_view.dart"> - <Code code={counterViewLifecycleObserverCode} lang="dart" mark={marks} /> + <Code code={counterViewRtDependencyLifecycleCode} lang="dart" mark={marks} /> </TabItem> <TabItem label="counter.dart"> - <Code code={counterLifecycleObserverCode} lang="dart" mark={marks} /> + <Code code={counterRtDependencyLifecycleCode} lang="dart" mark={marks} /> </TabItem> <TabItem label="main.dart"> - <Code code={counterMainLifecycleObserverCode} lang="dart" mark={marks} /> + <Code code={counterMainRtDependencyLifecycleCode} lang="dart" mark={marks} /> </TabItem> </Tabs> </CodeTabs> \ No newline at end of file diff --git a/website/src/content/docs/core_concepts/lifecycle.mdx b/website/src/content/docs/core_concepts/lifecycle.mdx index f6c4003a..48308a71 100644 --- a/website/src/content/docs/core_concepts/lifecycle.mdx +++ b/website/src/content/docs/core_concepts/lifecycle.mdx @@ -19,10 +19,10 @@ import counterEventHandlerCode from '@/examples/lifecycle_event_handler/lib/coun import counterViewEventHandlerCode from '@/examples/lifecycle_event_handler/lib/counter_view.dart?raw'; import counterMainEventHandlerCode from '@/examples/lifecycle_event_handler/lib/main.dart?raw'; -import counterControllerLifecycleObserverCode from '@/examples/lifecycle_observer/lib/counter_controller.dart?raw'; -import counterLifecycleObserverCode from '@/examples/lifecycle_observer/lib/counter.dart?raw'; -import counterViewLifecycleObserverCode from '@/examples/lifecycle_observer/lib/counter_view.dart?raw'; -import counterMainLifecycleObserverCode from '@/examples/lifecycle_observer/lib/main.dart?raw'; +import counterControllerRtDependencyLifecycleCode from '@/examples/rt_dependency_lifecycle/lib/counter_controller.dart?raw'; +import counterRtDependencyLifecycleCode from '@/examples/rt_dependency_lifecycle/lib/counter.dart?raw'; +import counterViewRtDependencyLifecycleCode from '@/examples/rt_dependency_lifecycle/lib/counter_view.dart?raw'; +import counterMainRtDependencyLifecycleCode from '@/examples/rt_dependency_lifecycle/lib/main.dart?raw'; import counterControllerLifecycleUseEffectCode from '@/examples/lifecycle_use_effect/lib/counter_controller.dart?raw'; import counterLifecycleUseEffectCode from '@/examples/lifecycle_use_effect/lib/counter.dart?raw'; @@ -81,32 +81,32 @@ You can listen to the lifecycle events of an instance (like a dependency or stat It ideal to use the <HT>[`RtDependency`](/reactter/api/classes/rt_dependency)</HT> class when the dependency is not initialize yet. ::: -## Using LifecycleObserver +## Using RtDependencyLifecycle -Extend your instances with <HT>[`LifecycleObserver`](/reactter/api/classes/lifecycle_observer)</HT> +Extend your instances with <HT>[`RtDependencyLifecycle`](/reactter/api/classes/)</HT> and use its methods to observe the lifecycle events. e.g: <TipLifecycleExample /> <CodeTabs> - <ZappButton path="examples/lifecycle_observer"/> + <ZappButton path="examples/"/> <Tabs> <TabItem> <HM single slot="label">counter_controller.dart</HM> - <Code code={counterControllerLifecycleObserverCode} lang="dart" mark={[...marks, {range: "14-17"}, {range: "19-22"}, {range: "24-27"}, {range: "29-32"}, {range: "34-37"}, {range: "39-42"}, {range: "44-47"}]}/> + <Code code={counterControllerRtDependencyLifecycleCode} lang="dart" mark={[...marks, {range: "14-17"}, {range: "19-22"}, {range: "24-27"}, {range: "29-32"}, {range: "34-37"}, {range: "39-42"}, {range: "44-47"}]}/> </TabItem> <TabItem label="counter_view.dart"> - <Code code={counterViewLifecycleObserverCode} lang="dart" mark={marks} /> + <Code code={counterViewRtDependencyLifecycleCode} lang="dart" mark={marks} /> </TabItem> <TabItem label="counter.dart"> - <Code code={counterLifecycleObserverCode} lang="dart" mark={marks} /> + <Code code={counterRtDependencyLifecycleCode} lang="dart" mark={marks} /> </TabItem> <TabItem label="main.dart"> - <Code code={counterMainLifecycleObserverCode} lang="dart" mark={marks} /> + <Code code={counterMainRtDependencyLifecycleCode} lang="dart" mark={marks} /> </TabItem> </Tabs> </CodeTabs> diff --git a/website/src/content/docs/es/core_concepts/lifecycle.mdx b/website/src/content/docs/es/core_concepts/lifecycle.mdx index b31521e4..29e1176a 100644 --- a/website/src/content/docs/es/core_concepts/lifecycle.mdx +++ b/website/src/content/docs/es/core_concepts/lifecycle.mdx @@ -19,10 +19,10 @@ import counterEventHandlerCode from '@/examples/lifecycle_event_handler/lib/coun import counterViewEventHandlerCode from '@/examples/lifecycle_event_handler/lib/counter_view.dart?raw'; import counterMainEventHandlerCode from '@/examples/lifecycle_event_handler/lib/main.dart?raw'; -import counterControllerLifecycleObserverCode from '@/examples/lifecycle_observer/lib/counter_controller.dart?raw'; -import counterLifecycleObserverCode from '@/examples/lifecycle_observer/lib/counter.dart?raw'; -import counterViewLifecycleObserverCode from '@/examples/lifecycle_observer/lib/counter_view.dart?raw'; -import counterMainLifecycleObserverCode from '@/examples/lifecycle_observer/lib/main.dart?raw'; +import counterControllerRtDependencyLifecycleCode from '@/examples/rt_dependency_lifecycle/lib/counter_controller.dart?raw'; +import counterRtDependencyLifecycleCode from '@/examples/rt_dependency_lifecycle/lib/counter.dart?raw'; +import counterViewRtDependencyLifecycleCode from '@/examples/rt_dependency_lifecycle/lib/counter_view.dart?raw'; +import counterMainRtDependencyLifecycleCode from '@/examples/rt_dependency_lifecycle/lib/main.dart?raw'; import counterControllerLifecycleUseEffectCode from '@/examples/lifecycle_use_effect/lib/counter_controller.dart?raw'; import counterLifecycleUseEffectCode from '@/examples/lifecycle_use_effect/lib/counter.dart?raw'; @@ -82,9 +82,9 @@ Puedes escuchar los eventos del ciclo de vida de una instancia (como una depende Es ideal usar la clase <HT>[`RtDependency`](/reactter/es/api/classes/rt_dependency)</HT> cuando la dependencia aún no se ha inicializado. ::: -## Usando LifecycleObserver +## Usando RtDependencyLifecycle -Extiende tus instancias con <HT>[`LifecycleObserver`](/reactter/es/api/classes/lifecycle_observer)</HT> para escuchar los eventos del ciclo de vida de una dependencia o estado. Por ejemplo: +Extiende tus instancias con <HT>[`RtDependencyLifecycle`](/reactter/es/api/classes/lifecycle_observer)</HT> para escuchar los eventos del ciclo de vida de una dependencia o estado. Por ejemplo: <TipLifecycleExample /> @@ -94,19 +94,19 @@ Extiende tus instancias con <HT>[`LifecycleObserver`](/reactter/es/api/classes/l <Tabs> <TabItem> <HM single slot="label">counter_controller.dart</HM> - <Code code={counterControllerLifecycleObserverCode} lang="dart" mark={[...marks, {range: "6-8"}, {range: "10-12"}, {range: "14-16"}, {range: "18-20"}, {range: "22-24"}, {range: "26-28"}, {range: "30-32"}]}/> + <Code code={counterControllerRtDependencyLifecycleCode} lang="dart" mark={[...marks, {range: "6-8"}, {range: "10-12"}, {range: "14-16"}, {range: "18-20"}, {range: "22-24"}, {range: "26-28"}, {range: "30-32"}]}/> </TabItem> <TabItem label="counter_view.dart"> - <Code code={counterViewLifecycleObserverCode} lang="dart" mark={marks} /> + <Code code={counterViewRtDependencyLifecycleCode} lang="dart" mark={marks} /> </TabItem> <TabItem label="counter.dart"> - <Code code={counterLifecycleObserverCode} lang="dart" mark={marks} /> + <Code code={counterRtDependencyLifecycleCode} lang="dart" mark={marks} /> </TabItem> <TabItem label="main.dart"> - <Code code={counterMainLifecycleObserverCode} lang="dart" mark={marks} /> + <Code code={counterMainRtDependencyLifecycleCode} lang="dart" mark={marks} /> </TabItem> </Tabs> </CodeTabs> diff --git a/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx b/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx index 7d9f7214..0729e49c 100644 --- a/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx +++ b/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx @@ -41,12 +41,13 @@ If you encounter any issues, refer to the [official documentation](https://pub.d - Replace [`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html) to [`UseAsyncStateStatus.idle`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseAsyncStateStatus.html). - Rename [`DispatchEffect`](https://pub.dev/documentation/reactter/7.3.0/reactter/DispatchEffect-class.html) to [`AutoDispatchEffect`](https://pub.dev/documentation/reactter/8.0.0/reactter/DispatchEffect-class.html). - Move `asyncFunction` to the first parameter of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseAsyncState/withArg.html). -- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/isActive.html) instead. -- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/getDependencyMode.html) instead. -- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0/reactter/DependencyMode.html) instead. -- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0/reactter/Lifecycle.html) instead. -- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0/reactter/Lifecycle.html) instead. -- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`LifecycleObserver.onCreated`](https://pub.dev/documentation/reactter/8.0.0//reactter/LifecycleObserver/onCreated.html) instead. +- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/isActive.html) instead. +- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/getDependencyMode.html) instead. +- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0/reactter/DependencyMode.html) instead. +- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0/reactter/Lifecycle.html) instead. +- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0/reactter/Lifecycle.html) instead. +- Replace [`LifecycleObserver`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver-class.html) to [`RtDependencyLifecycle`](https://pub.dev/documentation/reactter/8.0.0-dev.25/reactter/RtDependencyLifecycle-class.html). +- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`RtDependencyLifecycle.onCreated`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtDependencyLifecycle/onCreated.html) instead. - Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtDependency-class.html) instead. - Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtHook-class.html) instead. - Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtInterface-class.html) instead. @@ -159,12 +160,21 @@ If you encounter any issues, refer to the [official documentation](https://pub.d Lifecycle.destroyed; Lifecycle.deleted; ``` +- Replace <HM>`LifecycleObserver`</HM> with <HM>`RtDependencyLifecycle`</HM>. -- Replace <HM>`LifecycleObserver.onInitialized`</HM> with <HM>`LifecycleObserver.onCreated`</HM>. + ```dart showLineNumbers=false del={1} del="LifecycleObserver" ins={2} ins="RtDependencyLifecycle" + class MyClass extends LifecycleObserver { + class MyClass extends RtDependencyLifecycle { + // Implementation + } + ``` + +- Replace <HM>`LifecycleObserver.onInitialized`</HM> with <HM>`RtDependencyLifecycle.onCreated`</HM>. - ```dart showLineNumbers=false del={2} del="onInitialized" ins={3} ins="onCreated" + ```dart showLineNumbers=false del={1,2} del="LifecycleObserver" del="onInitialized" ins={3,4} ins="RtDependencyLifecycle" ins="onCreated" class MyClass extends LifecycleObserver { void onInitialized() { + class MyClass extends RtDependencyLifecycle { void onCreated() { // Implementation } diff --git a/website/src/examples/marks.ts b/website/src/examples/marks.ts index 6deaa4b1..f822cb88 100644 --- a/website/src/examples/marks.ts +++ b/website/src/examples/marks.ts @@ -27,7 +27,10 @@ export const marks = [ "RtHook", "RtContextMixin", "RtState", + "RtStateObserver", "RtDependency", + "RtDependencyLifecycle", + 'RtDependencyObserver', "RtDependencyMode", "RtDependencyMode.builder", "RtDependencyMode.factory", @@ -51,7 +54,6 @@ export const marks = [ "UseCompute", "UseEffect", "UseDependency", - "LifecycleObserver", "Lifecycle", "Lifecycle.registered", "Lifecycle.created", diff --git a/website/src/examples/lifecycle_observer/lib/counter.dart b/website/src/examples/rt_dependency_lifecycle/lib/counter.dart similarity index 100% rename from website/src/examples/lifecycle_observer/lib/counter.dart rename to website/src/examples/rt_dependency_lifecycle/lib/counter.dart diff --git a/website/src/examples/lifecycle_observer/lib/counter_controller.dart b/website/src/examples/rt_dependency_lifecycle/lib/counter_controller.dart similarity index 93% rename from website/src/examples/lifecycle_observer/lib/counter_controller.dart rename to website/src/examples/rt_dependency_lifecycle/lib/counter_controller.dart index b9718ade..a45d8b12 100644 --- a/website/src/examples/lifecycle_observer/lib/counter_controller.dart +++ b/website/src/examples/rt_dependency_lifecycle/lib/counter_controller.dart @@ -1,6 +1,6 @@ import 'package:flutter_reactter/flutter_reactter.dart'; -class CounterController extends LifecycleObserver { +class CounterController extends RtDependencyLifecycle { final count = Signal(0); void increment() { diff --git a/website/src/examples/lifecycle_observer/lib/counter_view.dart b/website/src/examples/rt_dependency_lifecycle/lib/counter_view.dart similarity index 92% rename from website/src/examples/lifecycle_observer/lib/counter_view.dart rename to website/src/examples/rt_dependency_lifecycle/lib/counter_view.dart index af3cdd9b..b0b00c15 100644 --- a/website/src/examples/lifecycle_observer/lib/counter_view.dart +++ b/website/src/examples/rt_dependency_lifecycle/lib/counter_view.dart @@ -11,7 +11,7 @@ class CounterView extends StatelessWidget { return Scaffold( appBar: AppBar( - title: const Text("Counter - Lifecycle using LifecycleObserver"), + title: const Text("Counter - Lifecycle using RtDependencyLifecycle"), ), body: RtSignalWatcher( builder: (context, child) { diff --git a/website/src/examples/lifecycle_observer/lib/main.dart b/website/src/examples/rt_dependency_lifecycle/lib/main.dart similarity index 100% rename from website/src/examples/lifecycle_observer/lib/main.dart rename to website/src/examples/rt_dependency_lifecycle/lib/main.dart diff --git a/website/src/examples/lifecycle_observer/pubspec.yaml b/website/src/examples/rt_dependency_lifecycle/pubspec.yaml similarity index 70% rename from website/src/examples/lifecycle_observer/pubspec.yaml rename to website/src/examples/rt_dependency_lifecycle/pubspec.yaml index 0fb844dd..7d5c9081 100644 --- a/website/src/examples/lifecycle_observer/pubspec.yaml +++ b/website/src/examples/rt_dependency_lifecycle/pubspec.yaml @@ -1,5 +1,5 @@ -name: LifecycleObserver -description: LifecycleObserver example +name: RtDependencyLifecycle +description: RtDependencyLifecycle example version: 0.1.0 environment: From 7860e5f2d076fd5c730bcce92f1866dbbcde68fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Wed, 19 Feb 2025 00:03:09 -0600 Subject: [PATCH 123/141] refactor(website): Update `RtStateObserver` documentation. --- .../docs/api/classes/rt_state_observer.mdx | 60 ++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/website/src/content/docs/api/classes/rt_state_observer.mdx b/website/src/content/docs/api/classes/rt_state_observer.mdx index b80e2adb..139d4ab3 100644 --- a/website/src/content/docs/api/classes/rt_state_observer.mdx +++ b/website/src/content/docs/api/classes/rt_state_observer.mdx @@ -1,6 +1,62 @@ --- -title: RtStateObserver 🚧 +title: RtStateObserver description: The base class for all state observers in Reactter. sidebar: order: 2 ---- \ No newline at end of file +--- + +import { HM, HT } from '@/components/Highlight'; + +<HT><a href="https://pub.dev/documentation/reactter/latest/reactter/RtStateObserver-class.html" target="_blank">`RtStateObserver`</a></HT> is a class that provides a way to observe the lifecycle of states in Reactter. + +## Syntax + +```dart showLineNumbers=false + RtStateObserver({ + void onBound(RtState state, Object instance), + void onUnbound(RtState state, Object instance),, + void onCreated(RtState state), + void onUpdated(RtState state), + void onDisponsed(RtState state), + }); +``` +## Properties + +- <HM>**`onBound`**</HM>: A callback that is called when a state is bound to an instance. +- <HM>**`onUnbound`**</HM>: A callback that is called when a state is unbound from an instance. +- <HM>**`onCreated`**</HM>: A callback that is called when a state is created. +- <HM>**`onUpdated`**</HM>: A callback that is called when a state is updated. +- <HM>**`onDisposed`**</HM>: A callback that is called when a state is disposed. + +## Usage + +To use <HT>`RtStateObserver`</HT>, you need to instantiate it, pass the callbacks you want to use and then add it using the <HM>`Rt.addObserver`</HM> method. + +```dart "RtStateObserver" "Rt.addObserver" +import 'package:reactter/reactter.dart'; + +void main() { + // Create a state observer + final stateObserver = RtStateObserver( + onBound: (state, instance) { + print('State($state) bound to instance($instance)'); + }, + onUnbound: (state, instance) { + print('State($state) unbound from instance($instance)'); + }, + onCreated: (state) { + print('State($state) created'); + }, + onUpdated: (state) { + print('State($state) updated'); + }, + onDisposed: (state) { + print('State($state) disposed'); + }, + ); + + // Add the state observer + Rt.addObserver(stateObserver); +} + +``` From 8c395cce05a8464f83ce921c9eab968c51fa3d2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Wed, 19 Feb 2025 00:15:24 -0600 Subject: [PATCH 124/141] refactor(website): Update `RtStateObserver` callbacks documentation. --- .../docs/api/classes/rt_state_observer.mdx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/website/src/content/docs/api/classes/rt_state_observer.mdx b/website/src/content/docs/api/classes/rt_state_observer.mdx index 139d4ab3..ac63714f 100644 --- a/website/src/content/docs/api/classes/rt_state_observer.mdx +++ b/website/src/content/docs/api/classes/rt_state_observer.mdx @@ -13,20 +13,20 @@ import { HM, HT } from '@/components/Highlight'; ```dart showLineNumbers=false RtStateObserver({ - void onBound(RtState state, Object instance), - void onUnbound(RtState state, Object instance),, - void onCreated(RtState state), - void onUpdated(RtState state), - void onDisponsed(RtState state), + void onBound(RtState state, Object instance)?, + void onUnbound(RtState state, Object instance)?, + void onCreated(RtState state)?, + void onUpdated(RtState state)?, + void onDisponsed(RtState state)?, }); ``` ## Properties -- <HM>**`onBound`**</HM>: A callback that is called when a state is bound to an instance. -- <HM>**`onUnbound`**</HM>: A callback that is called when a state is unbound from an instance. -- <HM>**`onCreated`**</HM>: A callback that is called when a state is created. -- <HM>**`onUpdated`**</HM>: A callback that is called when a state is updated. -- <HM>**`onDisposed`**</HM>: A callback that is called when a state is disposed. +- <HM>**`onBound`**</HM>: *(optional)* A callback that is called when a `state` is bound to an `instance`. +- <HM>**`onUnbound`**</HM>: *(optional)* A callback that is called when a `state` is unbound from an `instance`. +- <HM>**`onCreated`**</HM>: *(optional)* A callback that is called when a `state` is created. +- <HM>**`onUpdated`**</HM>: *(optional)* A callback that is called when a `state` is updated. +- <HM>**`onDisposed`**</HM>: *(optional)* A callback that is called when a `state` is disposed. ## Usage From 0f2421b94a3fa34863be838f9503f807cb2c066a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Wed, 19 Feb 2025 01:09:51 -0600 Subject: [PATCH 125/141] refactor(framework, interfaces): Simplify `DependencyFail` enum values and update related usages for clarity. --- .../lib/src/core/dependency_injection.dart | 4 ++-- .../lib/src/framework/rt_dependency_observer.dart | 14 +++++++------- packages/reactter/lib/src/interfaces/observer.dart | 4 ++-- packages/reactter/lib/src/logger.dart | 6 +++--- .../framework/rt_dependency_observer_test.dart | 5 +++-- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/packages/reactter/lib/src/core/dependency_injection.dart b/packages/reactter/lib/src/core/dependency_injection.dart index 34a140e6..ab4cf543 100644 --- a/packages/reactter/lib/src/core/dependency_injection.dart +++ b/packages/reactter/lib/src/core/dependency_injection.dart @@ -339,14 +339,14 @@ abstract class DependencyInjection implements IContext { _notifyDependencyFailed( dependencyRegister, - DependencyFail.builderRetainedAsFactory, + DependencyFail.builderRetained, ); return true; case DependencyMode.singleton: _notifyDependencyFailed( dependencyRegister, - DependencyFail.dependencyRetainedAsSingleton, + DependencyFail.dependencyRetained, ); } diff --git a/packages/reactter/lib/src/framework/rt_dependency_observer.dart b/packages/reactter/lib/src/framework/rt_dependency_observer.dart index 524220c1..754e83ca 100644 --- a/packages/reactter/lib/src/framework/rt_dependency_observer.dart +++ b/packages/reactter/lib/src/framework/rt_dependency_observer.dart @@ -42,25 +42,25 @@ part of '../framework.dart'; /// {@endtemplate} class RtDependencyObserver implements IDependencyObserver { /// {@macro reactter.i_dependency_observer.on_dependency_registered} - final void Function(DependencyRef)? onRegistered; + final void Function(DependencyRef dependency)? onRegistered; /// {@macro reactter.i_dependency_observer.on_dependency_created} - final void Function(DependencyRef, Object?)? onCreated; + final void Function(DependencyRef dependency, Object? instance)? onCreated; /// {@macro reactter.i_dependency_observer.on_dependency_mounted} - final void Function(DependencyRef, Object?)? onMounted; + final void Function(DependencyRef dependency, Object? instance)? onMounted; /// {@macro reactter.i_dependency_observer.on_dependency_unmounted} - final void Function(DependencyRef, Object?)? onUnmounted; + final void Function(DependencyRef dependency, Object? instance)? onUnmounted; /// {@macro reactter.i_dependency_observer.on_dependency_deleted} - final void Function(DependencyRef, Object?)? onDeleted; + final void Function(DependencyRef dependency, Object? instance)? onDeleted; /// {@macro reactter.i_dependency_observer.on_dependency_unregistered} - final void Function(DependencyRef)? onUnregistered; + final void Function(DependencyRef dependency)? onUnregistered; /// {@macro reactter.i_dependency_observer.on_dependency_failed} - final void Function(DependencyRef, DependencyFail)? onFailed; + final void Function(DependencyRef dependency, DependencyFail fail)? onFailed; RtDependencyObserver({ this.onRegistered, diff --git a/packages/reactter/lib/src/interfaces/observer.dart b/packages/reactter/lib/src/interfaces/observer.dart index 0aea99b5..5a6f4396 100644 --- a/packages/reactter/lib/src/interfaces/observer.dart +++ b/packages/reactter/lib/src/interfaces/observer.dart @@ -146,7 +146,7 @@ enum DependencyFail { alreadyDeleted, alreadyUnregistered, missingInstanceBuilder, - builderRetainedAsFactory, - dependencyRetainedAsSingleton, + builderRetained, + dependencyRetained, cannotUnregisterActiveInstance, } diff --git a/packages/reactter/lib/src/logger.dart b/packages/reactter/lib/src/logger.dart index 9bd743d2..87f8a0f6 100644 --- a/packages/reactter/lib/src/logger.dart +++ b/packages/reactter/lib/src/logger.dart @@ -217,14 +217,14 @@ class RtLogger implements IStateObserver, IDependencyObserver { stackTrace: StackTrace.current, ); break; - case DependencyFail.builderRetainedAsFactory: + case DependencyFail.builderRetained: log( "${prettyFormat(dependency)}'s instance retained because it's factory mode.", level: LogLevel.info, stackTrace: StackTrace.current, ); break; - case DependencyFail.dependencyRetainedAsSingleton: + case DependencyFail.dependencyRetained: log( "${prettyFormat(dependency)} retained because it's singleton mode.", level: LogLevel.info, @@ -233,7 +233,7 @@ class RtLogger implements IStateObserver, IDependencyObserver { break; case DependencyFail.missingInstanceBuilder: log( - "${prettyFormat(dependency)} couldn't register.\n" + "${prettyFormat(dependency)} builder was not registered previously.\n" "You should register the instance build with: \n" "\t`Rt.register<$T>(() => $T(...)$idParam);`\n" "\t`Rt.create<$T>(() => $T(...)$idParam);`", diff --git a/packages/reactter/test/framework/rt_dependency_observer_test.dart b/packages/reactter/test/framework/rt_dependency_observer_test.dart index b00b6766..818596f1 100644 --- a/packages/reactter/test/framework/rt_dependency_observer_test.dart +++ b/packages/reactter/test/framework/rt_dependency_observer_test.dart @@ -3,6 +3,7 @@ import 'package:reactter/reactter.dart'; import 'package:reactter/src/internals.dart'; import '../shareds/test_controllers.dart'; + void main() { group("RtDependencyObserver", () { test("should be observed when a dependency is registered", () { @@ -183,14 +184,14 @@ void main() { Rt.delete<TestController>(); expect(onFailedCalledCount, 7); - expect(lastFail, DependencyFail.builderRetainedAsFactory); + expect(lastFail, DependencyFail.builderRetained); Rt.destroy<TestController>(); Rt.create(() => TestController(), mode: DependencyMode.singleton); Rt.delete<TestController>(); expect(onFailedCalledCount, 8); - expect(lastFail, DependencyFail.dependencyRetainedAsSingleton); + expect(lastFail, DependencyFail.dependencyRetained); Rt.unregister<TestController>(); From 52bace7b44587b1e218bcbe5d90b453fe2ad875f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Wed, 19 Feb 2025 01:10:10 -0600 Subject: [PATCH 126/141] refactor(website): Simplify builder function signatures in `UseDependency` documentation for clarity. --- .../content/docs/api/hooks/use_dependency.mdx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/website/src/content/docs/api/hooks/use_dependency.mdx b/website/src/content/docs/api/hooks/use_dependency.mdx index fce24541..57139387 100644 --- a/website/src/content/docs/api/hooks/use_dependency.mdx +++ b/website/src/content/docs/api/hooks/use_dependency.mdx @@ -42,7 +42,7 @@ It's recommended read that first if you're new in Reactter. ```dart showLineNumbers=false UseDependency<T>.register( - T Function() builder, { + T builder(), { String? id, DependencyMode mode = DependencyMode.builder, String? debugLabel, @@ -54,7 +54,7 @@ It's recommended read that first if you're new in Reactter. ```dart showLineNumbers=false UseDependency<T>.create( - T Function() builder, { + T builder(), { String? id, DependencyMode mode = DependencyMode.builder, String? debugLabel, @@ -66,7 +66,7 @@ It's recommended read that first if you're new in Reactter. ```dart showLineNumbers=false UseDependency<T>.lazyBuilder( - T Function() builder, { + T builder(), { String? id, String? debugLabel }, @@ -77,7 +77,7 @@ It's recommended read that first if you're new in Reactter. ```dart showLineNumbers=false UseDependency<T>.lazyFactory( - T Function() builder, { + T builder(), { String? id, String? debugLabel }, @@ -88,7 +88,7 @@ It's recommended read that first if you're new in Reactter. ```dart showLineNumbers=false UseDependency<T>.lazySingleton( - T Function() builder, { + T builder(), { String? id, String? debugLabel }, @@ -99,7 +99,7 @@ It's recommended read that first if you're new in Reactter. ```dart showLineNumbers=false UseDependency<T>.builder( - T Function() builder, { + T builder(), { String? id, DependencyMode mode = DependencyMode.builder, String? debugLabel, @@ -111,7 +111,7 @@ It's recommended read that first if you're new in Reactter. ```dart showLineNumbers=false UseDependency<T>.factory( - T Function() builder, { + T builder(), { String? id, DependencyMode mode = DependencyMode.builder, String? debugLabel, @@ -123,7 +123,7 @@ It's recommended read that first if you're new in Reactter. ```dart showLineNumbers=false UseDependency<T>.singleton( - T Function() builder, { + T builder(), { String? id, DependencyMode mode = DependencyMode.builder, String? debugLabel, From aa1f347fe5568de1b5d0b7db4d848e1a0f1a8f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Wed, 19 Feb 2025 01:21:59 -0600 Subject: [PATCH 127/141] refactor(website): Add `RtDependencyObserver` documentation. --- .../api/classes/rt_dependency_observer.mdx | 80 ++++++++++++++++++- 1 file changed, 77 insertions(+), 3 deletions(-) diff --git a/website/src/content/docs/api/classes/rt_dependency_observer.mdx b/website/src/content/docs/api/classes/rt_dependency_observer.mdx index 8c47fde4..b366675e 100644 --- a/website/src/content/docs/api/classes/rt_dependency_observer.mdx +++ b/website/src/content/docs/api/classes/rt_dependency_observer.mdx @@ -1,6 +1,80 @@ --- -title: RtDependencyObserver 🚧 -description: The base class for all dependency observers in Reactter. +title: RtDependencyObserver +description: The base class for observing and reacting to the lifecycle events of dependencies in Reactter, such as registration, creation, mounting, and deletion. sidebar: order: 5 ---- \ No newline at end of file +--- + +import { HE, HM, HT } from '@/components/Highlight'; + +<HT><a href="https://pub.dev/documentation/reactter/latest/reactter/RtDependencyObserver-class.html" target="_blank">`RtDependencyObserver`</a></HT> is a class that provides a way to observe the lifecycle of dependencies in Reactter. + +## Syntax + +```dart showLineNumbers=false + RtDependencyObserver({ + void onRegistered(DependencyRef dependency)?, + void onCreated(DependencyRef dependency, Object? instance)?, + void onMounted(DependencyRef dependency, Object? instance)?, + void onUnmounted(DependencyRef dependency, Object? instance)?, + void onDeleted(DependencyRef dependency, Object? instance)?, + void onUnregistered(DependencyRef dependency)?, + void onFailed(DependencyRef dependency, DependencyFail fail)?, + }); +``` + +The <HT>`RtDependencyObserver`</HT> class accepts these parameters: + +- <HM>**`onRegistered`**</HM>: *(optional)* Called when a `dependency` is registered. +- <HM>**`onCreated`**</HM>: *(optional)* Called when a `instance` of a `dependency` is created. +- <HM>**`onMounted`**</HM>: *(optional)* Called when the `instance` of a `dependency` is mounted. +- <HM>**`onUnmounted`**</HM>: *(optional)* Called when the `instance` of a `dependency` is unmounted. +- <HM>**`onDeleted`**</HM>: *(optional)* Called when the `instance` of a `dependency` is deleted. +- <HM>**`onUnregistered`**</HM>: *(optional)* Called when a `dependency` is unregistered. +- <HM>**`onFailed`**</HM>: *(optional)* Called when a `dependency` fails, indicating one of the following failures: + - <HE>**`DependencyFail.alreadyRegistered`**</HE>: The dependency is already registered. + - <HE>**`DependencyFail.alreadyCreated`**</HE>: An instance of dependency is already created. + - <HE>**`DependencyFail.alreadyDeleted`**</HE>: The instance of dependency is already deleted. + - <HE>**`DependencyFail.alreadyUnregistered`**</HE>: The dependency is already unregistered. + - <HE>**`DependencyFail.missingInstanceBuilder`**</HE>: The dependency was not registered previously. + - <HE>**`DependencyFail.builderRetained`**</HE>: The builder is retained because is in factory mode. + - <HE>**`DependencyFail.dependencyRetained`**</HE>: The builder and instance is retained because is in singleton mode. + - <HE>**`DependencyFail.cannotUnregisterActiveInstance`**</HE>: Cannot unregister the dependency because it has an active instance. + +## Usage + +To use <HT>`RtDependencyObserver`</HT>, you need to instantiate it, pass the callbacks you want to use, and then add it using the <HM>`Rt.addObserver`</HM> method, e.g.: + +```dart "RtDependencyObserver" "Rt.addObserver" +import 'package:reactter/reactter.dart'; + +void main() { + // Create a dependency observer with lifecycle callbacks + final dependencyObserver = RtDependencyObserver( + onRegistered: (dependency) { + print('Dependency($dependency) registered'); + }, + onCreated: (dependency, instance) { + print('Dependency($dependency) created with instance($instance)'); + }, + onMounted: (dependency, instance) { + print('Dependency($dependency) mounted with instance($instance)'); + }, + onUnmounted: (dependency, instance) { + print('Dependency($dependency) unmounted with instance($instance)'); + }, + onDeleted: (dependency, instance) { + print('Dependency($dependency) deleted with instance($instance)'); + }, + onUnregistered: (dependency) { + print('Dependency($dependency) unregistered'); + }, + onFailed: (dependency, fail) { + print('Dependency($dependency) failed with $fail'); + }, + ); + + // Add the dependency observer to Reactter's observer system + Rt.addObserver(dependencyObserver); +} +``` \ No newline at end of file From b8e64741b640e23a3627d175d4e459b961baad04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Wed, 19 Feb 2025 01:22:48 -0600 Subject: [PATCH 128/141] refactor(framework, website): Correct spelling of 'disposed' in method names and documentation for clarity. --- .../reactter/lib/src/framework/rt_state.dart | 4 ++-- .../lib/src/framework/rt_state_observer.dart | 6 ++--- .../framework/rt_state_observer_test.dart | 2 +- .../docs/api/classes/rt_state_observer.mdx | 24 +++++++++---------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/reactter/lib/src/framework/rt_state.dart b/packages/reactter/lib/src/framework/rt_state.dart index 7756d6ae..13d173f0 100644 --- a/packages/reactter/lib/src/framework/rt_state.dart +++ b/packages/reactter/lib/src/framework/rt_state.dart @@ -189,7 +189,7 @@ abstract class RtState implements IState { if (_isDisposed) return; - _notifyDisponsed(); + _notifyDisposed(); _isDisposed = true; } @@ -248,7 +248,7 @@ abstract class RtState implements IState { } } - void _notifyDisponsed() { + void _notifyDisposed() { for (final observer in IStateObserver._observers.toList(growable: false)) { observer.onStateDisposed(this); } diff --git a/packages/reactter/lib/src/framework/rt_state_observer.dart b/packages/reactter/lib/src/framework/rt_state_observer.dart index 31e77bb4..8f96c03b 100644 --- a/packages/reactter/lib/src/framework/rt_state_observer.dart +++ b/packages/reactter/lib/src/framework/rt_state_observer.dart @@ -43,7 +43,7 @@ class RtStateObserver implements IStateObserver { final void Function(RtState state)? onCreated; /// {@macro reactter.i_state_observer.on_state_mounted} - final void Function(RtState state)? onDisponsed; + final void Function(RtState state)? onDisposed; /// {@macro reactter.i_state_observer.on_state_unbound} final void Function(RtState state, Object instance)? onUnbound; @@ -54,7 +54,7 @@ class RtStateObserver implements IStateObserver { RtStateObserver({ this.onBound, this.onCreated, - this.onDisponsed, + this.onDisposed, this.onUnbound, this.onUpdated, }); @@ -74,7 +74,7 @@ class RtStateObserver implements IStateObserver { /// {@macro reactter.i_state_observer.on_state_mounted} @override void onStateDisposed(RtState state) { - onDisponsed?.call(state); + onDisposed?.call(state); } /// {@macro reactter.i_state_observer.on_state_unbound} diff --git a/packages/reactter/test/framework/rt_state_observer_test.dart b/packages/reactter/test/framework/rt_state_observer_test.dart index addd5f80..3da9652a 100644 --- a/packages/reactter/test/framework/rt_state_observer_test.dart +++ b/packages/reactter/test/framework/rt_state_observer_test.dart @@ -133,7 +133,7 @@ void main() { String? lastStateDisposed; final observer = RtStateObserver( - onDisponsed: (state) { + onDisposed: (state) { expect(state, isA<RtState>()); onDisposedCalledCount++; lastStateDisposed = state.debugLabel; diff --git a/website/src/content/docs/api/classes/rt_state_observer.mdx b/website/src/content/docs/api/classes/rt_state_observer.mdx index ac63714f..9ad15424 100644 --- a/website/src/content/docs/api/classes/rt_state_observer.mdx +++ b/website/src/content/docs/api/classes/rt_state_observer.mdx @@ -1,6 +1,6 @@ --- title: RtStateObserver -description: The base class for all state observers in Reactter. +description: The base class for observing and reacting to the lifecycle events of states in Reactter, such as creation, binding, updating, and disposal. sidebar: order: 2 --- @@ -17,26 +17,27 @@ import { HM, HT } from '@/components/Highlight'; void onUnbound(RtState state, Object instance)?, void onCreated(RtState state)?, void onUpdated(RtState state)?, - void onDisponsed(RtState state)?, + void onDisposed(RtState state)?, }); ``` -## Properties -- <HM>**`onBound`**</HM>: *(optional)* A callback that is called when a `state` is bound to an `instance`. -- <HM>**`onUnbound`**</HM>: *(optional)* A callback that is called when a `state` is unbound from an `instance`. -- <HM>**`onCreated`**</HM>: *(optional)* A callback that is called when a `state` is created. -- <HM>**`onUpdated`**</HM>: *(optional)* A callback that is called when a `state` is updated. -- <HM>**`onDisposed`**</HM>: *(optional)* A callback that is called when a `state` is disposed. +The <HT>`RtStateObserver`</HT> class accepts these parameters: + +- <HM>**`onBound`**</HM>: *(optional)* Called when a `state` is bound to an `instance`. +- <HM>**`onUnbound`**</HM>: *(optional)* Called when a `state` is unbound from an `instance`. +- <HM>**`onCreated`**</HM>: *(optional)* Called when a `state` is initialized. +- <HM>**`onUpdated`**</HM>: *(optional)* Called when a `state` undergoes a change or update. +- <HM>**`onDisposed`**</HM>: *(optional)* Called when a `state` is disposed or cleaned up. ## Usage -To use <HT>`RtStateObserver`</HT>, you need to instantiate it, pass the callbacks you want to use and then add it using the <HM>`Rt.addObserver`</HM> method. +To use <HT>`RtStateObserver`</HT>, you need to instantiate it, pass the callbacks you want to use and then add it using the <HM>`Rt.addObserver`</HM> method, e.g.: ```dart "RtStateObserver" "Rt.addObserver" import 'package:reactter/reactter.dart'; void main() { - // Create a state observer + // Create a state observer with lifecycle callbacks final stateObserver = RtStateObserver( onBound: (state, instance) { print('State($state) bound to instance($instance)'); @@ -55,8 +56,7 @@ void main() { }, ); - // Add the state observer + // Add the state observer to Reactter's observer system Rt.addObserver(stateObserver); } - ``` From 32d19482b518d6573c8d705cfffb5b484551468d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Wed, 19 Feb 2025 01:54:50 -0600 Subject: [PATCH 129/141] refactor(framework): Rename `RtDependency` to `RtDependencyRef` for consistency and clarity. --- .../lib/src/framework/provider_impl.dart | 23 +++---- packages/reactter/lib/src/framework.dart | 2 +- ...dependency.dart => rt_dependency_ref.dart} | 6 +- .../lib/src/hooks/use_dependency.dart | 12 ++-- .../test/core/event_handler_test.dart | 66 +++++++++---------- .../test/core/lifecycle_observer_test.dart | 8 +-- .../rt_dependency_observer_test.dart | 9 +-- .../test/framework/rt_dependency_test.dart | 10 +-- packages/reactter/test/logger_test.dart | 16 ++--- 9 files changed, 77 insertions(+), 75 deletions(-) rename packages/reactter/lib/src/framework/{rt_dependency.dart => rt_dependency_ref.dart} (58%) diff --git a/packages/flutter_reactter/lib/src/framework/provider_impl.dart b/packages/flutter_reactter/lib/src/framework/provider_impl.dart index 962e91b3..0113458c 100644 --- a/packages/flutter_reactter/lib/src/framework/provider_impl.dart +++ b/packages/flutter_reactter/lib/src/framework/provider_impl.dart @@ -149,15 +149,15 @@ class ProvideImpl<T extends Object?, I extends String?> extends ProviderBase<T> } } -/// [ProviderElement] is a class that manages the lifecycle of the [RtDependency] and -/// provides the [RtDependency] to its descendants +/// [ProviderElement] is a class that manages the lifecycle of the [RtDependencyRef] and +/// provides the [RtDependencyRef] to its descendants @internal class ProviderElement<T extends Object?> extends InheritedElement with ScopeElementMixin { - static final Map<RtDependency, int> _instanceMountCount = {}; + static final Map<RtDependencyRef, int> _instanceMountCount = {}; Widget? _prevChild; - HashMap<RtDependency, ProviderElement<T>>? _inheritedElementsWithId; + HashMap<RtDependencyRef, ProviderElement<T>>? _inheritedElementsWithId; bool _isLazyInstanceObtained = false; @override @@ -189,7 +189,7 @@ class ProviderElement<T extends Object?> extends InheritedElement @override void mount(Element? parent, Object? newSlot) { - final dependency = RtDependency<T?>(widget.id); + final dependency = RtDependencyRef<T?>(widget.id); var count = _instanceMountCount.putIfAbsent(dependency, () => 0); _instanceMountCount[dependency] = ++count; final shouldNotifyMount = count == 1; @@ -238,7 +238,7 @@ class ProviderElement<T extends Object?> extends InheritedElement @override void unmount() { final ref = widget.ref; - final dependency = RtDependency<T?>(widget.id); + final dependency = RtDependencyRef<T?>(widget.id); final count = (_instanceMountCount[dependency] ?? 0) - 1; final shouldNotifyUnmount = count < 1; @@ -266,11 +266,11 @@ class ProviderElement<T extends Object?> extends InheritedElement } } - /// Gets [ProviderElement] that it has the [RtDependency]'s id. + /// Gets [ProviderElement] that it has the [RtDependencyRef]'s id. ProviderElement<T>? getInheritedElementOfExactId( String id, ) => - _inheritedElementsWithId?[RtDependency<T?>(id)]; + _inheritedElementsWithId?[RtDependencyRef<T?>(id)]; /// updates [inheritedElementsWithId] /// with all ancestor [ProviderElement] with id @@ -282,14 +282,15 @@ class ProviderElement<T extends Object?> extends InheritedElement as ProviderElement<T>?; if (ancestorInheritedElement?._inheritedElementsWithId != null) { - _inheritedElementsWithId = HashMap<RtDependency, ProviderElement<T>>.of( + _inheritedElementsWithId = + HashMap<RtDependencyRef, ProviderElement<T>>.of( ancestorInheritedElement!._inheritedElementsWithId!, ); } else { - _inheritedElementsWithId = HashMap<RtDependency, ProviderElement<T>>(); + _inheritedElementsWithId = HashMap<RtDependencyRef, ProviderElement<T>>(); } - _inheritedElementsWithId![RtDependency<T?>(widget.id)] = this; + _inheritedElementsWithId![RtDependencyRef<T?>(widget.id)] = this; } } diff --git a/packages/reactter/lib/src/framework.dart b/packages/reactter/lib/src/framework.dart index e3e0fbe9..1a11d421 100644 --- a/packages/reactter/lib/src/framework.dart +++ b/packages/reactter/lib/src/framework.dart @@ -4,7 +4,7 @@ import 'internals.dart'; export 'internals.dart' show DependencyMode, Lifecycle, RtState; -part 'framework/rt_dependency.dart'; +part 'framework/rt_dependency_ref.dart'; part 'framework/rt_dependency_observer.dart'; part 'framework/rt_hook.dart'; part 'framework/rt_interface.dart'; diff --git a/packages/reactter/lib/src/framework/rt_dependency.dart b/packages/reactter/lib/src/framework/rt_dependency_ref.dart similarity index 58% rename from packages/reactter/lib/src/framework/rt_dependency.dart rename to packages/reactter/lib/src/framework/rt_dependency_ref.dart index 821db05b..7b156b57 100644 --- a/packages/reactter/lib/src/framework/rt_dependency.dart +++ b/packages/reactter/lib/src/framework/rt_dependency_ref.dart @@ -1,10 +1,10 @@ part of '../framework.dart'; -/// {@template reactter.rt_dependency} +/// {@template reactter.rt_dependency_ref} /// Represents dependency managed by Reactter's dependency injection. /// /// This class extends [DependencyRef] and provides an optional [id] parameter. /// {@endtemplate} -class RtDependency<T> extends DependencyRef<T> { - const RtDependency([String? id]) : super(id); +class RtDependencyRef<T> extends DependencyRef<T> { + const RtDependencyRef([String? id]) : super(id); } diff --git a/packages/reactter/lib/src/hooks/use_dependency.dart b/packages/reactter/lib/src/hooks/use_dependency.dart index 70d219f8..e52a08e1 100644 --- a/packages/reactter/lib/src/hooks/use_dependency.dart +++ b/packages/reactter/lib/src/hooks/use_dependency.dart @@ -261,15 +261,15 @@ class UseDependency<T extends Object> extends RtHook { } void _listen() { - Rt.on(RtDependency<T>(id), Lifecycle.created, _onInstance); - Rt.on(RtDependency<T>(id), Lifecycle.willMount, _onInstance); - Rt.on(RtDependency<T>(id), Lifecycle.deleted, _onInstance); + Rt.on(RtDependencyRef<T>(id), Lifecycle.created, _onInstance); + Rt.on(RtDependencyRef<T>(id), Lifecycle.willMount, _onInstance); + Rt.on(RtDependencyRef<T>(id), Lifecycle.deleted, _onInstance); } void _unlisten() { - Rt.off(RtDependency<T>(id), Lifecycle.created, _onInstance); - Rt.off(RtDependency<T>(id), Lifecycle.willMount, _onInstance); - Rt.off(RtDependency<T>(id), Lifecycle.deleted, _onInstance); + Rt.off(RtDependencyRef<T>(id), Lifecycle.created, _onInstance); + Rt.off(RtDependencyRef<T>(id), Lifecycle.willMount, _onInstance); + Rt.off(RtDependencyRef<T>(id), Lifecycle.deleted, _onInstance); } void _onInstance(inst, param) { diff --git a/packages/reactter/test/core/event_handler_test.dart b/packages/reactter/test/core/event_handler_test.dart index 81b6778b..9ff6dcc9 100644 --- a/packages/reactter/test/core/event_handler_test.dart +++ b/packages/reactter/test/core/event_handler_test.dart @@ -43,19 +43,19 @@ void main() { }); test( - "should listen and emit event using RtDependency", + "should listen and emit event using RtDependencyRef", () { _testListenAndEmitEvent( - RtDependency<TestController>(), - RtDependency<TestController>(), + RtDependencyRef<TestController>(), + RtDependencyRef<TestController>(), instanceMatcher: isNull, ); final testController = Rt.create(() => TestController())!; _testListenAndEmitEvent( - RtDependency<TestController>(), - RtDependency<TestController>(), + RtDependencyRef<TestController>(), + RtDependencyRef<TestController>(), instanceMatcher: testController, ); @@ -64,11 +64,11 @@ void main() { ); test( - "should listen and emit event using RtDependency with id", + "should listen and emit event using RtDependencyRef with id", () { _testListenAndEmitEvent( - RtDependency<TestController>('uniqueId'), - RtDependency<TestController>('uniqueId'), + RtDependencyRef<TestController>('uniqueId'), + RtDependencyRef<TestController>('uniqueId'), instanceMatcher: isNull, ); @@ -78,8 +78,8 @@ void main() { )!; _testListenAndEmitEvent( - RtDependency<TestController>('uniqueId'), - RtDependency<TestController>('uniqueId'), + RtDependencyRef<TestController>('uniqueId'), + RtDependencyRef<TestController>('uniqueId'), instanceMatcher: testController, ); @@ -88,13 +88,13 @@ void main() { ); test( - "should listen event using dependency and emit event using RtDependency", + "should listen event using dependency and emit event using RtDependencyRef", () { final testController = Rt.create(() => TestController())!; _testListenAndEmitEvent( testController, - RtDependency<TestController>(), + RtDependencyRef<TestController>(), instanceMatcher: testController, ); @@ -103,7 +103,7 @@ void main() { ); test( - "should listen event using the dependency and emit event using RtDependency with id", + "should listen event using the dependency and emit event using RtDependencyRef with id", () { final testController = Rt.create( () => TestController(), @@ -112,7 +112,7 @@ void main() { _testListenAndEmitEvent( testController, - RtDependency<TestController>('uniqueId'), + RtDependencyRef<TestController>('uniqueId'), instanceMatcher: testController, ); @@ -121,12 +121,12 @@ void main() { ); test( - "should listen event using RtDependency and emit event using the dependency", + "should listen event using RtDependencyRef and emit event using the dependency", () { final testController = Rt.create(() => TestController())!; _testListenAndEmitEvent( - RtDependency<TestController>(), + RtDependencyRef<TestController>(), testController, instanceMatcher: testController, ); @@ -136,7 +136,7 @@ void main() { ); test( - "should listen event using RtDependency and emit event using the dependency with id", + "should listen event using RtDependencyRef and emit event using the dependency with id", () { final testController = Rt.create( () => TestController(), @@ -144,7 +144,7 @@ void main() { )!; _testListenAndEmitEvent( - RtDependency<TestController>('uniqueId'), + RtDependencyRef<TestController>('uniqueId'), testController, instanceMatcher: testController, ); @@ -164,21 +164,21 @@ void main() { ); _testListenAndEmitEvent( - RtDependency<TestController>(), - RtDependency<TestController>(), + RtDependencyRef<TestController>(), + RtDependencyRef<TestController>(), instanceMatcher: testController, isOnce: true, ); _testListenAndEmitEvent( testController, - RtDependency<TestController>(), + RtDependencyRef<TestController>(), instanceMatcher: testController, isOnce: true, ); _testListenAndEmitEvent( - RtDependency<TestController>(), + RtDependencyRef<TestController>(), testController, instanceMatcher: testController, isOnce: true, @@ -201,21 +201,21 @@ void main() { ); _testListenAndEmitEvent( - RtDependency<TestController>('uniqueId'), - RtDependency<TestController>('uniqueId'), + RtDependencyRef<TestController>('uniqueId'), + RtDependencyRef<TestController>('uniqueId'), instanceMatcher: testController, isOnce: true, ); _testListenAndEmitEvent( testController, - RtDependency<TestController>('uniqueId'), + RtDependencyRef<TestController>('uniqueId'), instanceMatcher: testController, isOnce: true, ); _testListenAndEmitEvent( - RtDependency<TestController>('uniqueId'), + RtDependencyRef<TestController>('uniqueId'), testController, instanceMatcher: testController, isOnce: true, @@ -316,7 +316,7 @@ void _testUnlistenEvent({bool withId = false}) { Rt.on(testController, Events.TestEvent, onTestEvent); Rt.on( - RtDependency<TestController>(id), + RtDependencyRef<TestController>(id), Events.TestEvent, (inst, param) { expect(inst, testController); @@ -324,7 +324,7 @@ void _testUnlistenEvent({bool withId = false}) { }, ); Rt.on( - RtDependency<TestController>(id), + RtDependencyRef<TestController>(id), Events.TestEvent2, onTestEvent2, ); @@ -338,7 +338,7 @@ void _testUnlistenEvent({bool withId = false}) { expect(countEvent2, 0); Rt.emit( - RtDependency<TestController>(id), + RtDependencyRef<TestController>(id), Events.TestEvent, TEST_EVENT_PARAM_NAME, ); @@ -347,7 +347,7 @@ void _testUnlistenEvent({bool withId = false}) { expect(countEvent2, 0); Rt.emit( - RtDependency<TestController>(id), + RtDependencyRef<TestController>(id), Events.TestEvent2, TEST_EVENT2_PARAM_NAME, ); @@ -357,7 +357,7 @@ void _testUnlistenEvent({bool withId = false}) { Rt.off(testController, Events.TestEvent, onTestEvent); Rt.emit( - RtDependency<TestController>(id), + RtDependencyRef<TestController>(id), Events.TestEvent, TEST_EVENT_PARAM_NAME, ); @@ -372,13 +372,13 @@ void _testUnlistenEvent({bool withId = false}) { expect(countEvent2, 2); Rt.off( - RtDependency<TestController>(id), + RtDependencyRef<TestController>(id), Events.TestEvent2, onTestEvent2, ); Rt.emit(testController, Events.TestEvent2, TEST_EVENT2_PARAM_NAME); Rt.emit( - RtDependency<TestController>(id), + RtDependencyRef<TestController>(id), Events.TestEvent2, TEST_EVENT2_PARAM_NAME, ); diff --git a/packages/reactter/test/core/lifecycle_observer_test.dart b/packages/reactter/test/core/lifecycle_observer_test.dart index cb727617..6b988b3d 100644 --- a/packages/reactter/test/core/lifecycle_observer_test.dart +++ b/packages/reactter/test/core/lifecycle_observer_test.dart @@ -21,7 +21,7 @@ void main() { expect(testLifecycleController, isA<TestLifecycleController>()); Rt.emit( - RtDependency<TestLifecycleController>(), + RtDependencyRef<TestLifecycleController>(), Lifecycle.willMount, ); @@ -35,7 +35,7 @@ void main() { expect(testLifecycleController.lastState, null); Rt.emit( - RtDependency<TestLifecycleController>(), + RtDependencyRef<TestLifecycleController>(), Lifecycle.didMount, ); @@ -77,7 +77,7 @@ void main() { ); Rt.emit( - RtDependency<TestLifecycleController>(), + RtDependencyRef<TestLifecycleController>(), Lifecycle.willUnmount, ); @@ -94,7 +94,7 @@ void main() { ); Rt.emit( - RtDependency<TestLifecycleController>(), + RtDependencyRef<TestLifecycleController>(), Lifecycle.didUnmount, ); diff --git a/packages/reactter/test/framework/rt_dependency_observer_test.dart b/packages/reactter/test/framework/rt_dependency_observer_test.dart index 818596f1..486bc5d7 100644 --- a/packages/reactter/test/framework/rt_dependency_observer_test.dart +++ b/packages/reactter/test/framework/rt_dependency_observer_test.dart @@ -54,7 +54,7 @@ void main() { final observer = RtDependencyObserver( onMounted: (dependencyRef, instance) { - expect(dependencyRef, isA<RtDependency<TestController>>()); + expect(dependencyRef, isA<RtDependencyRef<TestController>>()); expect(instance, isA<TestController>()); onMountedCalled = true; instanceMounted = instance; @@ -63,7 +63,7 @@ void main() { Rt.addObserver(observer); final instance = Rt.create(() => TestController()); - Rt.emit(RtDependency<TestController>(), Lifecycle.didMount, instance); + Rt.emit(RtDependencyRef<TestController>(), Lifecycle.didMount, instance); expect(onMountedCalled, isTrue); expect(instanceMounted, instance); @@ -78,7 +78,7 @@ void main() { final observer = RtDependencyObserver( onUnmounted: (dependencyRef, instance) { - expect(dependencyRef, isA<RtDependency<TestController>>()); + expect(dependencyRef, isA<RtDependencyRef<TestController>>()); expect(instance, isA<TestController>()); onUnmountedCalled = true; instanceUnmounted = instance; @@ -87,7 +87,8 @@ void main() { Rt.addObserver(observer); final instance = Rt.create(() => TestController()); - Rt.emit(RtDependency<TestController>(), Lifecycle.didUnmount, instance); + Rt.emit( + RtDependencyRef<TestController>(), Lifecycle.didUnmount, instance); expect(onUnmountedCalled, isTrue); expect(instanceUnmounted, instance); diff --git a/packages/reactter/test/framework/rt_dependency_test.dart b/packages/reactter/test/framework/rt_dependency_test.dart index 959d175f..3921b2d4 100644 --- a/packages/reactter/test/framework/rt_dependency_test.dart +++ b/packages/reactter/test/framework/rt_dependency_test.dart @@ -4,7 +4,7 @@ import 'package:test/test.dart'; import '../shareds/test_controllers.dart'; void main() { - group("RtDependency", () { + group("RtDependencyRef", () { test("should throw the life-cycle events", () async { late TestController? instance; late bool willUpdateChecked; @@ -12,14 +12,14 @@ void main() { late bool isDeleted; Rt.on( - RtDependency<TestController>(), + RtDependencyRef<TestController>(), Lifecycle.created, (TestController? inst, __) { instance = inst; }, ); Rt.on( - RtDependency<TestController>(), + RtDependencyRef<TestController>(), Lifecycle.willUpdate, (TestController? inst, UseState hook) { willUpdateChecked = true; @@ -28,7 +28,7 @@ void main() { }, ); Rt.on( - RtDependency<TestController>(), + RtDependencyRef<TestController>(), Lifecycle.didUpdate, (TestController? inst, UseState hook) { didUpdateChecked = true; @@ -37,7 +37,7 @@ void main() { }, ); Rt.on( - RtDependency<TestController>(), + RtDependencyRef<TestController>(), Lifecycle.deleted, (_, __) { isDeleted = true; diff --git a/packages/reactter/test/logger_test.dart b/packages/reactter/test/logger_test.dart index b6d5c85b..a5085792 100644 --- a/packages/reactter/test/logger_test.dart +++ b/packages/reactter/test/logger_test.dart @@ -118,11 +118,11 @@ void main() { expect(logs.last['level'], LogLevel.fine); expect( logs.last['message'], - '${prettyFormat(RtDependency<StateTest>(id))} registered.', + '${prettyFormat(RtDependencyRef<StateTest>(id))} registered.', ); final dependency = Rt.getDependencyRegisterByRef( - RtDependency<StateTest>(id), + RtDependencyRef<StateTest>(id), ); Rt.create(() => instance, id: id); @@ -191,7 +191,7 @@ void main() { expect(logs.last['level'], LogLevel.info); expect( logs.last['message'], - '${prettyFormat(RtDependency<StateTest>(id))} already deleted.', + '${prettyFormat(RtDependencyRef<StateTest>(id))} already deleted.', ); Rt.unregister<StateTest>(id); @@ -200,12 +200,12 @@ void main() { expect(logs.last['level'], LogLevel.info); expect( logs.last['message'], - '${prettyFormat(RtDependency<StateTest>(id))} already unregistered.', + '${prettyFormat(RtDependencyRef<StateTest>(id))} already unregistered.', ); Rt.create(() => StateTest(), id: id, mode: DependencyMode.factory); final dependencyFactoryRef = Rt.getDependencyRegisterByRef( - RtDependency<StateTest>(id), + RtDependencyRef<StateTest>(id), ); Rt.delete<StateTest>(id); @@ -218,7 +218,7 @@ void main() { Rt.destroy<StateTest>(id: id); Rt.create(() => StateTest(), id: id, mode: DependencyMode.singleton); final dependencySingletonRef = Rt.getDependencyRegisterByRef( - RtDependency<StateTest>(id), + RtDependencyRef<StateTest>(id), ); Rt.delete<StateTest>(id); @@ -235,7 +235,7 @@ void main() { expect( logs.last['message'], startsWith( - "${prettyFormat(RtDependency<StateTest>(id))} couldn't register."), + "${prettyFormat(RtDependencyRef<StateTest>(id))} couldn't register."), ); Rt.create(() => StateTest(), id: id); @@ -245,7 +245,7 @@ void main() { expect( logs.last['message'], startsWith( - "${prettyFormat(RtDependency<StateTest>(id))} couldn't unregister", + "${prettyFormat(RtDependencyRef<StateTest>(id))} couldn't unregister", ), ); }); From 78a69ba6f5a78f3d5f84719e2eb7abf10e72d304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Wed, 19 Feb 2025 01:57:00 -0600 Subject: [PATCH 130/141] refactor(website): Add documentation for `RtDependencyRef` and update examples to use it. --- .../docs/api/classes/rt_dependency.mdx | 6 -- .../docs/api/classes/rt_dependency_ref.mdx | 55 +++++++++++++++++++ .../content/docs/core_concepts/lifecycle.mdx | 4 +- .../docs/es/core_concepts/lifecycle.mdx | 4 +- .../docs/migration/v7.3.0_to_v8.0.0.mdx | 2 +- .../examples/event_handler_off/lib/main.dart | 4 +- .../event_handler_off_all/lib/main.dart | 4 +- .../event_handler_on_emit/lib/main.dart | 6 +- .../examples/event_handler_one/lib/main.dart | 4 +- .../lib/counter_view.dart | 8 +-- website/src/examples/marks.ts | 2 +- .../rt_provider_mode/lib/counter_view.dart | 8 +-- 12 files changed, 78 insertions(+), 29 deletions(-) delete mode 100644 website/src/content/docs/api/classes/rt_dependency.mdx create mode 100644 website/src/content/docs/api/classes/rt_dependency_ref.mdx diff --git a/website/src/content/docs/api/classes/rt_dependency.mdx b/website/src/content/docs/api/classes/rt_dependency.mdx deleted file mode 100644 index 7b908ca1..00000000 --- a/website/src/content/docs/api/classes/rt_dependency.mdx +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: RtDependency 🚧 -description: Represents dependency managed by Reactter's dependency injection. -sidebar: - order: 4 ---- \ No newline at end of file diff --git a/website/src/content/docs/api/classes/rt_dependency_ref.mdx b/website/src/content/docs/api/classes/rt_dependency_ref.mdx new file mode 100644 index 00000000..bdd1b15f --- /dev/null +++ b/website/src/content/docs/api/classes/rt_dependency_ref.mdx @@ -0,0 +1,55 @@ +--- +title: RtDependencyRef +description: Represents dependency managed by Reactter's dependency injection. +sidebar: + order: 4 +--- + +import { HE, HM, HT } from '@/components/Highlight'; + +<HT><a href="https://pub.dev/documentation/reactter/latest/reactter/RtDependencyRef-class.html" target="_blank">`RtDependencyRef`</a></HT> is a class that represents a reference to a dependency managed by Reactter's dependency injection system. + +## Syntax + +```dart showLineNumbers=false + RtDependencyRef<T>([String? id]); +``` + +The <HT>`RtDependencyRef`</HT> class accepts this parameter: + +- <HM>**`id`**</HM>: *(optional)* A unique identifier for the dependency. + +## Usage + +To use <HT>`RtDependencyRef`</HT>, you need to instantiate it, pass the type of dependency you want to reference and use for event handling, e.g.: + +```dart "RtDependencyRef" "Rt.on" "Rt.register" "Rt.get" "Rt.delete" +import 'package:reactter/reactter.dart'; + +// Define a dependency class +class MyDependency {} + +void main() { + // Create a dependency reference + final dependencyRef = RtDependencyRef<MyDependency>(); + + // Use the dependency reference for event handling + Rt.on(dependencyRef, Lifecycle.registered, (_, __) { + print('Dependency registered'); + }); + Rt.on(dependencyRef, Lifecycle.created, (_, __) { + print('Dependency created'); + }); + Rt.on(dependencyRef, Lifecycle.deleted, (_, __) { + print('Dependency deleted'); + }); + Rt.on(dependencyRef, Lifecycle.unregistered, (_, __) { + print('Dependency unregistered'); + }); + + // Register, create, delete, and unregister the dependency + Rt.register(() => MyDependency()); + Rt.get<MyDependency>(); + Rt.delete<MyDependency>(); +} +``` diff --git a/website/src/content/docs/core_concepts/lifecycle.mdx b/website/src/content/docs/core_concepts/lifecycle.mdx index 48308a71..5aa677cb 100644 --- a/website/src/content/docs/core_concepts/lifecycle.mdx +++ b/website/src/content/docs/core_concepts/lifecycle.mdx @@ -77,8 +77,8 @@ You can listen to the lifecycle events of an instance (like a dependency or stat </CodeTabs> :::note - The <HT>[`RtDependency`](/reactter/api/classes/rt_dependency)</HT> is a generic class used to identify the dependency in the event handler. - It ideal to use the <HT>[`RtDependency`](/reactter/api/classes/rt_dependency)</HT> class when the dependency is not initialize yet. + The <HT>[`RtDependencyRef`](/reactter/api/classes/rt_dependency_ref)</HT> is a generic class used to identify the dependency in the event handler. + It ideal to use the <HT>[`RtDependencyRef`](/reactter/api/classes/rt_dependency_ref)</HT> class when the dependency is not initialize yet. ::: ## Using RtDependencyLifecycle diff --git a/website/src/content/docs/es/core_concepts/lifecycle.mdx b/website/src/content/docs/es/core_concepts/lifecycle.mdx index 29e1176a..0dbf6edb 100644 --- a/website/src/content/docs/es/core_concepts/lifecycle.mdx +++ b/website/src/content/docs/es/core_concepts/lifecycle.mdx @@ -78,8 +78,8 @@ Puedes escuchar los eventos del ciclo de vida de una instancia (como una depende </CodeTabs> :::note - El <HT>[`RtDependency`](/reactter/es/api/classes/rt_dependency)</HT> es una clase genérica que se utiliza para identificar la dependencia en el manejador de eventos. - Es ideal usar la clase <HT>[`RtDependency`](/reactter/es/api/classes/rt_dependency)</HT> cuando la dependencia aún no se ha inicializado. + El <HT>[`RtDependencyRef`](/reactter/es/api/classes/rt_dependency_ref)</HT> es una clase genérica que se utiliza para identificar la dependencia en el manejador de eventos. + Es ideal usar la clase <HT>[`RtDependencyRef`](/reactter/es/api/classes/rt_dependency_ref)</HT> cuando la dependencia aún no se ha inicializado. ::: ## Usando RtDependencyLifecycle diff --git a/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx b/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx index 0729e49c..145a81ff 100644 --- a/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx +++ b/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx @@ -48,7 +48,7 @@ If you encounter any issues, refer to the [official documentation](https://pub.d - Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0/reactter/Lifecycle.html) instead. - Replace [`LifecycleObserver`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver-class.html) to [`RtDependencyLifecycle`](https://pub.dev/documentation/reactter/8.0.0-dev.25/reactter/RtDependencyLifecycle-class.html). - Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`RtDependencyLifecycle.onCreated`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtDependencyLifecycle/onCreated.html) instead. -- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtDependency-class.html) instead. +- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependencyRef`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtDependencyRef-class.html) instead. - Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtHook-class.html) instead. - Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtInterface-class.html) instead. - Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtState/notify.html) instead. diff --git a/website/src/examples/event_handler_off/lib/main.dart b/website/src/examples/event_handler_off/lib/main.dart index eadc8c3a..2021d6b2 100644 --- a/website/src/examples/event_handler_off/lib/main.dart +++ b/website/src/examples/event_handler_off/lib/main.dart @@ -21,7 +21,7 @@ void onDidUpdateStateB(instance, Signal state) { void main() { // Listen to the `myEvent` event of the `MyDependency` before it's created. Rt.on( - RtDependency<MyDependency>(), + RtDependencyRef<MyDependency>(), CustomEvent.myEvent, onMyEvent, ); @@ -61,7 +61,7 @@ void main() { // Stop listening to the `myEvent` event of the `MyDependency` Rt.off( - RtDependency<MyDependency>(), + RtDependencyRef<MyDependency>(), CustomEvent.myEvent, onMyEvent, ); diff --git a/website/src/examples/event_handler_off_all/lib/main.dart b/website/src/examples/event_handler_off_all/lib/main.dart index e158afd1..1d82cf40 100644 --- a/website/src/examples/event_handler_off_all/lib/main.dart +++ b/website/src/examples/event_handler_off_all/lib/main.dart @@ -5,7 +5,7 @@ import 'my_dependency.dart'; void main() { // Listen to the `myEvent` event of the `MyDependency` before it's created. Rt.on( - RtDependency<MyDependency>(), + RtDependencyRef<MyDependency>(), CustomEvent.myEvent, (instance, param) => print('CustomEvent emitted with param: $param'), ); @@ -55,7 +55,7 @@ void main() { Rt.offAll(myDependency.stateA); // Remove all event listeners of `myDependency`, - // including the generic listeners (using `RtDependency`). + // including the generic listeners (using `RtDependencyRef`). Rt.offAll(myDependency, true); // No print for neither `stateA` nor the `MyDependency` instance diff --git a/website/src/examples/event_handler_on_emit/lib/main.dart b/website/src/examples/event_handler_on_emit/lib/main.dart index 9e06e8d4..4a6de1d1 100644 --- a/website/src/examples/event_handler_on_emit/lib/main.dart +++ b/website/src/examples/event_handler_on_emit/lib/main.dart @@ -5,7 +5,7 @@ import 'my_dependency.dart'; void main() { // Listen to the `myEvent` event of the `MyDependency` before it's created. Rt.on( - RtDependency<MyDependency>(), + RtDependencyRef<MyDependency>(), CustomEvent.myEvent, (instance, param) => print('CustomEvent emitted with param: $param'), ); @@ -62,10 +62,10 @@ void main() { // because it's deleted. Rt.emit(myDependency, CustomEvent.myEvent, 'This is not printed'); - // Can still emit to the `myEvent` event using the `RtDependency` + // Can still emit to the `myEvent` event using the `RtDependencyRef` // Print: // CustomEvent.myEvent emitted with param: 42 - Rt.emit(RtDependency<MyDependency>(), CustomEvent.myEvent, 42); + Rt.emit(RtDependencyRef<MyDependency>(), CustomEvent.myEvent, 42); runApp(MyApp()); } diff --git a/website/src/examples/event_handler_one/lib/main.dart b/website/src/examples/event_handler_one/lib/main.dart index 4457fd76..98ee04de 100644 --- a/website/src/examples/event_handler_one/lib/main.dart +++ b/website/src/examples/event_handler_one/lib/main.dart @@ -5,7 +5,7 @@ import 'my_dependency.dart'; void main() { // Listen to the `myEvent` event of the `MyDependency` before it's created. Rt.one( - RtDependency<MyDependency>(), + RtDependencyRef<MyDependency>(), CustomEvent.myEvent, (instance, param) => print('CustomEvent emitted with param: $param'), ); @@ -61,7 +61,7 @@ void main() { /// Cannot listen to the `myEvent` event using the `MyDependency` instance /// because the listener is one-time. - Rt.emit(RtDependency<MyDependency>(), CustomEvent.myEvent, 42); + Rt.emit(RtDependencyRef<MyDependency>(), CustomEvent.myEvent, 42); runApp(MyApp()); } diff --git a/website/src/examples/lifecycle_event_handler/lib/counter_view.dart b/website/src/examples/lifecycle_event_handler/lib/counter_view.dart index ce794c95..d3918632 100644 --- a/website/src/examples/lifecycle_event_handler/lib/counter_view.dart +++ b/website/src/examples/lifecycle_event_handler/lib/counter_view.dart @@ -9,25 +9,25 @@ class CounterView extends StatelessWidget { @override Widget build(BuildContext context) { Rt.on( - RtDependency<CounterController>(), + RtDependencyRef<CounterController>(), Lifecycle.unregistered, (_, __) => print('CounterController unregistered'), ); Rt.on( - RtDependency<CounterController>(), + RtDependencyRef<CounterController>(), Lifecycle.registered, (_, __) => print('CounterController registered'), ); Rt.on( - RtDependency<CounterController>(), + RtDependencyRef<CounterController>(), Lifecycle.created, (_, __) => print('CounterController created'), ); Rt.on( - RtDependency<CounterController>(), + RtDependencyRef<CounterController>(), Lifecycle.deleted, (_, __) => print('CounterController deleted'), ); diff --git a/website/src/examples/marks.ts b/website/src/examples/marks.ts index f822cb88..786ecc55 100644 --- a/website/src/examples/marks.ts +++ b/website/src/examples/marks.ts @@ -28,7 +28,7 @@ export const marks = [ "RtContextMixin", "RtState", "RtStateObserver", - "RtDependency", + "RtDependencyRef", "RtDependencyLifecycle", 'RtDependencyObserver', "RtDependencyMode", diff --git a/website/src/examples/rt_provider_mode/lib/counter_view.dart b/website/src/examples/rt_provider_mode/lib/counter_view.dart index 24ef5dec..5975c449 100644 --- a/website/src/examples/rt_provider_mode/lib/counter_view.dart +++ b/website/src/examples/rt_provider_mode/lib/counter_view.dart @@ -51,7 +51,7 @@ class CounterView extends StatelessWidget { void _listenLifecycle() { for (final mode in DependencyMode.values) { Rt.on( - RtDependency<CounterController>( + RtDependencyRef<CounterController>( mode.toString(), ), Lifecycle.registered, @@ -61,7 +61,7 @@ class CounterView extends StatelessWidget { ); Rt.on( - RtDependency<CounterController>( + RtDependencyRef<CounterController>( mode.toString(), ), Lifecycle.created, @@ -71,7 +71,7 @@ class CounterView extends StatelessWidget { ); Rt.on( - RtDependency<CounterController>( + RtDependencyRef<CounterController>( mode.toString(), ), Lifecycle.deleted, @@ -81,7 +81,7 @@ class CounterView extends StatelessWidget { ); Rt.on( - RtDependency<CounterController>( + RtDependencyRef<CounterController>( mode.toString(), ), Lifecycle.unregistered, From 4ca1773abc8f30c93346d7bdac7d1e91a6002a6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Wed, 19 Feb 2025 01:57:15 -0600 Subject: [PATCH 131/141] refactor(website): Update documentation to include links for `RtDependencyObserver` and `RtStateObserver` usage examples. --- .../api/classes/rt_dependency_observer.mdx | 2 +- .../docs/api/classes/rt_state_observer.mdx | 2 +- .../src/content/docs/api/classes/signal.mdx | 2 +- .../docs/api/methods/debugging_methods.mdx | 6 +- .../docs/extra_topics/_renaming_classes.mdx | 96 ------------------- 5 files changed, 6 insertions(+), 102 deletions(-) delete mode 100644 website/src/content/docs/extra_topics/_renaming_classes.mdx diff --git a/website/src/content/docs/api/classes/rt_dependency_observer.mdx b/website/src/content/docs/api/classes/rt_dependency_observer.mdx index b366675e..680774d6 100644 --- a/website/src/content/docs/api/classes/rt_dependency_observer.mdx +++ b/website/src/content/docs/api/classes/rt_dependency_observer.mdx @@ -43,7 +43,7 @@ The <HT>`RtDependencyObserver`</HT> class accepts these parameters: ## Usage -To use <HT>`RtDependencyObserver`</HT>, you need to instantiate it, pass the callbacks you want to use, and then add it using the <HM>`Rt.addObserver`</HM> method, e.g.: +To use <HT>`RtDependencyObserver`</HT>, you need to instantiate it, pass the callbacks you want to use, and then add it using the <HM>[`Rt.addObserver`](/reactter/api/methods/debugging_methods/#rtaddobserver)</HM> method, e.g.: ```dart "RtDependencyObserver" "Rt.addObserver" import 'package:reactter/reactter.dart'; diff --git a/website/src/content/docs/api/classes/rt_state_observer.mdx b/website/src/content/docs/api/classes/rt_state_observer.mdx index 9ad15424..769a3691 100644 --- a/website/src/content/docs/api/classes/rt_state_observer.mdx +++ b/website/src/content/docs/api/classes/rt_state_observer.mdx @@ -31,7 +31,7 @@ The <HT>`RtStateObserver`</HT> class accepts these parameters: ## Usage -To use <HT>`RtStateObserver`</HT>, you need to instantiate it, pass the callbacks you want to use and then add it using the <HM>`Rt.addObserver`</HM> method, e.g.: +To use <HT>`RtStateObserver`</HT>, you need to instantiate it, pass the callbacks you want to use and then add it using the <HM>[`Rt.addObserver`](/reactter/api/methods/debugging_methods/#rtaddobserver)</HM> method, e.g.: ```dart "RtStateObserver" "Rt.addObserver" import 'package:reactter/reactter.dart'; diff --git a/website/src/content/docs/api/classes/signal.mdx b/website/src/content/docs/api/classes/signal.mdx index 54556929..f1283078 100644 --- a/website/src/content/docs/api/classes/signal.mdx +++ b/website/src/content/docs/api/classes/signal.mdx @@ -20,7 +20,7 @@ import * as ListeningChangesState from '@/content/docs/shareds/listening_changes Signal<T>(T initialValue); ``` -<HT>`Signal`</HT> accepts this property: +<HT>`Signal`</HT> accepts this parameter: - **`initialValue`**: Initial value of <HT>`T`</HT> type that the it will hold. diff --git a/website/src/content/docs/api/methods/debugging_methods.mdx b/website/src/content/docs/api/methods/debugging_methods.mdx index 5f088143..6fa6f2b3 100644 --- a/website/src/content/docs/api/methods/debugging_methods.mdx +++ b/website/src/content/docs/api/methods/debugging_methods.mdx @@ -115,11 +115,11 @@ Rt.addObserver(IObserver observer); ### Parameters -- **`observer`**: The observer to add like <HT>`RtStateObserver`</HT> or <HT>`RtDependencyObserver`</HT>. +- **`observer`**: The observer to add like <HT>[`RtStateObserver`](/reactter/api/classes/rt_state_observer)</HT> or <HT>[`RtDependencyObserver`](/reactter/api/classes/rt_dependency_observer)</HT>. ### Usage -You can add a <HT>`RtStateObserver`</HT> to listen to the state lifecycle, e.g.: +You can add a <HT>[`RtStateObserver`](/reactter/api/classes/rt_state_observer)</HT> to listen to the state lifecycle, e.g.: ```dart showLineNumbers=false final stateObserver = RtStateObserver( @@ -131,7 +131,7 @@ final stateObserver = RtStateObserver( Rt.addObserver(stateObserver); ``` -Or add a <HT>`RtDependencyObserver`</HT> to listen to the dependency lifecycle, e.g.: +Or add a <HT>[`RtDependencyObserver`](/reactter/api/classes/rt_dependency_observer)</HT> to listen to the dependency lifecycle, e.g.: ```dart showLineNumbers=false final dependencyObserver = RtDependencyObserver( diff --git a/website/src/content/docs/extra_topics/_renaming_classes.mdx b/website/src/content/docs/extra_topics/_renaming_classes.mdx deleted file mode 100644 index 13ca9617..00000000 --- a/website/src/content/docs/extra_topics/_renaming_classes.mdx +++ /dev/null @@ -1,96 +0,0 @@ ---- -title: Renaming Classes -description: Renaming Classes in Reactter. ---- - -import { Code } from "@astrojs/starlight/components"; -import { HE, HK, HM, HN, HS, HT } from '@/components/Highlight'; -import { Tabs, TabItem } from "@/components/Tabs"; - -:::caution -This practices is not recommended. Renaming classes can cause any the following issues: -- It can make your codebase harder to understand and maintain. -- It can make it harder to find information about the class you are looking for. -- It can cause conflicts with other packages that depend on the original name. - -It's recommended to do it only when you have a good reason to do so and you understand the potential consequences. -::: - -Reactter classes are generally prefixed with <HT>`Reactter`</HT> to avoid conflicts with other packages, so you can easily identify them. This may seem verbose and redundant, but you can rename them to shorter names or other aliases if you prefer. - -To rename a class, use <HK>`typedef`</HK> to create an alias for the class. -Here is an example of renaming classes to prefix them with <HT>`Rt`</HT> instead of <HT>`Reactter`</HT>: - -<Tabs groupName="project_type"> - <TabItem name="dart" label="Only Dart" icon="dart"> - ```dart title="reactter.dart" - import 'package:reactter/reactter.dart'; - - /// Alias for [Reactter] - /// ignore: non_constant_identifier_names - final Rt = Reactter; - - /// Alias for [ReactterState] - typedef RtState = ReactterState; - - /// Alias for [ReactterHook] - typedef RtHook = ReactterHook; - - /// Alias for [ReactterDependency] - typedef RtDependency = ReactterDependency; - - /// Alias for [ReactterAction] - typedef RtAction = ReactterAction; - - /// Alias for [ReactterActionCallable] - typedef RtActionCallable = ReactterActionCallable; - ``` - </TabItem> - <TabItem name="flutter" label="With Flutter" icon="flutter"> - ```dart title="flutter_reactter.dart" - import 'package:flutter_reactter/flutter_reactter.dart'; - - /// Alias for [Reactter] - /// ignore: non_constant_identifier_names - final Rt = Reactter; - - /// Alias for [ReactterState] - typedef RtState = ReactterState; - - /// Alias for [ReactterHook] - typedef RtHook = ReactterHook; - - /// Alias for [ReactterDependency] - typedef RtDependency = ReactterDependency; - - /// Alias for [ReactterAction] - typedef RtAction = ReactterAction; - - /// Alias for [ReactterActionCallable] - typedef RtActionCallable = ReactterActionCallable; - - /// Alias for [ReactterComponent] - typedef RtComponent = ReactterComponent; - - /// Alias for [ReactterScope] - typedef RtScope = ReactterScope; - - /// Alias for [ReactterProvider] - typedef RtProvider = ReactterProvider; - - /// Alias for [ReactterProviders] - typedef RtProviders = ReactterProviders; - - /// Alias for [ReactterConsumer] - typedef RtConsumer = ReactterConsumer; - - /// Alias for [ReactterSelector] - typedef RtSelector = ReactterSelector; - - /// Alias for [ReactterWatcher] - typedef RtWatcher = ReactterWatcher; - ``` - </TabItem> -</Tabs> - - From 341d58fbbd26c9205b78dafdda9eadeaacd3c268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Wed, 19 Feb 2025 18:01:15 -0600 Subject: [PATCH 132/141] refactor(website): Add `RtDependencyRef` documentation. --- .../docs/api/classes/rt_dependency_ref.mdx | 62 ++++++++++--------- .../content/docs/core_concepts/lifecycle.mdx | 2 +- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/website/src/content/docs/api/classes/rt_dependency_ref.mdx b/website/src/content/docs/api/classes/rt_dependency_ref.mdx index bdd1b15f..fbd63961 100644 --- a/website/src/content/docs/api/classes/rt_dependency_ref.mdx +++ b/website/src/content/docs/api/classes/rt_dependency_ref.mdx @@ -6,6 +6,16 @@ sidebar: --- import { HE, HM, HT } from '@/components/Highlight'; +import CodeTabs from '@/components/CodeTabs.astro'; +import { Tabs, TabItem } from "@/components/Tabs"; +import ZappButton from "@/components/ZappButton.astro"; +import { Code } from "@astrojs/starlight/components"; +import { marks } from '@/examples/marks.ts' + +import counterControllerEventHandlerCode from '@/examples/lifecycle_event_handler/lib/counter_controller.dart?raw'; +import counterEventHandlerCode from '@/examples/lifecycle_event_handler/lib/counter.dart?raw'; +import counterViewEventHandlerCode from '@/examples/lifecycle_event_handler/lib/counter_view.dart?raw'; +import counterMainEventHandlerCode from '@/examples/lifecycle_event_handler/lib/main.dart?raw'; <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/RtDependencyRef-class.html" target="_blank">`RtDependencyRef`</a></HT> is a class that represents a reference to a dependency managed by Reactter's dependency injection system. @@ -23,33 +33,25 @@ The <HT>`RtDependencyRef`</HT> class accepts this parameter: To use <HT>`RtDependencyRef`</HT>, you need to instantiate it, pass the type of dependency you want to reference and use for event handling, e.g.: -```dart "RtDependencyRef" "Rt.on" "Rt.register" "Rt.get" "Rt.delete" -import 'package:reactter/reactter.dart'; - -// Define a dependency class -class MyDependency {} - -void main() { - // Create a dependency reference - final dependencyRef = RtDependencyRef<MyDependency>(); - - // Use the dependency reference for event handling - Rt.on(dependencyRef, Lifecycle.registered, (_, __) { - print('Dependency registered'); - }); - Rt.on(dependencyRef, Lifecycle.created, (_, __) { - print('Dependency created'); - }); - Rt.on(dependencyRef, Lifecycle.deleted, (_, __) { - print('Dependency deleted'); - }); - Rt.on(dependencyRef, Lifecycle.unregistered, (_, __) { - print('Dependency unregistered'); - }); - - // Register, create, delete, and unregister the dependency - Rt.register(() => MyDependency()); - Rt.get<MyDependency>(); - Rt.delete<MyDependency>(); -} -``` +<CodeTabs> + <ZappButton path="examples/lifecycle_event_handler"/> + + <Tabs> + <TabItem> + <HM single slot="label">counter_view.dart</HM> + <Code code={counterViewEventHandlerCode} lang="dart" collapse={["34-60"]} mark={[...marks, {range: "11-15"}, {range: "17-21"}, {range: "23-27"}, {range: "29-33"}]} /> + </TabItem> + + <TabItem label="counter_controller.dart"> + <Code code={counterControllerEventHandlerCode} lang="dart" mark={marks}/> + </TabItem> + + <TabItem label="counter.dart"> + <Code code={counterEventHandlerCode} lang="dart" mark={marks} /> + </TabItem> + + <TabItem label="main.dart"> + <Code code={counterMainEventHandlerCode} lang="dart" mark={marks} /> + </TabItem> + </Tabs> +</CodeTabs> \ No newline at end of file diff --git a/website/src/content/docs/core_concepts/lifecycle.mdx b/website/src/content/docs/core_concepts/lifecycle.mdx index 5aa677cb..94082e23 100644 --- a/website/src/content/docs/core_concepts/lifecycle.mdx +++ b/website/src/content/docs/core_concepts/lifecycle.mdx @@ -77,7 +77,7 @@ You can listen to the lifecycle events of an instance (like a dependency or stat </CodeTabs> :::note - The <HT>[`RtDependencyRef`](/reactter/api/classes/rt_dependency_ref)</HT> is a generic class used to identify the dependency in the event handler. + The <HT>[`RtDependencyRef`](/reactter/api/classes/rt_dependency)</HT> is a generic class used to identify the dependency in the event handler. It ideal to use the <HT>[`RtDependencyRef`](/reactter/api/classes/rt_dependency_ref)</HT> class when the dependency is not initialize yet. ::: From db5f7452d6aef66bbaf57d09b056aca1bb89c2aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Wed, 19 Feb 2025 18:01:38 -0600 Subject: [PATCH 133/141] refactor(core): Update documentation to specify that `buildState` returns an instance of class extending [S]. --- packages/reactter/lib/src/core/state_management.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reactter/lib/src/core/state_management.dart b/packages/reactter/lib/src/core/state_management.dart index b048161f..ec70d4d3 100644 --- a/packages/reactter/lib/src/core/state_management.dart +++ b/packages/reactter/lib/src/core/state_management.dart @@ -16,7 +16,7 @@ abstract class StateManagement<S extends IState> implements IContext { /// Creates a new state by invoking the provided `buildState` function. /// - /// The `buildState` function should return an instance of a class that extends [IState]. + /// The `buildState` function should return an instance of a class that extends [S]. /// The created state is automatically bound to the current binding zone using `BindingZone.autoBinding`. /// /// Example usage: From 80d25d12102d374f2e21a3e30d55c2b809af3929 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Wed, 19 Feb 2025 18:01:53 -0600 Subject: [PATCH 134/141] refactor(website): Update documentation for `Rt.createState` with registration details and examples. --- .../src/content/docs/api/classes/rt_state.mdx | 2 +- .../api/methods/state_management_methods.mdx | 45 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/website/src/content/docs/api/classes/rt_state.mdx b/website/src/content/docs/api/classes/rt_state.mdx index 72b15c19..1c8c3d4a 100644 --- a/website/src/content/docs/api/classes/rt_state.mdx +++ b/website/src/content/docs/api/classes/rt_state.mdx @@ -40,7 +40,7 @@ abstract class RtState<E extends RtState<E>> { ### Declaration To create a custom state, extend the <HT>`RtState`</HT> class and define the state properties and methods. -Register the state using <HM>`Rt.createState`</HM> or as a dependency within the Reactter framework, e.g.: +Register the state using <HM>[`Rt.createState`](/reactter/api/methods/state_management_methods/#rtcreatestate)</HM> or as a dependency within the Reactter framework, e.g.: ```dart collapse={13-20} mark={1,12} "RtState" "Rt.createState" "Rt.on" "Lifecycle.didUpdate" /update(?=\u0028)/ class MyState extends RtState<MyState> { diff --git a/website/src/content/docs/api/methods/state_management_methods.mdx b/website/src/content/docs/api/methods/state_management_methods.mdx index 024d689a..04274e4b 100644 --- a/website/src/content/docs/api/methods/state_management_methods.mdx +++ b/website/src/content/docs/api/methods/state_management_methods.mdx @@ -63,6 +63,51 @@ void main() { In the example, the `uCount` state is lazily declared inside the <HT>`CountController`</HT> class using <HM>`Rt.lazyState`</HM>. It's accessed after creating the <HT>`CountController`</HT> instance, and when the instance is deleted, the state is automatically disposed, ensuring efficient resource management. + +## <HM>`Rt.createState`</HM> + +<HM>`Rt.createState`</HM> is a method that allows to register a <HT>[`RtState`](/reactter/api/classes/rt_state)</HT> instance. +All states created must be registered using this method for proper management and disposal. + +:::note +In most cases, you don't need to manually use the `Rt.createState` method to create a state when using `RtHook` or `Signal` classes, as they automatically handle the registration of the state instance. + +However, if you're creating a custom state using the `RtState` class directly, you **must** use `Rt.createState` to properly register the state instance. +::: + +#### Syntax + +```dart showLineNumbers=false +T Rt.createState<T extends RtState>(T stateFn()); +``` + +#### Example + +In this example, the custom state using <HT>[`RtState`](/reactter/api/classes/rt_state)</HT> class is created and registered using <HM>`Rt.createState`</HM> method. + +```dart collapse={13-20} mark={1,12} "RtState" "Rt.createState" "Rt.on" "Lifecycle.didUpdate" /update(?=\u0028)/ +class MyState extends RtState<MyState> { + int _value = 0; + int get value => _value; + set value(int n) { + if (n == _value) return; + update(() => _value = n); // set new value and notify observers + } + + MyState([int value = 0]) : _value = value; +} + +final state = Rt.createState<MyState>(() => MyState()); // Register state + +Rt.on( + state, + Lifecycle.didUpdate, + (_, __) => print('State updated: ${state.value}') +); + +state.value = 42; // Calls update internally +``` + ## <HM>`Rt.batch`</HM> The <HM>`Rt.batch`</HM> method allows multiple state changes to be grouped together, triggering them all at the end when the <HM>`callback`</HM> completes. This ensures that associated side effects occur only once, improving performance and reducing unnecessary re-renders. From 1cbaf9ace0bcf294c2092b8d06ff58cd5bf29d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Wed, 19 Feb 2025 18:20:31 -0600 Subject: [PATCH 135/141] refactor(core): Add instance of RtInterface for framework interaction --- packages/reactter/lib/src/framework/rt_interface.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/reactter/lib/src/framework/rt_interface.dart b/packages/reactter/lib/src/framework/rt_interface.dart index ee205c33..10f2e730 100644 --- a/packages/reactter/lib/src/framework/rt_interface.dart +++ b/packages/reactter/lib/src/framework/rt_interface.dart @@ -16,4 +16,5 @@ class RtInterface /// This class represents the interface for the Reactter framework. /// It provides methods and properties for interacting with the Reactter framework. /// {@endtemplate} +// ignore: non_constant_identifier_names final Rt = RtInterface(); From fda281e35190f08cdf56decaf6e853a4a133ca96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Wed, 19 Feb 2025 19:27:55 -0600 Subject: [PATCH 136/141] refactor(tests): Replace 'test' package imports with 'flutter_test'. --- packages/reactter/lib/src/logger.dart | 4 ++-- packages/reactter/test/args_test.dart | 2 +- packages/reactter/test/core/dependency_injection_test.dart | 2 +- packages/reactter/test/core/event_handler_test.dart | 2 +- packages/reactter/test/core/lifecycle_observer_test.dart | 2 +- packages/reactter/test/core/state_management_test.dart | 2 +- packages/reactter/test/framework/rt_dependency_test.dart | 2 +- .../reactter/test/framework/rt_state_observer_test.dart | 2 +- packages/reactter/test/hooks/use_async_state_test.dart | 2 +- packages/reactter/test/hooks/use_compute_test.dart | 2 +- packages/reactter/test/hooks/use_dependency_test.dart | 2 +- packages/reactter/test/hooks/use_effect_test.dart | 2 +- packages/reactter/test/hooks/use_reducer_test.dart | 2 +- packages/reactter/test/hooks/use_state_test.dart | 2 +- packages/reactter/test/logger_test.dart | 6 +++--- packages/reactter/test/memo_test.dart | 2 +- packages/reactter/test/signal_test.dart | 2 +- 17 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/reactter/lib/src/logger.dart b/packages/reactter/lib/src/logger.dart index 87f8a0f6..d3587d83 100644 --- a/packages/reactter/lib/src/logger.dart +++ b/packages/reactter/lib/src/logger.dart @@ -219,14 +219,14 @@ class RtLogger implements IStateObserver, IDependencyObserver { break; case DependencyFail.builderRetained: log( - "${prettyFormat(dependency)}'s instance retained because it's factory mode.", + "${prettyFormat(dependency)}'s instance retained because is in factory mode.", level: LogLevel.info, stackTrace: StackTrace.current, ); break; case DependencyFail.dependencyRetained: log( - "${prettyFormat(dependency)} retained because it's singleton mode.", + "${prettyFormat(dependency)} retained because is in singleton mode.", level: LogLevel.info, stackTrace: StackTrace.current, ); diff --git a/packages/reactter/test/args_test.dart b/packages/reactter/test/args_test.dart index 446725cf..854319c4 100644 --- a/packages/reactter/test/args_test.dart +++ b/packages/reactter/test/args_test.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:reactter/reactter.dart'; -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; typedef AryFunctionType<T> = FutureOr<T> Function<T>(Args<dynamic> args); diff --git a/packages/reactter/test/core/dependency_injection_test.dart b/packages/reactter/test/core/dependency_injection_test.dart index 2643e0ed..2cb89746 100644 --- a/packages/reactter/test/core/dependency_injection_test.dart +++ b/packages/reactter/test/core/dependency_injection_test.dart @@ -1,5 +1,5 @@ import 'package:reactter/reactter.dart'; -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; import '../shareds/test_controllers.dart'; diff --git a/packages/reactter/test/core/event_handler_test.dart b/packages/reactter/test/core/event_handler_test.dart index 9ff6dcc9..e56e8042 100644 --- a/packages/reactter/test/core/event_handler_test.dart +++ b/packages/reactter/test/core/event_handler_test.dart @@ -1,7 +1,7 @@ // ignore_for_file: constant_identifier_names import 'package:reactter/reactter.dart'; -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; import '../shareds/test_controllers.dart'; diff --git a/packages/reactter/test/core/lifecycle_observer_test.dart b/packages/reactter/test/core/lifecycle_observer_test.dart index 6b988b3d..2f9edd86 100644 --- a/packages/reactter/test/core/lifecycle_observer_test.dart +++ b/packages/reactter/test/core/lifecycle_observer_test.dart @@ -1,5 +1,5 @@ import 'package:reactter/reactter.dart'; -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; import '../shareds/test_controllers.dart'; diff --git a/packages/reactter/test/core/state_management_test.dart b/packages/reactter/test/core/state_management_test.dart index c2da29da..6530709a 100644 --- a/packages/reactter/test/core/state_management_test.dart +++ b/packages/reactter/test/core/state_management_test.dart @@ -1,5 +1,5 @@ import 'package:reactter/reactter.dart'; -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; void main() { group("lazyState", () { diff --git a/packages/reactter/test/framework/rt_dependency_test.dart b/packages/reactter/test/framework/rt_dependency_test.dart index 3921b2d4..1fb5684e 100644 --- a/packages/reactter/test/framework/rt_dependency_test.dart +++ b/packages/reactter/test/framework/rt_dependency_test.dart @@ -1,5 +1,5 @@ import 'package:reactter/reactter.dart'; -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; import '../shareds/test_controllers.dart'; diff --git a/packages/reactter/test/framework/rt_state_observer_test.dart b/packages/reactter/test/framework/rt_state_observer_test.dart index 3da9652a..ae6d5523 100644 --- a/packages/reactter/test/framework/rt_state_observer_test.dart +++ b/packages/reactter/test/framework/rt_state_observer_test.dart @@ -1,6 +1,6 @@ import 'package:reactter/reactter.dart'; import 'package:reactter/src/internals.dart'; -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; void main() { group("RtStateObserver", () { diff --git a/packages/reactter/test/hooks/use_async_state_test.dart b/packages/reactter/test/hooks/use_async_state_test.dart index 5b95afd8..323e5f86 100644 --- a/packages/reactter/test/hooks/use_async_state_test.dart +++ b/packages/reactter/test/hooks/use_async_state_test.dart @@ -1,5 +1,5 @@ import 'package:reactter/reactter.dart'; -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; import '../shareds/test_controllers.dart'; diff --git a/packages/reactter/test/hooks/use_compute_test.dart b/packages/reactter/test/hooks/use_compute_test.dart index 9c4e55ff..47ed5629 100644 --- a/packages/reactter/test/hooks/use_compute_test.dart +++ b/packages/reactter/test/hooks/use_compute_test.dart @@ -1,5 +1,5 @@ import 'package:reactter/reactter.dart'; -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; import '../shareds/test_controllers.dart'; diff --git a/packages/reactter/test/hooks/use_dependency_test.dart b/packages/reactter/test/hooks/use_dependency_test.dart index 6405010d..d957a5c1 100644 --- a/packages/reactter/test/hooks/use_dependency_test.dart +++ b/packages/reactter/test/hooks/use_dependency_test.dart @@ -1,7 +1,7 @@ // ignore_for_file: constant_identifier_names import 'package:reactter/reactter.dart'; -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; import '../shareds/test_controllers.dart'; diff --git a/packages/reactter/test/hooks/use_effect_test.dart b/packages/reactter/test/hooks/use_effect_test.dart index c5f51e81..f8a1de05 100644 --- a/packages/reactter/test/hooks/use_effect_test.dart +++ b/packages/reactter/test/hooks/use_effect_test.dart @@ -1,5 +1,5 @@ import 'package:reactter/reactter.dart'; -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; import '../shareds/test_controllers.dart'; diff --git a/packages/reactter/test/hooks/use_reducer_test.dart b/packages/reactter/test/hooks/use_reducer_test.dart index 2c7da00a..e75ffde0 100644 --- a/packages/reactter/test/hooks/use_reducer_test.dart +++ b/packages/reactter/test/hooks/use_reducer_test.dart @@ -1,5 +1,5 @@ import 'package:reactter/reactter.dart'; -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; import '../shareds/test_controllers.dart'; diff --git a/packages/reactter/test/hooks/use_state_test.dart b/packages/reactter/test/hooks/use_state_test.dart index 76b3c9bc..b15a15b7 100644 --- a/packages/reactter/test/hooks/use_state_test.dart +++ b/packages/reactter/test/hooks/use_state_test.dart @@ -1,5 +1,5 @@ import 'package:reactter/reactter.dart'; -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; import '../shareds/test_controllers.dart'; diff --git a/packages/reactter/test/logger_test.dart b/packages/reactter/test/logger_test.dart index a5085792..c0a8bd55 100644 --- a/packages/reactter/test/logger_test.dart +++ b/packages/reactter/test/logger_test.dart @@ -212,7 +212,7 @@ void main() { expect(logs.last['level'], LogLevel.info); expect( logs.last['message'], - "${prettyFormat(dependencyFactoryRef)}'s instance retained because it's factory mode.", + "${prettyFormat(dependencyFactoryRef)}'s instance retained because is in factory mode.", ); Rt.destroy<StateTest>(id: id); @@ -225,7 +225,7 @@ void main() { expect(logs.last['level'], LogLevel.info); expect( logs.last['message'], - "${prettyFormat(dependencySingletonRef)} retained because it's singleton mode.", + "${prettyFormat(dependencySingletonRef)} retained because is in singleton mode.", ); Rt.destroy<StateTest>(id: id); @@ -235,7 +235,7 @@ void main() { expect( logs.last['message'], startsWith( - "${prettyFormat(RtDependencyRef<StateTest>(id))} couldn't register."), + "${prettyFormat(RtDependencyRef<StateTest>(id))} builder was not registered previously."), ); Rt.create(() => StateTest(), id: id); diff --git a/packages/reactter/test/memo_test.dart b/packages/reactter/test/memo_test.dart index 5f95a38a..d0935edb 100644 --- a/packages/reactter/test/memo_test.dart +++ b/packages/reactter/test/memo_test.dart @@ -1,7 +1,7 @@ import 'package:fake_async/fake_async.dart'; import 'package:reactter/reactter.dart'; -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; import 'shareds/test_controllers.dart'; diff --git a/packages/reactter/test/signal_test.dart b/packages/reactter/test/signal_test.dart index 4ca2f78f..780e8012 100644 --- a/packages/reactter/test/signal_test.dart +++ b/packages/reactter/test/signal_test.dart @@ -1,5 +1,5 @@ import 'package:reactter/reactter.dart'; -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; import 'shareds/test_controllers.dart'; From 227a7a7e0b424f0508674a80599e004f051c07d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Thu, 20 Feb 2025 00:10:48 -0600 Subject: [PATCH 137/141] refactor: Update `flutter_reactter` and `reactter` dependency version to ^8.0.0-dev --- CHANGELOG.md | 106 +++++++++--------- .../flutter_reactter/example/pubspec.yaml | 6 +- packages/flutter_reactter/pubspec.yaml | 14 ++- packages/reactter/pubspec.yaml | 12 +- .../framework/rt_state_observer_test.dart | 1 - .../reactter_devtools_extension/pubspec.yaml | 4 +- .../docs/migration/v7.3.0_to_v8.0.0.mdx | 2 +- .../src/examples/build_context/pubspec.yaml | 2 +- .../build_context_select/pubspec.yaml | 2 +- website/src/examples/counter/pubspec.yaml | 2 +- website/src/examples/custom_hook/pubspec.yaml | 2 +- .../dependency_injection_builder/pubspec.yaml | 2 +- .../dependency_injection_create/pubspec.yaml | 2 +- .../dependency_injection_delete/pubspec.yaml | 2 +- .../dependency_injection_destroy/pubspec.yaml | 2 +- .../dependency_injection_exists/pubspec.yaml | 2 +- .../dependency_injection_factory/pubspec.yaml | 2 +- .../dependency_injection_find/pubspec.yaml | 2 +- .../dependency_injection_get/pubspec.yaml | 2 +- .../pubspec.yaml | 2 +- .../pubspec.yaml | 2 +- .../pubspec.yaml | 2 +- .../pubspec.yaml | 2 +- .../pubspec.yaml | 2 +- .../pubspec.yaml | 2 +- .../pubspec.yaml | 2 +- .../pubspec.yaml | 2 +- .../examples/event_handler_off/pubspec.yaml | 2 +- .../event_handler_off_all/pubspec.yaml | 2 +- .../event_handler_on_emit/pubspec.yaml | 2 +- .../examples/event_handler_one/pubspec.yaml | 2 +- .../lifecycle_event_handler/pubspec.yaml | 2 +- .../lifecycle_use_effect/pubspec.yaml | 2 +- .../src/examples/rt_component/pubspec.yaml | 2 +- website/src/examples/rt_consumer/pubspec.yaml | 2 +- .../examples/rt_consumer_child/pubspec.yaml | 2 +- .../src/examples/rt_consumer_id/pubspec.yaml | 2 +- .../rt_consumer_listen_all/pubspec.yaml | 2 +- .../rt_consumer_listen_all_2/pubspec.yaml | 2 +- .../rt_consumer_listen_states/pubspec.yaml | 2 +- .../rt_dependency_lifecycle/pubspec.yaml | 2 +- .../examples/rt_multi_provider/pubspec.yaml | 2 +- .../examples/rt_provider_child/pubspec.yaml | 2 +- .../src/examples/rt_provider_id/pubspec.yaml | 2 +- .../examples/rt_provider_init/pubspec.yaml | 2 +- .../examples/rt_provider_lazy/pubspec.yaml | 2 +- .../examples/rt_provider_mode/pubspec.yaml | 2 +- website/src/examples/rt_scope/pubspec.yaml | 2 +- website/src/examples/rt_selector/pubspec.yaml | 2 +- .../examples/rt_selector_child/pubspec.yaml | 2 +- .../src/examples/rt_selector_id/pubspec.yaml | 2 +- .../examples/rt_signal_watcher/pubspec.yaml | 2 +- website/src/examples/rt_watcher/pubspec.yaml | 2 +- 53 files changed, 120 insertions(+), 117 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfc87f1c..4f5179a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,70 +1,70 @@ # Reactter -## 8.0.0-dev.22 +## 8.0.0-dev.31 ### Enhancements - Add Reactter devtools extension. -- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtInterface/removeObserver.html) methods to manage the observers. -- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). -- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). -- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. -- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. -- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtStateBase/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtStateBase/debugInfo.html) to states and hooks to get info for debugging. -- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. -- Add [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtStateBase-class.html) abstract class to implement the base logic of the state. -- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtInterface/createState.html) method to create a new state. -- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. -- Add [`initHook`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtHook/initHook.html) method to `RtHook` to call when hook is created. -- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. -- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. +- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtInterface/removeObserver.html) methods to manage the observers. +- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). +- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). +- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. +- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. +- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtState/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtState/debugInfo.html) to states and hooks to get info for debugging. +- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. +- Add [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtState-class.html) abstract class to implement the base logic of the state. +- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtInterface/createState.html) method to create a new state. +- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. +- Add [`initHook`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtHook/initHook.html) method to `RtHook` to call when hook is created. +- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. +- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. - Enhance event handling and notifier logic for improved stability and performance. - Enhance state management and error handling. -- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.22/flutter_reactter/RtComponent-class.html). +- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.31/flutter_reactter/RtComponent-class.html). - Enhance dependency management to ensure to re-build when is detected changes while building. ### Breakings -- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/Rt.html) instead. -- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtStateBase-class.html) instead. -- Replace [`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html) to [`UseAsyncStateStatus.idle`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/UseAsyncStateStatus.html). -- Rename [`DispatchEffect`](https://pub.dev/documentation/reactter/7.3.0/reactter/DispatchEffect-class.html) to [`AutoDispatchEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/DispatchEffect-class.html). -- Move `asyncFunction` to the first parameter of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/UseAsyncState/withArg.html). -- Convert [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtState-class.html) to interface class, use [`RtStateBase`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtStateBase-class.html) abstract class instead, to implement the state logic. -- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtInterface/isActive.html) instead. -- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtInterface/getDependencyMode.html) instead. -- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/DependencyMode.html) instead. -- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/Lifecycle.html) instead. -- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/Lifecycle.html) instead. -- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`LifecycleObserver.onCreated`](https://pub.dev/documentation/reactter/8.0.0-dev.22//reactter/LifecycleObserver/onCreated.html) instead. -- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.22//reactter/RtDependency-class.html) instead. -- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0-dev.22//reactter/RtHook-class.html) instead. -- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0-dev.22//reactter/RtInterface-class.html) instead. -- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtState/notify.html) instead. -- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/UseDependency-class.html) instead. -- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtAction-class.html) instead. -- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtActionCallable-class.html) instead. -- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MultiMemoInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/MultiMemoInterceptor-class.html) instead. -- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/MemoSafeAsyncInterceptor-class.html) instead. -- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/MemoTemporaryCacheInterceptor-class.html) instead. -- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/MemoWrapperInterceptor-class.html) instead. +- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/Rt.html) instead. +- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtState-class.html) instead. +- Replace [`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html) to [`UseAsyncStateStatus.idle`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/UseAsyncStateStatus.html). +- Rename [`DispatchEffect`](https://pub.dev/documentation/reactter/7.3.0/reactter/DispatchEffect-class.html) to [`AutoDispatchEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/DispatchEffect-class.html). +- Move `asyncFunction` to the first parameter of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/UseAsyncState/withArg.html). +- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtInterface/isActive.html) instead. +- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtInterface/getDependencyMode.html) instead. +- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/DependencyMode.html) instead. +- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/Lifecycle.html) instead. +- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/Lifecycle.html) instead. +- Replace [`LifecycleObserver`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver-class.html) to [`RtDependencyLifecycle`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtDependencyLifecycle-class.html). +- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`RtDependencyLifecycle.onCreated`](https://pub.dev/documentation/reactter/8.0.0-dev.31//reactter/RtDependencyLifecycle/onCreated.html) instead. +- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.31//reactter/RtDependency-class.html) instead. +- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0-dev.31//reactter/RtHook-class.html) instead. +- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0-dev.31//reactter/RtInterface-class.html) instead. +- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtState/notify.html) instead. +- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/UseDependency-class.html) instead. +- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtAction-class.html) instead. +- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtActionCallable-class.html) instead. +- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MultiMemoInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/MultiMemoInterceptor-class.html) instead. +- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/MemoSafeAsyncInterceptor-class.html) instead. +- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/MemoTemporaryCacheInterceptor-class.html) instead. +- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/MemoWrapperInterceptor-class.html) instead. - Remove [`Obj`](https://pub.dev/documentation/reactter/7.3.0/reactter/Obj-class.html). -- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.22/flutter_reactter/RtProvider/RtProvider.init.html) instead. -- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.22/flutter_reactter/RtComponent-class.html) instead. -- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.22/flutter_reactter/RtConsumer-class.html) instead. -- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.22/flutter_reactter/RtProvider-class.html) instead. -- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.22/flutter_reactter/RtMultiProvider-class.html) instead. -- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.22/flutter_reactter/RtScope-class.html) instead. -- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.22/flutter_reactter/RtSignalWatcher-class.html) instead. -- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.22/flutter_reactter/RtDependencyNotFoundException-class.html) instead. -- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.22/flutter_reactter/RtScopeNotFoundException-class.html) instead. +- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.31/flutter_reactter/RtProvider/RtProvider.init.html) instead. +- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.31/flutter_reactter/RtComponent-class.html) instead. +- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.31/flutter_reactter/RtConsumer-class.html) instead. +- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.31/flutter_reactter/RtProvider-class.html) instead. +- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.31/flutter_reactter/RtMultiProvider-class.html) instead. +- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.31/flutter_reactter/RtScope-class.html) instead. +- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0-dev.31/flutter_reactter/RtSignalWatcher-class.html) instead. +- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.31/flutter_reactter/RtDependencyNotFoundException-class.html) instead. +- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0-dev.31/flutter_reactter/RtScopeNotFoundException-class.html) instead. ### Fixes -- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/UseEffect-class.html) causing dependencies to not be unwatched. -- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtInterface/register.html) method. -- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/RtInterface/batch.html) method to work correctly. -- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0-dev.22/reactter/Notifier-class.html) class. +- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/UseEffect-class.html) causing dependencies to not be unwatched. +- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtInterface/register.html) method. +- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtInterface/batch.html) method to work correctly. +- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/Notifier-class.html) class. - Fix to propagate state param to `boundInstance`. - Remove listeners correctly from single-use listeners and handle null cases. @@ -85,7 +85,7 @@ - Restructure the framework and core classes. - Update `RtWatcher` to extend `RtScope` and refactor code. - Update examples to use new features. -- Improve dispose logic in `RtStateBase` and `UseDependency`. +- Improve dispose logic in `RtState` and `UseDependency`. - Update hook registration and state attachment logic. - Improve `null` listener handling in `Notifier` class. - Improve dispose, register and notify logic of state. @@ -442,7 +442,7 @@ If `ReactterProvider.contextOf` doesn't have a type defined, use `ReactterScope` - **refactor(framework):** Rename `_RegisterHook` to `HookRegister`. - **test:** Add `ReactterStateListExtension` test and other adjustments. - **refactor(widgets):** Make `ReactterConsumer.builder` required. -- **fix(framework):** Add instance attached validation before `UseCompute` is disponsed. +- **fix(framework):** Add instance attached validation before `UseCompute` is disposed. - **refactor(extensions):** Use `UseCompute` type on `when` method. - **test:** Add `UseCompute` test and other adjustments. - **build(example):** Change folder structure. diff --git a/packages/flutter_reactter/example/pubspec.yaml b/packages/flutter_reactter/example/pubspec.yaml index b151ac96..4dab4dcb 100644 --- a/packages/flutter_reactter/example/pubspec.yaml +++ b/packages/flutter_reactter/example/pubspec.yaml @@ -1,6 +1,6 @@ name: examples description: Reactter examples -version: 3.0.0+1 +version: 3.0.0+2 publish_to: "none" environment: @@ -12,7 +12,9 @@ dependencies: http: ^0.13.6 url_launcher: ^6.1.2 intl: ^0.17.0 - flutter_reactter: ^8.0.0-dev.22 + flutter_reactter: ^8.0.0-dev + # flutter_reactter: + # path: ../../flutter_reactter dev_dependencies: custom_lint: ^0.5.0 diff --git a/packages/flutter_reactter/pubspec.yaml b/packages/flutter_reactter/pubspec.yaml index fdbeeb14..b6f1355a 100644 --- a/packages/flutter_reactter/pubspec.yaml +++ b/packages/flutter_reactter/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://2devs-team.github.io/reactter/ documentation: https://2devs-team.github.io/reactter/ repository: https://github.com/2devs-team/reactter/tree/master/packages/flutter_reactter license: MIT License -version: 8.0.0-dev.22 +version: 8.0.0-dev.31 topics: - reactive @@ -26,7 +26,9 @@ dependencies: flutter: sdk: flutter meta: ^1.7.0 - reactter: ^8.0.0-dev.20 + reactter: ^8.0.0-dev.31 + # reactter: + # path: ../reactter dev_dependencies: flutter_test: @@ -34,7 +36,7 @@ dev_dependencies: flutter_lints: ^2.0.3 scripts: - test: "flutter test --coverage" - analyze: "dart analyze ." - public-dry-run: "dart pub publish --dry-run" - public: "dart pub publish" \ No newline at end of file + test: "fvm flutter test --coverage" + analyze: "fvm dart analyze ." + public-dry-run: "fvm dart pub publish --dry-run" + public: "fvm dart pub publish" \ No newline at end of file diff --git a/packages/reactter/pubspec.yaml b/packages/reactter/pubspec.yaml index 877e40bd..cd4f4e1f 100644 --- a/packages/reactter/pubspec.yaml +++ b/packages/reactter/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://2devs-team.github.io/reactter/ documentation: https://2devs-team.github.io/reactter/ repository: https://github.com/2devs-team/reactter/tree/master/packages/reactter license: MIT License -version: 8.0.0-dev.20 +version: 8.0.0-dev.31 topics: - reactive @@ -18,7 +18,6 @@ screenshots: - description: "Reactter Example App" path: doc/screenshots/example_app.png - environment: sdk: ">=2.19.2 <4.0.0" @@ -28,12 +27,11 @@ dependencies: dev_dependencies: fake_async: ^1.3.1 lints: ^2.0.1 - test: ^1.24.0 flutter_test: sdk: flutter scripts: - test: "flutter test --coverage" - analyze: "dart analyze ." - public-dry-run: "dart pub publish --dry-run" - public: "dart pub publish" \ No newline at end of file + test: "fvm flutter test --coverage" + analyze: "fvm dart analyze ." + public-dry-run: "fvm dart pub publish --dry-run" + public: "fvm dart pub publish" \ No newline at end of file diff --git a/packages/reactter/test/framework/rt_state_observer_test.dart b/packages/reactter/test/framework/rt_state_observer_test.dart index ae6d5523..654bb70b 100644 --- a/packages/reactter/test/framework/rt_state_observer_test.dart +++ b/packages/reactter/test/framework/rt_state_observer_test.dart @@ -1,5 +1,4 @@ import 'package:reactter/reactter.dart'; -import 'package:reactter/src/internals.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { diff --git a/packages/reactter_devtools_extension/pubspec.yaml b/packages/reactter_devtools_extension/pubspec.yaml index 6e5b609a..a8cd1ee7 100644 --- a/packages/reactter_devtools_extension/pubspec.yaml +++ b/packages/reactter_devtools_extension/pubspec.yaml @@ -13,7 +13,9 @@ dependencies: devtools_app_shared: ^0.1.1 vm_service: ^14.2.5 queue: ^3.1.0+2 - flutter_reactter: ^8.0.0-dev.16 + flutter_reactter: ^8.0.0-dev + # flutter_reactter: + # path: ../flutter_reactter dev_dependencies: flutter_test: diff --git a/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx b/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx index 145a81ff..2952e322 100644 --- a/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx +++ b/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx @@ -46,7 +46,7 @@ If you encounter any issues, refer to the [official documentation](https://pub.d - Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0/reactter/DependencyMode.html) instead. - Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0/reactter/Lifecycle.html) instead. - Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0/reactter/Lifecycle.html) instead. -- Replace [`LifecycleObserver`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver-class.html) to [`RtDependencyLifecycle`](https://pub.dev/documentation/reactter/8.0.0-dev.25/reactter/RtDependencyLifecycle-class.html). +- Replace [`LifecycleObserver`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver-class.html) to [`RtDependencyLifecycle`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtDependencyLifecycle-class.html). - Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`RtDependencyLifecycle.onCreated`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtDependencyLifecycle/onCreated.html) instead. - Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependencyRef`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtDependencyRef-class.html) instead. - Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtHook-class.html) instead. diff --git a/website/src/examples/build_context/pubspec.yaml b/website/src/examples/build_context/pubspec.yaml index 126d118d..e6bf5209 100644 --- a/website/src/examples/build_context/pubspec.yaml +++ b/website/src/examples/build_context/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/build_context_select/pubspec.yaml b/website/src/examples/build_context_select/pubspec.yaml index bb14edd6..7235c85b 100644 --- a/website/src/examples/build_context_select/pubspec.yaml +++ b/website/src/examples/build_context_select/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/counter/pubspec.yaml b/website/src/examples/counter/pubspec.yaml index 27670a8f..f1fb8fe3 100644 --- a/website/src/examples/counter/pubspec.yaml +++ b/website/src/examples/counter/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/custom_hook/pubspec.yaml b/website/src/examples/custom_hook/pubspec.yaml index 45b3bb5f..dd318b93 100644 --- a/website/src/examples/custom_hook/pubspec.yaml +++ b/website/src/examples/custom_hook/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_builder/pubspec.yaml b/website/src/examples/dependency_injection_builder/pubspec.yaml index 6717e131..824c7905 100644 --- a/website/src/examples/dependency_injection_builder/pubspec.yaml +++ b/website/src/examples/dependency_injection_builder/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_create/pubspec.yaml b/website/src/examples/dependency_injection_create/pubspec.yaml index 6458556f..1133d717 100644 --- a/website/src/examples/dependency_injection_create/pubspec.yaml +++ b/website/src/examples/dependency_injection_create/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_delete/pubspec.yaml b/website/src/examples/dependency_injection_delete/pubspec.yaml index 40be9cbc..abb3c046 100644 --- a/website/src/examples/dependency_injection_delete/pubspec.yaml +++ b/website/src/examples/dependency_injection_delete/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_destroy/pubspec.yaml b/website/src/examples/dependency_injection_destroy/pubspec.yaml index 90f67f42..cd28c3e9 100644 --- a/website/src/examples/dependency_injection_destroy/pubspec.yaml +++ b/website/src/examples/dependency_injection_destroy/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_exists/pubspec.yaml b/website/src/examples/dependency_injection_exists/pubspec.yaml index 0ca0243a..5503268c 100644 --- a/website/src/examples/dependency_injection_exists/pubspec.yaml +++ b/website/src/examples/dependency_injection_exists/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_factory/pubspec.yaml b/website/src/examples/dependency_injection_factory/pubspec.yaml index 7f15361d..ad4a09c1 100644 --- a/website/src/examples/dependency_injection_factory/pubspec.yaml +++ b/website/src/examples/dependency_injection_factory/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_find/pubspec.yaml b/website/src/examples/dependency_injection_find/pubspec.yaml index c1092bca..d822a9f2 100644 --- a/website/src/examples/dependency_injection_find/pubspec.yaml +++ b/website/src/examples/dependency_injection_find/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_get/pubspec.yaml b/website/src/examples/dependency_injection_get/pubspec.yaml index ed101da7..e0257999 100644 --- a/website/src/examples/dependency_injection_get/pubspec.yaml +++ b/website/src/examples/dependency_injection_get/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_get_dependency_mode/pubspec.yaml b/website/src/examples/dependency_injection_get_dependency_mode/pubspec.yaml index faeb722e..dd8c13a6 100644 --- a/website/src/examples/dependency_injection_get_dependency_mode/pubspec.yaml +++ b/website/src/examples/dependency_injection_get_dependency_mode/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_is_active/pubspec.yaml b/website/src/examples/dependency_injection_is_active/pubspec.yaml index 478f5fe0..b48e4bc2 100644 --- a/website/src/examples/dependency_injection_is_active/pubspec.yaml +++ b/website/src/examples/dependency_injection_is_active/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_lazy_builder/pubspec.yaml b/website/src/examples/dependency_injection_lazy_builder/pubspec.yaml index 31dbf7bd..24288010 100644 --- a/website/src/examples/dependency_injection_lazy_builder/pubspec.yaml +++ b/website/src/examples/dependency_injection_lazy_builder/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_lazy_factory/pubspec.yaml b/website/src/examples/dependency_injection_lazy_factory/pubspec.yaml index a0c7386a..86ac9089 100644 --- a/website/src/examples/dependency_injection_lazy_factory/pubspec.yaml +++ b/website/src/examples/dependency_injection_lazy_factory/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_lazy_singleton/pubspec.yaml b/website/src/examples/dependency_injection_lazy_singleton/pubspec.yaml index 86bb80ee..52917f1c 100644 --- a/website/src/examples/dependency_injection_lazy_singleton/pubspec.yaml +++ b/website/src/examples/dependency_injection_lazy_singleton/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_register/pubspec.yaml b/website/src/examples/dependency_injection_register/pubspec.yaml index c9967c43..d222f35d 100644 --- a/website/src/examples/dependency_injection_register/pubspec.yaml +++ b/website/src/examples/dependency_injection_register/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_singleton/pubspec.yaml b/website/src/examples/dependency_injection_singleton/pubspec.yaml index 19fa3f73..5914a0d0 100644 --- a/website/src/examples/dependency_injection_singleton/pubspec.yaml +++ b/website/src/examples/dependency_injection_singleton/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/dependency_injection_unregister/pubspec.yaml b/website/src/examples/dependency_injection_unregister/pubspec.yaml index 199ded38..c213a71d 100644 --- a/website/src/examples/dependency_injection_unregister/pubspec.yaml +++ b/website/src/examples/dependency_injection_unregister/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/event_handler_off/pubspec.yaml b/website/src/examples/event_handler_off/pubspec.yaml index 3ad079d2..84816d07 100644 --- a/website/src/examples/event_handler_off/pubspec.yaml +++ b/website/src/examples/event_handler_off/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/event_handler_off_all/pubspec.yaml b/website/src/examples/event_handler_off_all/pubspec.yaml index 369541cc..9b96a98c 100644 --- a/website/src/examples/event_handler_off_all/pubspec.yaml +++ b/website/src/examples/event_handler_off_all/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/event_handler_on_emit/pubspec.yaml b/website/src/examples/event_handler_on_emit/pubspec.yaml index ad53b0ac..6c7f0936 100644 --- a/website/src/examples/event_handler_on_emit/pubspec.yaml +++ b/website/src/examples/event_handler_on_emit/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/event_handler_one/pubspec.yaml b/website/src/examples/event_handler_one/pubspec.yaml index cf6ff6d5..efc1554f 100644 --- a/website/src/examples/event_handler_one/pubspec.yaml +++ b/website/src/examples/event_handler_one/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/lifecycle_event_handler/pubspec.yaml b/website/src/examples/lifecycle_event_handler/pubspec.yaml index d8dd59cd..a6371eef 100644 --- a/website/src/examples/lifecycle_event_handler/pubspec.yaml +++ b/website/src/examples/lifecycle_event_handler/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/lifecycle_use_effect/pubspec.yaml b/website/src/examples/lifecycle_use_effect/pubspec.yaml index 3368612a..5b96f096 100644 --- a/website/src/examples/lifecycle_use_effect/pubspec.yaml +++ b/website/src/examples/lifecycle_use_effect/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/rt_component/pubspec.yaml b/website/src/examples/rt_component/pubspec.yaml index 0ca2c60f..fb211f8b 100644 --- a/website/src/examples/rt_component/pubspec.yaml +++ b/website/src/examples/rt_component/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/rt_consumer/pubspec.yaml b/website/src/examples/rt_consumer/pubspec.yaml index ce2bc8f1..c4380608 100644 --- a/website/src/examples/rt_consumer/pubspec.yaml +++ b/website/src/examples/rt_consumer/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/rt_consumer_child/pubspec.yaml b/website/src/examples/rt_consumer_child/pubspec.yaml index d117e7e3..16a02ef8 100644 --- a/website/src/examples/rt_consumer_child/pubspec.yaml +++ b/website/src/examples/rt_consumer_child/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/rt_consumer_id/pubspec.yaml b/website/src/examples/rt_consumer_id/pubspec.yaml index 5f87df3b..2ae88d4a 100644 --- a/website/src/examples/rt_consumer_id/pubspec.yaml +++ b/website/src/examples/rt_consumer_id/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/rt_consumer_listen_all/pubspec.yaml b/website/src/examples/rt_consumer_listen_all/pubspec.yaml index 1f44c29b..475e258a 100644 --- a/website/src/examples/rt_consumer_listen_all/pubspec.yaml +++ b/website/src/examples/rt_consumer_listen_all/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/rt_consumer_listen_all_2/pubspec.yaml b/website/src/examples/rt_consumer_listen_all_2/pubspec.yaml index 53e24026..144e04cc 100644 --- a/website/src/examples/rt_consumer_listen_all_2/pubspec.yaml +++ b/website/src/examples/rt_consumer_listen_all_2/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/rt_consumer_listen_states/pubspec.yaml b/website/src/examples/rt_consumer_listen_states/pubspec.yaml index b0c4ae59..2edc0448 100644 --- a/website/src/examples/rt_consumer_listen_states/pubspec.yaml +++ b/website/src/examples/rt_consumer_listen_states/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/rt_dependency_lifecycle/pubspec.yaml b/website/src/examples/rt_dependency_lifecycle/pubspec.yaml index 7d5c9081..bec822d0 100644 --- a/website/src/examples/rt_dependency_lifecycle/pubspec.yaml +++ b/website/src/examples/rt_dependency_lifecycle/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/rt_multi_provider/pubspec.yaml b/website/src/examples/rt_multi_provider/pubspec.yaml index f3d4f355..50f64a0e 100644 --- a/website/src/examples/rt_multi_provider/pubspec.yaml +++ b/website/src/examples/rt_multi_provider/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/rt_provider_child/pubspec.yaml b/website/src/examples/rt_provider_child/pubspec.yaml index 469a8514..d266e0c8 100644 --- a/website/src/examples/rt_provider_child/pubspec.yaml +++ b/website/src/examples/rt_provider_child/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/rt_provider_id/pubspec.yaml b/website/src/examples/rt_provider_id/pubspec.yaml index 6db9b2e9..1131fb60 100644 --- a/website/src/examples/rt_provider_id/pubspec.yaml +++ b/website/src/examples/rt_provider_id/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/rt_provider_init/pubspec.yaml b/website/src/examples/rt_provider_init/pubspec.yaml index 1b3fda41..e6cbdf82 100644 --- a/website/src/examples/rt_provider_init/pubspec.yaml +++ b/website/src/examples/rt_provider_init/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/rt_provider_lazy/pubspec.yaml b/website/src/examples/rt_provider_lazy/pubspec.yaml index c8d9635f..1983f66f 100644 --- a/website/src/examples/rt_provider_lazy/pubspec.yaml +++ b/website/src/examples/rt_provider_lazy/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/rt_provider_mode/pubspec.yaml b/website/src/examples/rt_provider_mode/pubspec.yaml index 07515f27..112ca5fc 100644 --- a/website/src/examples/rt_provider_mode/pubspec.yaml +++ b/website/src/examples/rt_provider_mode/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/rt_scope/pubspec.yaml b/website/src/examples/rt_scope/pubspec.yaml index 396500dc..1b39e40e 100644 --- a/website/src/examples/rt_scope/pubspec.yaml +++ b/website/src/examples/rt_scope/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/rt_selector/pubspec.yaml b/website/src/examples/rt_selector/pubspec.yaml index 9e8574d1..0ffd2d82 100644 --- a/website/src/examples/rt_selector/pubspec.yaml +++ b/website/src/examples/rt_selector/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/rt_selector_child/pubspec.yaml b/website/src/examples/rt_selector_child/pubspec.yaml index a186ff11..3ee5c4ea 100644 --- a/website/src/examples/rt_selector_child/pubspec.yaml +++ b/website/src/examples/rt_selector_child/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/rt_selector_id/pubspec.yaml b/website/src/examples/rt_selector_id/pubspec.yaml index 84206ac9..993cbaa5 100644 --- a/website/src/examples/rt_selector_id/pubspec.yaml +++ b/website/src/examples/rt_selector_id/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/rt_signal_watcher/pubspec.yaml b/website/src/examples/rt_signal_watcher/pubspec.yaml index 2cb45353..e45e7010 100644 --- a/website/src/examples/rt_signal_watcher/pubspec.yaml +++ b/website/src/examples/rt_signal_watcher/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true diff --git a/website/src/examples/rt_watcher/pubspec.yaml b/website/src/examples/rt_watcher/pubspec.yaml index 3d9ed3cd..64b51095 100644 --- a/website/src/examples/rt_watcher/pubspec.yaml +++ b/website/src/examples/rt_watcher/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_reactter: ^8.0.0-dev.20 + flutter_reactter: ^8.0.0-dev flutter: uses-material-design: true From 558ea96b8885d9c31b189a7db9513665afe46f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Sun, 23 Feb 2025 00:27:17 -0600 Subject: [PATCH 138/141] refactor: Replace `createState` with `registerState`. --- CHANGELOG.md | 2 +- .../lib/examples/6_tree/states/tree_list.dart | 2 +- .../lib/examples/6_tree/states/tree_node.dart | 2 +- .../lib/src/core/state_management.dart | 10 +++++----- .../reactter/lib/src/framework/rt_state.dart | 8 ++++---- packages/reactter/lib/src/signal/signal.dart | 2 +- .../test/framework/rt_state_base_test.dart | 8 ++++---- packages/reactter/test/logger_test.dart | 2 +- .../lib/src/bases/tree_list.dart | 5 ++--- .../lib/src/nodes/dart/closure_node.dart | 2 +- .../lib/src/nodes/dart/iterable_node.dart | 2 +- .../lib/src/nodes/dart/key_value_node.dart | 2 +- .../lib/src/nodes/dart/map_node.dart | 2 +- .../src/nodes/dart/plain_instance_node.dart | 2 +- .../lib/src/nodes/dart/record_node.dart | 2 +- .../src/nodes/dependency/dependency_node.dart | 2 +- .../lib/src/nodes/instance/instance_node.dart | 2 +- .../lib/src/nodes/sentinel_node.dart | 2 +- .../lib/src/nodes/slot_node.dart | 2 +- .../lib/src/nodes/state/state_node.dart | 2 +- packages/reactter_lint/README.md | 20 +++++++++---------- packages/reactter_lint/lib/reactter_lint.dart | 2 +- .../src/rules/rt_invalid_state_creation.dart | 17 ++++++++-------- .../rules/rt_no_logic_in_create_state.dart | 10 +++++----- packages/reactter_lint/lib/src/types.dart | 4 ++-- 25 files changed, 58 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f5179a3..9f1c553d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ - Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtState/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtState/debugInfo.html) to states and hooks to get info for debugging. - Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. - Add [`RtState`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtState-class.html) abstract class to implement the base logic of the state. -- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtInterface/createState.html) method to create a new state. +- Add [`Rt.registerState`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtInterface/registerState.html) method to create a new state. - Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. - Add [`initHook`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/RtHook/initHook.html) method to `RtHook` to call when hook is created. - Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0-dev.31/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. diff --git a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart index 024d9b88..2a82c60c 100644 --- a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart +++ b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_list.dart @@ -7,7 +7,7 @@ class TreeList extends LinkedList<TreeNode> with RtState { TreeList._(); factory TreeList() { - return Rt.createState(() => TreeList._()); + return Rt.registerState(() => TreeList._()); } @override diff --git a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart index 138d8a99..8e5ce83c 100644 --- a/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart +++ b/packages/flutter_reactter/example/lib/examples/6_tree/states/tree_node.dart @@ -80,7 +80,7 @@ class TreeNode extends LinkedListEntry<TreeNode> with RtState { /// and keep it in memory until it is destroyed. factory TreeNode([TreeNode? parent]) { return Rt.singleton( - () => Rt.createState(() { + () => Rt.registerState(() { return TreeNode._(parent); }), id: _lastId.toString(), diff --git a/packages/reactter/lib/src/core/state_management.dart b/packages/reactter/lib/src/core/state_management.dart index ec70d4d3..4f795fbd 100644 --- a/packages/reactter/lib/src/core/state_management.dart +++ b/packages/reactter/lib/src/core/state_management.dart @@ -14,10 +14,10 @@ abstract class StateManagement<S extends IState> implements IContext { final LinkedHashMap<EventNotifier, Object?> _deferredEvents = LinkedHashMap(); - /// Creates a new state by invoking the provided `buildState` function. + /// Register a new state by invoking the provided `buildState` function. /// /// The `buildState` function should return an instance of a class that extends [S]. - /// The created state is automatically bound to the current binding zone using `BindingZone.autoBinding`. + /// The register state is automatically bound to the current binding zone using `BindingZone.autoBinding`. /// /// Example usage: /// ```dart @@ -30,11 +30,11 @@ abstract class StateManagement<S extends IState> implements IContext { /// } /// } /// - /// final state = Rt.createState<MyState>(() => MyState()); + /// final state = Rt.registerState<MyState>(() => MyState()); /// ``` /// - /// Returns the created state. - T createState<T extends S>(T Function() buildState) { + /// Returns [T] state. + T registerState<T extends S>(T Function() buildState) { return BindingZone.autoBinding(buildState); } diff --git a/packages/reactter/lib/src/framework/rt_state.dart b/packages/reactter/lib/src/framework/rt_state.dart index 13d173f0..7330d8ef 100644 --- a/packages/reactter/lib/src/framework/rt_state.dart +++ b/packages/reactter/lib/src/framework/rt_state.dart @@ -3,7 +3,7 @@ part of '../internals.dart'; /// {@template reactter.rt_state} /// A base class for creating a state object in Reactter. /// -/// The state object must be registered using the [Rt.createState] method or +/// The state object must be registered using the [Rt.registerState] method or /// registered as a dependency using the dependency injection. e.g. /// /// ```dart @@ -16,11 +16,11 @@ part of '../internals.dart'; /// } /// } /// -/// final state = Rt.createState<MyState>(() => MyState()); +/// final state = Rt.registerState<MyState>(() => MyState()); /// ``` /// /// See also: -/// - [Rt.createState], for creating a state object. +/// - [Rt.registerState], for register a state object. /// /// {@endtemplate} abstract class RtState implements IState { @@ -36,7 +36,7 @@ abstract class RtState implements IState { if (!isRegistering) { throw AssertionError( "The state must be create within the BindingZone.\n" - "You can use the 'Rt.createState' method or register as dependency " + "You can use the 'Rt.registerState' method or register as dependency " "using the dependency injection to ensure that the state is registered.", ); } diff --git a/packages/reactter/lib/src/signal/signal.dart b/packages/reactter/lib/src/signal/signal.dart index 8280540d..685103c8 100644 --- a/packages/reactter/lib/src/signal/signal.dart +++ b/packages/reactter/lib/src/signal/signal.dart @@ -125,7 +125,7 @@ class Signal<T> with RtState { _debugLabel = debugLabel; factory Signal(T value, {String? debugLabel}) { - return Rt.createState(() => Signal._(value, debugLabel: debugLabel)); + return Rt.registerState(() => Signal._(value, debugLabel: debugLabel)); } /// Gets and/or sets to [value] like a function diff --git a/packages/reactter/test/framework/rt_state_base_test.dart b/packages/reactter/test/framework/rt_state_base_test.dart index 2589b01b..ffa3fdac 100644 --- a/packages/reactter/test/framework/rt_state_base_test.dart +++ b/packages/reactter/test/framework/rt_state_base_test.dart @@ -29,7 +29,7 @@ class StateTest with RtState { } factory StateTest() { - return Rt.createState(() => StateTest._()); + return Rt.registerState(() => StateTest._()); } @override @@ -41,7 +41,7 @@ void main() { test('should create a state object within the BindingZone', () { expect(() => CountTest(), throwsA(isA<AssertionError>())); - final countState = Rt.createState(() => CountTest()); + final countState = Rt.registerState(() => CountTest()); expect(countState.debugLabel, 'CountTest'); final state = StateTest(); @@ -62,7 +62,7 @@ void main() { }); test('should update the state', () { - final countState = Rt.createState(() => CountTest()); + final countState = Rt.registerState(() => CountTest()); expect(countState.count, 0); countState.count = 1; @@ -70,7 +70,7 @@ void main() { }); test('should have debug info', () { - final countState = Rt.createState(() => CountTest()); + final countState = Rt.registerState(() => CountTest()); expect(countState.debugInfo, {"count": 0}); countState.count = 1; diff --git a/packages/reactter/test/logger_test.dart b/packages/reactter/test/logger_test.dart index c0a8bd55..138c0158 100644 --- a/packages/reactter/test/logger_test.dart +++ b/packages/reactter/test/logger_test.dart @@ -6,7 +6,7 @@ class StateTest with RtState { StateTest._(); factory StateTest() { - return Rt.createState(() => StateTest._()); + return Rt.registerState(() => StateTest._()); } @override diff --git a/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart b/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart index 3e651376..cf1f7cff 100644 --- a/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart +++ b/packages/reactter_devtools_extension/lib/src/bases/tree_list.dart @@ -3,13 +3,12 @@ import 'dart:collection'; import 'package:flutter_reactter/reactter.dart'; import 'package:reactter_devtools_extension/src/bases/tree_node.dart'; -base class TreeList<E extends TreeNode<E>> extends LinkedList<E> - with RtState { +base class TreeList<E extends TreeNode<E>> extends LinkedList<E> with RtState { final uMaxDepth = UseState(0); TreeList._(); - factory TreeList() => Rt.createState(() => TreeList<E>._()); + factory TreeList() => Rt.registerState(() => TreeList<E>._()); @override void add(E entry) => update(() => super.add(entry)); diff --git a/packages/reactter_devtools_extension/lib/src/nodes/dart/closure_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/dart/closure_node.dart index d14b339e..b0ea9012 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/dart/closure_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/dart/closure_node.dart @@ -16,7 +16,7 @@ final class ClosureNode extends AsyncNode { }); factory ClosureNode({required String key, required InstanceRef instanceRef}) { - return Rt.createState( + return Rt.registerState( () => ClosureNode.$( key: key, instanceRef: instanceRef, diff --git a/packages/reactter_devtools_extension/lib/src/nodes/dart/iterable_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/dart/iterable_node.dart index a31262a1..ff0128df 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/dart/iterable_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/dart/iterable_node.dart @@ -18,7 +18,7 @@ final class IterableNode extends AsyncNode { required String key, required InstanceRef instanceRef, }) { - return Rt.createState( + return Rt.registerState( () => IterableNode.$( key: key, instanceRef: instanceRef, diff --git a/packages/reactter_devtools_extension/lib/src/nodes/dart/key_value_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/dart/key_value_node.dart index b92657d1..bbeb809a 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/dart/key_value_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/dart/key_value_node.dart @@ -26,7 +26,7 @@ final class KeyValueNode extends Node { required String value, required String kind, }) { - return Rt.createState( + return Rt.registerState( () => KeyValueNode.$(key: key, value: value, kind: kind), ); } diff --git a/packages/reactter_devtools_extension/lib/src/nodes/dart/map_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/dart/map_node.dart index 0d407091..cf9efd44 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/dart/map_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/dart/map_node.dart @@ -15,7 +15,7 @@ final class MapNode extends AsyncNode { }); factory MapNode({required String key, required InstanceRef instanceRef}) { - return Rt.createState( + return Rt.registerState( () => MapNode.$(key: key, instanceRef: instanceRef), ); } diff --git a/packages/reactter_devtools_extension/lib/src/nodes/dart/plain_instance_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/dart/plain_instance_node.dart index f6880a21..7ae5ad94 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/dart/plain_instance_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/dart/plain_instance_node.dart @@ -20,7 +20,7 @@ base class PlainInstanceNode extends AsyncNode { required String key, required InstanceRef instanceRef, }) { - return Rt.createState( + return Rt.registerState( () => PlainInstanceNode._( key: key, instanceRef: instanceRef, diff --git a/packages/reactter_devtools_extension/lib/src/nodes/dart/record_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/dart/record_node.dart index 5d4fb311..8c955669 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/dart/record_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/dart/record_node.dart @@ -15,7 +15,7 @@ final class RecordNode extends AsyncNode { }); factory RecordNode({required String key, required InstanceRef instanceRef}) { - return Rt.createState( + return Rt.registerState( () => RecordNode.$( key: key, instanceRef: instanceRef, diff --git a/packages/reactter_devtools_extension/lib/src/nodes/dependency/dependency_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/dependency/dependency_node.dart index e16f3cea..a6f8ea01 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/dependency/dependency_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/dependency/dependency_node.dart @@ -8,7 +8,7 @@ final class DependencyNode extends InstanceNode<DependencyInfo> { DependencyNode.$({required super.key}) : super.$(); factory DependencyNode({required String key}) { - return Rt.createState( + return Rt.registerState( () => DependencyNode.$(key: key), ); } diff --git a/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart index 19bfffb5..f564c6cb 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/instance/instance_node.dart @@ -11,7 +11,7 @@ base class InstanceNode<I extends InstanceInfo> extends Node<I> { : super(kind: InstanceKind.kPlainInstance); factory InstanceNode({required String key}) { - return Rt.createState( + return Rt.registerState( () => InstanceNode.$(key: key), ); } diff --git a/packages/reactter_devtools_extension/lib/src/nodes/sentinel_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/sentinel_node.dart index 58ffe9ca..150cd65a 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/sentinel_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/sentinel_node.dart @@ -6,7 +6,7 @@ final class SentinelNode extends Node<NodeInfo> { SentinelNode._({required super.key}) : super(kind: 'sentinel'); factory SentinelNode({required String key}) { - return Rt.createState( + return Rt.registerState( () => SentinelNode._(key: key), ); } diff --git a/packages/reactter_devtools_extension/lib/src/nodes/slot_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/slot_node.dart index 62f10d3d..5f00e100 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/slot_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/slot_node.dart @@ -6,7 +6,7 @@ final class SlotNode extends Node<NodeInfo> { SlotNode._({required super.key}) : super(kind: 'slot'); factory SlotNode({required String key}) { - return Rt.createState( + return Rt.registerState( () => SlotNode._(key: key), ); } diff --git a/packages/reactter_devtools_extension/lib/src/nodes/state/state_node.dart b/packages/reactter_devtools_extension/lib/src/nodes/state/state_node.dart index 7533c762..305838c6 100644 --- a/packages/reactter_devtools_extension/lib/src/nodes/state/state_node.dart +++ b/packages/reactter_devtools_extension/lib/src/nodes/state/state_node.dart @@ -11,7 +11,7 @@ final class StateNode extends InstanceNode<StateInfo> { StateNode.$({required super.key}) : super.$(); factory StateNode({required String key}) { - return Rt.createState( + return Rt.registerState( () => StateNode.$(key: key), ); } diff --git a/packages/reactter_lint/README.md b/packages/reactter_lint/README.md index 02029e25..f8abd043 100644 --- a/packages/reactter_lint/README.md +++ b/packages/reactter_lint/README.md @@ -179,24 +179,24 @@ class MyState with RtState<MyState> {...} #### God -Fix: Use `Rt.createState` method for creating the state under the Reactter context. +Fix: Use `Rt.registerState` method for registering the state under the Reactter context. ```dart class MyState with RtState<MyState> {...} -final myState = Rt.createState(() => MyState()); +final myState = Rt.registerState(() => MyState()); ``` ### rt_no_logic_in_create_state -Don't put logic in `createState` method +Don't put logic in `registerState` method #### Bad -Cause: The `createState` method includes additional logic. +Cause: The `registerState` method includes additional logic. ```dart -> final myState = Rt.createState(() { +> final myState = Rt.registerState(() { > final inst = MyState(); > inst.foo(); > return inst; @@ -204,7 +204,7 @@ Cause: The `createState` method includes additional logic. ``` ```dart -> final myState = Rt.createState(() { +> final myState = Rt.registerState(() { > if (flag) return MyState(); > > return OtherState(); @@ -213,17 +213,17 @@ Cause: The `createState` method includes additional logic. #### God -Fix: Try moving the logic out of `createState` method. +Fix: Try moving the logic out of `registerState` method. ```dart -final myState = Rt.createState(() => MyState()); +final myState = Rt.registerState(() => MyState()); myState.foo(); ``` ```dart final myState = flag - ? Rt.createState(() => MyState()) - : Rt.createState(() => OtherState()); + ? Rt.registerState(() => MyState()) + : Rt.registerState(() => OtherState()); ``` ### rt_state_late_convention diff --git a/packages/reactter_lint/lib/reactter_lint.dart b/packages/reactter_lint/lib/reactter_lint.dart index 2fa80436..d56e0d7a 100644 --- a/packages/reactter_lint/lib/reactter_lint.dart +++ b/packages/reactter_lint/lib/reactter_lint.dart @@ -19,7 +19,7 @@ class _ReactterLinter extends PluginBase { RtHookNameConvention(), RtInvalidHookPosition(), RtInvalidHookRegister(), - RtNoLogicInCreateState(), + RtNoLogicInRegisterState(), RtInvalidStateCreation(), ]; } diff --git a/packages/reactter_lint/lib/src/rules/rt_invalid_state_creation.dart b/packages/reactter_lint/lib/src/rules/rt_invalid_state_creation.dart index ac83fb79..5b986ad7 100644 --- a/packages/reactter_lint/lib/src/rules/rt_invalid_state_creation.dart +++ b/packages/reactter_lint/lib/src/rules/rt_invalid_state_creation.dart @@ -11,8 +11,9 @@ class RtInvalidStateCreation extends DartLintRule { static const _code = LintCode( name: "rt_invalid_state_creation", errorSeverity: ErrorSeverity.WARNING, - problemMessage: "The `{0}` state must be create under the Reactter context.", - correctionMessage: "Use `Rt.createState` method.", + problemMessage: + "The `{0}` state must be create under the Reactter context.", + correctionMessage: "Use `Rt.registerState` method.", ); @override @@ -37,12 +38,12 @@ class RtInvalidStateCreation extends DartLintRule { final methodInvocation = node.thisOrAncestorOfType<MethodInvocation>(); final targetType = methodInvocation?.realTarget?.staticType; final methodName = methodInvocation?.methodName.staticElement; - final isCreateState = targetType != null && + final isRegisterState = targetType != null && methodName != null && rtInterface.isAssignableFromType(targetType) && - createStateType.isAssignableFrom(methodName); + registerStateType.isAssignableFrom(methodName); - if (methodInvocation == null || !isCreateState) return onInvalid(node); + if (methodInvocation == null || !isRegisterState) return onInvalid(node); final functionArg = methodInvocation.argumentList.arguments .whereType<FunctionExpression>() @@ -64,7 +65,7 @@ class RtInvalidStateCreation extends DartLintRule { }); } - bool isUsedCreateStateMethod(Set<AstNode> nodes, FunctionBody body) { + bool isUsedRegisterStateMethod(Set<AstNode> nodes, FunctionBody body) { final returnExpression = body.returnExpression; print( "RETURN EXPRESSION: $returnExpression, ${returnExpression.runtimeType}"); @@ -116,14 +117,14 @@ class _RtInvalidStateCreationFix extends DartFix { try { final changeBuilder = reporter.createChangeBuilder( message: - "Convert '${node.toString()}' to use `Rt.createState` method.", + "Convert '${node.toString()}' to use `Rt.registerState` method.", priority: 1, ); changeBuilder.addDartFileEdit((builder) { builder.addSimpleReplacement( node.sourceRange, - "Rt.createState(() => ${node.toString()})", + "Rt.registerState(() => ${node.toString()})", ); }); } catch (e) { diff --git a/packages/reactter_lint/lib/src/rules/rt_no_logic_in_create_state.dart b/packages/reactter_lint/lib/src/rules/rt_no_logic_in_create_state.dart index 3d19b1a1..7fcbd0a0 100644 --- a/packages/reactter_lint/lib/src/rules/rt_no_logic_in_create_state.dart +++ b/packages/reactter_lint/lib/src/rules/rt_no_logic_in_create_state.dart @@ -5,14 +5,14 @@ import 'package:custom_lint_builder/custom_lint_builder.dart'; import 'package:reactter_lint/src/extensions.dart'; import 'package:reactter_lint/src/types.dart'; -class RtNoLogicInCreateState extends DartLintRule { - const RtNoLogicInCreateState() : super(code: _code); +class RtNoLogicInRegisterState extends DartLintRule { + const RtNoLogicInRegisterState() : super(code: _code); static const _code = LintCode( name: "rt_no_logic_in_create_state", errorSeverity: ErrorSeverity.WARNING, - problemMessage: "Don't put logic in `createState` method.", - correctionMessage: "Try moving the logic out of `createState` method.", + problemMessage: "Don't put logic in `registerState` method.", + correctionMessage: "Try moving the logic out of `registerState` method.", ); static whenIsInvalid({ @@ -26,7 +26,7 @@ class RtNoLogicInCreateState extends DartLintRule { if (targetType == null || methodName == null) return; if (!rtInterface.isAssignableFromType(targetType)) return; - if (!createStateType.isAssignableFrom(methodName)) return; + if (!registerStateType.isAssignableFrom(methodName)) return; final functionArg = _getFunctionFromArgument(node); if (functionArg == null || functionArg.body is! BlockFunctionBody) return; diff --git a/packages/reactter_lint/lib/src/types.dart b/packages/reactter_lint/lib/src/types.dart index 4c3acc42..59825db0 100644 --- a/packages/reactter_lint/lib/src/types.dart +++ b/packages/reactter_lint/lib/src/types.dart @@ -11,7 +11,7 @@ final reactterType = TypeChecker.fromPackage('reactter'); final rtInterface = TypeChecker.fromName('RtInterface', packageName: 'reactter'); -final createStateType = - TypeChecker.fromName('createState', packageName: 'reactter'); +final registerStateType = + TypeChecker.fromName('registerState', packageName: 'reactter'); final lazyStateType = TypeChecker.fromName('lazyState', packageName: 'reactter'); From 2eba07f6580daa593b98c48296aa86ebfad77dd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Sun, 23 Feb 2025 00:27:44 -0600 Subject: [PATCH 139/141] refactor(website): Update documentation to replace `Rt.createState` with `Rt.registerState`. --- .../src/content/docs/api/classes/rt_state.mdx | 20 +++++++++---------- .../api/methods/state_management_methods.mdx | 16 +++++++-------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/website/src/content/docs/api/classes/rt_state.mdx b/website/src/content/docs/api/classes/rt_state.mdx index 1c8c3d4a..dbfb558c 100644 --- a/website/src/content/docs/api/classes/rt_state.mdx +++ b/website/src/content/docs/api/classes/rt_state.mdx @@ -40,9 +40,9 @@ abstract class RtState<E extends RtState<E>> { ### Declaration To create a custom state, extend the <HT>`RtState`</HT> class and define the state properties and methods. -Register the state using <HM>[`Rt.createState`](/reactter/api/methods/state_management_methods/#rtcreatestate)</HM> or as a dependency within the Reactter framework, e.g.: +Register the state using <HM>[`Rt.registerState`](/reactter/api/methods/state_management_methods/#rtregisterstate)</HM> or as a dependency within the Reactter framework, e.g.: -```dart collapse={13-20} mark={1,12} "RtState" "Rt.createState" "Rt.on" "Lifecycle.didUpdate" /update(?=\u0028)/ +```dart collapse={13-20} mark={1,12} "RtState" "Rt.registerState" "Rt.on" "Lifecycle.didUpdate" /update(?=\u0028)/ class MyState extends RtState<MyState> { int _value = 0; int get value => _value; @@ -54,7 +54,7 @@ class MyState extends RtState<MyState> { MyState([int value = 0]) : _value = value; } -final state = Rt.createState<MyState>(() => MyState()); // Register state +final state = Rt.registerState<MyState>(() => MyState()); // Register state Rt.on( state, @@ -68,7 +68,7 @@ state.value = 42; // Calls update internally :::tip You can also use the factory constructor to create a state, e.g.: -```dart collapse={2-8, 15-21} mark={11-13} "RtState" "Rt.createState" "Rt.on" "Lifecycle.didUpdate" /update(?=\u0028)/ +```dart collapse={2-8, 15-21} mark={11-13} "RtState" "Rt.registerState" "Rt.on" "Lifecycle.didUpdate" /update(?=\u0028)/ class MyState extends RtState<MyState> { int _value = 0; int get value => _value; @@ -80,7 +80,7 @@ class MyState extends RtState<MyState> { MyState._([int value = 0]) : _value = value; factory MyState(int value) { - return Rt.createState(() => MyState._(value)); // Register state + return Rt.registerState(() => MyState._(value)); // Register state } } @@ -93,14 +93,14 @@ Rt.on( state.value = 42; // Calls update internally ``` -This approach allows you to create a state directly without the need to call the <HM>`Rt.createState`</HM> method explicitly. +This approach allows you to create a state directly without the need to call the <HM>`Rt.registerState`</HM> method explicitly. ::: ### Updating the state To update the state, call the <HM>`update`</HM> method and provide a callback function that modifies the state, e.g.: -```dart collapse={2-3, 8-13} mark={6, 22} "RtState" "Rt.createState" "Rt.on" "Lifecycle.didUpdate" /update(?=\u0028)/ +```dart collapse={2-3, 8-13} mark={6, 22} "RtState" "Rt.registerState" "Rt.on" "Lifecycle.didUpdate" /update(?=\u0028)/ class MyState extends RtState<MyState> { int _value = 0; int get value => _value; @@ -112,7 +112,7 @@ class MyState extends RtState<MyState> { MyState._([int value = 0]) : _value = value; factory MyState(int value) { - return Rt.createState(() => MyState._(value)); // Register state + return Rt.registerState(() => MyState._(value)); // Register state } } @@ -132,7 +132,7 @@ When a state is updated, it emits the following lifecycle events: - <HE>`Lifecycle.willUpdate`</HE> event is triggered before the callback function of <HM>`update`</HM> method have been executed. - <HE>`Lifecycle.didUpdate`</HE> event is triggered after the callback function of <HM>`update`</HM> method have been executed or after the <HM>`notify`</HM> method have been invoked. -```dart collapse={2-3, 8-13} mark={16-20} "RtState" "Rt.createState" "Rt.on" "Lifecycle.didUpdate" /update(?=\u0028)/ +```dart collapse={2-3, 8-13} mark={16-20} "RtState" "Rt.registerState" "Rt.on" "Lifecycle.didUpdate" /update(?=\u0028)/ class MyState extends RtState<MyState> { int _value = 0; int get value => _value; @@ -144,7 +144,7 @@ class MyState extends RtState<MyState> { MyState._([int value = 0]) : _value = value; factory MyState(int value) { - return Rt.createState(() => MyState._(value)); // Register state + return Rt.registerState(() => MyState._(value)); // Register state } } diff --git a/website/src/content/docs/api/methods/state_management_methods.mdx b/website/src/content/docs/api/methods/state_management_methods.mdx index 04274e4b..5c05fa20 100644 --- a/website/src/content/docs/api/methods/state_management_methods.mdx +++ b/website/src/content/docs/api/methods/state_management_methods.mdx @@ -64,28 +64,28 @@ void main() { In the example, the `uCount` state is lazily declared inside the <HT>`CountController`</HT> class using <HM>`Rt.lazyState`</HM>. It's accessed after creating the <HT>`CountController`</HT> instance, and when the instance is deleted, the state is automatically disposed, ensuring efficient resource management. -## <HM>`Rt.createState`</HM> +## <HM>`Rt.registerState`</HM> -<HM>`Rt.createState`</HM> is a method that allows to register a <HT>[`RtState`](/reactter/api/classes/rt_state)</HT> instance. +<HM>`Rt.registerState`</HM> is a method that allows to register a <HT>[`RtState`](/reactter/api/classes/rt_state)</HT> instance. All states created must be registered using this method for proper management and disposal. :::note -In most cases, you don't need to manually use the `Rt.createState` method to create a state when using `RtHook` or `Signal` classes, as they automatically handle the registration of the state instance. +In most cases, you don't need to manually use the <HM>`Rt.registerState`</HM> method to create a state when using <HT>[`RtHook`](/reactter/api/classes/rt_hook)</HT> or <HT>[`Signal`](/reactter/api/classes/signal)</HT> classes, as they automatically handle the registration of the state instance. -However, if you're creating a custom state using the `RtState` class directly, you **must** use `Rt.createState` to properly register the state instance. +However, if you're creating a custom state using the <HT>[`RtState`](/reactter/api/classes/rt_state)</HT> class directly, you **must** use <HM>`Rt.registerState`</HM> to properly register the state instance. ::: #### Syntax ```dart showLineNumbers=false -T Rt.createState<T extends RtState>(T stateFn()); +T Rt.registerState<T extends RtState>(T stateFn()); ``` #### Example -In this example, the custom state using <HT>[`RtState`](/reactter/api/classes/rt_state)</HT> class is created and registered using <HM>`Rt.createState`</HM> method. +In this example, the custom state using <HT>[`RtState`](/reactter/api/classes/rt_state)</HT> class is created and registered using <HM>`Rt.registerState`</HM> method. -```dart collapse={13-20} mark={1,12} "RtState" "Rt.createState" "Rt.on" "Lifecycle.didUpdate" /update(?=\u0028)/ +```dart collapse={13-20} mark={1,12} "RtState" "Rt.registerState" "Rt.on" "Lifecycle.didUpdate" /update(?=\u0028)/ class MyState extends RtState<MyState> { int _value = 0; int get value => _value; @@ -97,7 +97,7 @@ class MyState extends RtState<MyState> { MyState([int value = 0]) : _value = value; } -final state = Rt.createState<MyState>(() => MyState()); // Register state +final state = Rt.registerState<MyState>(() => MyState()); // Register state Rt.on( state, From 7ee9fba9acddd02aee5483d7e57aa99c98fd6100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Sun, 23 Feb 2025 00:28:47 -0600 Subject: [PATCH 140/141] refactor(website): Update override, migration and state management documentation. --- .../docs/core_concepts/state_management.mdx | 6 +- .../es/core_concepts/state_management.mdx | 10 +- website/src/content/docs/es/overview.mdx | 31 +++-- .../content/docs/es/shareds/state_methods.mdx | 29 ++--- .../docs/es/shareds/state_methods_lite.mdx | 16 --- .../es/shareds/state_properties_methods.mdx | 10 ++ .../shareds/state_properties_methods_ref.mdx | 11 ++ .../docs/migration/v7.3.0_to_v8.0.0.mdx | 110 +++++++++--------- website/src/content/docs/overview.mdx | 35 +++--- .../content/docs/shareds/state_methods.mdx | 1 - .../custom_hook/lib/use_text_input.dart | 2 - 11 files changed, 129 insertions(+), 132 deletions(-) delete mode 100644 website/src/content/docs/es/shareds/state_methods_lite.mdx create mode 100644 website/src/content/docs/es/shareds/state_properties_methods.mdx create mode 100644 website/src/content/docs/es/shareds/state_properties_methods_ref.mdx diff --git a/website/src/content/docs/core_concepts/state_management.mdx b/website/src/content/docs/core_concepts/state_management.mdx index 94142eb9..8f29285d 100644 --- a/website/src/content/docs/core_concepts/state_management.mdx +++ b/website/src/content/docs/core_concepts/state_management.mdx @@ -96,8 +96,8 @@ void countdown(Timer timer) { Now let's see what the <HT>`Signal`</HT> class contains and how the `count` state is updated in the example above. -```dart title="signal.dart" "RtContextMixin" "Rt.createState" "RtState" "_value" "update" -class Signal<T> with RtState<Signal<T>> { +```dart title="signal.dart" "RtContextMixin" "Rt.registerState" "RtState" "_value" "update" +class Signal<T> with RtState { // State value T _value; @@ -116,7 +116,7 @@ class Signal<T> with RtState<Signal<T>> { factory Signal(T value) { // Register a new state in the Reactter context - return Rt.createState( + return Rt.registerState( () => Signal._(value), ); } diff --git a/website/src/content/docs/es/core_concepts/state_management.mdx b/website/src/content/docs/es/core_concepts/state_management.mdx index 0582104a..b486cddf 100644 --- a/website/src/content/docs/es/core_concepts/state_management.mdx +++ b/website/src/content/docs/es/core_concepts/state_management.mdx @@ -48,9 +48,8 @@ Todos los estados en Reactter son clases que heredan de <HT><a href="https://pub la cual encapsula los datos y el comportamiento de un estado particular, y proporciona una forma de notificar a los observadores cuando el estado cambia ### Metodos del estado -Reactter ofrece tres enfoques fundamentales para crear estados: <HT>[`RtState`](/reactter/es/api/classes/RtState)</HT>, <HT>[`Signal`](/reactter/es/api/classes/signal)</HT> y <HT>[`Hooks`](/reactter/es/core_concepts/hooks)</HT>. -La clase <HT><a href="https://pub.dev/documentation/reactter/latest/reactter/RtState-class.html" target="_blank">`RtState`</a></HT> proporciona algunos métodos para gestionar los estados, que son: +<HT>[`RtState`](/reactter/api/classes/rt_state)</HT> clase proporciona algunos métodos para la gestión de estados. Conozcámoslos: <StateMethods /> @@ -90,9 +89,10 @@ void countdown(Timer timer) { ``` Ahora veamos que contiene la clase <HT>`Signal`</HT> y cómo se actualiza el estado `count` en el ejemplo anterior -class Signal<T> with RtContextMixin, RtState<Signal<T>> { + +```dart title="signal.dart" "Rt.registerState" "RtState" "_value" "update" +class Signal<T> with RtState { // Valor del estado -```dart title="signal.dart" "RtContextMixin" "Rt.createState" "RtState" "_value" "update" T _value; // Constructor privada, solo se puede crear una instancia de `Signal` a través del factory. @@ -100,7 +100,7 @@ class Signal<T> with RtContextMixin, RtState<Signal<T>> { factory Signal(T value) { // Se registra un nuevo estado en el contexto de Reactter - return Rt.createState( + return Rt.registerState( () => Signal._(value), ); } diff --git a/website/src/content/docs/es/overview.mdx b/website/src/content/docs/es/overview.mdx index 4bfbc137..783905bc 100644 --- a/website/src/content/docs/es/overview.mdx +++ b/website/src/content/docs/es/overview.mdx @@ -3,27 +3,24 @@ title: Introducción description: Conoce Reactter, por qué se creó y sus características. --- -La gestión de estados es fundamental en las aplicaciones, ya que determina cómo se almacena, actualiza y comparte la información dentro de la misma. -En Flutter nos proporciona la clase `StafulWidget` para manejar el estado local, que suele ser suficiente para aplicaciones pequeñas, -sin embargo, a medida que la aplicación crece, la gestión del estado se vuelve más compleja y difícil de mantener. Tambien contamos -con la clase `InheritedWidget` para gestionar el estado global, pero es un poco complicado de usar. -Es por ello que existen diversos paquetes para gestionar el estado global. +import { HT } from '@/components/Highlight'; -Aunque la gestión del estado es un componente clave de la arquitectura de una aplicación, no es el único factor a tener en cuenta. -La inyección de dependencias, el manejo de eventos, y, en el caso de las aplicaciones Flutter, un control adecuado del renderizado, -son igualmente cruciales para asegurar que la aplicación sea escalable, flexible y fácil de mantener a largo plazo. -Todos estos aspectos deben ser considerados de forma conjunta para crear una arquitectura sólida y eficiente. +La gestión del estado es la columna vertebral de cualquier aplicación Flutter, pero a medida que las aplicaciones crecen en complejidad, también lo hacen los retos de mantener un código limpio, escalable y eficiente. +Aunque Flutter ofrece herramientas integradas como <HT>`StatefulWidget`</HT>, a menudo se quedan cortas cuando se trata de gestionar el estado global o manejar casos de uso avanzados. +Esto ha dado lugar a una amplia variedad de paquetes de gestión de estado, cada uno con sus propios puntos fuertes, pero también con sus propias desventajas. -Si bien existen numerosos paquetes para gestionar el estado, muchos de ellos no ofrecen soluciones completas ni lo suficientemente robustas. -Este desafío se intensifica aún más en aplicaciones desarrolladas exclusivamente con Dart, donde la falta de opciones adecuadas para el manejo del estado global hace que la gestión se vuelva aún más desafiante. +**Reactter** nació del deseo de crear algo diferente, una solución de gestión de estados que no se limita a resolver problemas, sino que eleva toda la experiencia de desarrollo. +Inspirado en la simplicidad y reactividad de ReactJS, Reactter aporta un enfoque fresco y moderno al desarrollo con Dart y Flutter. +No es sólo otro paquete de gestión de estado, es un framework completo, diseñado para hacer su aplicación más rápida, su código más limpio y su vida como desarrollador más fácil. -Con esto en mente, se creó **Reactter**, un paquete **_ligero_**, **_poderoso_** y **_reactivo_** [**gestión de estados**](/reactter/es/core_concepts/state_management), [**inyección de dependencias**](/reactter/es/core_concepts/dependency_injection) y [**manejo de eventos**](/reactter/es/core_concepts/event_handler) para Dart/Flutter. +¿Qué diferencia a Reactter? Mientras que otros paquetes se centran únicamente en la gestión de estados, Reactter va más allá integrando a la perfección **inyección de dependencias**, **manejo de eventos** y **control de renderizado** en un único framework cohesionado. +Es ligero pero potente, sencillo pero flexible, y está diseñado para funcionar a la perfección con cualquier arquitectura o patrón. +Tanto si estás construyendo un proyecto pequeño como una aplicación a gran escala, Reactter te da las herramientas para escribir menos codigo repetitivo, mejorar la legibilidad y mantener un control granular sobre el comportamiento de tu aplicación. -## Motivación - -**Reactter** nace como un experimento al desarrollar una nueva forma de gestionar el estado en proyectos Flutter que fuera eficiente, reactivo, escalable, y a la vez simple y fácil de usar. - -Inspirado en ReactJS, Reactter(**Reac**t-Fu**tter**) adopta conceptos clave de esta biblioteca a Dart y Flutter, ofreciendo una solución optimizada y coherente con las necesidades del desarrollo actual. +Pero Reactter no se trata sólo de características, se trata de una filosofía. +Creemos que la gestión de estados debe ser intuitiva, no intimidante. +Es por eso que Reactter se construye con una mentalidad *centrada en el desarrollador*, ofreciendo una sintaxis que es fácil de aprender, un flujo de trabajo que es fácil de depurar, y un diseño que es fácil de amar. +Con Reactter, no solo gestionas el estado, sino que desbloqueas todo el potencial de tus aplicaciones. ## Características diff --git a/website/src/content/docs/es/shareds/state_methods.mdx b/website/src/content/docs/es/shareds/state_methods.mdx index 76e4f255..4c659666 100644 --- a/website/src/content/docs/es/shareds/state_methods.mdx +++ b/website/src/content/docs/es/shareds/state_methods.mdx @@ -3,21 +3,22 @@ title: Metodos del estado --- import { HE, HM, HT } from '@/components/Highlight'; -- <HM><a href="https://pub.dev/documentation/reactter/latest/reactter/RtState/update.html" target="_blank">`update`</a></HM>: Ejecuta una función de devolución de llamada y notifica a sus oyentes que el estado ha cambiado. -Cuando se invoca, emite dos eventos para señalar la transición de estado: -Primeramente se emite <HE>`Lifecycle.willUpdate`</HE>, indicando la próxima actualización, seguido de <HE>`Lifecycle.didUpdate`</HE> una vez que el proceso de actualización está completo. -- <HM><a href="https://pub.dev/documentation/reactter/latest/reactter/RtState/notify.html" target="_blank">`notify`</a></HM>: Fuerza la notificacion a los oyentes del estado de su cambio. -A diferencia de <HM>`update`</HM>, emite solo el evento <HE>`Lifecycle.didUpdate`</HE>, ya que no implica ningún paso preparatorio antes de la notificación. -- <HM><a href="https://pub.dev/documentation/reactter/latest/reactter/RtState/bind.html" target="_blank">`bind`</a></HM>: Establece una conexión entre el estado y una instancia específica. -Esta conexión permite que la instancia se actualice de forma reactiva en función de los cambios en el estado. -Al enlazar el estado, la instancia es notificada de los cambios en el estado y puede reflejar adecuadamente esos cambios en su comportamiento. -- <HM><a href="https://pub.dev/documentation/reactter/latest/reactter/RtState/unbind.html" target="_blank">`unbind`</a></HM>: Libera la conexión entre el estado y la instancia. -Al desvincular, la instancia ya no recibirá actualizaciones del estado. -Esto puede ser útil cuando una instancia ya no está utilizando activamente el estado o cuando necesita separarse del estado temporalmente o permanentemente. -- <HM><a href="https://pub.dev/documentation/reactter/latest/reactter/RtState/dispose.html" target="_blank">`dispose`</a></HM>: Es responsable de limpiar el estado y cualquier oyente o recurso asociado. +- <HM>**`update`**</HM>: Ejecuta una función callback y notifica a sus observadores que el estado ha cambiado. +Cuando se invoca, emite dos eventos [lifecycle](/reactter/es/core_concepts/lifecycle) para señalar la transición de estado: + - <HE>`Lifecycle.willUpdate`</HE> se emite primero, indicando la actualización inminente. + - <HE>`Lifecycle.didUpdate`</HE> se emite una vez que el proceso de actualización se ha completado. +- <HM>**`notify`**</HM>: Fuerza al estado a notificar a sus observadores. +A diferencia de <HM>`update`</HM>, sólo emite el evento <HE>`Lifecycle.didUpdate`</HE>, ya que no implica ningún paso previo a la notificación. +- <HM>**`bind`**</HM>: Establece una conexión entre el estado y una instancia específica. +Esta conexión permite a la instancia actualizarse de forma reactiva en función de los cambios en el estado. +Al vincular el estado, la instancia se da cuenta de los cambios en el estado y puede reflejar adecuadamente esos cambios en su comportamiento. +- <HM>**`unbind`**</HM>: Libera la conexión entre el estado y la instancia. +Al desvincularse, la instancia dejará de recibir actualizaciones del estado. +Esto puede ser útil cuando una instancia ya no está utilizando activamente el estado o cuando necesita desvincularse del estado temporal o permanentemente. +- <HM>**`dispose`**</HM>: Es responsable de limpiar el estado y cualquier observador o recurso asociado. Disponer del estado garantiza que se libere correctamente y que ya no consuma memoria o recursos de procesamiento innecesariamente. :::note -Los métodos <HM>`bind`</HM>, <HM>`unbind`</HM> y <HM>`dispose`</HM> se utilizan normalmente para gestionar el <HT>[Ciclo de vida](/reactter/es/core_concepts/lifecycle)</HT> de un estado. -Sin embargo, Reactter maneja esto automáticamente, cuando el estado se declara dentro de una instancia que existe dentro del contexto de Reactter a través de la [inyección de dependencias](/reactter/es/core_concepts/dependency_injection/), por lo que no es necesario preocuparse por ello(Aprende más sobre [vinculación del estado a una dependencia](/reactter/es/extra_topics/binding_state_to_dependency/)). +Mientras que los métodos <HM>`bind`</HM>, <HM>`unbind`</HM> y <HM>`dispose`</HM> se utilizan normalmente para gestionar el [ciclo de vida](/reactter/es/core_concepts/lifecycle) de un estado, +Reactter maneja esto automáticamente, cuando el estado es declarado dentro de una instancia que existe dentro del contexto de Reactter vía [dependecy injection](/reactter/es/core_concepts/dependency_injection/), por lo que no necesitas preocuparte por ello(Learn more about [Binding State to Dependency](/reactter/es/extra_topics/binding_state_to_dependency/)). ::: \ No newline at end of file diff --git a/website/src/content/docs/es/shareds/state_methods_lite.mdx b/website/src/content/docs/es/shareds/state_methods_lite.mdx deleted file mode 100644 index 0516696d..00000000 --- a/website/src/content/docs/es/shareds/state_methods_lite.mdx +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: State Methods ---- - -import { HM, HT } from '@/components/Highlight'; - -- Metodos heredados de <HT>`RtState`</HT>(Aprende más [aquí](/reactter/es/core_concepts/state_management/#state-methods)): - - <HM>`update`</HM>: Un método para notificar cambios después de ejecutar un conjunto de instrucciones. - - <HM>`notify`</HM>: Un método para forzar a notificar cambios. - - *<HM>`bind`</HM>: Un método para vincular una instancia a él. - - *<HM>`unbind`</HM>: Un método para desvincular una instancia de él. - - *<HM>`dispose`</HM>: Un método para eliminar todos los oyentes y liberar recursos. - - :::note - \* Estos métodos son innecesarios cuando se declara dentro de una dependencia(clase registrada a través de la [inyección de dependencia](/reactter/core_concepts/dependency_injection)). - ::: diff --git a/website/src/content/docs/es/shareds/state_properties_methods.mdx b/website/src/content/docs/es/shareds/state_properties_methods.mdx new file mode 100644 index 00000000..f078664f --- /dev/null +++ b/website/src/content/docs/es/shareds/state_properties_methods.mdx @@ -0,0 +1,10 @@ +--- +title: Properties & Methods of RtState +--- + +import { HT } from '@/components/Highlight'; +import StateMethods from '@/content/docs/es/shareds/state_methods.mdx'; + +- **`debugLabel`**: Una cadena que representa la etiqueta del estado para propósitos de depuración. +- **`debugInfo`**: Un <HT>`Map`</HT> que contiene información de depuración sobre el estado. +<StateMethods/> diff --git a/website/src/content/docs/es/shareds/state_properties_methods_ref.mdx b/website/src/content/docs/es/shareds/state_properties_methods_ref.mdx new file mode 100644 index 00000000..54e0f6fc --- /dev/null +++ b/website/src/content/docs/es/shareds/state_properties_methods_ref.mdx @@ -0,0 +1,11 @@ +--- +title: Properties & Methods of RtState +--- + +import { HT } from '@/components/Highlight'; +import StatePropertiesMethods from '@/content/docs/es/shareds/state_properties_methods.mdx'; + +<details> + <summary>Propiedades y métodos heradados de <HT>[`RtState`](/reactter/api/classes/rt_state)</HT></summary> + <StatePropertiesMethods/> +</details> \ No newline at end of file diff --git a/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx b/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx index 2952e322..a17a8517 100644 --- a/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx +++ b/website/src/content/docs/migration/v7.3.0_to_v8.0.0.mdx @@ -9,74 +9,74 @@ The new version introduces several **enhancements**, **breaking changes**, and * Below, we'll walk you through the necessary steps to update your codebase. By following this guide, you should be able to successfully migrate your project to Reactter v8.0.0. -If you encounter any issues, refer to the [official documentation](https://pub.dev/documentation/reactter/8.0.0) or report them as an [issue on Github](https://github.com/2devs-team/reactter/issues).. +If you encounter any issues, refer to the [official documentation](https://pub.dev/documentation/reactter/8.0.0) or report them as an [issue on Github](https://github.com/2devs-team/reactter/issues). ## 1. Overview of Changes ### Enhancements - Add Reactter devtools extension. -- Add [`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/addObserver.html) and [`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/removeObserver.html) methods to manage the observers. -- Add [`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtStateObserver-class.html) class to monitor the lifecycle of state(`onStateCreated`, `onStateBound`, `onStateUnbound`, `onStateUpdated` and `onStateDisposed`). -- Add [`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtDependencyObserver-class.html) class to monitor the lifecycle of dependency(`onDependencyRegistered`, `onDependencyCreated`, `onDependencyMounted`, `onDependencyUnmounted`, `onDependencyDeleted`, `onDependencyUnregistered` and `onDependencyFailed`). -- Add [`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtLoggerExt/initializeLogger.html) to initialize the Reactter logger. -- Add [`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtDevToolsExt/initializeDevTools.html) to initialize the devtools for observing the states and dependencies. -- Add [`debugLabel`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtState/debugLabel.html) and [`debugInfo`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtState/debugInfo.html) to states and hooks to get info for debugging. -- Add [`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtContextMixin-mixin.html) mixin to provide access to the `Rt` instance. -- Add [`RtState`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtState-class.html) abstract class to implement the base logic of the state. -- Add [`Rt.createState`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/createState.html) method to create a new state. -- Add [`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/getRefAt.html) to get the reference of dependency created. -- Add [`initHook`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtHook/initHook.html) method to `RtHook` to call when hook is created. -- Add [`cancel`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseAsyncState/cancel.html) method to `UseAsyncState` to cancel current method. -- Update [`batch`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/batch.html) and [`untracked`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/untracked.html) methods to support asynchronous callbacks. +- Add <HM>[`Rt.addObserver`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/addObserver.html)</HM> and <HM>[`Rt.removeObserver`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/removeObserver.html)</HM> methods to manage the observers. +- Add <HT>[`RtStateObserver`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtStateObserver-class.html)</HT> class to monitor the lifecycle of state(<HM>`onStateCreated`</HM>, <HM>`onStateBound`</HM>, <HM>`onStateUnbound`</HM>, <HM>`onStateUpdated`</HM> and <HM>`onStateDisposed`</HM>). +- Add <HT>[`RtDependencyObserver`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtDependencyObserver-class.html)</HT> class to monitor the lifecycle of dependency(<HM>`onDependencyRegistered`</HM>, <HM>`onDependencyCreated`</HM>, <HM>`onDependencyMounted`</HM>, <HM>`onDependencyUnmounted`</HM>, <HM>`onDependencyDeleted`</HM>,<HM> `onDependencyUnregistered`</HM> and <HM>`onDependencyFailed`</HM>). +- Add <HM>[`Rt.initializeLogger`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtLoggerExt/initializeLogger.html)</HM> to initialize the Reactter logger. +- Add <HM>[`Rt.initializeDevTools`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtDevToolsExt/initializeDevTools.html)</HM> to initialize the devtools for observing the states and dependencies. +- Add <HM>[`debugLabel`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtState/debugLabel.html)</HM> and <HM>[`debugInfo`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtState/debugInfo.html)</HM> to states and hooks to get info for debugging. +- Add <HT>[`RtContextMixin`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtContextMixin-mixin.html)</HT> mixin to provide access to the `Rt` instance. +- Add <HT>[`RtState`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtState-class.html)</HT> abstract class to implement the base logic of the state. +- Add <HM>[`Rt.registerState`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/registerState.html)</HM> method to create a new state. +- Add <HM>[`Rt.getRefAt`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/getRefAt.html)</HM> to get the reference of dependency created. +- Add <HM>[`initHook`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtHook/initHook.html)</HM> method to <HT>`RtHook`</HT> to call when hook is created. +- Add <HM>[`cancel`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseAsyncState/cancel.html)</HM> method to <HT>`UseAsyncState`</HT> to cancel current method. +- Update <HM>[`batch`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/batch.html)</HM> and <HM>[`untracked`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/untracked.html)</HM> methods to support asynchronous callbacks. - Enhance event handling and notifier logic for improved stability and performance. - Enhance state management and error handling. -- Add dependency `mode` to [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtComponent-class.html). +- Add dependency <HE>`mode`</HE> to <HT>[`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtComponent-class.html)</HT>. - Enhance dependency management to ensure to re-build when is detected changes while building. ### Breakings -- Remove [`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html), use [`Rt`](https://pub.dev/documentation/reactter/8.0.0/reactter/Rt.html) instead. -- Remove [`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html) and [`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html), use [`RtState`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtState-class.html) instead. -- Replace [`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html) to [`UseAsyncStateStatus.idle`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseAsyncStateStatus.html). -- Rename [`DispatchEffect`](https://pub.dev/documentation/reactter/7.3.0/reactter/DispatchEffect-class.html) to [`AutoDispatchEffect`](https://pub.dev/documentation/reactter/8.0.0/reactter/DispatchEffect-class.html). -- Move `asyncFunction` to the first parameter of [`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseAsyncState-class.html) and [`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseAsyncState/withArg.html). -- Remove [`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html), use [`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/isActive.html) instead. -- Remove [`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html), use [`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/getDependencyMode.html) instead. -- Remove [`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html), use [`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0/reactter/DependencyMode.html) instead. -- Remove [`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0/reactter/Lifecycle.html) instead. -- Remove [`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html), use [`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0/reactter/Lifecycle.html) instead. -- Replace [`LifecycleObserver`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver-class.html) to [`RtDependencyLifecycle`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtDependencyLifecycle-class.html). -- Remove [`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html), use [`RtDependencyLifecycle.onCreated`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtDependencyLifecycle/onCreated.html) instead. -- Remove [`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html) and [`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html), use [`RtDependencyRef`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtDependencyRef-class.html) instead. -- Remove [`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html), use [`RtHook`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtHook-class.html) instead. -- Remove [`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html), use [`RtInterface`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtInterface-class.html) instead. -- Remove [`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html), use [`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtState/notify.html) instead. -- Remove [`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html), use [`UseDependency`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseDependency-class.html) instead. -- Remove [`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html), use [`RtAction`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtAction-class.html) instead. -- Remove [`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html), use [`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtActionCallable-class.html) instead. -- Remove [`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html), use [`MultiMemoInterceptor`](https://pub.dev/documentation/reactter/8.0.0/reactter/MultiMemoInterceptor-class.html) instead. -- Remove [`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html), use [`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0/reactter/MemoSafeAsyncInterceptor-class.html) instead. -- Remove [`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html), use [`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0/reactter/MemoTemporaryCacheInterceptor-class.html) instead. -- Remove [`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html), use [`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0/reactter/MemoWrapperInterceptor-class.html) instead. -- Remove [`Obj`](https://pub.dev/documentation/reactter/7.3.0/reactter/Obj-class.html). -- Remove [`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html) property from `RtProvider`, use [`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtProvider/RtProvider.init.html) instead. -- Remove [`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html), use [`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtComponent-class.html) instead. -- Remove [`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html), use [`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtConsumer-class.html) instead. -- Remove [`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html), use [`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtProvider-class.html) instead. -- Remove [`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html), use [`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtMultiProvider-class.html) instead. -- Remove [`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html), use [`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtScope-class.html) instead. -- Remove [`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html), use [`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtSignalWatcher-class.html) instead. -- Remove [`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html), use [`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0/flutter_reactter/RtDependencyNotFoundException-class.html) instead. -- Remove [`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html), use [`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0/flutter_reactter/RtScopeNotFoundException-class.html) instead. +- Remove <HT>[`Reactter`](https://pub.dev/documentation/reactter/7.3.0/reactter/Reactter.html)</HT>, use <HT>[`Rt`](https://pub.dev/documentation/reactter/8.0.0/reactter/Rt.html)</HT> instead. +- Remove <HT>[`ReactterStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterStateImpl.html)</HT> and <HT>[`RtStateImpl`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtStateImpl-class.html)</HT>, use <HT>[`RtState`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtState-class.html)</HT> instead. +- Replace <HE>[`UseAsyncStateStatus.standby`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseAsyncStateStatus.html)</HE> to <HE>[`UseAsyncStateStatus.idle`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseAsyncStateStatus.html)</HE>. +- Rename <HT>[`DispatchEffect`](https://pub.dev/documentation/reactter/7.3.0/reactter/DispatchEffect-class.html)</HT> to <HT>[`AutoDispatchEffect`](https://pub.dev/documentation/reactter/8.0.0/reactter/DispatchEffect-class.html)</HT>. +- Move <HM>`asyncFunction`</HM> to the first parameter of <HT>[`UseAsyncState`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseAsyncState-class.html)</HT> and <HM>[`UseAsyncState.withArg`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseAsyncState/withArg.html)</HM>. +- Remove <HT>[`Rt.isRegistered`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/isRegistered.html)</HT>, use <HM>[`Rt.isActive`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/isActive.html)</HM> instead. +- Remove <HT>[`Rt.getInstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtInterface/getInstanceManageMode.html)</HT>, use <HM>[`Rt.getDependencyMode`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/getDependencyMode.html)</HM> instead. +- Remove <HT>[`InstanceManageMode`](https://pub.dev/documentation/reactter/7.3.0/reactter/InstanceManageMode.html)</HT>, use <HT>[`DependencyMode`](https://pub.dev/documentation/reactter/8.0.0/reactter/DependencyMode.html)</HT> instead. +- Remove <HE>[`Lifecycle.initialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html)</HE>, use <HE>[`Lifecycle.created`](https://pub.dev/documentation/reactter/8.0.0/reactter/Lifecycle.html)</HE> instead. +- Remove <HE>[`Lifecycle.destroyed`](https://pub.dev/documentation/reactter/7.3.0/reactter/Lifecycle.html)</HE>, use <HE>[`Lifecycle.deleted`](https://pub.dev/documentation/reactter/8.0.0/reactter/Lifecycle.html)</HE> instead. +- Replace <HT>[`LifecycleObserver`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver-class.html)</HT> to <HT>[`RtDependencyLifecycle`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtDependencyLifecycle-class.html)</HT>. +- Remove <HM>[`LifecycleObserver.onInitialized`](https://pub.dev/documentation/reactter/7.3.0/reactter/LifecycleObserver/onInitialized.html)</HM>, use <HM>[`RtDependencyLifecycle.onCreated`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtDependencyLifecycle/onCreated.html)</HM> instead. +- Remove <HT>[`ReactterInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInstance.html)</HT> and <HT>[`ReactterDependency`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterDependency.html)</HT>, use <HT>[`RtDependencyRef`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtDependencyRef-class.html)</HT> instead. +- Remove <HT>[`ReactterHook`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterHook.html)</HT>, use <HT>[`RtHook`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtHook-class.html)</HT> instead. +- Remove <HT>[`ReactterInterface`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterInterface.html)</HT>, use <HT>[`RtInterface`](https://pub.dev/documentation/reactter/8.0.0//reactter/RtInterface-class.html)</HT> instead. +- Remove <HM>[`RtState.refresh`](https://pub.dev/documentation/reactter/7.3.0/reactter/RtState/refresh.html)</HM>, use <HM>[`RtState.notify`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtState/notify.html)</HM> instead. +- Remove <HT>[`UseInstance`](https://pub.dev/documentation/reactter/7.3.0/reactter/UseInstance-class.html)</HT>, use <HT>[`UseDependency`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseDependency-class.html)</HT> instead. +- Remove <HT>[`ReactterAction`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterAction.html)</HT>, use <HT>[`RtAction`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtAction-class.html)</HT> instead. +- Remove <HT>[`ReactterActionCallable`](https://pub.dev/documentation/reactter/7.3.0/reactter/ReactterActionCallable.html)</HT>, use <HT>[`RtActionCallable`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtActionCallable-class.html)</HT> instead. +- Remove <HT>[`MemoInterceptors`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptors.html)</HT>, use <HT>[`MultiMemoInterceptor`](https://pub.dev/documentation/reactter/8.0.0/reactter/MultiMemoInterceptor-class.html)</HT> instead. +- Remove <HT>[`AsyncMemoSafe`](https://pub.dev/documentation/reactter/7.3.0/reactter/AsyncMemoSafe.html)</HT>, use <HT>[`MemoSafeAsyncInterceptor`](https://pub.dev/documentation/reactter/8.0.0/reactter/MemoSafeAsyncInterceptor-class.html)</HT> instead. +- Remove <HT>[`TemporaryCacheMemo`](https://pub.dev/documentation/reactter/7.3.0/reactter/TemporaryCacheMemo.html)</HT>, use <HT>[`MemoTemporaryCacheInterceptor`](https://pub.dev/documentation/reactter/8.0.0/reactter/MemoTemporaryCacheInterceptor-class.html)</HT> instead. +- Remove <HT>[`MemoInterceptorWrapper`](https://pub.dev/documentation/reactter/7.3.0/reactter/MemoInterceptorWrapper.html)</HT>, use <HT>[`MemoWrapperInterceptor`](https://pub.dev/documentation/reactter/8.0.0/reactter/MemoWrapperInterceptor-class.html)</HT> instead. +- Remove <HT>[`Obj`](https://pub.dev/documentation/reactter/7.3.0/reactter/Obj-class.html)</HT>. +- Remove <HM>[`init`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/RtProvider/init.html)</HM> property from <HT>`RtProvider`</HT>, use <HM>[`RtProvider.init`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtProvider/RtProvider.init.html)</HM> instead. +- Remove <HT>[`ReactterComponent`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterComponent-class.html)</HT>, use <HT>[`RtComponent`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtComponent-class.html)</HT> instead. +- Remove <HT>[`ReactterConsumer`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterConsumer-class.html)</HT>, use <HT>[`RtConsumer`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtConsumer-class.html)</HT> instead. +- Remove <HT>[`ReactterProvider`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProvider-class.html)</HT>, use <HT>[`RtProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtProvider-class.html)</HT> instead. +- Remove <HT>[`ReactterProviders`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterProviders-class.html)</HT>, use <HT>[`RtMultiProvider`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtMultiProvider-class.html)</HT> instead. +- Remove <HT>[`ReactterScope`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterScope-class.html)</HT>, use <HT>[`RtScope`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtScope-class.html)</HT> instead. +- Remove <HT>[`ReactterWatcher`](https://pub.dev/documentation/flutter_reactter/7.3.1/flutter_reactter/ReactterWatcher-class.html)</HT>, use <HT>[`RtSignalWatcher`](https://pub.dev/documentation/flutter_reactter/8.0.0/flutter_reactter/RtSignalWatcher-class.html)</HT> instead. +- Remove <HT>[`ReactterDependencyNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterDependencyNotFoundException-class.html)</HT>, use <HT>[`RtDependencyNotFoundException`](https://pub.dev/documentation/reactter/8.0.0/flutter_reactter/RtDependencyNotFoundException-class.html)</HT> instead. +- Remove <HT>[`ReactterScopeNotFoundException`](https://pub.dev/documentation/reactter/7.3.0/flutter_reactter/ReactterScopeNotFoundException-class.html)</HT>, use <HT>[`RtScopeNotFoundException`](https://pub.dev/documentation/reactter/8.0.0/flutter_reactter/RtScopeNotFoundException-class.html)</HT> instead. ### Fixes -- Fix bug in [`UseEffect`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseEffect-class.html) causing dependencies to not be unwatched. -- Fix to show the correct dependency mode in the logger of the [`Rt.register`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/register.html) method. -- Fix nested [`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/batch.html) method to work correctly. -- Add error handling listeners and continue to next listeners in [`Notifier`](https://pub.dev/documentation/reactter/8.0.0/reactter/Notifier-class.html) class. -- Fix to propagate state param to `boundInstance`. +- Fix bug in <HT>[`UseEffect`](https://pub.dev/documentation/reactter/8.0.0/reactter/UseEffect-class.html)</HT> causing dependencies to not be unwatched. +- Fix to show the correct dependency mode in the logger of the <HM>[`Rt.register`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/register.html)</HM> method. +- Fix nested <HM>[`Rt.batch`](https://pub.dev/documentation/reactter/8.0.0/reactter/RtInterface/batch.html)</HM> method to work correctly. +- Add error handling listeners and continue to next listeners in <HT>[`Notifier`](https://pub.dev/documentation/reactter/8.0.0/reactter/Notifier-class.html)</HT> class. +- Fix to propagate state param to <HM>`boundInstance`</HM>. - Remove listeners correctly from single-use listeners and handle null cases. ## 2. Implement the changes @@ -84,8 +84,8 @@ If you encounter any issues, refer to the [official documentation](https://pub.d - Replace all occurrences of <HT>`Reactter`</HT> with <HT>`Rt`</HT>. ```dart showLineNumbers=false del={1,3} del="Reactter" ins={2,4} ins="Rt" - Reactter.createState(...); - Rt.createState(...); + Reactter.create(...); + Rt.create(...); ReactterProvider(...); RtProvider(...); ``` diff --git a/website/src/content/docs/overview.mdx b/website/src/content/docs/overview.mdx index 94b76ed4..da987325 100644 --- a/website/src/content/docs/overview.mdx +++ b/website/src/content/docs/overview.mdx @@ -2,27 +2,24 @@ title: Overview description: Know about Reactter, why it was created and its features. --- +import { HT } from '@/components/Highlight'; -State management is fundamental to applications, as it determines how information is stored, updated and shared within the application. -Flutter provides `StafulWidgets` to manage local state, which is usually sufficient for small applications, -However, as the application grows, state management becomes more complex and difficult to maintain. -This is why there are several packages to manage global state. +State management is the backbone of any Flutter application, but as apps grow in complexity, so do the challenges of maintaining clean, scalable, and efficient code. +While Flutter offers built-in tools like <HT>`StatefulWidget`</HT>, they often fall short when it comes to managing global state or handling advanced use cases. +This has led to a wide variety of state management packages, each with its own strengths, but also its own trade-offs. -Although state management is a key component of an application architecture, it is not the only factor to consider. -Dependency injection, event handling, and, in the case of Flutter applications, proper rendering control, -are equally crucial to ensure that the application is scalable, flexible and easy to maintain in the long term. -All these aspects must be considered together to create a robust and efficient architecture. +**Reactter** was born out of a desire to create something different, a state management solution that doesn't just solve problems but elevates the entire development experience. +Inspired by the simplicity and reactivity of ReactJS, Reactter brings a fresh, modern approach to Dart and Flutter development. +It's not just another state management package, it's a comprehensive framework designed to make your app faster, your code cleaner, and your life as a developer easier. -While numerous state management packages exist, many of them do not offer complete or robust enough solutions. -This challenge is further intensified in Dart-only applications, where the lack of adequate options for global state management makes management even more challenging. +What sets Reactter apart? While other packages focus solely on state management, Reactter goes further by seamlessly integrating **dependency injection**, **event handling**, and **rendering control** into a single, cohesive framework. +It's lightweight yet powerful, simple yet flexible, and designed to work seamlessly with any architecture or pattern. +Whether you're building a small project or a large-scale application, Reactter gives you the tools to write less boilerplate, improve readability, and maintain granular control over your app's behavior. -With this in mind, **Reactter** was created—a **_light_**, **_powerful_** and **_reactive_** [**state management**](/reactter/en/core_concepts/state_management), [**dependency injection**](/reactter/en/core_concepts/dependency_injection) and [**event_handler**](/reactter/en/core_concepts/event_handler) package for Dart/Flutter. - -## Motivation - -**Reactter** was born as an experiment in developing a new way to manage state in Flutter projects that was efficient, reactive, scalable, yet simple and easy to use. - -Inspired by ReactJS, Reactter(**Reac**t-Fu**tter**) adopts key concepts from this library to Dart and Flutter, offering a streamlined solution consistent with the needs of development modern. +But Reactter isn't just about features, it's about philosophy. +We believe that state management should be intuitive, not intimidating. +That's why Reactter is built with a *developer-first* mindset, offering a syntax that's easy to learn, a workflow that's easy to debug, and a design that's easy to love. +With Reactter, you're not just managing state, you're unlocking the full potential of your applications. ## Features @@ -48,6 +45,6 @@ Experience the full potential of Reactter by trying it online on <a href="https: <iframe src="https://zapp.run/edit/zn9806bin990?theme=dark&lazy=false&file=lib/examples/1_counter/counter_page.dart" class="w-full !h-[600px]" style="border: 0; overflow: hidden;"></iframe> -:::tip[The power is in your hand!!] - [Getting](/reactter/guides/getting_started) Reactter into your projects and unlocking its full potential. +:::tip[Unlock the full potential of Reactter!] + [Get started](/reactter/guides/getting_started) and transform your Dart/Flutter development experience with Reactter. ::: \ No newline at end of file diff --git a/website/src/content/docs/shareds/state_methods.mdx b/website/src/content/docs/shareds/state_methods.mdx index 710875be..b86b948a 100644 --- a/website/src/content/docs/shareds/state_methods.mdx +++ b/website/src/content/docs/shareds/state_methods.mdx @@ -3,7 +3,6 @@ title: State Methods --- import { HE, HM, HT } from '@/components/Highlight'; - - <HM>**`update`**</HM>: Executes a callback function and notify its observers that the state has changed. When it is invoked, it emits two [lifecycle](/reactter/core_concepts/lifecycle) events to signal the state transition: - <HE>`Lifecycle.willUpdate`</HE> is emitted first, indicating the impending update. diff --git a/website/src/examples/custom_hook/lib/use_text_input.dart b/website/src/examples/custom_hook/lib/use_text_input.dart index 228c9b78..e35955c4 100644 --- a/website/src/examples/custom_hook/lib/use_text_input.dart +++ b/website/src/examples/custom_hook/lib/use_text_input.dart @@ -19,7 +19,5 @@ class UseTextInput extends RtHook { return controller.dispose; }, []); - - super.initHook(); } } From 9fea37a9feda0d8d7d8cff4e73f328a29e19ddb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= <carlos.leon@meru.com> Date: Mon, 24 Feb 2025 21:44:57 -0600 Subject: [PATCH 141/141] refactor(website): Improve documentation clarity and update state management examples --- website/public/opengraph.png | Bin 142761 -> 151070 bytes .../es/core_concepts/state_management.mdx | 2 +- .../binding_state_to_dependency.mdx | 156 ++++++++++++++++ .../updating_objects_in_state.mdx | 169 ++++++++++++++++++ website/src/content/docs/es/overview.mdx | 2 +- .../content/docs/es/shareds/state_methods.mdx | 4 +- .../binding_state_to_dependency.mdx | 37 ++-- .../updating_objects_in_state.mdx | 14 +- website/src/content/docs/overview.mdx | 2 +- 9 files changed, 362 insertions(+), 24 deletions(-) create mode 100644 website/src/content/docs/es/extra_topics/binding_state_to_dependency.mdx create mode 100644 website/src/content/docs/es/extra_topics/updating_objects_in_state.mdx diff --git a/website/public/opengraph.png b/website/public/opengraph.png index cf2c54144c34023f92a1f8cfa4def0435e32a319..b0f78c163f0688518a83cbbc7bd9db03a542d29e 100644 GIT binary patch literal 151070 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 literal 142761 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 A&Hw-a diff --git a/website/src/content/docs/es/core_concepts/state_management.mdx b/website/src/content/docs/es/core_concepts/state_management.mdx index b486cddf..f0d73f44 100644 --- a/website/src/content/docs/es/core_concepts/state_management.mdx +++ b/website/src/content/docs/es/core_concepts/state_management.mdx @@ -49,7 +49,7 @@ la cual encapsula los datos y el comportamiento de un estado particular, y propo ### Metodos del estado -<HT>[`RtState`](/reactter/api/classes/rt_state)</HT> clase proporciona algunos métodos para la gestión de estados. Conozcámoslos: +La clase <HT>[`RtState`](/reactter/api/classes/rt_state)</HT> proporciona algunos métodos para la gestión de estados. Conozcámoslos: <StateMethods /> diff --git a/website/src/content/docs/es/extra_topics/binding_state_to_dependency.mdx b/website/src/content/docs/es/extra_topics/binding_state_to_dependency.mdx new file mode 100644 index 00000000..6e319a24 --- /dev/null +++ b/website/src/content/docs/es/extra_topics/binding_state_to_dependency.mdx @@ -0,0 +1,156 @@ +--- +title: Vinculación del estado a la dependencia +description: Aprende a vincular un estado a una dependencia en Reactter para una gestión de estado más eficiente y reactiva. +--- + +import { Code } from "@astrojs/starlight/components"; +import { HE, HK, HM, HN, HS, HT } from '@/components/Highlight'; + +:::tip +Este guía asume que ya has leído sobre [Inyección de dependencias](/reactter/es/core_concepts/dependency_injection) y [Gestión del estado](/reactter/es/core_concepts/state_management). +Se recomienda leer eso primero si eres nuevo en Reactter. +::: + +Un estado de Reactter(<HT>[`RtState`](/reactter/es/core_concepts/state_management/#state)</HT>) como <HT>[`Signal`](/reactter/es/api/classes/signal)</HT> o cualquier <HT>[`Hooks`](/reactter/es/core_concepts/hooks)</HT> puede ser vinculado a la dependencia, permitiendo que el estado sea manipulado directamente desde la dependencia y notificar a sus oyentes sobre cualquier cambio. +Además, asegura que el estado se elimine automáticamente cuando la dependencia ya no sea necesaria. + +Al integrar el estado directamente dentro de las dependencias, se beneficia de un **código más limpio** y **mantenible**. +El manejo automático por parte de Reactter significa **menos código repetitivo** y **menos errores* relacionados con la gestión manual del estado, lo que conduce a un proceso de desarrollo más eficiente. +Este enfoque **simplifica** la sincronización entre el estado y su dependencia asociada, **mejorando la capacidad de respuesta** y la **fiabilidad** general de su aplicación. + +### Vinculación automática + +Para que esto suceda automáticamente, el estado debe declararse como propiedad o dentro del constructor de la dependencia. +Cuando se hace esto, Reactter se encarga automáticamente de vincular el estado a la dependencia, asegurando una gestión y reactividad del estado sin problemas, por ejemplo: + +```dart title="count_controller.dart" "UseState" "UseEffect" +import "package:reactter/reactter.dart"; + +class CountController { + // Estado declarado como propiedad + final uCount = UseState(0); + + CountController() { + // Estado declarado dentro del constructor + UseEffect(() { + print("Count: ${uCount.value}"); + }, [uCount]); + } +} +``` + +En el ejemplo anterior, el estado `uCount` se declara como propiedad de la clase <HT>`CountController`</HT> y el hook <HT>`UseEffect`</HT> se utiliza dentro del constructor para reaccionar a los cambios en el estado `uCount`, imprimiendo su valor cada vez que cambia. +Esto vincula automáticamente el estado `uCount` y el hook <HT>`UseEffect`</HT> a la instancia de <HT>`CountController`</HT>, demostrando cómo Reactter maneja la vinculación y reactividad de forma transparente. + +:::caution +Si el estado se declara de forma perezosa (por ejemplo, utilizando la palabra clave <HK>`late`</HK>), no se vinculará automáticamente a la dependencia. +En tales casos, debes usar el método <HT>`Rt.lazyState`</HT> para vincular el estado a la dependencia(Ver [vinculación perezoso](#vinculación-perezosa)). +::: + +### Vinculación perezosa + +Cuando un estado se declara de forma perezosa, no se vincula automáticamente a la dependencia. +En tales casos, puedes usar el método <HT>[`Rt.lazyState`](/reactter/es/api/methods/state_management_methods/#rtlazy_state)</HT> para vincular el estado a la dependencia, por ejemplo: + +```dart title="count_controller.dart" "UseState" "Rt.lazyState" +import "package:reactter/reactter.dart"; + +class CountController { + final int initialCount; + + late final uCount = Rt.lazyState( + () => UseState(this.initialCount), + this, + ); + + CountController([this.initialCount = 0]) { + UseEffect(() { + print("Count: ${uCount.value}"); + }, [uCount]); + } +} +``` + +En el ejemplo anterior, el estado `uCount` se declara de forma perezosa utilizando la palabra clave <HK>`late`</HK>. +Para vincular el estado a la instancia de <HT>`CountController`</HT>, se utiliza el método <HT>[`Rt.lazyState`](/reactter/es/api/methods/state_management_methods/#rtlazy_state)</HT>, pasando la función de creación del estado y la instancia de la dependencia como argumentos. +Esto asegura que cuando se accede a `uCount`, se vinculará automáticamente a la instancia de <HT>`CountController`</HT>. + +### Vinculación manual + +Si bien la vinculación automática simplifica la gestión del estado, puede haber escenarios en los que necesites vincular manualmente el estado a una dependencia. +La vinculación manual proporciona un mayor control sobre cómo y cuándo se asocia el estado con la dependencia. + +Para vincular manualmente un estado a una dependencia, debes vincular explícitamente el estado dentro de la dependencia utilizando el método <HM>`bind`</HM> del estado, por ejemplo: + +```dart title="count_controller.dart" "UseState" "bind" +class CountController { + late final uCount = UseState(this.initialCount); + + final int initialCount; + + CountController([this.initialCount = 0]) { + count.bind(this); + } +} +``` + +En el ejemplo anterior, el estado `uCount` se declara de forma perezosa utilizando la palabra clave <HK>`late`</HK>. +Para vincular el estado a la instancia de <HT>`CountController`</HT>, se utiliza el método <HM>`bind`</HM> del estado, pasando la instancia de la dependencia como argumento. +Esto asegura que el estado `uCount` esté asociado a la instancia de <HT>`CountController`</HT>, lo que permite que la dependencia actualice reactivamente en función de los cambios en el estado. + +:::note +La [vinculación manual](#vinculación-manual) y [vinculación perezosa](#vinculación-perezosa) tienen propositos diferentes. +La vinculación manual es útil cuando se necesita un control preciso sobre el proceso de vinculación de estados, mientras que la **vinculación perezosa** es adecuada cuando se desea que el estado se inicialice sólo cuando se accede a él por primera vez. +Elige el método adecuado en función de tus necesidades específicas. +::: + +### Desvinculación automática + +Cuando se elimina una dependencia, el estado asociado se elimina automáticamente. +Este mecanismo de desvinculación automática simplifica la gestión de estados y reduce el riesgo de fugas de memoria o desperdicio de recursos. + +En el ejemplo siguiente, el estado `uCount` se elimina automáticamente cuando se borra la instancia <HT>`CountController`</HT>, lo que garantiza que los recursos se liberan de forma eficiente: + +```dart title="main.dart" "Rt.create" "Rt.delete" +import "./count_controller.dart"; + +void main() { + final controller = Rt.create(() => CountController(10)); + controller.uCount.value += 2; // Count: 12 + Rt.delete<CountController>(); + controller.uCount.value += 3; // Error: "Can't update when it's been disposed" +} +``` + +### Desvinculación manual + +En algunos casos, puede que necesites desvincular manualmente un estado de una dependencia. +La desvinculación manual proporciona un mayor control sobre cuándo se libera el estado y puede ser útil en escenarios en los que se desea desvincular el estado de forma temporal o permanente. + +Para desvincular manualmente un estado de una dependencia, puedes utilizar el método <HM>`unbind`</HM> del estado, por ejemplo: + +```dart title="count_controller.dart" "UseState" "unbind" +class CountController { + late final uCount = UseState(this.initialCount); + + final int initialCount; + + CountController([this.initialCount = 0]) { + count.bind(this); + } + + void dispose() { + count.unbind(this); + } +} +``` + +:::caution +Aunque la desvinculación manual proporciona un mayor control, un uso inadecuado puede provocar problemas de gestión de memoria y aumentar el riesgo de errores. +Se recomienda utilizar la [desvinculación automática](#desvinculación-automática) siempre que sea posible, ya que simplifica el proceso y reduce la probabilidad de introducir fugas de memoria o de no liberar los recursos correctamente. + +La desvinculación manual debe utilizarse con precaución y sólo cuando sea absolutamente necesario. +Además, los desarrolladores deben asegurarse de realizar un seguimiento del proceso de desvinculación para evitar dejar estados no utilizados en memoria, lo que podría afectar negativamente al rendimiento y la utilización de recursos. +::: + + diff --git a/website/src/content/docs/es/extra_topics/updating_objects_in_state.mdx b/website/src/content/docs/es/extra_topics/updating_objects_in_state.mdx new file mode 100644 index 00000000..5fc8cd48 --- /dev/null +++ b/website/src/content/docs/es/extra_topics/updating_objects_in_state.mdx @@ -0,0 +1,169 @@ +--- +title: Actualizar objectos en estado +description: Aprende a actualizar objectos en estado de Reactter. +--- +import { HM, HN, HS, HT } from '@/components/Highlight'; + +Un estado de Reactter(<HT>`RtState`</HT>) como <HT>`Signal`</HT>, <HT>`UseState`</HT>, <HT>`UseAsyncState`</HT>, <HT>`UseReducer`</HT> y <HT>`UseCompute`</HT> pueden contener cualquier tipo de valor de Dart, incluidos objetos. +Sin embargo, no debes modificar los objetos contenidos en el estado de Reactter directamente. + +En esta guía, aprenderás cómo actualizar objetos de forma segura y efectiva en un estado Reactter. + +## ¿Qué es una mutación? + +Puedes almacenar cualquier tipo de valor de Dart en el estado. + +```dart showLineNumbers=false +final count = Signal(0); +``` + +Hasta ahora has estado trabajando con números, cadenas y booleanos. +Estos tipos de valores de Dart son _**inmutables**_, lo que significa que no se pueden cambiar o son _**de solo lectura**_. +Puedes desencadenar una nueva representación para reemplazar un valor: + +```dart showLineNumbers=false +count.value = 2; +``` + +El estado `count` cambió de <HN>`0`</HN> a <HN>`2`</HN>, pero el número <HN>`0`</HN> en sí mismo no cambió. +No es posible realizar cambios en los valores primitivos incorporados como números, cadenas y booleanos en Dart. + +Ahora considera un objeto en un estado: + +```dart showLineNumbers=false collapse={1-7} +class User { + String name; + int age; + + User({required this.name, required this.age}); +} + +final user = Signal(User(name: "Jane Doe", age: 25)); +``` + +Tecnicamente, es posible cambiar el contenido del objeto en sí. Esto se llama una **mutación**: + +```dart showLineNumbers=false +user.value.name = "John Doe"; +``` + +Sin embargo, aunque los objetos en el estado de Reactter son técnicamente mutables, debes tratarlos como si fueran inmutables, como números, booleanos y cadenas. +En lugar de mutarlos, siempre debes reemplazarlos. + +## Tratar el estado como de solo lectura + +En otras palabras, debes tratar cualquier objeto de Dart que pongas en el estado como de solo lectura. + +El siguiente ejemplo mantiene un objeto en el estado para representar al usuario. +El nombre del usuario se cambia de <HS>`"Jane Doe"`</HS> a <HS>`"John Doe"`</HS> cuando haces clic en el botón, +pero el nombre del usuario sigue siendo el mismo. + +```dart title="main.dart" "Signal" "User" "RtSignalWatcher" "user.value.name" "user.value.age" "user" mark={31} +import 'package:flutter/material.dart'; +import 'package:reactter/reactter.dart'; + +void main() { + runApp(MyApp()); +} + +class User { + String name; + int age; + + User({required this.name, required this.age}); +} + +final user = Signal(User(name: "Jane Doe", age: 25)); + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar(title: Text("Inmutable state example")), + body: Center( + child: RtSignalWatcher((){ + return Column( + children: [ + Text('Name: ${user.value.name}'), + Text('Age: ${user.value.age}'), + ElevatedButton( + onPressed: () { + user.value.name = "John Doe"; + }, + child: Text("Change Name"), + ), + ], + ); + }), + ), + ), + ); + } +} +``` + +El problema está en este fragmento de código. + +```dart startLineNumber=24 +user.value.name = "John Doe"; +``` + +Este código modifica el objeto asignado a un nuevo `name` cuando se hace clic en el botón +pero no desencadena un nuevo renderizado porque el objeto en sí no ha cambiado. +La propiedad `name` del objeto ha cambiado, pero el objeto en sí no lo ha hecho. +Y Reactter no sabe que el objeto ha cambiado porque sigue siendo el mismo objeto. +Aunque la mutación del estado puede funcionar en algunos casos, no lo recomendamos. +Debes tratar el valor de estado al que tienes acceso como de solo lectura. + +Para desencadenar un nuevo renderizado en este caso, crea un nuevo objeto y pásalo a la función de configuración del estado: + +```dart startLineNumber=24 +user.value = User(name: "John Doe", age: user.value.age); +``` + +Cunado `value` se establece en un nuevo objeto, Reactter sabe que el estado ha cambiado y desencadena un nuevo renderizado. + +## Copiar objetos + +En el ejemplo anterior, creaste un nuevo objeto con el mismo `age` y un `name` diferente. +Pero ¿qué pasa si quieres cambiar el `age` y mantener el `name` igual?. También puedes hacerlo: + +```dart showLineNumbers=false +user.value = User(name: user.value.name, age: 30); +``` + +Esto es un patrón común cuando trabajas con objetos en el estado. +Sin embargo, crear un nuevo objeto cada vez que quieras cambiar una propiedad puede ser tedioso. +Para simplificar este proceso, puedes agregar el siguiente método(<HM>`copyWith`</HM>) a la clase <HT>`User`</HT>: + +```dart collapse={2-6} ins="final" ins={7-13} mark={2-3} mark="copyWith" +class User { + final String name; + final int age; + + const User({required this.name, required this.age}); + + User copyWith({String? name, int? age}) { + return User( + name: name ?? this.name, + age: age ?? this.age, + ); + } +} +``` + +Este método crea un nuevo objeto con las mismas propiedades que el objeto original, excepto por las propiedades que especifiques. +Puedes usar este método para crear un nuevo objeto con el mismo `name` y un `age` diferente: + +```dart showLineNumbers=false ".copyWith" +user.value = user.value.copyWith(age: 30); +``` + +## Resumen + +- Trata los objetos en el estado como de solo lectura. +- Cuando necesites actualizar un objeto, crea uno nuevo o haz una copia del objeto existente, y luego establece el estado para usar este nuevo o copiado objeto. +- Evita mutar objetos en el estado directamente. +- Crea un nuevo objeto y pásalo a la función de configuración del estado para desencadenar un nuevo renderizado. +- Usa el método <HM>`copyWith`</HM> para crear un nuevo objeto con las mismas propiedades que el objeto original, excepto por las propiedades que especifiques. \ No newline at end of file diff --git a/website/src/content/docs/es/overview.mdx b/website/src/content/docs/es/overview.mdx index 783905bc..293ce7be 100644 --- a/website/src/content/docs/es/overview.mdx +++ b/website/src/content/docs/es/overview.mdx @@ -11,7 +11,7 @@ Esto ha dado lugar a una amplia variedad de paquetes de gestión de estado, cada **Reactter** nació del deseo de crear algo diferente, una solución de gestión de estados que no se limita a resolver problemas, sino que eleva toda la experiencia de desarrollo. Inspirado en la simplicidad y reactividad de ReactJS, Reactter aporta un enfoque fresco y moderno al desarrollo con Dart y Flutter. -No es sólo otro paquete de gestión de estado, es un framework completo, diseñado para hacer su aplicación más rápida, su código más limpio y su vida como desarrollador más fácil. +No es sólo otro paquete de gestión de estado, es una herramienta potente, diseñado para hacer su aplicación más rápida, su código más limpio y su vida como desarrollador más fácil. ¿Qué diferencia a Reactter? Mientras que otros paquetes se centran únicamente en la gestión de estados, Reactter va más allá integrando a la perfección **inyección de dependencias**, **manejo de eventos** y **control de renderizado** en un único framework cohesionado. Es ligero pero potente, sencillo pero flexible, y está diseñado para funcionar a la perfección con cualquier arquitectura o patrón. diff --git a/website/src/content/docs/es/shareds/state_methods.mdx b/website/src/content/docs/es/shareds/state_methods.mdx index 4c659666..cc7d87b1 100644 --- a/website/src/content/docs/es/shareds/state_methods.mdx +++ b/website/src/content/docs/es/shareds/state_methods.mdx @@ -11,7 +11,7 @@ Cuando se invoca, emite dos eventos [lifecycle](/reactter/es/core_concepts/lifec A diferencia de <HM>`update`</HM>, sólo emite el evento <HE>`Lifecycle.didUpdate`</HE>, ya que no implica ningún paso previo a la notificación. - <HM>**`bind`**</HM>: Establece una conexión entre el estado y una instancia específica. Esta conexión permite a la instancia actualizarse de forma reactiva en función de los cambios en el estado. -Al vincular el estado, la instancia se da cuenta de los cambios en el estado y puede reflejar adecuadamente esos cambios en su comportamiento. +Al vincular el estado, la instancia es notificado de los cambios en el estado y puede reflejar adecuadamente esos cambios en su comportamiento. - <HM>**`unbind`**</HM>: Libera la conexión entre el estado y la instancia. Al desvincularse, la instancia dejará de recibir actualizaciones del estado. Esto puede ser útil cuando una instancia ya no está utilizando activamente el estado o cuando necesita desvincularse del estado temporal o permanentemente. @@ -20,5 +20,5 @@ Disponer del estado garantiza que se libere correctamente y que ya no consuma me :::note Mientras que los métodos <HM>`bind`</HM>, <HM>`unbind`</HM> y <HM>`dispose`</HM> se utilizan normalmente para gestionar el [ciclo de vida](/reactter/es/core_concepts/lifecycle) de un estado, -Reactter maneja esto automáticamente, cuando el estado es declarado dentro de una instancia que existe dentro del contexto de Reactter vía [dependecy injection](/reactter/es/core_concepts/dependency_injection/), por lo que no necesitas preocuparte por ello(Learn more about [Binding State to Dependency](/reactter/es/extra_topics/binding_state_to_dependency/)). +Reactter maneja esto automáticamente, cuando el estado es declarado dentro de una instancia que existe dentro del contexto de Reactter vía [dependecy injection](/reactter/es/core_concepts/dependency_injection/), por lo que no necesitas preocuparte por ello(Apendas más acerca de la [vinculación del estado a la dependencia](/reactter/es/extra_topics/binding_state_to_dependency/)). ::: \ No newline at end of file diff --git a/website/src/content/docs/extra_topics/binding_state_to_dependency.mdx b/website/src/content/docs/extra_topics/binding_state_to_dependency.mdx index 807d8ea0..1d213dd1 100644 --- a/website/src/content/docs/extra_topics/binding_state_to_dependency.mdx +++ b/website/src/content/docs/extra_topics/binding_state_to_dependency.mdx @@ -11,10 +11,12 @@ This guies assumes you've already read the [Dependency Injection](/reactter/core It's recommended read that first if you're new in Reactter. ::: -A state(<HT>[`RtState`](/reactter/core_concepts/state_management/#state)</HT> like <HT>[`Signal`](/reactter/api/classes/signal)</HT> or any <HT>[`Hooks`](/reactter/core_concepts/hooks)</HT>) can be bound to the dependency, allowing the state to be manipulated directly from the dependency and to notify its listeners about any changes. +A Reactter state(<HT>[`RtState`](/reactter/core_concepts/state_management/#state)</HT>) such as <HT>[`Signal`](/reactter/api/classes/signal)</HT> or any <HT>[`Hooks`](/reactter/core_concepts/hooks)</HT> can be bound to the dependency, allowing the state to be manipulated directly from the dependency and to notify its listeners about any changes. Additionally, it ensures that the state is automatically disposed of when the dependency is no longer needed. -By integrating state directly within dependencies, you benefit from _**cleaner**_ and more _**maintainable**_ code. The automatic handling by Reactter means _**less boilerplate**_ and _**fewer errors**_ related to manual state management, leading to a more efficient development process. This approach _**simplifies**_ the synchronization between state and its associated dependency, enhancing the _**overall responsiveness**_ and _**reliability**_ of your application. +By integrating state directly within dependencies, you benefit from _**cleaner**_ and more _**maintainable**_ code. +The automatic handling by Reactter means _**less boilerplate**_ and _**fewer errors**_ related to manual state management, leading to a more efficient development process. +This approach _**simplifies**_ the synchronization between state and its associated dependency, enhancing the _**overall responsiveness**_ and _**reliability**_ of your application. ### Automatic binding @@ -37,15 +39,18 @@ class CountController { } ``` -In the example above, the `uCount` state is declared as a property of the <HT>`CountController`</HT> class and the <HT>`UseEffect`</HT> hook is used within the constructor to react to changes in the `uCount` state, printing its value whenever it changes. This automatically binds the `uCount` state and <HT>`UseEffect`</HT> hook to the <HT>`CountController`</HT> instance, demonstrates how Reactter handles the binding and reactivity seamlessly. +In the example above, the `uCount` state is declared as a property of the <HT>`CountController`</HT> class and the <HT>`UseEffect`</HT> hook is used within the constructor to react to changes in the `uCount` state, printing its value whenever it changes. +This automatically binds the `uCount` state and <HT>`UseEffect`</HT> hook to the <HT>`CountController`</HT> instance, demonstrates how Reactter handles the binding and reactivity seamlessly. :::caution -If the state is declared lazily (e.g., using <HK>`late`</HK> keyword), it will not be automatically bound to the dependency. In such cases, you need to use the <HT>`Rt.lazyState`</HT> method to bind the state to the dependency(See [Lazy state binding](#lazy-state-binding)). +If the state is declared lazily (e.g., using <HK>`late`</HK> keyword), it will not be automatically bound to the dependency. +In such cases, you need to use the <HT>`Rt.lazyState`</HT> method to bind the state to the dependency(See [lazy binding](#lazy-binding)). ::: -### Lazy state binding +### Lazy binding -When a state is declared lazily, it is not automatically bound to the dependency. In such cases, you can use the <HT>[`Rt.lazyState`](/reactter/api/methods/state_management_methods/#rtlazystate)</HT> method to bind the state to the dependency, e.g.: +When a state is declared lazily, it is not automatically bound to the dependency. +In such cases, you can use the <HT>[`Rt.lazyState`](/reactter/api/methods/state_management_methods/#rtlazystate)</HT> method to bind the state to the dependency, e.g.: ```dart title="count_controller.dart" "UseState" "Rt.lazyState" import "package:reactter/reactter.dart"; @@ -66,11 +71,14 @@ class CountController { } ``` -In the example above, the `uCount` state is declared lazily using the <HK>`late`</HK> keyword. To bind the state to the <HT>`CountController`</HT> instance, the <HT>[`Rt.lazyState`](/reactter/api/methods/state_management_methods/#rtlazystate)</HT> method is used, passing the state creation function and the dependency instance as arguments. This ensures that when `uCount` is accessed, it will be automatically bound to the <HT>`CountController`</HT> instance, e.g.: +In the example above, the `uCount` state is declared lazily using the <HK>`late`</HK> keyword. +To bind the state to the <HT>`CountController`</HT> instance, the <HT>[`Rt.lazyState`](/reactter/api/methods/state_management_methods/#rtlazystate)</HT> method is used, passing the state creation function and the dependency instance as arguments. +This ensures that when `uCount` is accessed, it will be automatically bound to the <HT>`CountController`</HT> instance. ### Manual binding -While automatic binding simplifies state management, there may be scenarios where you need to manually bind the state to a dependency. Manual binding provides greater control over how and when the state is associated with the dependency. +While automatic binding simplifies state management, there may be scenarios where you need to manually bind the state to a dependency. +Manual binding provides greater control over how and when the state is associated with the dependency. To manually bind a state to a dependency, you need to explicitly link the state within the dependency using the <HM>`bind`</HM> method of the state, e.g.: @@ -86,15 +94,19 @@ class CountController { } ``` -In the example above, the `uCount` state is declared lazily using the <HK>`late`</HK> keyword. To manually bind the state to the <HT>`CountController`</HT> instance, the <HM>`bind`</HM> method is called within the constructor, passing the dependency instance as an argument. This ensures that the `uCount` state is associated with the <HT>`CountController`</HT> instance, e.g.: +In the example above, the `uCount` state is declared lazily using the <HK>`late`</HK> keyword. +To manually bind the state to the <HT>`CountController`</HT> instance, the <HM>`bind`</HM> method is called within the constructor, passing the dependency instance as an argument. +This ensures that the `uCount` state is associated with the <HT>`CountController`</HT> instance, e.g.: :::note -[Manual binding](#manual-binding) and [lazy state binding](#lazy-state-binding) serve different purposes. **Manual binding** is useful when you need precise control over the state binding process, while **Lazy state binding** is suitable when you want the state to be initialized only when it is first accessed. Choose the appropriate approach based on your specific requirements. +[Manual binding](#manual-binding) and [lazy state binding](#lazy-state-binding) serve different purposes. +**Manual binding** is useful when you need precise control over the state binding process, while **lazy binding** is suitable when you want the state to be initialized only when it is first accessed. +Choose the appropriate approach based on your specific requirements. ::: ### Automatic unbinding -When a dependency is deleted, the associated state is automatically disposed of, ensuring that resources are released efficiently. +When a dependency is deleted, the associated state is automatically disposed of. This automatic unbinding mechanism simplifies state management and reduces the risk of memory leaks or resource wastage. In the example below, the `uCount` state is automatically disposed of when the <HT>`CountController`</HT> instance is deleted, ensuring that resources are released efficiently: @@ -137,7 +149,8 @@ class CountController { While manual unbinding provides greater control, improper use can lead to memory management issues and increase the risk of errors. It is recommended to use [automatic unbinding](#automatic-unbinding) whenever possible, as it simplifies the process and reduces the likelihood of introducing memory leaks or failing to release resources properly. -Manual unbinding should be used cautiously and only when absolutely necessary. Additionally, developers must ensure that they keep track of the unbinding process to avoid leaving unused states in memory, which could negatively affect performance and resource utilization. +Manual unbinding should be used cautiously and only when absolutely necessary. +Additionally, developers must ensure that they keep track of the unbinding process to avoid leaving unused states in memory, which could negatively affect performance and resource utilization. ::: diff --git a/website/src/content/docs/extra_topics/updating_objects_in_state.mdx b/website/src/content/docs/extra_topics/updating_objects_in_state.mdx index b8633e97..1a0f2201 100644 --- a/website/src/content/docs/extra_topics/updating_objects_in_state.mdx +++ b/website/src/content/docs/extra_topics/updating_objects_in_state.mdx @@ -4,10 +4,10 @@ description: Updating objects in state --- import { HM, HN, HS, HT } from '@/components/Highlight'; -States(<HT>`RtState`</HT>) like <HT>`Signal`</HT>, <HT>`UseState`</HT>, <HT>`UseAsyncState`</HT>, <HT>`UseReducer`</HT> and <HT>`UseCompute`</HT> can hold any type of Dart value, including objects. -However, you should not modify objects held in the Reactter state directly, -when you need to update an object, create a new one or make a copy of the existing object, -and then set the state to use this new or copied object. +A Reactter state(<HT>`RtState`</HT>) such as <HT>`Signal`</HT>, <HT>`UseState`</HT>, <HT>`UseAsyncState`</HT>, <HT>`UseReducer`</HT> and <HT>`UseCompute`</HT> can hold any type of Dart value, including objects. +However, you should never modify objects held in a Reactter state directly. + +In this guide, you'll learn how to safely and effectively update objects in a Reactter state. ## What is a mutation? @@ -41,7 +41,7 @@ class User { final user = Signal(User(name: "Jane Doe", age: 25)); ``` -Technically, it is possible to change the contents of the object itself. This is called a mutation: +Technically, it is possible to change the contents of the object itself. This is called a **mutation**: ```dart showLineNumbers=false user.value.name = "John Doe"; @@ -54,7 +54,7 @@ Instead of mutating them, you should always replace them. In other words, you should treat any Dart object that you put into state as read-only. -This example holds an object in state to represent the user. +The following example holds an object in state to represent the user. The user's name is changed from <HS>`"Jane Doe"`</HS> to <HS>`"John Doe"`</HS> when you click the button. But the user's name stays the same. @@ -135,7 +135,7 @@ user.value = User(name: user.value.name, age: 30); This is a common pattern when working with objects in state. However, creating a new object every time you want to change a property can be cumbersome. -To simplify this process, you can add the following method to the <HT>`User`</HT> class: +To simplify this process, you can add the following method(<HM>`copyWith`</HM>) to the <HT>`User`</HT> class: ```dart collapse={2-6} ins="final" ins={7-13} mark={2-3} mark="copyWith" class User { diff --git a/website/src/content/docs/overview.mdx b/website/src/content/docs/overview.mdx index da987325..99c399fe 100644 --- a/website/src/content/docs/overview.mdx +++ b/website/src/content/docs/overview.mdx @@ -10,7 +10,7 @@ This has led to a wide variety of state management packages, each with its own s **Reactter** was born out of a desire to create something different, a state management solution that doesn't just solve problems but elevates the entire development experience. Inspired by the simplicity and reactivity of ReactJS, Reactter brings a fresh, modern approach to Dart and Flutter development. -It's not just another state management package, it's a comprehensive framework designed to make your app faster, your code cleaner, and your life as a developer easier. +It's not just another state management package, it's a powerful tool designed to make your app faster, your code cleaner, and your life as a developer easier. What sets Reactter apart? While other packages focus solely on state management, Reactter goes further by seamlessly integrating **dependency injection**, **event handling**, and **rendering control** into a single, cohesive framework. It's lightweight yet powerful, simple yet flexible, and designed to work seamlessly with any architecture or pattern.
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
- - Dart only  - - Reactter - - - -Add the package on your project. - -- Using command: - - ```shell - dart pub add reactter - ``` - -- Or put directly into `pubspec.yaml` file: - - ```yaml - dependencies: - reactter: #add version here - ``` - - and run `dart pub get`. - -Now in your Dart code, you can use: - -```dart -import 'package:reactter/reactter.dart'; -``` - -